merge v0.8.13 changes

This commit is contained in:
sawka 2024-10-24 17:02:35 -07:00
commit e1d538ed8b
19 changed files with 218 additions and 33 deletions

View File

@ -3,7 +3,7 @@
# For more information on the Windows Code Signing, see https://docs.digicert.com/en/digicert-keylocker/ci-cd-integrations/plugins/github-custom-action-for-keypair-signing.html and https://docs.digicert.com/en/digicert-keylocker/signing-tools/sign-authenticode-with-electron-builder-using-ksp-integration.html # For more information on the Windows Code Signing, see https://docs.digicert.com/en/digicert-keylocker/ci-cd-integrations/plugins/github-custom-action-for-keypair-signing.html and https://docs.digicert.com/en/digicert-keylocker/signing-tools/sign-authenticode-with-electron-builder-using-ksp-integration.html
name: Build Helper name: Build Helper
run-name: Build ${{ github.ref_name }}${{ github.event_name == 'workflow_dispatch' && ' - Manual'}} run-name: Build ${{ github.ref_name }}${{ github.event_name == 'workflow_dispatch' && ' - Manual' || '' }}
on: on:
push: push:
tags: tags:

View File

@ -16,6 +16,9 @@ jobs:
with: with:
version: 3.x version: 3.x
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Install Snapcraft
run: sudo snap install snapcraft --classic
shell: bash
- name: Publish from staging - name: Publish from staging
run: "task artifacts:publish:${{ github.ref_name }}" run: "task artifacts:publish:${{ github.ref_name }}"
env: env:
@ -23,6 +26,16 @@ jobs:
AWS_SECRET_ACCESS_KEY: "${{ secrets.PUBLISHER_KEY_SECRET }}" AWS_SECRET_ACCESS_KEY: "${{ secrets.PUBLISHER_KEY_SECRET }}"
AWS_DEFAULT_REGION: us-west-2 AWS_DEFAULT_REGION: us-west-2
shell: bash shell: bash
- name: Download Snap from Release
uses: robinraju/release-downloader@v1
with:
tag: ${{github.ref_name}}
fileName: "*.snap"
- name: Publish to Snapcraft
run: "task artifacts:snap:publish:${{ github.ref_name }}"
env:
SNAPCRAFT_STORE_CREDENTIALS: "${{secrets.SNAPCRAFT_LOGIN_CREDS}}"
shell: bash
bump-winget: bump-winget:
if: ${{ startsWith(github.ref, 'refs/tags/') && !contains(github.ref_name, 'beta') }} if: ${{ startsWith(github.ref, 'refs/tags/') && !contains(github.ref_name, 'beta') }}
runs-on: windows-latest runs-on: windows-latest
@ -33,8 +46,8 @@ jobs:
with: with:
version: 3.x version: 3.x
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Submit Winget version bump - name: Submit WinGet version bump
run: "task artifacts:publish:winget:${{ github.ref_name }}" run: "task artifacts:winget:publish:${{ github.ref_name }}"
env: env:
GITHUB_TOKEN: ${{ secrets.WINGET_BUMP_PAT }} GITHUB_TOKEN: ${{ secrets.WINGET_BUMP_PAT }}
shell: pwsh shell: pwsh

View File

