Compare commits

..

14 Commits

Author SHA1 Message Date
wave-builder[bot] de08cc8340 chore: bump package version to 0.8.0 2024-09-20 21:11:56 +00:00
wave-builder[bot] 190a53662c chore: bump package version to 0.8.0-beta.15 2024-09-20 20:49:55 +00:00
Evan Simkowitz b19bef2ffb remove vscode storybook task, move to taskfile 2024-09-20 13:39:06 -07:00
Evan Simkowitz af38ebab54 typo in last pr 2024-09-20 13:31:12 -07:00
Evan Simkowitz 38f07ae3f9 Dismiss update banner after 10s on error (#800) 2024-09-20 13:29:37 -07:00
wave-builder[bot] 8423334cde chore: bump package version to 0.8.0-beta.14 2024-09-20 19:51:59 +00:00
Sylvie Crowe bc057876a1 Powershell Execution Policy (#798)
Bypasses the Execution Policy for setting the script that adds wsh to
the path.
2024-09-20 12:48:29 -07:00
Mike Sawka d344fd9b6d fix cpu plot timing issue (#799) 2024-09-20 12:47:10 -07:00
Mike Sawka 31414a7536 don't open files when stating directory (#797) 2024-09-20 12:25:46 -07:00
Mike Sawka e281cebb56 tips update (add to starter layout) (#796) 2024-09-20 12:17:49 -07:00
Mike Sawka 65c446e4a1 wash meta.url through url search (#795) 2024-09-20 11:34:04 -07:00
Mike Sawka 047513bf3a default url, default search, open in external browser (#794) 2024-09-20 11:24:37 -07:00
Evan Simkowitz 78f838439a prevent running this workflow on unprotected branches 2024-09-20 09:58:35 -07:00
wave-builder[bot] 470dfb19b7 chore: bump package version to 0.8.0-beta.13 2024-09-20 16:51:52 +00:00
21 changed files with 189 additions and 123 deletions
+1
View File
@@ -58,6 +58,7 @@ jobs:
shell: bash
- name: "Push version bump: ${{ steps.bump-version.outputs.WAVETERM_VERSION }}"
if: github.ref_protected
run: |
# Create a new commit for the package version bump in package.json
export VERSION=${{ steps.bump-version.outputs.WAVETERM_VERSION }}
-18
View File
@@ -1,18 +0,0 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Start Storybook",
"type": "shell",
"command": "yarn storybook",
"presentation": {
"reveal": "silent",
"panel": "shared"
},
"runOptions": {
"instanceLimit": 1,
"runOn": "folderOpen"
}
}
]
}
+15 -16
View File
@@ -17,8 +17,7 @@ vars:
tasks:
electron:dev:
desc: Run the Electron application via the Vite dev server (enables hot reloading).
cmds:
- yarn dev
cmd: yarn dev
deps:
- yarn
- build:backend
@@ -28,12 +27,15 @@ tasks:
electron:start:
desc: Run the Electron application directly.
cmds:
- yarn start
cmd: yarn start
deps:
- yarn
- build:backend
storybook:
desc: Start the Storybook server.
cmd: yarn storybook
package:
desc: Package the application for the current platform.
cmds:
@@ -98,12 +100,12 @@ tasks:
requires:
vars:
- ARCHS
cmds:
- cmd: CGO_ENABLED=1 GOARCH={{.GOARCH}} go build -tags "osusergo,netcgo,sqlite_omit_load_extension" -ldflags "{{.GO_LDFLAGS}} -X main.BuildTime=$({{.DATE}} +'%Y%m%d%H%M') -X main.WaveVersion={{.VERSION}}" -o dist/bin/wavesrv.{{if eq .GOARCH "amd64"}}x64{{else}}{{.GOARCH}}{{end}}{{exeExt}} cmd/server/main-server.go
for:
var: ARCHS
split: ","
as: GOARCH
cmd:
cmd: CGO_ENABLED=1 GOARCH={{.GOARCH}} go build -tags "osusergo,netcgo,sqlite_omit_load_extension" -ldflags "{{.GO_LDFLAGS}} -X main.BuildTime=$({{.DATE}} +'%Y%m%d%H%M') -X main.WaveVersion={{.VERSION}}" -o dist/bin/wavesrv.{{if eq .GOARCH "amd64"}}x64{{else}}{{.GOARCH}}{{end}}{{exeExt}} cmd/server/main-server.go
for:
var: ARCHS
split: ","
as: GOARCH
sources:
- "cmd/server/*.go"
- "pkg/**/*.go"
@@ -173,8 +175,7 @@ tasks:
- "pkg/**/*.go"
generates:
- dist/bin/wsh-{{.VERSION}}-{{.GOOS}}.{{.NORMALIZEDARCH}}{{.EXT}}
cmds:
- (CGO_ENABLED=0 GOOS={{.GOOS}} GOARCH={{.GOARCH}} go build -ldflags="-s -w -X main.BuildTime=$({{.DATE}} +'%Y%m%d%H%M') -X main.WaveVersion={{.VERSION}}" -o dist/bin/wsh-{{.VERSION}}-{{.GOOS}}.{{.NORMALIZEDARCH}}{{.EXT}} cmd/wsh/main-wsh.go)
cmd: (CGO_ENABLED=0 GOOS={{.GOOS}} GOARCH={{.GOARCH}} go build -ldflags="-s -w -X main.BuildTime=$({{.DATE}} +'%Y%m%d%H%M') -X main.WaveVersion={{.VERSION}}" -o dist/bin/wsh-{{.VERSION}}-{{.GOOS}}.{{.NORMALIZEDARCH}}{{.EXT}} cmd/wsh/main-wsh.go)
deps:
- go:mod:tidy
internal: true
@@ -251,8 +252,7 @@ tasks:
- yarn.lock
- package.json
- .yarnrc.yml
cmds:
- yarn
cmd: yarn
go:mod:tidy:
desc: Runs `go mod tidy`
@@ -261,5 +261,4 @@ tasks:
- go.sum
sources:
- go.mod
cmds:
- go mod tidy
cmd: go mod tidy
+11
View File
@@ -144,6 +144,17 @@ const QuickTips = () => {
</div>
</div>
</div>
<div className="tip-section-header">Need More Help?</div>
<div className="tip">
<div>
<div>
<a target="_blank" href="https://discord.gg/XfvZ334gwU" rel="noopener">
Join Our Discord
</a>
</div>
</div>
</div>
</div>
</div>
);
+1 -1
View File
@@ -11,7 +11,7 @@ import React, { createRef, useCallback, useEffect, useRef, useState } from "reac
import { debounce } from "throttle-debounce";
import { Tab } from "./tab";
import "./tabbar.less";
import { UpdateStatusBanner } from "./updatestatus";
import { UpdateStatusBanner } from "./updatebanner";
const TAB_DEFAULT_WIDTH = 130;
const TAB_MIN_WIDTH = 100;
+71
View File
@@ -0,0 +1,71 @@
import { Button } from "@/element/button";
import { atoms, getApi } from "@/store/global";
import { useAtomValue } from "jotai";
import { memo, useEffect, useState } from "react";
import "./updatebanner.less";
const UpdateStatusBannerComponent = ({ buttonRef }: { buttonRef: React.RefObject<HTMLButtonElement> }) => {
const appUpdateStatus = useAtomValue(atoms.updaterStatusAtom);
const [updateStatusMessage, setUpdateStatusMessage] = useState<string>();
const [dismissBannerTimeout, setDismissBannerTimeout] = useState<NodeJS.Timeout>();
useEffect(() => {
let message: string;
let dismissBanner = false;
switch (appUpdateStatus) {
case "ready":
message = "Update Available";
break;
case "downloading":
message = "Downloading Update";
break;
case "installing":
message = "Installing Update";
break;
case "error":
message = "Updater Error: Try Checking Again";
dismissBanner = true;
break;
default:
break;
}
setUpdateStatusMessage(message);
// Clear any existing timeout
if (dismissBannerTimeout) {
clearTimeout(dismissBannerTimeout);
}
// If we want to dismiss the banner, set the new timeout, otherwise clear the state
if (dismissBanner) {
setDismissBannerTimeout(
setTimeout(() => {
setUpdateStatusMessage(null);
setDismissBannerTimeout(null);
}, 10000)
);
} else {
setDismissBannerTimeout(null);
}
}, [appUpdateStatus]);
function onClick() {
getApi().installAppUpdate();
}
if (updateStatusMessage) {
return (
<Button
ref={buttonRef}
className="update-available-button"
title={appUpdateStatus === "ready" ? "Click to Install Update" : updateStatusMessage}
onClick={onClick}
disabled={appUpdateStatus !== "ready"}
>
{updateStatusMessage}
</Button>
);
}
};
export const UpdateStatusBanner = memo(UpdateStatusBannerComponent) as typeof UpdateStatusBannerComponent;
-49
View File
@@ -1,49 +0,0 @@
import { Button } from "@/element/button";
import { atoms, getApi } from "@/store/global";
import { useAtomValue } from "jotai";
import { memo } from "react";
import "./updatestatus.less";
const UpdateStatusBannerComponent = ({ buttonRef }: { buttonRef: React.RefObject<HTMLButtonElement> }) => {
const appUpdateStatus = useAtomValue(atoms.updaterStatusAtom);
function onClick() {
getApi().installAppUpdate();
}
let buttonText: string;
switch (appUpdateStatus) {
case "ready":
buttonText = "Update Available";
break;
case "checking":
buttonText = "Checking for Updates";
break;
case "downloading":
buttonText = "Downloading Update";
break;
case "installing":
buttonText = "Installing Update";
break;
case "error":
buttonText = "Updater Error: Try Checking Again";
break;
default:
break;
}
if (buttonText) {
return (
<Button
ref={buttonRef}
className="update-available-button"
title={appUpdateStatus === "ready" ? "Click to Install Update" : buttonText}
onClick={onClick}
disabled={appUpdateStatus !== "ready"}
>
{buttonText}
</Button>
);
}
};
export const UpdateStatusBanner = memo(UpdateStatusBannerComponent) as typeof UpdateStatusBannerComponent;
+8 -3
View File
@@ -149,8 +149,11 @@ class CpuPlotViewModel {
if (initialData == null) {
return;
}
const newData = this.getDefaultData();
const initialDataItems: DataItem[] = initialData.map(convertWaveEventToDataItem);
globalStore.set(this.addDataAtom, initialDataItems);
// splice the initial data into the default data (replacing the newest points)
newData.splice(newData.length - initialDataItems.length, initialDataItems.length, ...initialDataItems);
globalStore.set(this.addDataAtom, newData);
} catch (e) {
console.log("Error loading initial data for cpuplot", e);
} finally {
@@ -158,7 +161,7 @@ class CpuPlotViewModel {
}
}
getDefaultData(): Array<DataItem> {
getDefaultData(): DataItem[] {
// set it back one to avoid backwards line being possible
const numPoints = globalStore.get(this.numPoints);
const currentTime = Date.now() - 1000;
@@ -197,6 +200,8 @@ function CpuPlotView({ model, blockId }: CpuPlotViewProps) {
lastConnName.current = connName;
model.loadInitialData();
}
}, [connStatus.status, connName]);
React.useEffect(() => {
const unsubFn = waveEventSubscribe({
eventType: "sysinfo",
scope: connName,
@@ -209,11 +214,11 @@ function CpuPlotView({ model, blockId }: CpuPlotViewProps) {
addPlotData([dataItem]);
},
});
console.log("subscribe to sysinfo", connName);
return () => {
unsubFn();
};
}, [connName]);
React.useEffect(() => {}, [connName]);
if (connStatus?.status != "connected") {
return null;
}
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
.quicktips-view {
padding: 10px 2px;
padding: 10px 5px;
overflow: auto;
width: 100%;
}
+52 -22
View File
@@ -1,8 +1,10 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import { getApi, openLink } from "@/app/store/global";
import { getApi, openLink, useSettingsKeyAtom } from "@/app/store/global";
import { getSimpleControlShiftAtom } from "@/app/store/keymodel";
import { RpcApi } from "@/app/store/wshclientapi";
import { WindowRpcClient } from "@/app/store/wshrpcutil";
import { NodeModel } from "@/layout/index";
import { WOS, globalStore } from "@/store/global";
import * as services from "@/store/services";
@@ -30,6 +32,7 @@ export class WebViewModel implements ViewModel {
webviewRef: React.RefObject<WebviewTag>;
urlInputRef: React.RefObject<HTMLInputElement>;
nodeModel: NodeModel;
endIconButtons?: jotai.Atom<IconButtonDecl[]>;
constructor(blockId: string, nodeModel: NodeModel) {
this.nodeModel = nodeModel;
@@ -48,7 +51,8 @@ export class WebViewModel implements ViewModel {
this.webviewRef = React.createRef<WebviewTag>();
this.viewText = jotai.atom((get) => {
let url = get(this.blockAtom)?.meta?.url || "";
const defaultUrlAtom = useSettingsKeyAtom("web:defaulturl");
let url = get(this.blockAtom)?.meta?.url || get(defaultUrlAtom);
const currUrl = get(this.url);
if (currUrl !== undefined) {
url = currUrl;
@@ -91,6 +95,22 @@ export class WebViewModel implements ViewModel {
},
] as HeaderElem[];
});
this.endIconButtons = jotai.atom((get) => {
return [
{
elemtype: "iconbutton",
icon: "arrow-up-right-from-square",
title: "Open in External Browser",
click: () => {
const url = this.getUrl();
if (url != null && url != "") {
return getApi().openExternal(this.getUrl());
}
},
},
];
});
}
/**
@@ -199,7 +219,11 @@ export class WebViewModel implements ViewModel {
globalStore.set(this.url, url);
}
ensureUrlScheme(url: string) {
ensureUrlScheme(url: string, searchTemplate: string) {
if (url == null) {
url = "";
}
if (/^(http|https):/.test(url)) {
// If the URL starts with http: or https:, return it as is
return url;
@@ -223,23 +247,10 @@ export class WebViewModel implements ViewModel {
}
// Otherwise, treat it as a search query
return `https://www.google.com/search?q=${encodeURIComponent(url)}`;
}
normalizeUrl(url: string) {
if (!url) {
return url;
}
try {
const parsedUrl = new URL(url);
if (parsedUrl.hostname.startsWith("www.")) {
parsedUrl.hostname = parsedUrl.hostname.slice(4);
}
return parsedUrl.href;
} catch (e) {
return url.replace(/\/+$/, "") + "/";
if (searchTemplate == null) {
return `https://www.google.com/search?q=${encodeURIComponent(url)}`;
}
return searchTemplate.replace("{query}", encodeURIComponent(url));
}
/**
@@ -247,7 +258,9 @@ export class WebViewModel implements ViewModel {
* @param newUrl The new URL to load in the webview.
*/
loadUrl(newUrl: string, reason: string) {
const nextUrl = this.ensureUrlScheme(newUrl);
const defaultSearchAtom = useSettingsKeyAtom("web:defaultsearch");
const searchTemplate = globalStore.get(defaultSearchAtom);
const nextUrl = this.ensureUrlScheme(newUrl, searchTemplate);
console.log("webview loadUrl", reason, nextUrl, "cur=", this.webviewRef?.current.getURL());
if (newUrl != nextUrl) {
globalStore.set(this.url, nextUrl);
@@ -317,8 +330,20 @@ export class WebViewModel implements ViewModel {
return false;
}
getSettingsMenuItems() {
getSettingsMenuItems(): ContextMenuItem[] {
return [
{
label: "Set Homepage",
click: async () => {
const url = this.getUrl();
if (url != null && url != "") {
RpcApi.SetConfigCommand(WindowRpcClient, { "web:defaulturl": url });
}
},
},
{
type: "separator",
},
{
label: this.webviewRef.current?.isDevToolsOpened() ? "Close DevTools" : "Open DevTools",
click: async () => {
@@ -347,7 +372,12 @@ interface WebViewProps {
const WebView = memo(({ model }: WebViewProps) => {
const blockData = jotai.useAtomValue(model.blockAtom);
const metaUrl = blockData?.meta?.url;
const defaultUrlAtom = useSettingsKeyAtom("web:defaulturl");
const defaultUrl = jotai.useAtomValue(defaultUrlAtom);
const defaultSearchAtom = useSettingsKeyAtom("web:defaultsearch");
const defaultSearch = jotai.useAtomValue(defaultSearchAtom);
let metaUrl = blockData?.meta?.url || defaultUrl;
metaUrl = model.ensureUrlScheme(metaUrl, defaultSearch);
const metaUrlRef = React.useRef(metaUrl);
// The initial value of the block metadata URL when the component first renders. Used to set the starting src value for the webview.
+2
View File
@@ -421,6 +421,8 @@ declare global {
"editor:stickyscrollenabled"?: boolean;
"web:*"?: boolean;
"web:openlinksinternally"?: boolean;
"web:defaulturl"?: string;
"web:defaultsearch"?: string;
"blockheader:*"?: boolean;
"blockheader:showblockids"?: boolean;
"autoupdate:*"?: boolean;
+1 -1
View File
@@ -7,7 +7,7 @@
"productName": "Wave",
"description": "Open-Source AI-Native Terminal Built for Seamless Workflows",
"license": "Apache-2.0",
"version": "0.8.0-beta.12",
"version": "0.8.0",
"homepage": "https://waveterm.dev",
"build": {
"appId": "dev.commandline.waveterm"
+2 -2
View File
@@ -166,7 +166,7 @@ func StartRemoteShellProc(termSize waveobj.TermSize, cmdStr string, cmdOpts Comm
} else if remote.IsPowershell(shellPath) {
// powershell is weird about quoted path executables and requires an ampersand first
shellPath = "& " + shellPath
shellOpts = append(shellOpts, "-NoExit", "-File", homeDir+fmt.Sprintf("/.waveterm/%s/wavepwsh.ps1", shellutil.PwshIntegrationDir))
shellOpts = append(shellOpts, "-ExecutionPolicy", "Bypass", "-NoExit", "-File", homeDir+fmt.Sprintf("/.waveterm/%s/wavepwsh.ps1", shellutil.PwshIntegrationDir))
} else {
if cmdOpts.Login {
shellOpts = append(shellOpts, "-l")
@@ -276,7 +276,7 @@ func StartShellProc(termSize waveobj.TermSize, cmdStr string, cmdOpts CommandOpt
// cant set -l or -i with --rcfile
shellOpts = append(shellOpts, "--rcfile", shellutil.GetBashRcFileOverride())
} else if remote.IsPowershell(shellPath) {
shellOpts = append(shellOpts, "-NoExit", "-File", shellutil.GetWavePowershellEnv())
shellOpts = append(shellOpts, "-ExecutionPolicy", "Bypass", "-NoExit", "-File", shellutil.GetWavePowershellEnv())
} else {
if cmdOpts.Login {
shellOpts = append(shellOpts, "-l")
+4 -1
View File
@@ -629,7 +629,7 @@ func CopyToChannel(outputCh chan<- []byte, reader io.Reader) error {
// on error just returns ""
// does not return "application/octet-stream" as this is considered a detection failure
// can pass an existing fileInfo to avoid re-statting the file
func DetectMimeType(path string, fileInfo fs.FileInfo) string {
func DetectMimeType(path string, fileInfo fs.FileInfo, extended bool) string {
if fileInfo == nil {
statRtn, err := os.Stat(path)
if err != nil {
@@ -657,6 +657,9 @@ func DetectMimeType(path string, fileInfo fs.FileInfo) string {
if mimeType := mime.TypeByExtension(ext); mimeType != "" {
return mimeType
}
if !extended {
return ""
}
fd, err := os.Open(path)
if err != nil {
return ""
@@ -27,8 +27,7 @@
"label": "web",
"blockdef": {
"meta": {
"view": "web",
"url": "https://waveterm.dev/"
"view": "web"
}
}
},
+2
View File
@@ -6,6 +6,8 @@
"autoupdate:installonquit": true,
"autoupdate:intervalms": 3600000,
"editor:minimapenabled": true,
"web:defaulturl": "https://github.com/wavetermdev/waveterm",
"web:defaultsearch": "https://www.google.com/search?q={query}",
"window:tilegapsize": 3,
"telemetry:enabled": true
}
+2
View File
@@ -24,6 +24,8 @@ const (
ConfigKey_WebClear = "web:*"
ConfigKey_WebOpenLinksInternally = "web:openlinksinternally"
ConfigKey_WebDefaultUrl = "web:defaulturl"
ConfigKey_WebDefaultSearch = "web:defaultsearch"
ConfigKey_BlockHeaderClear = "blockheader:*"
ConfigKey_BlockHeaderShowBlockIds = "blockheader:showblockids"
+4 -2
View File
@@ -54,8 +54,10 @@ type SettingsType struct {
EditorMinimapEnabled bool `json:"editor:minimapenabled,omitempty"`
EditorStickyScrollEnabled bool `json:"editor:stickyscrollenabled,omitempty"`
WebClear bool `json:"web:*,omitempty"`
WebOpenLinksInternally bool `json:"web:openlinksinternally,omitempty"`
WebClear bool `json:"web:*,omitempty"`
WebOpenLinksInternally bool `json:"web:openlinksinternally,omitempty"`
WebDefaultUrl string `json:"web:defaulturl,omitempty"`
WebDefaultSearch string `json:"web:defaultsearch,omitempty"`
BlockHeaderClear bool `json:"blockheader:*,omitempty"`
BlockHeaderShowBlockIds bool `json:"blockheader:showblockids,omitempty"`
+6 -1
View File
@@ -55,10 +55,15 @@ func GetStarterLayout() PortableLayout {
}},
{IndexArr: []int{2}, BlockDef: &waveobj.BlockDef{
Meta: waveobj.MetaMapType{
waveobj.MetaKey_View: "help",
waveobj.MetaKey_View: "tips",
},
}},
{IndexArr: []int{2, 1}, BlockDef: &waveobj.BlockDef{
Meta: waveobj.MetaMapType{
waveobj.MetaKey_View: "help",
},
}},
{IndexArr: []int{2, 2}, BlockDef: &waveobj.BlockDef{
Meta: waveobj.MetaMapType{
waveobj.MetaKey_View: "waveai",
},

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