mirror of
https://github.com/wavetermdev/backup.git
synced 2026-04-22 15:26:58 -07:00
Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f0dd9b8ad7 | |||
| a3192cad45 | |||
| 3c8ad9ef14 | |||
| 380f8612b0 | |||
| b7f6dbe392 | |||
| c81b07b9d7 | |||
| 89b383df7d | |||
| c811e5af52 | |||
| 1b44764074 | |||
| 19a336b978 | |||
| 894e6c152e | |||
| 2c3dab6ee9 | |||
| 595102fefa | |||
| 63526fbe8a | |||
| f2faa6b735 | |||
| b0f8de5331 | |||
| 3c81eae8af | |||
| f8269b7495 | |||
| fbf4915210 | |||
| d0c0a7fd96 | |||
| 3d72e97831 | |||
| d54a61658f | |||
| e38c5bf16a | |||
| 56f44151df | |||
| 936537a03a | |||
| 66310e8bfb | |||
| 2361154551 | |||
| 7888a61766 | |||
| 29ad232ac6 | |||
| 067bef0cb5 | |||
| 97deb2ef14 | |||
| 69d58ef525 | |||
| d7173c970c | |||
| 1234919229 | |||
| 652250ac3c | |||
| 11b110d633 | |||
| 14b38b31c8 | |||
| 1ace947d1e | |||
| 13f4203437 | |||
| cd15beba26 | |||
| aac9e27382 | |||
| d07068c09d | |||
| 1307a77b56 | |||
| 2772c76728 | |||
| 4384525ca6 | |||
| d6b82e3792 |
@@ -16,6 +16,10 @@ jobs:
|
||||
arch: "amd64"
|
||||
runner: "ubuntu-latest"
|
||||
scripthaus: "build-package-linux"
|
||||
- platform: "linux"
|
||||
arch: "arm64"
|
||||
runner: ubuntu-24.04-arm64-16core
|
||||
scripthaus: "build-package-linux"
|
||||
runs-on: ${{ matrix.runner }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -28,6 +32,8 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends -y libarchive-tools libopenjp2-tools rpm
|
||||
- name: Install FPM # The version of FPM that comes bundled with electron-builder doesn't include a Linux ARM target. Installing Gems onto the runner is super quick so we'll just do this for all targets.
|
||||
run: sudo gem install fpm
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{env.GO_VERSION}}
|
||||
@@ -58,6 +64,7 @@ jobs:
|
||||
run: scripthaus run ${{ matrix.scripthaus }}
|
||||
env:
|
||||
GOARCH: ${{ matrix.arch }}
|
||||
USE_SYSTEM_FPM: true # Ensure that the installed version of FPM is used rather than the bundled one.
|
||||
CSC_LINK: ${{ matrix.platform == 'darwin' && secrets.PROD_MACOS_CERTIFICATE}}
|
||||
CSC_KEY_PASSWORD: ${{ matrix.platform == 'darwin' && secrets.PROD_MACOS_CERTIFICATE_PWD }}
|
||||
APPLE_ID: ${{ matrix.platform == 'darwin' && secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }}
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
name: TestDriver.ai Regression Testing
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
schedule:
|
||||
- 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
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dashcamio/testdriver@main
|
||||
id: testdriver
|
||||
with:
|
||||
version: v2.10.2
|
||||
prerun: |
|
||||
rm ~/Desktop/WITH-LOVE-FROM-AMERICA.txt
|
||||
cd ~/actions-runner/_work/testdriver/testdriver/
|
||||
brew install go
|
||||
brew tap scripthaus-dev/scripthaus
|
||||
brew install corepack
|
||||
brew install scripthaus
|
||||
corepack enable
|
||||
yarn install
|
||||
scripthaus run build-backend
|
||||
echo "Yarn"
|
||||
yarn
|
||||
echo "Rebuild"
|
||||
scripthaus run electron-rebuild
|
||||
echo "Webpack"
|
||||
scripthaus run webpack-build
|
||||
echo "Starting Electron"
|
||||
scripthaus run electron 1>/dev/null 2>&1 &
|
||||
echo "Electron Done"
|
||||
exit
|
||||
prompt: |
|
||||
1. wait 10 seconds
|
||||
1. click "Get Started"
|
||||
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
|
||||
@@ -0,0 +1,49 @@
|
||||
name: TestDriver.ai Regression Testing - Waveterm
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
schedule:
|
||||
- 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"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dashcamio/testdriver@main
|
||||
id: testdriver
|
||||
with:
|
||||
key: ${{secrets.DASHCAM_API}}
|
||||
os: mac
|
||||
prerun: |
|
||||
cd ~/actions-runner/_work/testdriver/testdriver/
|
||||
brew install go
|
||||
brew tap scripthaus-dev/scripthaus
|
||||
brew install corepack
|
||||
brew install scripthaus
|
||||
corepack enable
|
||||
yarn install
|
||||
scripthaus run build-backend
|
||||
echo "Yarn"
|
||||
yarn
|
||||
echo "Rebuild"
|
||||
scripthaus run electron-rebuild
|
||||
echo "Webpack"
|
||||
scripthaus run webpack-build
|
||||
echo "Starting Electron"
|
||||
scripthaus run electron 1>/dev/null 2>&1 &
|
||||
echo "Electron Done"
|
||||
npm install testdriverai -g
|
||||
npm install dashcam -g
|
||||
exit
|
||||
prompt: |
|
||||
1. /run testdriver/theme.yml
|
||||
@@ -1,18 +0,0 @@
|
||||
rm ~/Desktop/WITH-LOVE-FROM-AMERICA.txt
|
||||
cd ~/actions-runner/_work/testdriver/testdriver/
|
||||
brew install go
|
||||
brew tap scripthaus-dev/scripthaus
|
||||
brew install scripthaus
|
||||
corepack enable
|
||||
yarn install
|
||||
scripthaus run build-backend
|
||||
echo "Yarn"
|
||||
yarn
|
||||
echo "Rebuild"
|
||||
scripthaus run electron-rebuild
|
||||
echo "Webpack"
|
||||
scripthaus run webpack-build
|
||||
echo "Starting Electron"
|
||||
scripthaus run electron 1>/dev/null 2>&1 &
|
||||
echo "Electron Done"
|
||||
exit
|
||||
@@ -285,10 +285,6 @@
|
||||
"keys": ["Ctrl:r"],
|
||||
"commandStr": ["/history"]
|
||||
},
|
||||
{
|
||||
"command": "cmdinput:openAIChat",
|
||||
"keys": ["Ctrl:Space"]
|
||||
},
|
||||
{
|
||||
"command": "history:closeHistory",
|
||||
"keys": ["Ctrl:g", "Ctrl:c"]
|
||||
@@ -332,5 +328,9 @@
|
||||
{
|
||||
"command": "codeedit:togglePreview",
|
||||
"keys": ["Cmd:p"]
|
||||
},
|
||||
{
|
||||
"command": "rightsidebar:toggle",
|
||||
"keys": ["Cmd:Shift:Space"]
|
||||
}
|
||||
]
|
||||
+13
-10
@@ -34,20 +34,23 @@ You'll now have to move the built `scripthaus` binary to a directory in your pat
|
||||
sudo cp scripthaus /usr/local/bin
|
||||
```
|
||||
|
||||
## Install nodejs, npm, and yarn
|
||||
## Install nodejs and yarn
|
||||
|
||||
We use [nvm](https://github.com/nvm-sh/nvm) to install nodejs on Linux (you can use an alternate installer if you wish). You must have a relatively recent version of node in order to build the terminal. Different distributions and shells will require different setup instructions. These instructions work for Ubuntu 22 using bash (will install node v20.8.1):
|
||||
You also need a relatively modern nodejs with npm and yarn installed.
|
||||
|
||||
```
|
||||
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash
|
||||
source ~/.bashrc
|
||||
nvm install v20.8.1
|
||||
```
|
||||
Node can be installed from [https://nodejs.org](https://nodejs.org).
|
||||
|
||||
Now we can install yarn:
|
||||
We use Yarn Modern to manage our packages. The recommended way to install Yarn Modern is using Corepack, a new utility shipped by NodeJS that lets you manage your package manager versioning as you would any packages.
|
||||
|
||||
```
|
||||
npm install -g yarn
|
||||
If you installed NodeJS from the official feed (via the website or using NVM), this should come preinstalled. If you use Homebrew or some other feed, you may need to manually install Corepack using `npm install -g corepack`.
|
||||
|
||||
For more information on Corepack, check out [this link](https://yarnpkg.com/corepack).
|
||||
|
||||
Once you've verified that you have Corepack installed, run the following script to set up Yarn for the repository:
|
||||
|
||||
```sh
|
||||
corepack enable
|
||||
yarn install
|
||||
```
|
||||
|
||||
## Clone the Wave Repo
|
||||
|
||||
Generated
+14710
File diff suppressed because it is too large
Load Diff
+54
-50
@@ -6,7 +6,7 @@
|
||||
},
|
||||
"productName": "Wave",
|
||||
"description": "An Open-Source, AI-Native, Terminal Built for Seamless Workflows",
|
||||
"version": "0.7.5",
|
||||
"version": "0.7.6",
|
||||
"main": "dist/emain.js",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
@@ -18,91 +18,95 @@
|
||||
"appId": "dev.commandline.waveterm"
|
||||
},
|
||||
"dependencies": {
|
||||
"@lexical/react": "^0.14.3",
|
||||
"@monaco-editor/react": "^4.5.1",
|
||||
"@lexical/react": "^0.17.0",
|
||||
"@monaco-editor/react": "^4.6.0",
|
||||
"@table-nav/core": "^0.0.7",
|
||||
"@table-nav/react": "^0.0.7",
|
||||
"@tanstack/match-sorter-utils": "^8.8.4",
|
||||
"@tanstack/react-table": "^8.10.3",
|
||||
"@tanstack/match-sorter-utils": "^8.19.4",
|
||||
"@tanstack/react-table": "^8.20.1",
|
||||
"@withfig/autocomplete": "^2.672.0",
|
||||
"autobind-decorator": "^2.4.0",
|
||||
"base64-js": "^1.5.1",
|
||||
"clsx": "^2.1.1",
|
||||
"dayjs": "^1.11.3",
|
||||
"dompurify": "^3.0.2",
|
||||
"electron-squirrel-startup": "^1.0.0",
|
||||
"electron-updater": "^6.1.8",
|
||||
"framer-motion": "^10.16.16",
|
||||
"dayjs": "^1.11.12",
|
||||
"dompurify": "^3.1.6",
|
||||
"electron-squirrel-startup": "^1.0.1",
|
||||
"electron-updater": "^6.3.2",
|
||||
"framer-motion": "^10.18.0",
|
||||
"lexical": "0.14.5",
|
||||
"mobx": "6.12",
|
||||
"mobx-react": "^7.5.0",
|
||||
"mobx": "6.12.5",
|
||||
"mobx-react": "^7.6.0",
|
||||
"monaco-editor": "0.48.0",
|
||||
"mustache": "^4.2.0",
|
||||
"node-fetch": "^3.2.10",
|
||||
"overlayscrollbars": "^2.6.1",
|
||||
"overlayscrollbars-react": "^0.5.5",
|
||||
"node-fetch": "^3.3.2",
|
||||
"overlayscrollbars": "^2.10.0",
|
||||
"overlayscrollbars-react": "^0.5.6",
|
||||
"papaparse": "^5.4.1",
|
||||
"react": "^18.1.0",
|
||||
"react-dom": "^18.1.0",
|
||||
"react-markdown": "^9.0.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-markdown": "^9.0.1",
|
||||
"remark": "^15.0.1",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"sprintf-js": "^1.1.2",
|
||||
"throttle-debounce": "^5.0.0",
|
||||
"sprintf-js": "^1.1.3",
|
||||
"throttle-debounce": "^5.0.2",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"tsx-control-statements": "^5.1.1",
|
||||
"uuid": "^9.0.0",
|
||||
"winston": "^3.8.2",
|
||||
"uuid": "^9.0.1",
|
||||
"winston": "^3.13.1",
|
||||
"xterm": "^5.3.0",
|
||||
"xterm-addon-serialize": "^0.11.0",
|
||||
"xterm-addon-web-links": "^0.9.0",
|
||||
"xterm-addon-webgl": "^0.16.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.17.10",
|
||||
"@babel/core": "^7.18.2",
|
||||
"@babel/cli": "^7.24.8",
|
||||
"@babel/core": "^7.25.2",
|
||||
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||
"@babel/plugin-proposal-decorators": "^7.18.2",
|
||||
"@babel/plugin-proposal-decorators": "^7.24.7",
|
||||
"@babel/plugin-proposal-private-methods": "^7.18.6",
|
||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||
"@babel/plugin-transform-react-jsx": "^7.17.12",
|
||||
"@babel/plugin-transform-runtime": "^7.23.4",
|
||||
"@babel/preset-env": "^7.18.2",
|
||||
"@babel/preset-react": "^7.23.3",
|
||||
"@babel/preset-typescript": "^7.17.12",
|
||||
"@babel/plugin-transform-react-jsx": "^7.25.2",
|
||||
"@babel/plugin-transform-runtime": "^7.24.7",
|
||||
"@babel/preset-env": "^7.25.3",
|
||||
"@babel/preset-react": "^7.24.7",
|
||||
"@babel/preset-typescript": "^7.24.7",
|
||||
"@electron/rebuild": "^3.6.0",
|
||||
"@svgr/webpack": "^8.1.0",
|
||||
"@types/electron": "^1.6.10",
|
||||
"@types/node": "^20.11.0",
|
||||
"@types/papaparse": "^5.3.10",
|
||||
"@types/react": "^18.0.12",
|
||||
"@types/semver": "^7.5.6",
|
||||
"@types/sprintf-js": "^1.1.3",
|
||||
"@types/throttle-debounce": "^5.0.1",
|
||||
"@types/uuid": "^9.0.7",
|
||||
"@types/webpack-env": "^1.18.3",
|
||||
"@types/node": "^22.1.0",
|
||||
"@types/papaparse": "^5.3.14",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/semver": "^7.5.8",
|
||||
"@types/sprintf-js": "^1.1.4",
|
||||
"@types/throttle-debounce": "^5.0.2",
|
||||
"@types/tinycolor2": "^1.4.6",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"@types/webpack-env": "^1.18.5",
|
||||
"@withfig/autocomplete-types": "^1.31.0",
|
||||
"babel-loader": "^9.1.3",
|
||||
"babel-plugin-jsx-control-statements": "^4.1.2",
|
||||
"copy-webpack-plugin": "^12.0.0",
|
||||
"css-loader": "^7.1.0",
|
||||
"electron": "^30.0.1",
|
||||
"copy-webpack-plugin": "^12.0.2",
|
||||
"css-loader": "^7.1.2",
|
||||
"electron": "^31.3.1",
|
||||
"electron-builder": "^24.13.3",
|
||||
"electron-builder-squirrel-windows": "^24.13.3",
|
||||
"electron-builder-squirrel-windows": "25.0.0-alpha.10",
|
||||
"file-loader": "^6.2.0",
|
||||
"http-server": "^14.1.1",
|
||||
"less": "^4.1.2",
|
||||
"less-loader": "^12.0.0",
|
||||
"less": "^4.2.0",
|
||||
"less-loader": "^12.2.0",
|
||||
"lodash-webpack-plugin": "^0.11.6",
|
||||
"mini-css-extract-plugin": "^2.6.0",
|
||||
"mini-css-extract-plugin": "^2.9.0",
|
||||
"prettier": "^2.8.8",
|
||||
"raw-loader": "^4.0.2",
|
||||
"react-split-it": "^2.0.0",
|
||||
"style-loader": "4.0.0",
|
||||
"typescript": "^5.0.0",
|
||||
"webpack": "^5.73.0",
|
||||
"webpack-bundle-analyzer": "^4.10.1",
|
||||
"typescript": "^5.5.4",
|
||||
"webpack": "^5.94.0",
|
||||
"webpack-bundle-analyzer": "^4.10.2",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-dev-server": "^5.0.4",
|
||||
"webpack-merge": "^5.8.0",
|
||||
"yaml": "^2.4.0"
|
||||
"webpack-merge": "^5.10.0",
|
||||
"yaml": "^2.5.0"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "electron-builder install-app-deps"
|
||||
|
||||
@@ -170,6 +170,7 @@
|
||||
--cmdinput-button-bg-color: var(--tab-green);
|
||||
--cmdinput-disabled-bg-color: var(--app-text-bg-disabled-color);
|
||||
--cmdinput-history-bg-color: var(--app-bg-color);
|
||||
--cmdinput-ghost-text-color: rgb(145, 150, 144);
|
||||
|
||||
/* screen view color */
|
||||
--screen-view-text-caption-color: rgb(139, 145, 138);
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
--modal-header-bottom-border-color: rgba(0, 0, 0, 0.3);
|
||||
|
||||
/* cmd input */
|
||||
--cmdinput-ghost-text-color: rgb(116, 116, 116);
|
||||
|
||||
/* scroll colors */
|
||||
--scrollbar-background-color: var(--app-bg-color);
|
||||
|
||||
+16
-9
@@ -31,12 +31,20 @@ class App extends React.Component<{}, {}> {
|
||||
dcWait: OV<boolean> = mobx.observable.box(false, { name: "dcWait" });
|
||||
mainContentRef: React.RefObject<HTMLDivElement> = React.createRef();
|
||||
termThemesLoaded: OV<boolean> = mobx.observable.box(false, { name: "termThemesLoaded" });
|
||||
chatFocusTimeoutId: NodeJS.Timeout = null;
|
||||
|
||||
constructor(props: {}) {
|
||||
super(props);
|
||||
if (GlobalModel.isDev) document.body.classList.add("is-dev");
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.chatFocusTimeoutId) {
|
||||
clearTimeout(this.chatFocusTimeoutId);
|
||||
this.chatFocusTimeoutId = null;
|
||||
}
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleContextMenu(e: any) {
|
||||
let isInNonTermInput = false;
|
||||
@@ -68,16 +76,15 @@ class App extends React.Component<{}, {}> {
|
||||
|
||||
@boundMethod
|
||||
openMainSidebar() {
|
||||
const mainSidebarModel = GlobalModel.mainSidebarModel;
|
||||
const width = mainSidebarModel.getWidth(true);
|
||||
mainSidebarModel.saveState(width, false);
|
||||
GlobalModel.mainSidebarModel.setCollapsed(false);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
openRightSidebar() {
|
||||
const rightSidebarModel = GlobalModel.rightSidebarModel;
|
||||
const width = rightSidebarModel.getWidth(true);
|
||||
rightSidebarModel.saveState(width, false);
|
||||
GlobalModel.rightSidebarModel.setCollapsed(false);
|
||||
this.chatFocusTimeoutId = setTimeout(() => {
|
||||
GlobalModel.inputModel.setChatSidebarFocus();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
@@ -159,13 +166,13 @@ class App extends React.Component<{}, {}> {
|
||||
</div>
|
||||
</div>
|
||||
</If>
|
||||
<If condition={GlobalModel.isDev && rightSidebarCollapsed && activeMainView == "session"}>
|
||||
<div className="right-sidebar-triggers">
|
||||
<If condition={rightSidebarCollapsed && activeMainView == "session"}>
|
||||
<div className="right-sidebar-triggers" title="Open Wave AI (Cmd-Shift-Space)">
|
||||
<Button
|
||||
className="secondary ghost right-sidebar-trigger"
|
||||
onClick={this.openRightSidebar}
|
||||
>
|
||||
<i className="fa-sharp fa-solid fa-sidebar-flip"></i>
|
||||
<i className="fa-sharp fa-regular fa-sparkles"></i>
|
||||
</Button>
|
||||
</div>
|
||||
</If>
|
||||
|
||||
@@ -72,3 +72,4 @@ export const ErrorCode_InvalidCwd = "ERRCWD";
|
||||
export const InputAuxView_History = "history";
|
||||
export const InputAuxView_Info = "info";
|
||||
export const InputAuxView_AIChat = "aichat";
|
||||
export const InputAuxView_Suggestions = "suggestions";
|
||||
|
||||
@@ -11,10 +11,10 @@ import { Toggle, InlineSettingsTextEdit, SettingsError, Dropdown } from "@/commo
|
||||
import { commandRtnHandler, isBlank } from "@/util/util";
|
||||
import { getTermThemes } from "@/util/themeutil";
|
||||
import * as appconst from "@/app/appconst";
|
||||
import { MainView } from "@/common/elements/mainview";
|
||||
import { OverlayScrollbarsComponent } from "overlayscrollbars-react";
|
||||
|
||||
import "./clientsettings.less";
|
||||
import { MainView } from "../common/elements/mainview";
|
||||
import { OverlayScrollbarsComponent } from "overlayscrollbars-react";
|
||||
|
||||
class ClientSettingsKeybindings extends React.Component<{}, {}> {
|
||||
componentDidMount() {
|
||||
@@ -110,6 +110,19 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
|
||||
GlobalModel.getElectronApi().changeAutoUpdate(val);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleChangeAutocompleteEnabled(val: boolean): void {
|
||||
const prtn: Promise<CommandRtnType> = GlobalCommandRunner.setAutocompleteEnabled(val);
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleChangeAutocompleteDebuggingEnabled(val: boolean): void {
|
||||
mobx.action(() => {
|
||||
GlobalModel.autocompleteModel.loggingEnabled = val;
|
||||
})();
|
||||
}
|
||||
|
||||
getFontSizes(): DropdownItem[] {
|
||||
const availableFontSizes: DropdownItem[] = [];
|
||||
for (let s = appconst.MinFontSize; s <= appconst.MaxFontSize; s++) {
|
||||
@@ -447,6 +460,24 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Command Autocomplete</div>
|
||||
<div className="settings-input">
|
||||
<Toggle
|
||||
checked={cdata.clientopts.autocompleteenabled ?? false}
|
||||
onChange={this.handleChangeAutocompleteEnabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Command Autocomplete Debugging</div>
|
||||
<div className="settings-input">
|
||||
<Toggle
|
||||
checked={GlobalModel.autocompleteModel.loggingEnabled}
|
||||
onChange={this.handleChangeAutocompleteDebuggingEnabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<SettingsError errorMessage={this.errorMessage} />
|
||||
</div>
|
||||
</MainView>
|
||||
|
||||
@@ -3,11 +3,13 @@ import { Button } from "./button";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
import * as mobxReact from "mobx-react";
|
||||
import * as mobx from "mobx";
|
||||
import { clsx } from "clsx";
|
||||
|
||||
import "./copybutton.less";
|
||||
|
||||
type CopyButtonProps = {
|
||||
title: string;
|
||||
className?: string;
|
||||
onClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
};
|
||||
|
||||
@@ -34,10 +36,14 @@ class CopyButton extends React.Component<CopyButtonProps, {}> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { title, onClick } = this.props;
|
||||
const { title, className } = this.props;
|
||||
const isCopied = this.isCopied.get();
|
||||
return (
|
||||
<Button onClick={this.handleOnClick} className="copy-button secondary ghost" title={title}>
|
||||
<Button
|
||||
onClick={this.handleOnClick}
|
||||
className={clsx("copy-button secondary ghost", className)}
|
||||
title={title}
|
||||
>
|
||||
{isCopied ? (
|
||||
<i className="fa-sharp fa-solid fa-check"></i>
|
||||
) : (
|
||||
|
||||
@@ -19,3 +19,4 @@ export { TabIcon } from "./tabicon";
|
||||
export { DatePicker } from "./datepicker";
|
||||
export { TermStyleList } from "./termstyle";
|
||||
export { CopyButton } from "./copybutton";
|
||||
export { TypingIndicator } from "./typingindicator";
|
||||
|
||||
@@ -48,28 +48,67 @@
|
||||
|
||||
pre {
|
||||
background-color: var(--markdown-bg-color);
|
||||
margin: 4px 10px 4px 10px;
|
||||
padding: 0.7em;
|
||||
margin: 4px 10px;
|
||||
padding: 0.4em 0.7em;
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
|
||||
code {
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
line-height: normal;
|
||||
line-height: 1.5;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
overflow: auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
pre.selected {
|
||||
outline: 2px solid var(--markdown-outline-color);
|
||||
.codeblock-actions {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
border-radius: 4px;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
background-color: var(--line-actions-bg-color);
|
||||
backdrop-filter: blur(8px);
|
||||
|
||||
i {
|
||||
color: var(--line-actions-inactive-color);
|
||||
|
||||
&:first-child {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--line-actions-active-color);
|
||||
}
|
||||
|
||||
&.fa-check {
|
||||
color: var(--app-success-color);
|
||||
}
|
||||
|
||||
&.fa-square-terminal {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover .codeblock-actions {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
code {
|
||||
color: var(--app-text-color);
|
||||
background-color: var(--markdown-bg-color);
|
||||
font-family: var(--termfontfamily);
|
||||
border-radius: 4px;
|
||||
background-color: var(--markdown-bg-color);
|
||||
padding: 0.15em 0.5em;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
pre.selected {
|
||||
outline: 2px solid var(--markdown-outline-color);
|
||||
}
|
||||
|
||||
.title {
|
||||
@@ -101,6 +140,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.markdown.content {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.markdown > *:first-child {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
@@ -5,14 +5,14 @@ import * as React from "react";
|
||||
import * as mobxReact from "mobx-react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import { CopyButton } from "@/elements";
|
||||
import { clsx } from "clsx";
|
||||
import { GlobalModel } from "@/models";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import * as mobx from "mobx";
|
||||
import { If } from "tsx-control-statements/components";
|
||||
|
||||
import "./markdown.less";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
|
||||
function LinkRenderer(props: any): any {
|
||||
function Link(props: any): JSX.Element {
|
||||
let newUrl = "https://extern?" + encodeURIComponent(props.href);
|
||||
return (
|
||||
<a href={newUrl} target="_blank" rel={"noopener"}>
|
||||
@@ -21,96 +21,86 @@ function LinkRenderer(props: any): any {
|
||||
);
|
||||
}
|
||||
|
||||
function HeaderRenderer(props: any, hnum: number): any {
|
||||
function Header(props: any, hnum: number): JSX.Element {
|
||||
return <div className={clsx("title", "is-" + hnum)}>{props.children}</div>;
|
||||
}
|
||||
|
||||
function CodeRenderer(props: any): any {
|
||||
function Code(props: any): JSX.Element {
|
||||
return <code>{props.children}</code>;
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class CodeBlockMarkdown extends React.Component<
|
||||
{ children: React.ReactNode; codeSelectSelectedIndex?: number; uuid: string },
|
||||
{}
|
||||
> {
|
||||
blockIndex: number;
|
||||
blockRef: React.RefObject<HTMLPreElement>;
|
||||
const CodeBlock = mobxReact.observer(
|
||||
(props: { children: React.ReactNode; onClickExecute?: (cmd: string) => void }): JSX.Element => {
|
||||
const copied: OV<boolean> = mobx.observable.box(false, { name: "copied" });
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.blockRef = React.createRef();
|
||||
this.blockIndex = GlobalModel.inputModel.addCodeBlockToCodeSelect(this.blockRef, this.props.uuid);
|
||||
}
|
||||
|
||||
render() {
|
||||
let clickHandler: (e: React.MouseEvent<HTMLElement>, blockIndex: number) => void;
|
||||
let inputModel = GlobalModel.inputModel;
|
||||
clickHandler = (e: React.MouseEvent<HTMLElement>, blockIndex: number) => {
|
||||
inputModel.setCodeSelectSelectedCodeBlock(blockIndex);
|
||||
const getTextContent = (children: any) => {
|
||||
if (typeof children === "string") {
|
||||
return children;
|
||||
} else if (Array.isArray(children)) {
|
||||
return children.map(getTextContent).join("");
|
||||
} else if (children.props && children.props.children) {
|
||||
return getTextContent(children.props.children);
|
||||
}
|
||||
return "";
|
||||
};
|
||||
let selected = this.blockIndex == this.props.codeSelectSelectedIndex;
|
||||
|
||||
const handleCopy = async (e: React.MouseEvent) => {
|
||||
let textToCopy = getTextContent(props.children);
|
||||
textToCopy = textToCopy.replace(/\n$/, ""); // remove trailing newline
|
||||
await navigator.clipboard.writeText(textToCopy);
|
||||
copied.set(true);
|
||||
setTimeout(() => copied.set(false), 2000); // Reset copied state after 2 seconds
|
||||
};
|
||||
|
||||
const handleExecute = (e: React.MouseEvent) => {
|
||||
let textToCopy = getTextContent(props.children);
|
||||
textToCopy = textToCopy.replace(/\n$/, ""); // remove trailing newline
|
||||
if (props.onClickExecute) {
|
||||
props.onClickExecute(textToCopy);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<pre
|
||||
ref={this.blockRef}
|
||||
className={clsx({ selected: selected })}
|
||||
onClick={(event) => clickHandler(event, this.blockIndex)}
|
||||
>
|
||||
{this.props.children}
|
||||
<pre className="codeblock">
|
||||
{props.children}
|
||||
<div className="codeblock-actions">
|
||||
<CopyButton className="copy-button" onClick={handleCopy} title="Copy" />
|
||||
<If condition={props.onClickExecute}>
|
||||
<i className="fa-regular fa-square-terminal" onClick={handleExecute}></i>
|
||||
</If>
|
||||
</div>
|
||||
</pre>
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@mobxReact.observer
|
||||
class Markdown extends React.Component<
|
||||
{ text: string; style?: any; extraClassName?: string; codeSelect?: boolean },
|
||||
{
|
||||
text: string;
|
||||
style?: any;
|
||||
className?: string;
|
||||
onClickExecute?: (cmd: string) => void;
|
||||
},
|
||||
{}
|
||||
> {
|
||||
curUuid: string;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.curUuid = uuidv4();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
CodeBlockRenderer(props: any, codeSelect: boolean, codeSelectIndex: number, curUuid: string): any {
|
||||
if (codeSelect) {
|
||||
return (
|
||||
<CodeBlockMarkdown codeSelectSelectedIndex={codeSelectIndex} uuid={curUuid}>
|
||||
{props.children}
|
||||
</CodeBlockMarkdown>
|
||||
);
|
||||
} else {
|
||||
const clickHandler = (e: React.MouseEvent<HTMLElement>) => {
|
||||
let blockText = (e.target as HTMLElement).innerText;
|
||||
if (blockText) {
|
||||
blockText = blockText.replace(/\n$/, ""); // remove trailing newline
|
||||
navigator.clipboard.writeText(blockText);
|
||||
}
|
||||
};
|
||||
return <pre onClick={(event) => clickHandler(event)}>{props.children}</pre>;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let text = this.props.text;
|
||||
let codeSelect = this.props.codeSelect;
|
||||
let curCodeSelectIndex = GlobalModel.inputModel.getCodeSelectSelectedIndex();
|
||||
let { text, className, onClickExecute } = this.props;
|
||||
let markdownComponents = {
|
||||
a: LinkRenderer,
|
||||
h1: (props) => HeaderRenderer(props, 1),
|
||||
h2: (props) => HeaderRenderer(props, 2),
|
||||
h3: (props) => HeaderRenderer(props, 3),
|
||||
h4: (props) => HeaderRenderer(props, 4),
|
||||
h5: (props) => HeaderRenderer(props, 5),
|
||||
h6: (props) => HeaderRenderer(props, 6),
|
||||
code: (props) => CodeRenderer(props),
|
||||
pre: (props) => this.CodeBlockRenderer(props, codeSelect, curCodeSelectIndex, this.curUuid),
|
||||
a: Link,
|
||||
h1: (props) => <Header {...props} hnum={1} />,
|
||||
h2: (props) => <Header {...props} hnum={2} />,
|
||||
h3: (props) => <Header {...props} hnum={3} />,
|
||||
h4: (props) => <Header {...props} hnum={4} />,
|
||||
h5: (props) => <Header {...props} hnum={5} />,
|
||||
h6: (props) => <Header {...props} hnum={6} />,
|
||||
code: Code,
|
||||
pre: (props) => <CodeBlock {...props} onClickExecute={onClickExecute} />,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={clsx("markdown content", this.props.extraClassName)} style={this.props.style}>
|
||||
<div className={clsx("markdown content", className)} style={this.props.style}>
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]} components={markdownComponents}>
|
||||
{text}
|
||||
</ReactMarkdown>
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
@dot-width: 11px;
|
||||
@dot-color: var(--app-success-color);
|
||||
@speed: 1.5s;
|
||||
|
||||
.typing {
|
||||
position: relative;
|
||||
height: @dot-width;
|
||||
|
||||
span {
|
||||
content: "";
|
||||
animation: blink @speed infinite;
|
||||
animation-fill-mode: both;
|
||||
height: @dot-width;
|
||||
width: @dot-width;
|
||||
background: @dot-color;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
border-radius: 50%;
|
||||
|
||||
&:nth-child(2) {
|
||||
animation-delay: 0.2s;
|
||||
margin-left: @dot-width * 1.5;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
animation-delay: 0.4s;
|
||||
margin-left: @dot-width * 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
0% {
|
||||
opacity: 0.1;
|
||||
}
|
||||
20% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import React from "react";
|
||||
import { clsx } from "clsx";
|
||||
|
||||
import "./typingindicator.less";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
};
|
||||
const TypingIndicator: React.FC<Props> = (props: { className?: string }) => {
|
||||
return (
|
||||
<div className={clsx("typing", props.className)}>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { TypingIndicator };
|
||||
@@ -135,14 +135,12 @@ export const StatusIndicator: React.FC<StatusIndicatorProps> = (props) => {
|
||||
*/
|
||||
React.useEffect(() => {
|
||||
if (runningCommands && !timeoutState) {
|
||||
console.log("show spinner");
|
||||
setTimeoutState(
|
||||
setTimeout(() => {
|
||||
setSpinnerVisible(true);
|
||||
}, 100)
|
||||
);
|
||||
} else if (!runningCommands) {
|
||||
console.log("clear spinner");
|
||||
clearSpinnerTimeout();
|
||||
}
|
||||
return () => {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user