mirror of
https://github.com/wavetermdev/backup.git
synced 2026-04-22 15:26:58 -07:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 72068f6fbc | |||
| 3025c8eb25 | |||
| bc4385370e | |||
| 1e87a98218 | |||
| 2f8890144d | |||
| 63e487f6e9 | |||
| 31d73ef976 | |||
| 91a85e06c8 | |||
| 3ea8f45284 | |||
| 4ff247d2f2 | |||
| 7708db7060 | |||
| 4907552379 | |||
| 96ad70ffdd | |||
| 62cc9d8a6b | |||
| de08cc8340 | |||
| 190a53662c | |||
| b19bef2ffb | |||
| af38ebab54 | |||
| 38f07ae3f9 | |||
| 8423334cde | |||
| bc057876a1 | |||
| d344fd9b6d | |||
| 31414a7536 | |||
| e281cebb56 | |||
| 65c446e4a1 | |||
| 047513bf3a | |||
| 78f838439a |
@@ -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 }}
|
||||
|
||||
Vendored
-18
@@ -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
-17
@@ -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:
|
||||
@@ -92,18 +94,17 @@ tasks:
|
||||
vars:
|
||||
ARCHS:
|
||||
sh: echo {{if eq "arm" ARCH}}arm64{{else}}{{ARCH}}{{end}}
|
||||
GO_LDFLAGS: -linkmode 'external' -extldflags=-static
|
||||
|
||||
build:server:internal:
|
||||
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,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 +174,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 +251,7 @@ tasks:
|
||||
- yarn.lock
|
||||
- package.json
|
||||
- .yarnrc.yml
|
||||
cmds:
|
||||
- yarn
|
||||
cmd: yarn
|
||||
|
||||
go:mod:tidy:
|
||||
desc: Runs `go mod tidy`
|
||||
@@ -261,5 +260,4 @@ tasks:
|
||||
- go.sum
|
||||
sources:
|
||||
- go.mod
|
||||
cmds:
|
||||
- go mod tidy
|
||||
cmd: go mod tidy
|
||||
|
||||
@@ -42,8 +42,8 @@ import (
|
||||
var WaveVersion = "0.0.0"
|
||||
var BuildTime = "0"
|
||||
|
||||
const InitialTelemetryWait = 30 * time.Second
|
||||
const TelemetryTick = 10 * time.Minute
|
||||
const InitialTelemetryWait = 10 * time.Second
|
||||
const TelemetryTick = 2 * time.Minute
|
||||
const TelemetryInterval = 4 * time.Hour
|
||||
|
||||
const ReadySignalPidVarName = "WAVETERM_READY_SIGNAL_PID"
|
||||
|
||||
@@ -47,11 +47,16 @@ func parseMetaSets(metaSets []string) (map[string]interface{}, error) {
|
||||
}
|
||||
meta[fields[0]] = val
|
||||
} else {
|
||||
fval, err := strconv.ParseFloat(setVal, 64)
|
||||
ival, err := strconv.ParseInt(setVal, 0, 64)
|
||||
if err == nil {
|
||||
meta[fields[0]] = fval
|
||||
meta[fields[0]] = ival
|
||||
} else {
|
||||
meta[fields[0]] = setVal
|
||||
fval, err := strconv.ParseFloat(setVal, 64)
|
||||
if err == nil {
|
||||
meta[fields[0]] = fval
|
||||
} else {
|
||||
meta[fields[0]] = setVal
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -12,8 +12,6 @@ import { FlexiModal } from "./modal";
|
||||
import { QuickTips } from "@/app/element/quicktips";
|
||||
import { atoms } from "@/app/store/global";
|
||||
import { modalsModel } from "@/app/store/modalmodel";
|
||||
import { RpcApi } from "@/app/store/wshclientapi";
|
||||
import { WindowRpcClient } from "@/app/store/wshrpcutil";
|
||||
import { atom, PrimitiveAtom, useAtom, useAtomValue, useSetAtom } from "jotai";
|
||||
import "./tos.less";
|
||||
|
||||
@@ -34,7 +32,7 @@ const ModalPage1 = () => {
|
||||
};
|
||||
|
||||
const setTelemetry = (value: boolean) => {
|
||||
RpcApi.SetConfigCommand(WindowRpcClient, { "telemetry:enabled": value })
|
||||
services.ClientService.TelemetryUpdate(value)
|
||||
.then(() => {
|
||||
setTelemetryEnabled(value);
|
||||
})
|
||||
|
||||
@@ -47,6 +47,9 @@ class ClientServiceType {
|
||||
MakeWindow(): Promise<WaveWindow> {
|
||||
return WOS.callBackendService("client", "MakeWindow", Array.from(arguments))
|
||||
}
|
||||
TelemetryUpdate(arg2: boolean): Promise<void> {
|
||||
return WOS.callBackendService("client", "TelemetryUpdate", Array.from(arguments))
|
||||
}
|
||||
}
|
||||
|
||||
export const ClientService = new ClientServiceType();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ import { Atom, atom, PrimitiveAtom } from "jotai";
|
||||
import "./helpview.less";
|
||||
|
||||
const helpText = `
|
||||
For more up-to-date documentation, please visit [our docs site](http://betadocs.waveterm.dev)
|
||||
|
||||
|
||||
## Blocks
|
||||
Every individual Component is contained in its own block. These can be added, removed, moved and resized. Each block has its own header which can be right clicked to reveal more operations you can do with that block.
|
||||
|
||||
|
||||
@@ -287,7 +287,8 @@ export class PreviewModel implements ViewModel {
|
||||
return null;
|
||||
}
|
||||
const mimeType = util.jotaiLoadableValue(get(this.fileMimeTypeLoadable), "");
|
||||
if (mimeType == "directory") {
|
||||
const metaPath = get(this.metaFilePath);
|
||||
if (mimeType == "directory" && metaPath == "/") {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
@@ -492,29 +493,24 @@ export class PreviewModel implements ViewModel {
|
||||
}
|
||||
|
||||
async goParentDirectory() {
|
||||
const blockMeta = globalStore.get(this.blockAtom)?.meta;
|
||||
const metaPath = globalStore.get(this.metaFilePath);
|
||||
const fileInfo = await globalStore.get(this.statFile);
|
||||
if (fileInfo == null) {
|
||||
return;
|
||||
this.updateOpenFileModalAndError(false);
|
||||
return true;
|
||||
}
|
||||
let newPath: string = null;
|
||||
if (!fileInfo.isdir) {
|
||||
newPath = fileInfo.dir;
|
||||
} else {
|
||||
const lastSlash = fileInfo.dir.lastIndexOf("/");
|
||||
newPath = fileInfo.dir.slice(0, lastSlash);
|
||||
if (newPath.indexOf("/") == -1) {
|
||||
return;
|
||||
}
|
||||
const conn = globalStore.get(this.connection);
|
||||
try {
|
||||
const newFileInfo = await RpcApi.RemoteFileJoinCommand(WindowRpcClient, [fileInfo.dir, ".."], {
|
||||
route: makeConnRoute(conn),
|
||||
});
|
||||
console.log(newFileInfo.path);
|
||||
this.updateOpenFileModalAndError(false);
|
||||
this.goHistory(newFileInfo.path);
|
||||
refocusNode(this.blockId);
|
||||
} catch (e) {
|
||||
globalStore.set(this.openFileError, e.message);
|
||||
console.error("Error opening file", [fileInfo.dir, ".."], e);
|
||||
}
|
||||
const updateMeta = historyutil.goHistory("file", metaPath, newPath, blockMeta);
|
||||
if (updateMeta == null) {
|
||||
return;
|
||||
}
|
||||
updateMeta.edit = false;
|
||||
const blockOref = WOS.makeORef("block", this.blockId);
|
||||
services.ObjectService.UpdateObjectMeta(blockOref, updateMeta);
|
||||
}
|
||||
|
||||
goHistoryBack() {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
.quicktips-view {
|
||||
padding: 10px 2px;
|
||||
padding: 10px 5px;
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
Vendored
+2
@@ -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
@@ -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.13",
|
||||
"version": "0.8.2",
|
||||
"homepage": "https://waveterm.dev",
|
||||
"build": {
|
||||
"appId": "dev.commandline.waveterm"
|
||||
|
||||
@@ -6,11 +6,14 @@ package clientservice
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/wavetermdev/waveterm/pkg/remote/conncontroller"
|
||||
"github.com/wavetermdev/waveterm/pkg/util/utilfn"
|
||||
"github.com/wavetermdev/waveterm/pkg/waveobj"
|
||||
"github.com/wavetermdev/waveterm/pkg/wcloud"
|
||||
"github.com/wavetermdev/waveterm/pkg/wconfig"
|
||||
"github.com/wavetermdev/waveterm/pkg/wcore"
|
||||
"github.com/wavetermdev/waveterm/pkg/wlayout"
|
||||
"github.com/wavetermdev/waveterm/pkg/wshrpc"
|
||||
@@ -106,3 +109,34 @@ func (cs *ClientService) AgreeTos(ctx context.Context) (waveobj.UpdatesRtnType,
|
||||
wlayout.BootstrapStarterLayout(ctx)
|
||||
return waveobj.ContextGetUpdatesRtn(ctx), nil
|
||||
}
|
||||
|
||||
func sendNoTelemetryUpdate(telemetryEnabled bool) {
|
||||
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancelFn()
|
||||
clientData, err := wstore.DBGetSingleton[*waveobj.Client](ctx)
|
||||
if err != nil {
|
||||
log.Printf("telemetry update: error getting client data: %v\n", err)
|
||||
return
|
||||
}
|
||||
if clientData == nil {
|
||||
log.Printf("telemetry update: client data is nil\n")
|
||||
return
|
||||
}
|
||||
err = wcloud.SendNoTelemetryUpdate(ctx, clientData.OID, !telemetryEnabled)
|
||||
if err != nil {
|
||||
log.Printf("[error] sending no-telemetry update: %v\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (cs *ClientService) TelemetryUpdate(ctx context.Context, telemetryEnabled bool) error {
|
||||
meta := waveobj.MetaMapType{
|
||||
wconfig.ConfigKey_TelemetryEnabled: telemetryEnabled,
|
||||
}
|
||||
err := wconfig.SetBaseConfigValue(meta)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error setting telemetry value: %w", err)
|
||||
}
|
||||
go sendNoTelemetryUpdate(telemetryEnabled)
|
||||
return nil
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user