@ -35,6 +35,19 @@ Arch:
sudo pacman -S zip zig sudo pacman -S zip zig
``` ```
##### For packaging
For packaging, the following additional packages are required:
- `fpm` — If you're on x64 you can skip this. If you're on ARM64, install fpm via [Gem](https://rubygems.org/gems/fpm)
- `rpm` — If you're not on Fedora, install RPM via your package manager.
- `snapd` — If your distro doesn't already include it, [install `snapd`](https://snapcraft.io/docs/installing-snapd)
- `lxd` — [Installation instructions](https://canonical.com/lxd/install)
- `snapcraft` — Run `sudo snap install snapcraft --classic`
- `libarchive-tools` — Install via your package manager
- `libopenjp2-tools` — Install via your package manager
- `squashfs-tools` — Install via your package manager
#### Windows #### Windows
You will need the GNU build toolchain installed in order for Go to work on Windows. In most cases, this requires installing MinGW-w64. You will need the GNU build toolchain installed in order for Go to work on Windows. In most cases, this requires installing MinGW-w64.
@ -55,7 +68,7 @@ Download and install Go via your package manager or directly from the website: h
### NodeJS ### NodeJS
Make sure you have a recent version of NodeJS installed (>= 20). Make sure you have a NodeJS 20 LTS installed.
See NodeJS's website for platform-specific instructions: https://nodejs.org/en/download See NodeJS's website for platform-specific instructions: https://nodejs.org/en/download
@ -118,6 +131,12 @@ Run the following command to generate a production build and package it. This le
task package task package
``` ```
If you're on Linux ARM64, run the following:
```sh
USE_SYSTEM_FPM=1 task package
```
## Debugging ## Debugging
### Frontend logs ### Frontend logs

View File

@ -29,6 +29,12 @@ Also available as a Homebrew Cask for macOS:
brew install --cask wave brew install --cask wave
``` ```
Also available as a [Snap](https://snapcraft.io/waveterm) on Linux:
```bash
sudo snap install waveterm --classic
```
Also available via the Windows Package Manager: Also available via the Windows Package Manager:
```Powershell ```Powershell

View File

@ -230,11 +230,20 @@ tasks:
echo "https://$SUFFIX" echo "https://$SUFFIX"
fi fi
done done
artifacts:snap:publish:*:
desc: Publishes the specified artifacts version to Snapcraft.
vars:
UP_VERSION: '{{ replace "v" "" (index .MATCH 0)}}'
CHANNEL: '{{if contains "beta" .UP_VERSION}}beta{{else}}beta,stable{{end}}'
cmd: |
echo "Releasing to channels: [{{.CHANNEL}}]"
snapcraft upload --release={{.CHANNEL}} waveterm_{{.UP_VERSION}}_arm64.snap
snapcraft upload --release={{.CHANNEL}} waveterm_{{.UP_VERSION}}_amd64.snap
artifacts:publish:winget:*: artifacts:winget:publish:*:
desc: Submits a version bump request to WinGet for the latest release. desc: Submits a version bump request to WinGet for the latest release.
status: status:
- exit {{if contains UP_VERSION "beta"}}0{{else}}1{{end}} - exit {{if contains "beta" .UP_VERSION}}0{{else}}1{{end}}
vars: vars:
UP_VERSION: '{{ replace "v" "" (index .MATCH 0)}}' UP_VERSION: '{{ replace "v" "" (index .MATCH 0)}}'
cmd: | cmd: |

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 37 KiB

View File

@ -1,20 +1,84 @@
<svg width="1024" height="727" viewBox="0 0 1024 727" fill="none" xmlns="http://www.w3.org/2000/svg"> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<path <svg
d="M328.87 332.526C280.49 332.526 253.744 364.034 239.191 434.533L20.5 403.025C47.2464 172.231 136.925 60.3787 314.317 60.3787C445.688 60.3787 574.307 160.022 637.24 160.022C686.012 160.022 712.365 126.151 726.918 58.0156L945.609 89.5233C921.223 320.711 829.184 432.563 651.793 432.563C518.454 432.17 394.556 332.526 328.87 332.526Z" width="1024"
fill="url(#paint0_linear_1814_3217)" /> height="1024"
<path viewBox="0 0 1024 1024"
d="M390.87 558.061C342.49 558.061 315.744 589.569 301.191 660.067L82.5 628.559C109.246 397.765 198.925 285.519 376.317 285.519C507.295 285.519 636.307 385.162 699.239 385.162C748.012 385.162 774.365 351.292 788.918 283.156L1007.61 314.664C983.223 545.852 891.184 657.704 713.793 657.704C580.454 657.704 456.556 558.061 390.87 558.061Z" fill="none"
fill="url(#paint1_linear_1814_3217)" /> version="1.1"
<defs> id="svg5"
<linearGradient id="paint0_linear_1814_3217" x1="20.5503" y1="246.309" x2="945.797" y2="246.309" sodipodi:docname="appicon-windows.svg"
gradientUnits="userSpaceOnUse"> inkscape:version="1.4 (e7c3feb1, 2024-10-09)"
<stop offset="0.1418" stop-color="#1F4D22" /> xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
<stop offset="0.8656" stop-color="#418D31" /> xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
</linearGradient> xmlns="http://www.w3.org/2000/svg"
<linearGradient id="paint1_linear_1814_3217" x1="82.5673" y1="471.774" x2="1007.81" y2="471.774" xmlns:svg="http://www.w3.org/2000/svg">
gradientUnits="userSpaceOnUse"> <sodipodi:namedview
<stop offset="0.2223" stop-color="#418D31" /> id="namedview5"
<stop offset="0.7733" stop-color="#58C142" /> pagecolor="#ffffff"
</linearGradient> bordercolor="#000000"
</defs> borderopacity="0.25"
</svg> inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
borderlayer="false"
inkscape:zoom="0.99449794"
inkscape:cx="511.31328"
inkscape:cy="524.3852"
inkscape:window-width="2288"
inkscape:window-height="1186"
inkscape:window-x="3103"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="svg5" />
<g
id="g5"
transform="translate(-2.055,152.9587)">
<path
d="m 328.87,332.526 c -48.38,0 -75.126,31.508 -89.679,102.007 L 20.5,403.025 C 47.2464,172.231 136.925,60.3787 314.317,60.3787 c 131.371,0 259.99,99.6433 322.923,99.6433 48.772,0 75.125,-33.871 89.678,-102.0064 L 945.609,89.5233 C 921.223,320.711 829.184,432.563 651.793,432.563 518.454,432.17 394.556,332.526 328.87,332.526 Z"
fill="url(#paint0_linear_1814_3217)"
id="path1"
style="fill:url(#paint0_linear_1814_3217)" />
<path
d="m 390.87,558.061 c -48.38,0 -75.126,31.508 -89.679,102.006 L 82.5,628.559 c 26.746,-230.794 116.425,-343.04 293.817,-343.04 130.978,0 259.99,99.643 322.922,99.643 48.773,0 75.126,-33.87 89.679,-102.006 l 218.692,31.508 c -24.387,231.188 -116.426,343.04 -293.817,343.04 -133.339,0 -257.237,-99.643 -322.923,-99.643 z"
fill="url(#paint1_linear_1814_3217)"
id="path2"
style="fill:url(#paint1_linear_1814_3217)" />
</g>
<defs
id="defs5">
<linearGradient
id="paint0_linear_1814_3217"
x1="20.550301"
y1="246.30901"
x2="945.797"
y2="246.30901"
gradientUnits="userSpaceOnUse">
<stop
offset="0.1418"
stop-color="#1F4D22"
id="stop2" />
<stop
offset="0.8656"
stop-color="#418D31"
id="stop3" />
</linearGradient>
<linearGradient
id="paint1_linear_1814_3217"
x1="82.567299"
y1="471.77399"
x2="1007.81"
y2="471.77399"
gradientUnits="userSpaceOnUse">
<stop
offset="0.2223"
stop-color="#418D31"
id="stop4" />
<stop
offset="0.7733"
stop-color="#58C142"
id="stop5" />
</linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
build/icons/128x128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
build/icons/16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
build/icons/256x256.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
build/icons/32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
build/icons/512x512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -88,6 +88,7 @@ const config = {
base: "core22", base: "core22",
confinement: "classic", confinement: "classic",
allowNativeWayland: true, allowNativeWayland: true,
artifactName: "${name}_${version}_${arch}.${ext}",
}, },
publish: { publish: {
provider: "generic", provider: "generic",

View File

@ -17,6 +17,7 @@ import {
getConnStatusAtom, getConnStatusAtom,
getSettingsKeyAtom, getSettingsKeyAtom,
globalStore, globalStore,
useBlockAtom,
useSettingsPrefixAtom, useSettingsPrefixAtom,
} from "@/store/global"; } from "@/store/global";
import * as services from "@/store/services"; import * as services from "@/store/services";
@ -55,6 +56,8 @@ class TermViewModel {
termWshClient: TermWshClient; termWshClient: TermWshClient;
shellProcStatusRef: React.MutableRefObject<string>; shellProcStatusRef: React.MutableRefObject<string>;
vdomBlockId: jotai.Atom<string>; vdomBlockId: jotai.Atom<string>;
fontSizeAtom: jotai.Atom<number>;
termThemeNameAtom: jotai.Atom<string>;
constructor(blockId: string, nodeModel: NodeModel) { constructor(blockId: string, nodeModel: NodeModel) {
this.viewType = "term"; this.viewType = "term";
@ -145,6 +148,25 @@ class TermViewModel {
const connAtom = getConnStatusAtom(connName); const connAtom = getConnStatusAtom(connName);
return get(connAtom); return get(connAtom);
}); });
this.fontSizeAtom = useBlockAtom(blockId, "fontsizeatom", () => {
return jotai.atom<number>((get) => {
const blockData = get(this.blockAtom);
const fsSettingsAtom = getSettingsKeyAtom("term:fontsize");
const settingsFontSize = get(fsSettingsAtom);
const rtnFontSize = blockData?.meta?.["term:fontsize"] ?? settingsFontSize ?? 12;
if (typeof rtnFontSize != "number" || isNaN(rtnFontSize) || rtnFontSize < 4 || rtnFontSize > 64) {
return 12;
}
return rtnFontSize;
});
});
this.termThemeNameAtom = useBlockAtom(blockId, "termthemeatom", () => {
return jotai.atom<string>((get) => {
const blockData = get(this.blockAtom);
const settingsKeyAtom = getSettingsKeyAtom("term:theme");
return blockData?.meta?.["term:theme"] ?? get(settingsKeyAtom) ?? "default-dark";
});
});
} }
setTermMode(mode: "term" | "vdom") { setTermMode(mode: "term" | "vdom") {
@ -257,6 +279,10 @@ class TermViewModel {
const fullConfig = globalStore.get(atoms.fullConfigAtom); const fullConfig = globalStore.get(atoms.fullConfigAtom);
const termThemes = fullConfig?.termthemes ?? {}; const termThemes = fullConfig?.termthemes ?? {};
const termThemeKeys = Object.keys(termThemes); const termThemeKeys = Object.keys(termThemes);
const curThemeName = globalStore.get(this.termThemeNameAtom);
const defaultFontSize = globalStore.get(getSettingsKeyAtom("term:fontsize")) ?? 12;
const blockData = globalStore.get(this.blockAtom);
const overrideFontSize = blockData?.meta?.["term:fontsize"];
termThemeKeys.sort((a, b) => { termThemeKeys.sort((a, b) => {
return termThemes[a]["display:order"] - termThemes[b]["display:order"]; return termThemes[a]["display:order"] - termThemes[b]["display:order"];
@ -265,13 +291,45 @@ class TermViewModel {
const submenu: ContextMenuItem[] = termThemeKeys.map((themeName) => { const submenu: ContextMenuItem[] = termThemeKeys.map((themeName) => {
return { return {
label: termThemes[themeName]["display:name"] ?? themeName, label: termThemes[themeName]["display:name"] ?? themeName,
type: "checkbox",
checked: curThemeName == themeName,
click: () => this.setTerminalTheme(themeName), click: () => this.setTerminalTheme(themeName),
}; };
}); });
const fontSizeSubMenu: ContextMenuItem[] = [6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18].map(
(fontSize: number) => {
return {
label: fontSize.toString() + "px",
type: "checkbox",
checked: overrideFontSize == fontSize,
click: () => {
RpcApi.SetMetaCommand(TabRpcClient, {
oref: WOS.makeORef("block", this.blockId),
meta: { "term:fontsize": fontSize },
});
},
};
}
);
fontSizeSubMenu.unshift({
label: "Default (" + defaultFontSize + "px)",
type: "checkbox",
checked: overrideFontSize == null,
click: () => {
RpcApi.SetMetaCommand(TabRpcClient, {
oref: WOS.makeORef("block", this.blockId),
meta: { "term:fontsize": null },
});
},
});
fullMenu.push({ fullMenu.push({
label: "Themes", label: "Themes",
submenu: submenu, submenu: submenu,
}); });
fullMenu.push({
label: "Font Size",
submenu: fontSizeSubMenu,
});
fullMenu.push({ type: "separator" }); fullMenu.push({ type: "separator" });
fullMenu.push({ fullMenu.push({
label: "Force Restart Controller", label: "Force Restart Controller",
@ -386,6 +444,8 @@ const TerminalView = ({ blockId, model }: TerminalViewProps) => {
} }
const termModeRef = React.useRef(termMode); const termModeRef = React.useRef(termMode);
const termFontSize = jotai.useAtomValue(model.fontSizeAtom);
React.useEffect(() => { React.useEffect(() => {
const fullConfig = globalStore.get(atoms.fullConfigAtom); const fullConfig = globalStore.get(atoms.fullConfigAtom);
const termTheme = computeTheme(fullConfig, blockData?.meta?.["term:theme"]); const termTheme = computeTheme(fullConfig, blockData?.meta?.["term:theme"]);
@ -404,12 +464,13 @@ const TerminalView = ({ blockId, model }: TerminalViewProps) => {
if (termScrollback > 10000) { if (termScrollback > 10000) {
termScrollback = 10000; termScrollback = 10000;
} }
const wasFocused = termRef.current != null && globalStore.get(model.nodeModel.isFocused);
const termWrap = new TermWrap( const termWrap = new TermWrap(
blockId, blockId,
connectElemRef.current, connectElemRef.current,
{ {
theme: themeCopy, theme: themeCopy,
fontSize: termSettings?.["term:fontsize"] ?? 12, fontSize: termFontSize,
fontFamily: termSettings?.["term:fontfamily"] ?? "Hack", fontFamily: termSettings?.["term:fontfamily"] ?? "Hack",
drawBoldTextInBrightColors: false, drawBoldTextInBrightColors: false,
fontWeight: "normal", fontWeight: "normal",
@ -429,11 +490,16 @@ const TerminalView = ({ blockId, model }: TerminalViewProps) => {
}); });
rszObs.observe(connectElemRef.current); rszObs.observe(connectElemRef.current);
termWrap.initTerminal(); termWrap.initTerminal();
if (wasFocused) {
setTimeout(() => {
model.giveFocus();
}, 10);
}
return () => { return () => {
termWrap.dispose(); termWrap.dispose();
rszObs.disconnect(); rszObs.disconnect();
}; };
}, [blockId, termSettings]); }, [blockId, termSettings, termFontSize]);
React.useEffect(() => { React.useEffect(() => {
if (termModeRef.current == "vdom" && termMode == "term") { if (termModeRef.current == "vdom" && termMode == "term") {

View File

@ -7,7 +7,7 @@
"productName": "Wave", "productName": "Wave",
"description": "Open-Source AI-Native Terminal Built for Seamless Workflows", "description": "Open-Source AI-Native Terminal Built for Seamless Workflows",
"license": "Apache-2.0", "license": "Apache-2.0",
"version": "0.8.12", "version": "0.8.13-beta.3",
"homepage": "https://waveterm.dev", "homepage": "https://waveterm.dev",
"build": { "build": {
"appId": "dev.commandline.waveterm" "appId": "dev.commandline.waveterm"

View File

@ -20,7 +20,7 @@ import (
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
) )
var userHostRe = regexp.MustCompile(`^([a-zA-Z0-9][a-zA-Z0-9._@\\-]*@)?([a-z0-9][a-z0-9.-]*)(?::([0-9]+))?$`) var userHostRe = regexp.MustCompile(`^([a-zA-Z0-9][a-zA-Z0-9._@\\-]*@)?([a-zA-Z0-9][a-zA-Z0-9.-]*)(?::([0-9]+))?$`)
func ParseOpts(input string) (*SSHOpts, error) { func ParseOpts(input string) (*SSHOpts, error) {
m := userHostRe.FindStringSubmatch(input) m := userHostRe.FindStringSubmatch(input)

View File

@ -4,6 +4,8 @@ import (
"io" "io"
"os" "os"
"os/exec" "os/exec"
"runtime"
"syscall"
"time" "time"
"github.com/creack/pty" "github.com/creack/pty"
@ -43,7 +45,11 @@ func (cw CmdWrap) KillGraceful(timeout time.Duration) {
if cw.Cmd.ProcessState != nil && cw.Cmd.ProcessState.Exited() { if cw.Cmd.ProcessState != nil && cw.Cmd.ProcessState.Exited() {
return return
} }
cw.Cmd.Process.Signal(os.Interrupt) if runtime.GOOS == "windows" {
cw.Cmd.Process.Signal(os.Interrupt)
} else {
cw.Cmd.Process.Signal(syscall.SIGTERM)
}
go func() { go func() {
time.Sleep(timeout) time.Sleep(timeout)
if cw.Cmd.ProcessState == nil || !cw.Cmd.ProcessState.Exited() { if cw.Cmd.ProcessState == nil || !cw.Cmd.ProcessState.Exited() {

View File

@ -863,9 +863,9 @@ func AtomicRenameCopy(dstPath string, srcPath string, perms os.FileMode) error {
if err != nil { if err != nil {
return err return err
} }
defer dstFd.Close()
_, err = io.Copy(dstFd, srcFd) _, err = io.Copy(dstFd, srcFd)
if err != nil { if err != nil {
dstFd.Close()
return err return err
} }
err = dstFd.Close() err = dstFd.Close()

View File

@ -231,10 +231,11 @@ func checkIsReadOnly(path string, fileInfo fs.FileInfo, exists bool) bool {
return false return false
} }
tmpFileName := filepath.Join(dirName, "wsh-tmp-"+randHexStr) tmpFileName := filepath.Join(dirName, "wsh-tmp-"+randHexStr)
_, err = os.Create(tmpFileName) fd, err := os.Create(tmpFileName)
if err != nil { if err != nil {
return true return true
} }
fd.Close()
os.Remove(tmpFileName) os.Remove(tmpFileName)
return false return false
} }