mirror of
https://github.com/wavetermdev/backup.git
synced 2026-04-22 15:26:58 -07:00
Compare commits
72 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3ae38b4aa4 | |||
| 2f57a6e067 | |||
| b762df179f | |||
| 6bcd37c28e | |||
| 9879fe4d11 | |||
| 1b81b2906c | |||
| 974275bcb4 | |||
| 57f07994e4 | |||
| 713acb664d | |||
| 613cd30c36 | |||
| c0309f1796 | |||
| 1d18a47f72 | |||
| d3e821af1c | |||
| 467664af85 | |||
| 0feaa57662 | |||
| 6ea70ff788 | |||
| f8e2aadf98 | |||
| f21a9e2aa1 | |||
| 13d3d3a180 | |||
| 5216f58e2f | |||
| 7b9287316d | |||
| 781ed2e57e | |||
| 4ac5d93ed2 | |||
| a7afefc340 | |||
| fc12dcac2f | |||
| aeacfb094e | |||
| 14da9b76e0 | |||
| 2f32d89b85 | |||
| 2e3e07d24b | |||
| e2d0664901 | |||
| 5638e7857b | |||
| 6c9dbabd56 | |||
| 13cd9dfe50 | |||
| 6fa799daf5 | |||
| 422338c04b | |||
| 76988a5277 | |||
| da69c0411d | |||
| ff65a3f042 | |||
| d431ac1ecf | |||
| f8024799ed | |||
| ebdf8b5aee | |||
| ed2a345555 | |||
| ea3be8115a | |||
| 077cb2be03 | |||
| 8f39f0fc5e | |||
| 00e709d515 | |||
| c2a894b280 | |||
| fad48b0d09 | |||
| 8ac1943d56 | |||
| f6a6068674 | |||
| e1e5b67ff6 | |||
| 0bb80b6a74 | |||
| 631a0867aa | |||
| 5bd7f73746 | |||
| ed1dff320d | |||
| 7f91efc314 | |||
| f737c2af86 | |||
| 8cbee1e1c3 | |||
| b2a1bb3818 | |||
| d1319c0a2c | |||
| 2c9272e3a7 | |||
| 452836bffc | |||
| 8d88e2cf94 | |||
| 2f7cce294c | |||
| 01074af129 | |||
| 6a1b2c8bd4 | |||
| 9980a6b204 | |||
| 019eaecdaa | |||
| de9c029ba8 | |||
| e7725e0e11 | |||
| 092a8de715 | |||
| c56ee23843 |
@@ -0,0 +1,102 @@
|
||||
name: "Build Helper"
|
||||
on: workflow_dispatch
|
||||
env:
|
||||
WAVETERM_VERSION: 0.6.0
|
||||
GO_VERSION: '1.21.5'
|
||||
NODE_VERSION: '21.5.0'
|
||||
jobs:
|
||||
runbuild-darwin-x64:
|
||||
name: "Build MacOS x64"
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{env.GO_VERSION}}
|
||||
cache-dependency-path: |
|
||||
wavesrv/go.sum
|
||||
waveshell/go.sum
|
||||
- run: brew tap scripthaus-dev/scripthaus
|
||||
- run: brew install scripthaus
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{env.NODE_VERSION}}
|
||||
cache: 'yarn'
|
||||
- run: yarn --frozen-lockfile
|
||||
- run: scripthaus run build-package
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: waveterm-build-darwin-x64
|
||||
path: out/make/zip/darwin/x64/*.zip
|
||||
retention-days: 2
|
||||
runbuild-darwin-arm64:
|
||||
name: "Build MacOS arm64"
|
||||
runs-on: macos-latest-xlarge
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{env.GO_VERSION}}
|
||||
cache-dependency-path: |
|
||||
wavesrv/go.sum
|
||||
waveshell/go.sum
|
||||
- run: brew tap scripthaus-dev/scripthaus
|
||||
- run: brew install scripthaus
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{env.NODE_VERSION}}
|
||||
cache: 'yarn'
|
||||
- run: yarn --frozen-lockfile
|
||||
- run: scripthaus run build-package
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: waveterm-build-darwin-arm64
|
||||
path: out/make/zip/darwin/arm64/*.zip
|
||||
retention-days: 2
|
||||
runbuild-linux:
|
||||
name: "Build Linux x64"
|
||||
runs-on: ubuntu-latest
|
||||
needs: [runbuild-darwin-x64, runbuild-darwin-arm64]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: scripthaus-dev/scripthaus
|
||||
path: scripthaus
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{env.GO_VERSION}}
|
||||
cache-dependency-path: |
|
||||
wavesrv/go.sum
|
||||
waveshell/go.sum
|
||||
scripthaus/go.sum
|
||||
- run: |
|
||||
go work use ./scripthaus;
|
||||
cd scripthaus;
|
||||
go get ./...;
|
||||
CGO_ENABLED=1 go build -o scripthaus cmd/main.go
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{env.NODE_VERSION}}
|
||||
cache: 'yarn'
|
||||
- run: yarn --frozen-lockfile
|
||||
- run: ./scripthaus/scripthaus run build-package-linux
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
merge-multiple: true
|
||||
path: buildtemp
|
||||
- run: |
|
||||
mv out/make/zip/linux/x64/Wave-linux-x64-$WAVETERM_VERSION.zip buildtemp/waveterm-linux-x64-v$WAVETERM_VERSION.zip
|
||||
- run: (cd buildtemp; zip ../waveterm-builds.zip *)
|
||||
- run: aws s3 cp waveterm-builds.zip s3://waveterm-github-artifacts/
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: "${{ secrets.S3_USERID }}"
|
||||
AWS_SECRET_ACCESS_KEY: "${{ secrets.S3_SECRETKEY }}"
|
||||
AWS_DEFAULT_REGION: us-west-2
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: waveterm-builds
|
||||
path: buildtemp
|
||||
retention-days: 2
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
@@ -76,6 +76,6 @@ jobs:
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# 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:
|
||||
|
||||
- [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 three disclaimer files linked above will be periodically regenerated to reflect new dependencies.
|
||||
|
||||
The [`scripthaus.md` file](../scripthaus.md) contains scripts to genrate the disclaimers and package them. To manually generate the disclaimers, run the following from the repository root directory:
|
||||
|
||||
```bash
|
||||
scripthaus run generate-license-disclaimers
|
||||
```
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,16 @@
|
||||
# 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,12 +1,21 @@
|
||||
#!/bin/bash
|
||||
|
||||
# assumes we have Wave-darwin-x64-[version].zip and Wave-darwin-arm64-[version].zip in current directory
|
||||
VERSION=0.5.3
|
||||
VERSION=0.6.0
|
||||
rm -rf temp
|
||||
rm -rf builds
|
||||
mkdir temp
|
||||
mkdir temp/x64
|
||||
X64_ZIP="Wave-darwin-x64-$VERSION.zip"
|
||||
ARM64_ZIP="Wave-darwin-arm64-$VERSION.zip"
|
||||
aws s3 cp s3://waveterm-github-artifacts/waveterm-builds.zip .
|
||||
BUILDS_ZIP=waveterm-builds.zip
|
||||
if ! [ -f $BUILDS_ZIP ]; then
|
||||
echo "no $BUILDS_ZIP found";
|
||||
exit 1;
|
||||
fi
|
||||
echo "unzipping $BUILDS_ZIP"
|
||||
unzip -q $BUILDS_ZIP -d builds
|
||||
X64_ZIP="builds/Wave-darwin-x64-$VERSION.zip"
|
||||
ARM64_ZIP="builds/Wave-darwin-arm64-$VERSION.zip"
|
||||
if ! [ -f $X64_ZIP ]; then
|
||||
echo "no $X64_ZIP found";
|
||||
exit 1;
|
||||
@@ -48,4 +57,6 @@ DMG_NAME="waveterm-macos-universal-${DMG_VERSION}.dmg"
|
||||
$DMG_NAME \
|
||||
"temp/Wave.app"
|
||||
echo "success, created $DMG_NAME"
|
||||
mv $DMG_NAME builds/
|
||||
rm builds/Wave-darwin-*.zip
|
||||
spctl -a -vvv -t install temp/Wave.app/
|
||||
|
||||
@@ -7,10 +7,10 @@ signAsync({
|
||||
app: "temp/Wave.app",
|
||||
binaries: [
|
||||
waveAppPath + "/Contents/Resources/app/bin/wavesrv",
|
||||
waveAppPath + "/Contents/Resources/app/bin/mshell/mshell-v0.3-linux.amd64",
|
||||
waveAppPath + "/Contents/Resources/app/bin/mshell/mshell-v0.3-linux.arm64",
|
||||
waveAppPath + "/Contents/Resources/app/bin/mshell/mshell-v0.3-darwin.amd64",
|
||||
waveAppPath + "/Contents/Resources/app/bin/mshell/mshell-v0.3-darwin.arm64",
|
||||
waveAppPath + "/Contents/Resources/app/bin/mshell/mshell-v0.4-linux.amd64",
|
||||
waveAppPath + "/Contents/Resources/app/bin/mshell/mshell-v0.4-linux.arm64",
|
||||
waveAppPath + "/Contents/Resources/app/bin/mshell/mshell-v0.4-darwin.amd64",
|
||||
waveAppPath + "/Contents/Resources/app/bin/mshell/mshell-v0.4-darwin.arm64",
|
||||
],
|
||||
}).then(() => {
|
||||
console.log("signing success");
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
||||
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/wavetermdev/ssh_config v0.0.0-20240109090616-36c8da3d7376 h1:tFhJgTu7lgd+hldLfPSzDCoWUpXI8wHKR3rxq5jTLkQ=
|
||||
github.com/wavetermdev/ssh_config v0.0.0-20240109090616-36c8da3d7376/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
||||
+13
-12
@@ -2,7 +2,7 @@
|
||||
"name": "waveterm",
|
||||
"author": "Command Line Inc",
|
||||
"productName": "Wave",
|
||||
"version": "0.5.3",
|
||||
"version": "0.6.0",
|
||||
"main": "dist/emain.js",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
@@ -13,6 +13,7 @@
|
||||
"@tanstack/react-table": "^8.10.3",
|
||||
"@types/semver": "^7.5.6",
|
||||
"autobind-decorator": "^2.4.0",
|
||||
"base64-js": "^1.5.1",
|
||||
"classnames": "^2.3.1",
|
||||
"dayjs": "^1.11.3",
|
||||
"dompurify": "^3.0.2",
|
||||
@@ -26,7 +27,6 @@
|
||||
"papaparse": "^5.4.1",
|
||||
"react": "^18.1.0",
|
||||
"react-dom": "^18.1.0",
|
||||
"react-json-view": "^1.21.3",
|
||||
"react-markdown": "^9.0.0",
|
||||
"remark": "^15.0.1",
|
||||
"remark-gfm": "^4.0.0",
|
||||
@@ -35,7 +35,8 @@
|
||||
"tsx-control-statements": "^4.1.1",
|
||||
"uuid": "^9.0.0",
|
||||
"winston": "^3.8.2",
|
||||
"xterm": "^5.0.0"
|
||||
"xterm": "^5.0.0",
|
||||
"xterm-addon-web-links": "^0.9.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.17.10",
|
||||
@@ -49,17 +50,17 @@
|
||||
"@babel/preset-env": "^7.18.2",
|
||||
"@babel/preset-react": "^7.23.3",
|
||||
"@babel/preset-typescript": "^7.17.12",
|
||||
"@electron-forge/cli": "^6.0.0-beta.70",
|
||||
"@electron-forge/maker-deb": "^6.0.0-beta.70",
|
||||
"@electron-forge/maker-rpm": "^6.0.0-beta.70",
|
||||
"@electron-forge/maker-snap": "^6.4.2",
|
||||
"@electron-forge/maker-squirrel": "^6.0.0-beta.70",
|
||||
"@electron-forge/maker-zip": "^6.0.0-beta.70",
|
||||
"@electron-forge/cli": "^7.2.0",
|
||||
"@electron-forge/maker-deb": "^7.2.0",
|
||||
"@electron-forge/maker-rpm": "^7.2.0",
|
||||
"@electron-forge/maker-snap": "^7.2.0",
|
||||
"@electron-forge/maker-squirrel": "^7.2.0",
|
||||
"@electron-forge/maker-zip": "^7.2.0",
|
||||
"@electron/rebuild": "^3.4.0",
|
||||
"@svgr/webpack": "^8.1.0",
|
||||
"@types/classnames": "^2.3.1",
|
||||
"@types/electron": "^1.6.10",
|
||||
"@types/node": "20.10.3",
|
||||
"@types/node": "20.11.0",
|
||||
"@types/papaparse": "^5.3.10",
|
||||
"@types/react": "^18.0.12",
|
||||
"@types/sprintf-js": "^1.1.3",
|
||||
@@ -70,7 +71,7 @@
|
||||
"babel-plugin-jsx-control-statements": "^4.1.2",
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"css-loader": "^6.7.1",
|
||||
"electron": "27.1.3",
|
||||
"electron": "28.1.3",
|
||||
"file-loader": "^6.2.0",
|
||||
"http-server": "^14.1.1",
|
||||
"less": "^4.1.2",
|
||||
@@ -81,7 +82,7 @@
|
||||
"raw-loader": "^4.0.2",
|
||||
"react-split-it": "^2.0.0",
|
||||
"style-loader": "^3.3.1",
|
||||
"typescript": "^4.7.3",
|
||||
"typescript": "^5.0.0",
|
||||
"webpack": "^5.73.0",
|
||||
"webpack-bundle-analyzer": "^4.10.1",
|
||||
"webpack-cli": "^5.1.4",
|
||||
|
||||
+24
-12
@@ -44,10 +44,10 @@ rm -rf bin/
|
||||
rm -rf build/
|
||||
node_modules/.bin/webpack --env prod
|
||||
GO_LDFLAGS="-s -w -X main.BuildTime=$(date +'%Y%m%d%H%M')"
|
||||
(cd waveshell; CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-v0.3-darwin.amd64 main-waveshell.go)
|
||||
(cd waveshell; CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-v0.3-darwin.arm64 main-waveshell.go)
|
||||
(cd waveshell; CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-v0.3-linux.amd64 main-waveshell.go)
|
||||
(cd waveshell; CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-v0.3-linux.arm64 main-waveshell.go)
|
||||
(cd waveshell; CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-v0.4-darwin.amd64 main-waveshell.go)
|
||||
(cd waveshell; CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-v0.4-darwin.arm64 main-waveshell.go)
|
||||
(cd waveshell; CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-v0.4-linux.amd64 main-waveshell.go)
|
||||
(cd waveshell; CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-v0.4-linux.arm64 main-waveshell.go)
|
||||
(cd wavesrv; CGO_ENABLED=1 go build -tags "osusergo,netgo,sqlite_omit_load_extension" -ldflags "-X main.BuildTime=$(date +'%Y%m%d%H%M')" -o ../bin/wavesrv ./cmd)
|
||||
node_modules/.bin/electron-forge make
|
||||
```
|
||||
@@ -60,10 +60,10 @@ rm -rf bin/
|
||||
rm -rf build/
|
||||
node_modules/.bin/webpack --env prod
|
||||
GO_LDFLAGS="-s -w -X main.BuildTime=$(date +'%Y%m%d%H%M')"
|
||||
(cd waveshell; CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-v0.3-darwin.amd64 main-waveshell.go)
|
||||
(cd waveshell; CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-v0.3-darwin.arm64 main-waveshell.go)
|
||||
(cd waveshell; CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-v0.3-linux.amd64 main-waveshell.go)
|
||||
(cd waveshell; CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-v0.3-linux.arm64 main-waveshell.go)
|
||||
(cd waveshell; CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-v0.4-darwin.amd64 main-waveshell.go)
|
||||
(cd waveshell; CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-v0.4-darwin.arm64 main-waveshell.go)
|
||||
(cd waveshell; CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-v0.4-linux.amd64 main-waveshell.go)
|
||||
(cd waveshell; CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-v0.4-linux.arm64 main-waveshell.go)
|
||||
# adds -extldflags=-static, *only* on linux (macos does not support fully static binaries) to avoid a glibc dependency
|
||||
(cd wavesrv; CGO_ENABLED=1 go build -tags "osusergo,netgo,sqlite_omit_load_extension" -ldflags "-linkmode 'external' -extldflags=-static $GO_LDFLAGS" -o ../bin/wavesrv ./cmd)
|
||||
node_modules/.bin/electron-forge make
|
||||
@@ -86,10 +86,10 @@ CGO_ENABLED=1 go build -tags "osusergo,netgo,sqlite_omit_load_extension" -ldflag
|
||||
set -e
|
||||
cd waveshell
|
||||
GO_LDFLAGS="-s -w -X main.BuildTime=$(date +'%Y%m%d%H%M')"
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-v0.3-linux.amd64 main-waveshell.go
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-v0.3-linux.arm64 main-waveshell.go
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-v0.3-darwin.amd64 main-waveshell.go
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-v0.3-darwin.arm64 main-waveshell.go
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-v0.4-linux.amd64 main-waveshell.go
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-v0.4-linux.arm64 main-waveshell.go
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-v0.4-darwin.amd64 main-waveshell.go
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags="$GO_LDFLAGS" -o ../bin/mshell/mshell-v0.4-darwin.arm64 main-waveshell.go
|
||||
```
|
||||
|
||||
```bash
|
||||
@@ -100,3 +100,15 @@ scripthaus run fullbuild-waveshell
|
||||
echo building wavesrv
|
||||
scripthaus run build-wavesrv
|
||||
```
|
||||
|
||||
```bash
|
||||
# @scripthaus command generate-license-disclaimers
|
||||
DISCLAIMER_DIR="./acknowledgements"
|
||||
DISCLAIMER_OUTPUT_DIR="$DISCLAIMER_DIR/disclaimers"
|
||||
if [ -d "$DISCLAIMER_OUTPUT_DIR" ]; then
|
||||
rm -rf "$DISCLAIMER_OUTPUT_DIR"
|
||||
fi
|
||||
mkdir "$DISCLAIMER_OUTPUT_DIR"
|
||||
go run github.com/google/go-licenses@latest report ./wavesrv/... ./waveshell/... --template "$DISCLAIMER_DIR/go_licenses_report.tpl" --ignore github.com/wavetermdev/waveterm > "$DISCLAIMER_OUTPUT_DIR/backend.md"
|
||||
yarn licenses generate-disclaimer > "$DISCLAIMER_OUTPUT_DIR/frontend.md"
|
||||
```
|
||||
|
||||
+84
-5
@@ -476,17 +476,26 @@ a.a-block {
|
||||
}
|
||||
}
|
||||
|
||||
.icon.color-default,
|
||||
.icon.color-green {
|
||||
path,
|
||||
circle {
|
||||
fill: @tab-green;
|
||||
}
|
||||
|
||||
i {
|
||||
color: @tab-green;
|
||||
}
|
||||
}
|
||||
|
||||
.icon.color-red {
|
||||
path,
|
||||
circle {
|
||||
fill: @tab-red;
|
||||
}
|
||||
}
|
||||
|
||||
.icon.color-green {
|
||||
path,
|
||||
circle {
|
||||
fill: @tab-green;
|
||||
i {
|
||||
color: @tab-red;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -495,6 +504,10 @@ a.a-block {
|
||||
circle {
|
||||
fill: @tab-orange;
|
||||
}
|
||||
|
||||
i {
|
||||
color: @tab-orange;
|
||||
}
|
||||
}
|
||||
|
||||
.icon.color-blue {
|
||||
@@ -502,6 +515,10 @@ a.a-block {
|
||||
circle {
|
||||
fill: @tab-blue;
|
||||
}
|
||||
|
||||
i {
|
||||
color: @tab-blue;
|
||||
}
|
||||
}
|
||||
|
||||
.icon.color-yellow {
|
||||
@@ -509,6 +526,10 @@ a.a-block {
|
||||
circle {
|
||||
fill: @tab-yellow;
|
||||
}
|
||||
|
||||
i {
|
||||
color: @tab-yellow;
|
||||
}
|
||||
}
|
||||
|
||||
.icon.color-pink {
|
||||
@@ -516,6 +537,10 @@ a.a-block {
|
||||
circle {
|
||||
fill: @tab-pink;
|
||||
}
|
||||
|
||||
i {
|
||||
color: @tab-pink;
|
||||
}
|
||||
}
|
||||
|
||||
.icon.color-mint {
|
||||
@@ -523,6 +548,10 @@ a.a-block {
|
||||
circle {
|
||||
fill: @tab-mint;
|
||||
}
|
||||
|
||||
i {
|
||||
color: @tab-mint;
|
||||
}
|
||||
}
|
||||
|
||||
.icon.color-cyan {
|
||||
@@ -530,6 +559,10 @@ a.a-block {
|
||||
circle {
|
||||
fill: @tab-cyan;
|
||||
}
|
||||
|
||||
i {
|
||||
color: @tab-cyan;
|
||||
}
|
||||
}
|
||||
|
||||
.icon.color-violet {
|
||||
@@ -537,6 +570,10 @@ a.a-block {
|
||||
circle {
|
||||
fill: @tab-violet;
|
||||
}
|
||||
|
||||
i {
|
||||
color: @tab-violet;
|
||||
}
|
||||
}
|
||||
|
||||
.icon.color-white {
|
||||
@@ -544,6 +581,10 @@ a.a-block {
|
||||
circle {
|
||||
fill: @tab-white;
|
||||
}
|
||||
|
||||
i {
|
||||
color: @tab-white;
|
||||
}
|
||||
}
|
||||
|
||||
.status-icon.status-connected {
|
||||
@@ -581,3 +622,41 @@ a.a-block {
|
||||
.text-selectable {
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.spin {
|
||||
animation: infiniteRotate 2s linear infinite;
|
||||
|
||||
@keyframes infiniteRotate {
|
||||
from {
|
||||
transform:rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform:rotate(360deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.view {
|
||||
background-color: @background-session;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
margin-bottom: 10px;
|
||||
margin-right: 10px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(241, 246, 243, 0.08);
|
||||
background: rgba(13, 13, 13, 0.85);
|
||||
|
||||
.header {
|
||||
margin: 24px 18px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.close-div {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-8
@@ -16,14 +16,10 @@ import { PluginsView } from "./pluginsview/pluginsview";
|
||||
import { BookmarksView } from "./bookmarks/bookmarks";
|
||||
import { HistoryView } from "./history/history";
|
||||
import { ConnectionsView } from "./connections/connections";
|
||||
import {
|
||||
ScreenSettingsModal,
|
||||
SessionSettingsModal,
|
||||
LineSettingsModal,
|
||||
ClientSettingsModal,
|
||||
} from "./common/modals/settings";
|
||||
import { ClientSettingsView } from "./clientsettings/clientsettings";
|
||||
import { MainSideBar } from "./sidebar/sidebar";
|
||||
import { DisconnectedModal, ClientStopModal, ModalsProvider } from "./common/modals/modals";
|
||||
import { DisconnectedModal, ClientStopModal } from "./common/modals";
|
||||
import { ModalsProvider } from "./common/modals/provider";
|
||||
import { ErrorBoundary } from "./common/error/errorboundary";
|
||||
import "./app.less";
|
||||
|
||||
@@ -74,7 +70,6 @@ class App extends React.Component<{}, {}> {
|
||||
}
|
||||
|
||||
render() {
|
||||
let clientSettingsModal = GlobalModel.clientSettingsModal.get();
|
||||
let remotesModel = GlobalModel.remotesModel;
|
||||
let disconnected = !GlobalModel.ws.open.get() || !GlobalModel.waveSrvRunning.get();
|
||||
let hasClientStop = GlobalModel.getHasClientStop();
|
||||
@@ -115,6 +110,7 @@ class App extends React.Component<{}, {}> {
|
||||
<HistoryView />
|
||||
<BookmarksView />
|
||||
<ConnectionsView model={remotesModel} />
|
||||
<ClientSettingsView model={remotesModel} />
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
<ModalsProvider />
|
||||
|
||||
@@ -7,7 +7,12 @@ export const SCREEN_SETTINGS = "screenSettings";
|
||||
export const SESSION_SETTINGS = "sessionSettings";
|
||||
export const LINE_SETTINGS = "lineSettings";
|
||||
export const CLIENT_SETTINGS = "clientSettings";
|
||||
export const TAB_SWITCHER = "tabSwitcher";
|
||||
|
||||
export const LineContainer_Main = "main";
|
||||
export const LineContainer_History = "history";
|
||||
export const LineContainer_Sidebar = "sidebar";
|
||||
|
||||
export const ConfirmKey_HideShellPrompt = "hideshellprompt";
|
||||
|
||||
export const NoStrPos = -1;
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
@import "../../app/common/themes/themes.less";
|
||||
|
||||
.clientsettings-view {
|
||||
background-color: @background-session;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
margin-bottom: 10px;
|
||||
margin-right: 10px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(241, 246, 243, 0.08);
|
||||
background: rgba(13, 13, 13, 0.85);
|
||||
|
||||
.header {
|
||||
margin: 24px 18px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 1px solid white;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 0 18px 0 30px;
|
||||
}
|
||||
|
||||
// just marked these as important since we're keeping the
|
||||
// settings-field styles below this intact until we figure out what do with them
|
||||
.settings-field {
|
||||
&:not(:first-child) {
|
||||
margin-top: 15px !important;
|
||||
}
|
||||
|
||||
.settings-label {
|
||||
width: 250px !important;
|
||||
}
|
||||
}
|
||||
|
||||
// @TODO: These styles are duplicated in the settings modals
|
||||
.settings-field {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
&.settings-field.sub-field {
|
||||
.settings-label {
|
||||
font-weight: normal;
|
||||
|
||||
text-align: right;
|
||||
padding-right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&.settings-error {
|
||||
color: @term-red;
|
||||
margin-top: 20px;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
background-color: #200707;
|
||||
border: 1px solid @term-red;
|
||||
font-weight: bold;
|
||||
|
||||
.error-dismiss {
|
||||
padding: 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-label {
|
||||
font-weight: bold;
|
||||
width: 12em;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-right: 10px;
|
||||
|
||||
.info-message {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-input {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
&.settings-clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.inline-edit.edit-active {
|
||||
input.input {
|
||||
padding: 0;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.button {
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
padding: 4px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.control {
|
||||
.icon {
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-color-icon.color-default path {
|
||||
fill: @tab-green;
|
||||
}
|
||||
.tab-color-icon.color-green path {
|
||||
fill: @tab-green;
|
||||
}
|
||||
.tab-color-icon.color-orange path {
|
||||
fill: @tab-orange;
|
||||
}
|
||||
.tab-color-icon.color-red path {
|
||||
fill: @tab-red;
|
||||
}
|
||||
.tab-color-icon.color-yellow path {
|
||||
fill: @tab-yellow;
|
||||
}
|
||||
.tab-color-icon.color-blue path {
|
||||
fill: @tab-blue;
|
||||
}
|
||||
.tab-color-icon.color-mint path {
|
||||
fill: @tab-mint;
|
||||
}
|
||||
.tab-color-icon.color-cyan path {
|
||||
fill: @tab-cyan;
|
||||
}
|
||||
.tab-color-icon.color-white path {
|
||||
fill: @tab-white;
|
||||
}
|
||||
.tab-color-icon.color-violet path {
|
||||
fill: @tab-violet;
|
||||
}
|
||||
.tab-color-icon.color-pink path {
|
||||
fill: @tab-pink;
|
||||
}
|
||||
|
||||
.tab-colors,
|
||||
.tab-icons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
.tab-color-sep,
|
||||
.tab-icon-sep {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.tab-color-icon,
|
||||
.tab-icon-icon {
|
||||
width: 1.1em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.tab-color-name,
|
||||
.tab-icon-name {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.tab-color-select,
|
||||
.tab-icon-select {
|
||||
cursor: pointer;
|
||||
margin: 5px;
|
||||
&:hover {
|
||||
outline: 2px solid white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.action-text {
|
||||
margin-left: 20px;
|
||||
|
||||
color: @term-red;
|
||||
}
|
||||
|
||||
.settings-share-link {
|
||||
width: 160px;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
// Copyright 2023, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import * as React from "react";
|
||||
import * as mobxReact from "mobx-react";
|
||||
import * as mobx from "mobx";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
import cn from "classnames";
|
||||
import { GlobalModel, GlobalCommandRunner, MinFontSize, MaxFontSize, RemotesModel } from "../../model/model";
|
||||
import { Toggle, InlineSettingsTextEdit, SettingsError, Dropdown } from "../common/common";
|
||||
import { CommandRtnType, ClientDataType } from "../../types/types";
|
||||
import { commandRtnHandler, isBlank } from "../../util/util";
|
||||
|
||||
import "./clientsettings.less";
|
||||
|
||||
type OV<V> = mobx.IObservableValue<V>;
|
||||
|
||||
// @ts-ignore
|
||||
const VERSION = __WAVETERM_VERSION__;
|
||||
// @ts-ignore
|
||||
const BUILD = __WAVETERM_BUILD__;
|
||||
|
||||
@mobxReact.observer
|
||||
class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hoveredItemId: string }> {
|
||||
fontSizeDropdownActive: OV<boolean> = mobx.observable.box(false, { name: "clientSettings-fontSizeDropdownActive" });
|
||||
errorMessage: OV<string> = mobx.observable.box(null, { name: "ClientSettings-errorMessage" });
|
||||
|
||||
@boundMethod
|
||||
dismissError(): void {
|
||||
mobx.action(() => {
|
||||
this.errorMessage.set(null);
|
||||
})();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleChangeFontSize(fontSize: string): void {
|
||||
let newFontSize = Number(fontSize);
|
||||
this.fontSizeDropdownActive.set(false);
|
||||
if (GlobalModel.termFontSize.get() == newFontSize) {
|
||||
return;
|
||||
}
|
||||
let prtn = GlobalCommandRunner.setTermFontSize(newFontSize, false);
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
togglefontSizeDropdown(): void {
|
||||
mobx.action(() => {
|
||||
this.fontSizeDropdownActive.set(!this.fontSizeDropdownActive.get());
|
||||
})();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleChangeTelemetry(val: boolean): void {
|
||||
let prtn: Promise<CommandRtnType> = null;
|
||||
if (val) {
|
||||
prtn = GlobalCommandRunner.telemetryOn(false);
|
||||
} else {
|
||||
prtn = GlobalCommandRunner.telemetryOff(false);
|
||||
}
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleChangeReleaseCheck(val: boolean): void {
|
||||
let prtn: Promise<CommandRtnType> = null;
|
||||
if (val) {
|
||||
prtn = GlobalCommandRunner.releaseCheckAutoOn(false);
|
||||
} else {
|
||||
prtn = GlobalCommandRunner.releaseCheckAutoOff(false);
|
||||
}
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
getFontSizes(): any {
|
||||
let availableFontSizes: { label: string; value: number }[] = [];
|
||||
for (let s = MinFontSize; s <= MaxFontSize; s++) {
|
||||
availableFontSizes.push({ label: s + "px", value: s });
|
||||
}
|
||||
return availableFontSizes;
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
inlineUpdateOpenAIModel(newModel: string): void {
|
||||
let prtn = GlobalCommandRunner.setClientOpenAISettings({ model: newModel });
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
inlineUpdateOpenAIToken(newToken: string): void {
|
||||
let prtn = GlobalCommandRunner.setClientOpenAISettings({ apitoken: newToken });
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
inlineUpdateOpenAIMaxTokens(newMaxTokensStr: string): void {
|
||||
let prtn = GlobalCommandRunner.setClientOpenAISettings({ maxtokens: newMaxTokensStr });
|
||||
commandRtnHandler(prtn, this.errorMessage);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
setErrorMessage(msg: string): void {
|
||||
mobx.action(() => {
|
||||
this.errorMessage.set(msg);
|
||||
})();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleClose(): void {
|
||||
GlobalModel.clientSettingsViewModel.closeView();
|
||||
}
|
||||
|
||||
render() {
|
||||
let isHidden = GlobalModel.activeMainView.get() != "clientsettings";
|
||||
if (isHidden) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let cdata: ClientDataType = GlobalModel.clientData.get();
|
||||
let openAIOpts = cdata.openaiopts ?? {};
|
||||
let apiTokenStr = isBlank(openAIOpts.apitoken) ? "(not set)" : "********";
|
||||
let maxTokensStr = String(
|
||||
openAIOpts.maxtokens == null || openAIOpts.maxtokens == 0 ? 1000 : openAIOpts.maxtokens
|
||||
);
|
||||
let curFontSize = GlobalModel.termFontSize.get();
|
||||
|
||||
return (
|
||||
<div className={cn("view clientsettings-view")}>
|
||||
<header className="header">
|
||||
<div className="clientsettings-title text-primary">Client Settings</div>
|
||||
<div className="close-div hoverEffect" title="Close (Escape)" onClick={this.handleClose}>
|
||||
<i className="fa-sharp fa-solid fa-xmark"></i>
|
||||
</div>
|
||||
</header>
|
||||
<div className="content">
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Term Font Size</div>
|
||||
<div className="settings-input">
|
||||
<Dropdown
|
||||
className="font-size-dropdown"
|
||||
options={this.getFontSizes()}
|
||||
defaultValue={`${curFontSize}px`}
|
||||
onChange={this.handleChangeFontSize}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Client ID</div>
|
||||
<div className="settings-input">{cdata.clientid}</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Client Version</div>
|
||||
<div className="settings-input">
|
||||
{VERSION} {BUILD}
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">DB Version</div>
|
||||
<div className="settings-input">{cdata.dbversion}</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Basic Telemetry</div>
|
||||
<div className="settings-input">
|
||||
<Toggle checked={!cdata.clientopts.notelemetry} onChange={this.handleChangeTelemetry} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">Check for Updates</div>
|
||||
<div className="settings-input">
|
||||
<Toggle
|
||||
checked={!cdata.clientopts.noreleasecheck}
|
||||
onChange={this.handleChangeReleaseCheck}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">OpenAI Token</div>
|
||||
<div className="settings-input">
|
||||
<InlineSettingsTextEdit
|
||||
placeholder=""
|
||||
text={apiTokenStr}
|
||||
value={""}
|
||||
onChange={this.inlineUpdateOpenAIToken}
|
||||
maxLength={100}
|
||||
showIcon={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">OpenAI Model</div>
|
||||
<div className="settings-input">
|
||||
<InlineSettingsTextEdit
|
||||
placeholder="gpt-3.5-turbo"
|
||||
text={isBlank(openAIOpts.model) ? "gpt-3.5-turbo" : openAIOpts.model}
|
||||
value={openAIOpts.model ?? ""}
|
||||
onChange={this.inlineUpdateOpenAIModel}
|
||||
maxLength={100}
|
||||
showIcon={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="settings-field">
|
||||
<div className="settings-label">OpenAI MaxTokens</div>
|
||||
<div className="settings-input">
|
||||
<InlineSettingsTextEdit
|
||||
placeholder=""
|
||||
text={maxTokensStr}
|
||||
value={maxTokensStr}
|
||||
onChange={this.inlineUpdateOpenAIMaxTokens}
|
||||
maxLength={10}
|
||||
showIcon={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<SettingsError errorMessage={this.errorMessage} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export { ClientSettingsView };
|
||||
@@ -188,14 +188,14 @@
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #9e9e9e;
|
||||
color: @term-bright-white;
|
||||
transition: color 250ms cubic-bezier(0.4, 0, 0.23, 1);
|
||||
}
|
||||
input[type="checkbox"] + label > span {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-right: 16px;
|
||||
margin-right: 10px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: transparent;
|
||||
@@ -205,10 +205,6 @@
|
||||
transition: all 250ms cubic-bezier(0.4, 0, 0.23, 1);
|
||||
}
|
||||
|
||||
input[type="checkbox"] + label:hover,
|
||||
input[type="checkbox"]:focus + label {
|
||||
color: #fff;
|
||||
}
|
||||
input[type="checkbox"] + label:hover > span,
|
||||
input[type="checkbox"]:focus + label > span {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
@@ -356,7 +352,7 @@
|
||||
background-color: @markdown-highlight;
|
||||
color: @term-white;
|
||||
font-family: @terminal-font;
|
||||
display: inline-block;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
code.inline {
|
||||
@@ -407,6 +403,11 @@
|
||||
background-color: @markdown-highlight;
|
||||
margin: 4px 10px 4px 10px;
|
||||
padding: 6px 6px 6px 10px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
pre.selected {
|
||||
outline: 2px solid @term-green;
|
||||
}
|
||||
|
||||
.title.is-1 {
|
||||
@@ -837,6 +838,14 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.no-label {
|
||||
height: 34px;
|
||||
|
||||
input {
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wave-input-decoration {
|
||||
@@ -1147,3 +1156,18 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
|
||||
&.error {
|
||||
color: @term-red;
|
||||
}
|
||||
&.success {
|
||||
color: @term-green;
|
||||
}
|
||||
&.output {
|
||||
color: @term-white;
|
||||
}
|
||||
}
|
||||
|
||||
+187
-38
@@ -9,8 +9,10 @@ import ReactMarkdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import cn from "classnames";
|
||||
import { If } from "tsx-control-statements/components";
|
||||
import type { RemoteType } from "../../types/types";
|
||||
import { RemoteType, StatusIndicatorLevel } from "../../types/types";
|
||||
import ReactDOM from "react-dom";
|
||||
import { GlobalModel } from "../../model/model";
|
||||
import * as appconst from "../appconst";
|
||||
|
||||
import { ReactComponent as CheckIcon } from "../assets/icons/line/check.svg";
|
||||
import { ReactComponent as CopyIcon } from "../assets/icons/history/copy.svg";
|
||||
@@ -99,23 +101,57 @@ class Toggle extends React.Component<{ checked: boolean; onChange: (value: boole
|
||||
}
|
||||
|
||||
class Checkbox extends React.Component<
|
||||
{ checked: boolean; onChange: (value: boolean) => void; label: React.ReactNode; id: string },
|
||||
{}
|
||||
{
|
||||
checked?: boolean;
|
||||
defaultChecked?: boolean;
|
||||
onChange: (value: boolean) => void;
|
||||
label: React.ReactNode;
|
||||
className?: string;
|
||||
id?: string;
|
||||
},
|
||||
{ checkedInternal: boolean }
|
||||
> {
|
||||
generatedId;
|
||||
static idCounter = 0;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
checkedInternal: this.props.checked !== undefined ? this.props.checked : Boolean(this.props.defaultChecked),
|
||||
};
|
||||
this.generatedId = `checkbox-${Checkbox.idCounter++}`;
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (this.props.checked !== undefined && this.props.checked !== prevProps.checked) {
|
||||
this.setState({ checkedInternal: this.props.checked });
|
||||
}
|
||||
}
|
||||
|
||||
handleChange = (e) => {
|
||||
const newChecked = e.target.checked;
|
||||
if (this.props.checked === undefined) {
|
||||
this.setState({ checkedInternal: newChecked });
|
||||
}
|
||||
this.props.onChange(newChecked);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { checked, onChange, label, id } = this.props;
|
||||
const { label, className, id } = this.props;
|
||||
const { checkedInternal } = this.state;
|
||||
const checkboxId = id || this.generatedId;
|
||||
|
||||
return (
|
||||
<div className="checkbox">
|
||||
<div className={cn("checkbox", className)}>
|
||||
<input
|
||||
type="checkbox"
|
||||
id={id}
|
||||
checked={checked}
|
||||
onChange={(e) => onChange(e.target.checked)}
|
||||
aria-checked={checked}
|
||||
id={checkboxId}
|
||||
checked={checkedInternal}
|
||||
onChange={this.handleChange}
|
||||
aria-checked={checkedInternal}
|
||||
role="checkbox"
|
||||
/>
|
||||
<label htmlFor={id}>
|
||||
<label htmlFor={checkboxId}>
|
||||
<span></span>
|
||||
{label}
|
||||
</label>
|
||||
@@ -231,6 +267,8 @@ interface ButtonProps {
|
||||
rightIcon?: React.ReactNode;
|
||||
color?: string;
|
||||
style?: React.CSSProperties;
|
||||
autoFocus?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
class Button extends React.Component<ButtonProps> {
|
||||
@@ -257,6 +295,7 @@ class Button extends React.Component<ButtonProps> {
|
||||
onClick={this.handleClick}
|
||||
disabled={disabled}
|
||||
style={style}
|
||||
autoFocus={this.props.autoFocus}
|
||||
>
|
||||
{leftIcon && <span className="icon-left">{leftIcon}</span>}
|
||||
{children}
|
||||
@@ -283,25 +322,24 @@ export default IconButton;
|
||||
|
||||
interface LinkButtonProps extends ButtonProps {
|
||||
href: string;
|
||||
rel?: string;
|
||||
target?: string;
|
||||
}
|
||||
|
||||
class LinkButton extends IconButton {
|
||||
class LinkButton extends React.Component<LinkButtonProps> {
|
||||
render() {
|
||||
// @ts-ignore
|
||||
const { href, target, leftIcon, rightIcon, children, theme, variant }: LinkButtonProps = this.props;
|
||||
const { leftIcon, rightIcon, children, className, ...rest } = this.props;
|
||||
|
||||
return (
|
||||
<a href={href} target={target} className={`wave-button link-button`}>
|
||||
<button {...this.props} className={`icon-button ${theme} ${variant}`}>
|
||||
{leftIcon && <span className="icon-left">{leftIcon}</span>}
|
||||
{children}
|
||||
{rightIcon && <span className="icon-right">{rightIcon}</span>}
|
||||
</button>
|
||||
<a {...rest} className={cn(`wave-button link-button`, className)}>
|
||||
{leftIcon && <span className="icon-left">{leftIcon}</span>}
|
||||
{children}
|
||||
{rightIcon && <span className="icon-right">{rightIcon}</span>}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
interface StatusProps {
|
||||
status: "green" | "red" | "gray" | "yellow";
|
||||
text: string;
|
||||
@@ -332,7 +370,7 @@ interface TextFieldDecorationProps {
|
||||
endDecoration?: React.ReactNode;
|
||||
}
|
||||
interface TextFieldProps {
|
||||
label: string;
|
||||
label?: string;
|
||||
value?: string;
|
||||
className?: string;
|
||||
onChange?: (value: string) => void;
|
||||
@@ -445,10 +483,11 @@ class TextField extends React.Component<TextFieldProps, TextFieldState> {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(`wave-textfield ${className || ""}`, {
|
||||
className={cn("wave-textfield", className, {
|
||||
focused: focused,
|
||||
error: error,
|
||||
disabled: disabled,
|
||||
"no-label": !label,
|
||||
})}
|
||||
onFocus={this.handleComponentFocus}
|
||||
onBlur={this.handleComponentBlur}
|
||||
@@ -456,15 +495,17 @@ class TextField extends React.Component<TextFieldProps, TextFieldState> {
|
||||
>
|
||||
{decoration?.startDecoration && <>{decoration.startDecoration}</>}
|
||||
<div className="wave-textfield-inner">
|
||||
<label
|
||||
className={cn("wave-textfield-inner-label", {
|
||||
float: this.state.hasContent || this.state.focused || placeholder,
|
||||
"offset-left": decoration?.startDecoration,
|
||||
})}
|
||||
htmlFor={label}
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
<If condition={label}>
|
||||
<label
|
||||
className={cn("wave-textfield-inner-label", {
|
||||
float: this.state.hasContent || this.state.focused || placeholder,
|
||||
"offset-left": decoration?.startDecoration,
|
||||
})}
|
||||
htmlFor={label}
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
</If>
|
||||
<input
|
||||
className={cn("wave-textfield-inner-input", { "offset-left": decoration?.startDecoration })}
|
||||
ref={this.inputRef}
|
||||
@@ -774,7 +815,7 @@ class InfoMessage extends React.Component<{ width: number; children: React.React
|
||||
function LinkRenderer(props: any): any {
|
||||
let newUrl = "https://extern?" + encodeURIComponent(props.href);
|
||||
return (
|
||||
<a href={newUrl} target="_blank" rel={"noopener"}>
|
||||
<a href={newUrl} target="_blank" rel={"noopener"}>
|
||||
{props.children}
|
||||
</a>
|
||||
);
|
||||
@@ -789,9 +830,59 @@ function CodeRenderer(props: any): any {
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class Markdown extends React.Component<{ text: string; style?: any; extraClassName?: string }, {}> {
|
||||
class CodeBlockMarkdown extends React.Component<{ children: React.ReactNode; codeSelectSelectedIndex?: number }, {}> {
|
||||
blockIndex: number;
|
||||
blockRef: React.RefObject<HTMLPreElement>;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.blockRef = React.createRef();
|
||||
this.blockIndex = GlobalModel.inputModel.addCodeBlockToCodeSelect(this.blockRef);
|
||||
}
|
||||
|
||||
render() {
|
||||
let clickHandler: (e: React.MouseEvent<HTMLElement>, blockIndex: number) => void;
|
||||
let inputModel = GlobalModel.inputModel;
|
||||
clickHandler = (e: React.MouseEvent<HTMLElement>, blockIndex: number) => {
|
||||
inputModel.setCodeSelectSelectedCodeBlock(blockIndex);
|
||||
};
|
||||
let selected = this.blockIndex == this.props.codeSelectSelectedIndex;
|
||||
return (
|
||||
<pre
|
||||
ref={this.blockRef}
|
||||
className={cn({ selected: selected })}
|
||||
onClick={(event) => clickHandler(event, this.blockIndex)}
|
||||
>
|
||||
{this.props.children}
|
||||
</pre>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class Markdown extends React.Component<
|
||||
{ text: string; style?: any; extraClassName?: string; codeSelect?: boolean },
|
||||
{}
|
||||
> {
|
||||
CodeBlockRenderer(props: any, codeSelect: boolean, codeSelectIndex: number): any {
|
||||
if (codeSelect) {
|
||||
return <CodeBlockMarkdown codeSelectSelectedIndex={codeSelectIndex}>{props.children}</CodeBlockMarkdown>;
|
||||
} else {
|
||||
let 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 markdownComponents = {
|
||||
a: LinkRenderer,
|
||||
h1: (props) => HeaderRenderer(props, 1),
|
||||
@@ -800,7 +891,8 @@ class Markdown extends React.Component<{ text: string; style?: any; extraClassNa
|
||||
h4: (props) => HeaderRenderer(props, 4),
|
||||
h5: (props) => HeaderRenderer(props, 5),
|
||||
h6: (props) => HeaderRenderer(props, 6),
|
||||
code: CodeRenderer,
|
||||
code: (props) => CodeRenderer(props),
|
||||
pre: (props) => this.CodeBlockRenderer(props, codeSelect, curCodeSelectIndex),
|
||||
};
|
||||
return (
|
||||
<div className={cn("markdown content", this.props.extraClassName)} style={this.props.style}>
|
||||
@@ -1082,16 +1174,18 @@ class Dropdown extends React.Component<DropdownProps, DropdownState> {
|
||||
}
|
||||
|
||||
interface ModalHeaderProps {
|
||||
onClose: () => void;
|
||||
onClose?: () => void;
|
||||
title: string;
|
||||
}
|
||||
|
||||
const ModalHeader: React.FC<ModalHeaderProps> = ({ onClose, title }) => (
|
||||
<div className="wave-modal-header">
|
||||
{<div className="wave-modal-title">{title}</div>}
|
||||
<IconButton theme="secondary" variant="ghost" onClick={onClose}>
|
||||
<i className="fa-sharp fa-solid fa-xmark"></i>
|
||||
</IconButton>
|
||||
<If condition={onClose}>
|
||||
<IconButton theme="secondary" variant="ghost" onClick={onClose}>
|
||||
<i className="fa-sharp fa-solid fa-xmark"></i>
|
||||
</IconButton>
|
||||
</If>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1141,10 +1235,63 @@ class Modal extends React.Component<ModalProps> {
|
||||
}
|
||||
|
||||
render() {
|
||||
return ReactDOM.createPortal(this.renderModal(), document.getElementById("app") );
|
||||
return ReactDOM.createPortal(this.renderModal(), document.getElementById("app"));
|
||||
}
|
||||
}
|
||||
|
||||
interface StatusIndicatorProps {
|
||||
level: StatusIndicatorLevel;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
class StatusIndicator extends React.Component<StatusIndicatorProps> {
|
||||
render() {
|
||||
const statusIndicatorLevel = this.props.level;
|
||||
let statusIndicator = null;
|
||||
if (statusIndicatorLevel != StatusIndicatorLevel.None) {
|
||||
let statusIndicatorClass = null;
|
||||
switch (statusIndicatorLevel) {
|
||||
case StatusIndicatorLevel.Output:
|
||||
statusIndicatorClass = "output";
|
||||
break;
|
||||
case StatusIndicatorLevel.Success:
|
||||
statusIndicatorClass = "success";
|
||||
break;
|
||||
case StatusIndicatorLevel.Error:
|
||||
statusIndicatorClass = "error";
|
||||
break;
|
||||
}
|
||||
statusIndicator = (
|
||||
<div
|
||||
className={`${this.props.className} fa-sharp fa-solid fa-circle-small status-indicator ${statusIndicatorClass}`}
|
||||
></div>
|
||||
);
|
||||
}
|
||||
return statusIndicator;
|
||||
}
|
||||
}
|
||||
|
||||
function ShowWaveShellInstallPrompt(callbackFn: () => void) {
|
||||
let message: string = `
|
||||
In order to use Wave's advanced features like unified history and persistent sessions, Wave installs a small, open-source helper program called WaveShell on your remote machine. WaveShell does not open any external ports and only communicates with your *local* Wave terminal instance over ssh. For more information please see [the docs](https://docs.waveterm.dev/reference/waveshell).
|
||||
`;
|
||||
message = message.trim();
|
||||
let prtn = GlobalModel.showAlert({
|
||||
message: message,
|
||||
confirm: true,
|
||||
markdown: true,
|
||||
confirmflag: appconst.ConfirmKey_HideShellPrompt,
|
||||
});
|
||||
prtn.then((confirm) => {
|
||||
if (!confirm) {
|
||||
return;
|
||||
}
|
||||
if (callbackFn) {
|
||||
callbackFn();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export {
|
||||
CmdStrCode,
|
||||
Toggle,
|
||||
@@ -1166,4 +1313,6 @@ export {
|
||||
LinkButton,
|
||||
Status,
|
||||
Modal,
|
||||
StatusIndicator,
|
||||
ShowWaveShellInstallPrompt,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
@import "../../../app/common/themes/themes.less";
|
||||
|
||||
.about-modal {
|
||||
.wave-modal-content {
|
||||
gap: 24px;
|
||||
|
||||
.wave-modal-body {
|
||||
margin-bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 24px;
|
||||
|
||||
.about-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
align-self: stretch;
|
||||
width: 100%;
|
||||
|
||||
.logo-wrapper {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
flex-shrink: 0;
|
||||
|
||||
img {
|
||||
border-radius: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.text-wrapper {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
align-self: stretch;
|
||||
font-style: normal;
|
||||
line-height: 20px;
|
||||
|
||||
div:first-child {
|
||||
color: @term-bright-white;
|
||||
font-size: 14.5px;
|
||||
}
|
||||
|
||||
div:last-child {
|
||||
color: @term-white;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.status {
|
||||
div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
|
||||
i {
|
||||
font-size: 16px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
div:first-child + div {
|
||||
color: @term-white;
|
||||
}
|
||||
}
|
||||
|
||||
.status.updated {
|
||||
div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
|
||||
i {
|
||||
color: @term-green;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status.outdated {
|
||||
div {
|
||||
i {
|
||||
color: @term-yellow;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.about-section:nth-child(3) {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
|
||||
.wave-button-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
i {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.about-section:last-child {
|
||||
margin-bottom: 24px;
|
||||
color: @term-white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
// Copyright 2023, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import * as React from "react";
|
||||
import * as mobxReact from "mobx-react";
|
||||
import * as mobx from "mobx";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
import { GlobalModel } from "../../../model/model";
|
||||
import { Modal, LinkButton } from "../common";
|
||||
import * as util from "../../../util/util";
|
||||
|
||||
import logo from "../../assets/waveterm-logo-with-bg.svg";
|
||||
import "./about.less";
|
||||
|
||||
// @ts-ignore
|
||||
const VERSION = __WAVETERM_VERSION__;
|
||||
// @ts-ignore
|
||||
let BUILD = __WAVETERM_BUILD__;
|
||||
|
||||
@mobxReact.observer
|
||||
class AboutModal extends React.Component<{}, {}> {
|
||||
@boundMethod
|
||||
closeModal(): void {
|
||||
mobx.action(() => {
|
||||
GlobalModel.modalsModel.popModal();
|
||||
})();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
isUpToDate(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
updateApp(): void {
|
||||
// GlobalCommandRunner.updateApp();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
getStatus(isUpToDate: boolean): JSX.Element {
|
||||
// TODO no up-to-date status reporting
|
||||
return (
|
||||
<div className="status updated">
|
||||
<div className="text-selectable">
|
||||
Client Version {VERSION} ({BUILD})
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
if (isUpToDate) {
|
||||
return (
|
||||
<div className="status updated">
|
||||
<div>
|
||||
<i className="fa-sharp fa-solid fa-circle-check" />
|
||||
<span>Up to Date</span>
|
||||
</div>
|
||||
<div className="selectable">
|
||||
Client Version {VERSION} ({BUILD})
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="status outdated">
|
||||
<div>
|
||||
<i className="fa-sharp fa-solid fa-triangle-exclamation" />
|
||||
<span>Outdated Version</span>
|
||||
</div>
|
||||
<div className="selectable">
|
||||
Client Version {VERSION} ({BUILD})
|
||||
</div>
|
||||
<div>
|
||||
<button onClick={this.updateApp} className="button color-green text-secondary">
|
||||
Update
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Modal className="about-modal">
|
||||
<Modal.Header onClose={this.closeModal} title="About" />
|
||||
<div className="wave-modal-body">
|
||||
<div className="about-section">
|
||||
<div className="logo-wrapper">
|
||||
<img src={logo} alt="logo" />
|
||||
</div>
|
||||
<div className="text-wrapper">
|
||||
<div>Wave Terminal</div>
|
||||
<div className="text-standard">
|
||||
Modern Terminal for
|
||||
<br />
|
||||
Seamless Workflow
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="about-section text-standard">{this.getStatus(this.isUpToDate())}</div>
|
||||
<div className="about-section">
|
||||
<LinkButton
|
||||
className="secondary solid"
|
||||
href={util.makeExternLink("https://github.com/wavetermdev/waveterm")}
|
||||
target="_blank"
|
||||
leftIcon={<i className="fa-brands fa-github"></i>}
|
||||
>
|
||||
Github
|
||||
</LinkButton>
|
||||
<LinkButton
|
||||
className="secondary solid"
|
||||
href={util.makeExternLink("https://www.waveterm.dev/")}
|
||||
target="_blank"
|
||||
leftIcon={<i className="fa-sharp fa-light fa-globe"></i>}
|
||||
>
|
||||
Website
|
||||
</LinkButton>
|
||||
<LinkButton
|
||||
className="secondary solid"
|
||||
href={util.makeExternLink(
|
||||
"https://github.com/wavetermdev/waveterm/blob/main/acknowledgements/README.md"
|
||||
)}
|
||||
target="_blank"
|
||||
rel={"noopener"}
|
||||
leftIcon={<i className="fa-sharp fa-light fa-heart"></i>}
|
||||
>
|
||||
Acknowledgements
|
||||
</LinkButton>
|
||||
</div>
|
||||
<div className="about-section text-standard">© 2023 Command Line Inc.</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export { AboutModal };
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user