Merge branch 'main' of github.com:wavetermdev/waveterm into red/aichat-sidebar

This commit is contained in:
Red Adaya
2024-05-01 09:42:28 +08:00
42 changed files with 2587 additions and 21888 deletions
+34 -14
View File
@@ -1,24 +1,28 @@
name: TestDriver.ai Regression Testing (test)
name: TestDriver.ai Regression Testing
on:
push:
branches: ["main"]
branches:
- main
pull_request:
branches: ["main"]
branches:
- main
schedule:
- cron: "0 21 * * *" # every day at 9pm
workflow_dispatch:
- cron: 0 21 * * *
workflow_dispatch: null
permissions:
contents: read # To allow the action to read repository contents
pull-requests: write # To allow the action to create/update pull request comments
jobs:
test:
name: "TestDriver"
name: TestDriver
runs-on: ubuntu-latest
steps:
- uses: dashcamio/testdriver@main
id: testdriver
# note that .testdriver/prerun.sh runs before this, so the app has launched already
with:
version: v2.9.4
version: v2.10.2
prerun: |
rm ~/Desktop/WITH-LOVE-FROM-AMERICA.txt
cd ~/actions-runner/_work/testdriver/testdriver/
@@ -38,8 +42,24 @@ jobs:
echo "Electron Done"
exit
prompt: |
2. click "Create new tab"
2. focus the Wave input with the keyboard shorcut Command + I
3. type 'ls' into the input
4. press return
5. validate Wave shows the result of 'ls'
1. wait 10 seconds
1. click "Continue"
1. click "Create new tab"
1. validate that overlapping text does not appear in the application
1. focus the Wave input with the keyboard shorcut Command + I
1. type 'ls' into the input
1. press return
1. validate Wave shows the result of 'ls'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: peter-evans/create-or-update-comment@v4
if: ${{always()}}
with:
issue-number: ${{ github.event.pull_request.number }}
body: |
## TestDriver Summary
${{ steps.testdriver.outputs.markdown }}
${{ steps.testdriver.outputs.summary }}
reactions: |
+1
-1
+6
View File
@@ -9,6 +9,8 @@
# Wave Terminal
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fwavetermdev%2Fwaveterm.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fwavetermdev%2Fwaveterm?ref=badge_shield)
Wave is an open-source AI-native terminal built for seamless workflows.
Wave isn't just another terminal emulator; it's a rethink on how terminals are built. Wave combines command line with the power of the open web to help veteran CLI users and new developers alike.
@@ -56,3 +58,7 @@ Find more information in our [Contributions Guide](CONTRIBUTING.md), which inclu
- [Ways to contribute](CONTRIBUTING.md#contributing-to-wave-terminal)
- [Contribution guidelines](CONTRIBUTING.md#before-you-start)
## License
Wave Terminal is licensed under the Apache-2.0 License. For more information on our dependencies, see [here](./acknowledgements/README.md).
+2 -17
View File
@@ -1,20 +1,5 @@
# Open-Source Acknowledgements
We make use of many amazing open-source projects to build Wave Terminal. Here are the links to the latest acknowledgements for each of our components, including license disclaimers for each dependency:
We make use of many amazing open-source projects to build Wave Terminal. We automatically generate license reports via FOSSA to comply with the license distribution requirements of our dependencies. Below is a summary of the licenses used by our product. Clicking on the image will take you to the full report on FOSSA's website.
- [Frontend](./disclaimers/frontend.md)
- [Backend](./disclaimers/backend.md)
## Generating license disclaimers
The license disclaimers for the backend are generated using the [go-licenses](https://github.com/google/go-licenses) tool. We supply a template file ([`go_licenses_report.tpl`](./go_licenses_report.tpl)) to generate a pretty print of the disclaimers for each dependency. This outputs to the file [`backend.md`](./disclaimers/backend.md).
The license disclaimers for the frontend are generated using the [`yarn licenses` tool](https://classic.yarnpkg.com/lang/en/docs/cli/licenses/). This outputs to the file [`frontend.md`](./disclaimers/frontend.md).
These disclaimer files linked above will be periodically regenerated to reflect new dependencies.
The [`scripthaus.md` file](../scripthaus.md) contains scripts to generate the disclaimers and package them. To manually generate the disclaimers, run the following from the repository root directory:
```bash
scripthaus run generate-license-disclaimers
```
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fwavetermdev%2Fwaveterm.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fwavetermdev%2Fwaveterm?ref=badge_large)
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
-16
View File
@@ -1,16 +0,0 @@
# Wave Terminal Backend Acknowledgements
The following sets forth attribution notices for third party software that may be contained in portions of the Wave Terminal product.
{{ range . }}
## {{ .Name }}
* Name: {{ .Name }}
* Version: {{ .Version }}
* License: [{{ .LicenseName }}]({{ .LicenseURL }})
```txt
{{ .LicenseText }}
```
-----
{{ end }}
+1 -1
View File
@@ -156,7 +156,7 @@
--table-border-color: rgba(241, 246, 243, 0.15);
--table-thead-border-top-color: rgba(250, 250, 250, 0.1);
--table-thead-bright-border-color: rgb(204, 204, 204);
--table-thead-bg-color: rgba(250, 250, 250, 0.02);
--table-thead-bg-color: rgb(5, 5, 5);
--table-tr-border-bottom-color: rgba(241, 246, 243, 0.15);
--table-tr-hover-bg-color: rgba(255, 255, 255, 0.06);
--table-tr-selected-bg-color: rgb(34, 34, 34);
+6 -2
View File
@@ -9,8 +9,8 @@
--app-accent-color: rgb(75, 166, 57);
--app-accent-bg-color: rgba(75, 166, 57, 0.2);
--app-text-color: rgb(0, 0, 0);
--app-text-primary-color: rgb(0, 0, 0, 0.9);
--app-text-secondary-color: rgb(0, 0, 0, 0.7);
--app-text-primary-color: rgb(23, 23, 23);
--app-text-secondary-color: rgb(76, 76, 76);
--app-border-color: rgb(139 145 138);
--app-panel-bg-color: rgb(224, 224, 224);
--app-panel-bg-color-dev: rgb(224, 224, 224);
@@ -57,6 +57,10 @@
--scrollbar-thumb-hover-color: rgba(0, 0, 0, 0.4);
--scrollbar-thumb-active-color: rgba(0, 0, 0, 0.5);
/* table colors */
--table-thead-bg-color: rgb(253, 253, 253);
--table-tr-selected-bg-color: rgb(216, 216, 216);
/* line color */
--line-actions-bg-color: rgba(0, 0, 0, 0.1);
--line-actions-inactive-color: rgba(0, 0, 0, 0.3);
+2 -1
View File
@@ -1,6 +1,7 @@
.clientsettings-view {
.content {
padding: 14px 18px 0 30px;
padding: 14px 18px 14px 30px;
overflow-y: scroll;
}
.wave-dropdown {
+7 -1
View File
@@ -14,6 +14,7 @@ import * as appconst from "@/app/appconst";
import "./clientsettings.less";
import { MainView } from "../common/elements/mainview";
import { OverlayScrollbarsComponent } from "overlayscrollbars-react";
class ClientSettingsKeybindings extends React.Component<{}, {}> {
componentDidMount() {
@@ -251,7 +252,12 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
const curSudoPwClearOnSleep = GlobalModel.getSudoPwClearOnSleep();
return (
<MainView className="clientsettings-view" title="Client Settings" onClose={this.handleClose}>
<MainView
className="clientsettings-view"
title="Client Settings"
onClose={this.handleClose}
scrollable={true}
>
<If condition={!isHidden}>
<ClientSettingsKeybindings></ClientSettingsKeybindings>
</If>
+2 -1
View File
@@ -40,7 +40,8 @@
}
.mainview-content {
padding-top: 14px;
display: flex;
flex-direction: column;
}
}
+19 -1
View File
@@ -7,6 +7,8 @@ import cn from "classnames";
import { GlobalModel } from "@/models";
import "./mainview.less";
import { Choose, If, Otherwise, When } from "tsx-control-statements/components";
import { OverlayScrollbarsComponent } from "overlayscrollbars-react";
@mobxReact.observer
class MainView extends React.Component<{
@@ -14,6 +16,8 @@ class MainView extends React.Component<{
onClose: () => void;
children: React.ReactNode;
className?: string;
scrollable?: boolean;
onScrollbarInitialized?: () => void;
}> {
render() {
const sidebarModel = GlobalModel.mainSidebarModel;
@@ -31,7 +35,21 @@ class MainView extends React.Component<{
</div>
</header>
</div>
<div className="mainview-content">{this.props.children}</div>
<Choose>
<When condition={this.props.scrollable}>
<OverlayScrollbarsComponent
className="mainview-content"
options={{ scrollbars: { autoHide: "leave" } }}
defer={true}
events={{ initialized: this.props.onScrollbarInitialized }}
>
{this.props.children}
</OverlayScrollbarsComponent>
</When>
<Otherwise>
<div className="mainview-content">{this.props.children}</div>
</Otherwise>
</Choose>
</div>
);
}
+1
View File
@@ -52,6 +52,7 @@
}
button {
padding-right: 4px !important;
i {
font-size: 18px;
}
+1 -1
View File
@@ -24,7 +24,7 @@ const ModalHeader: React.FC<ModalHeaderProps> = ({ onClose, keybindings, title }
</If>
{<div className="wave-modal-title">{title}</div>}
<If condition={onClose}>
<Button className="secondary ghost" onClick={onClose}>
<Button className="secondary ghost" onClick={() => onClose()} title="Close (ESC)">
<i className="fa-sharp fa-solid fa-xmark"></i>
</Button>
</If>
@@ -1,25 +1,24 @@
.rconndetail-modal {
width: auto;
max-width: 80vw;
min-height: 565px;
.wave-modal-content {
display: flex;
padding-bottom: 0px;
flex-direction: column;
align-items: center;
gap: 20px;
flex-shrink: 0;
width: auto;
max-width: 80vw;
max-height: 90vh;
.wave-modal-body {
display: flex;
padding: 0px 20px;
padding: 20px;
align-items: flex-start;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
gap: 16px;
align-self: stretch;
overflow-y: scroll;
.name-header-actions-wrapper {
display: flex;
@@ -53,6 +52,7 @@
.remote-detail {
width: 100%;
padding-top: 16px;
.settings-field {
display: flex;
+22 -29
View File
@@ -15,6 +15,7 @@ import * as appconst from "@/app/appconst";
import "./viewremoteconndetail.less";
import { ModalKeybindings } from "../elements/modal";
import { OverlayScrollbarsComponent } from "overlayscrollbars-react";
@mobxReact.observer
class ViewRemoteConnDetailModal extends React.Component<{}, {}> {
@@ -299,15 +300,33 @@ class ViewRemoteConnDetailModal extends React.Component<{}, {}> {
return (
<Modal className="rconndetail-modal">
<ModalKeybindings
onOk={() => {
if (selectedRemoteStatus == "connecting") {
return;
}
this.handleClose();
}}
onCancel={() => {
if (selectedRemoteStatus == "connecting") {
return;
}
this.handleClose();
}}
></ModalKeybindings>
<Modal.Header title="Connection" onClose={this.handleClose} />
<div className="wave-modal-body">
<OverlayScrollbarsComponent
className="wave-modal-body"
options={{ scrollbars: { autoHide: "leave" } }}
defer={true}
>
<div className="name-header-actions-wrapper">
<div className="name text-primary name-wrapper">
{util.getRemoteName(remote)}&nbsp; {getImportTooltip(remote)}
</div>
<div className="header-actions">{this.renderHeaderBtns(remote)}</div>
</div>
<div className="remote-detail" style={{ overflow: "hidden" }}>
<div className="remote-detail">
<div className="settings-field">
<div className="settings-label">Conn Id</div>
<div className="settings-input">{remote.remoteid}</div>
@@ -381,33 +400,7 @@ class ViewRemoteConnDetailModal extends React.Component<{}, {}> {
></div>
</div>
</div>
</div>
<div className="wave-modal-footer">
<ModalKeybindings
onOk={() => {
if (selectedRemoteStatus == "connecting") {
return;
}
this.handleClose();
}}
onCancel={() => {
if (selectedRemoteStatus == "connecting") {
return;
}
this.handleClose();
}}
></ModalKeybindings>
<Button
className="secondary"
disabled={selectedRemoteStatus == "connecting"}
onClick={this.handleClose}
>
Cancel
</Button>
<Button disabled={selectedRemoteStatus == "connecting"} onClick={this.handleClose}>
Done
</Button>
</div>
</OverlayScrollbarsComponent>
</Modal>
);
}
+17 -2
View File
@@ -9,10 +9,20 @@
margin: 20px 50px 20px 20px;
}
.connections-table-container {
display: flex;
flex-direction: column;
flex-grow: 1;
overflow-y: scroll;
max-height: 85vh;
width: fit-content;
max-width: 970px;
}
.connections-table {
margin: 0px 10px 10px 10px;
table-layout: fixed;
max-width: 970px;
position: relative;
colgroup {
.first-col {
@@ -28,13 +38,16 @@
thead {
border-radius: var(--sizing-2-xs, 4px);
border-bottom: 2px solid var(--table-thead-bright-border-color);
user-select: none;
th {
position: sticky;
top: 0;
height: 32px;
padding: 5px 15px 5px 10px;
color: var(--app-text-color);
border-bottom: 2px solid var(--table-thead-bright-border-color);
background: var(--table-thead-bg-color);
}
}
@@ -78,8 +91,10 @@
footer {
margin-left: 10px;
margin-top: 10px;
display: flex;
flex-direction: row;
flex-shrink: 0;
gap: 8px;
}
+56 -49
View File
@@ -13,6 +13,7 @@ import * as util from "@/util/util";
import "./connections.less";
import { MainView } from "../common/elements/mainview";
import { OverlayScrollbarsComponent } from "overlayscrollbars-react";
class ConnectionsKeybindings extends React.Component<{}, {}> {
componentDidMount() {
@@ -149,57 +150,63 @@ class ConnectionsView extends React.Component<{ model: RemotesModel }, { hovered
<If condition={!isHidden}>
<ConnectionsKeybindings></ConnectionsKeybindings>
</If>
<table
className="connections-table"
cellSpacing="0"
cellPadding="0"
border={0}
ref={this.tableRef}
onMouseLeave={this.handleTableHoverLeave}
<OverlayScrollbarsComponent
className="connections-table-container"
options={{ scrollbars: { autoHide: "leave" } }}
defer={true}
>
<colgroup>
<col className="first-col" />
<col className="second-col" />
<col className="third-col" />
</colgroup>
<thead>
<tr>
<th className="text-standard col-name">
<div>Name</div>
</th>
<th className="text-standard col-type">
<div>Type</div>
</th>
<th className="text-standard col-status">
<div>Status</div>
</th>
</tr>
</thead>
<tbody>
<For index="idx" each="item" of={items}>
<tr
key={item.remoteid}
className={cn("connections-item", {
hovered: this.state.hoveredItemId === item.remoteid,
})}
onClick={() => this.handleRead(item.remoteid)} // Moved onClick here
>
<td className="col-name">
<Status status={this.getStatus(item.status)} text=""></Status>
{this.getName(item)}&nbsp;{this.getImportSymbol(item)}
</td>
<td className="col-type">
<div>{item.remotetype}</div>
</td>
<td className="col-status">
<div>
<Status status={this.getStatus(item.status)} text={item.status} />
</div>
</td>
<table
className="connections-table"
cellSpacing="0"
cellPadding="0"
border={0}
ref={this.tableRef}
onMouseLeave={this.handleTableHoverLeave}
>
<colgroup>
<col className="first-col" />
<col className="second-col" />
<col className="third-col" />
</colgroup>
<thead>
<tr>
<th className="text-standard col-name">
<div>Name</div>
</th>
<th className="text-standard col-type">
<div>Type</div>
</th>
<th className="text-standard col-status">
<div>Status</div>
</th>
</tr>
</For>
</tbody>
</table>
</thead>
<tbody>
<For index="idx" each="item" of={items}>
<tr
key={item.remoteid}
className={cn("connections-item", {
hovered: this.state.hoveredItemId === item.remoteid,
})}
onClick={() => this.handleRead(item.remoteid)} // Moved onClick here
>
<td className="col-name">
<Status status={this.getStatus(item.status)} text=""></Status>
{this.getName(item)}&nbsp;{this.getImportSymbol(item)}
</td>
<td className="col-type">
<div>{item.remotetype}</div>
</td>
<td className="col-status">
<div>
<Status status={this.getStatus(item.status)} text={item.status} />
</div>
</td>
</tr>
</For>
</tbody>
</table>
</OverlayScrollbarsComponent>
<footer>
<Button
className="secondary"
+2 -2
View File
@@ -260,8 +260,8 @@ class MainSideBar extends React.Component<MainSideBarProps, {}> {
<div className="logo">
<WaveLogo />
</div>
<div className="close-button">
<i className="fa-sharp fa-solid fa-xmark-large" onClick={toggleCollapse} />
<div className="close-button" onClick={toggleCollapse}>
<i className="fa-sharp fa-solid fa-xmark-large" />
</div>
</div>
<div className="contents">
+19 -8
View File
@@ -61,9 +61,11 @@ class AIChat extends React.Component<{}, {}> {
constructor(props: any) {
super(props);
mobx.makeObservable(this);
this.chatWindowScrollRef = React.createRef();
this.textAreaRef = React.createRef();
}
componentDidMount() {
const inputModel = GlobalModel.inputModel;
if (this.chatWindowScrollRef?.current != null) {
@@ -88,7 +90,7 @@ class AIChat extends React.Component<{}, {}> {
}
submitChatMessage(messageStr: string) {
const curLine = GlobalModel.inputModel.getCurLine();
const curLine = GlobalModel.inputModel.curLine;
const prtn = GlobalModel.submitChatInfoCommand(messageStr, curLine, false);
prtn.then((rtn) => {
if (!rtn.success) {
@@ -103,15 +105,19 @@ class AIChat extends React.Component<{}, {}> {
return { numLines, linePos };
}
@mobx.action.bound
onTextAreaFocused(e: any) {
GlobalModel.inputModel.setAuxViewFocus(true);
this.onTextAreaChange(e);
}
@mobx.action.bound
onTextAreaBlur(e: any) {
GlobalModel.inputModel.setAuxViewFocus(false);
//GlobalModel.inputModel.setAuxViewFocus(false);
}
// Adjust the height of the textarea to fit the text
@boundMethod
onTextAreaChange(e: any) {
// Calculate the bounding height of the text area
const textAreaMaxLines = 4;
@@ -126,6 +132,9 @@ class AIChat extends React.Component<{}, {}> {
// Set the new height of the text area, bounded by the min and max height.
const newHeight = Math.min(Math.max(scrollHeight, textAreaMinHeight), textAreaMaxHeight);
this.textAreaRef.current.style.height = newHeight + "px";
}
onTextAreaInput(e: any) {
GlobalModel.inputModel.codeSelectDeselectAll();
}
@@ -140,8 +149,10 @@ class AIChat extends React.Component<{}, {}> {
this.submitChatMessage(messageStr);
currentRef.value = "";
} else {
inputModel.grabCodeSelectSelection();
inputModel.setAuxViewFocus(false);
mobx.action(() => {
inputModel.grabCodeSelectSelection();
inputModel.setAuxViewFocus(false);
})();
}
}
@@ -182,7 +193,6 @@ class AIChat extends React.Component<{}, {}> {
return true;
}
@mobx.action
@boundMethod
onKeyDown(e: any) {}
@@ -254,9 +264,10 @@ class AIChat extends React.Component<{}, {}> {
autoComplete="off"
autoCorrect="off"
id="chat-cmd-input"
onFocus={this.onTextAreaFocused.bind(this)}
onBlur={this.onTextAreaBlur.bind(this)}
onChange={this.onTextAreaChange.bind(this)}
onFocus={this.onTextAreaFocused}
onBlur={this.onTextAreaBlur}
onChange={this.onTextAreaChange}
onInput={this.onTextAreaInput}
onKeyDown={this.onKeyDown}
style={{ fontSize: this.termFontSize }}
className="chat-textarea"

Some files were not shown because too many files have changed in this diff Show More