diff --git a/frontend/app/view/webview/webview.tsx b/frontend/app/view/webview/webview.tsx index a8ebc17ed..6c3a87ae2 100644 --- a/frontend/app/view/webview/webview.tsx +++ b/frontend/app/view/webview/webview.tsx @@ -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; urlInputRef: React.RefObject; nodeModel: NodeModel; + endIconButtons?: jotai.Atom; constructor(blockId: string, nodeModel: NodeModel) { this.nodeModel = nodeModel; @@ -48,7 +51,8 @@ export class WebViewModel implements ViewModel { this.webviewRef = React.createRef(); 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,7 +247,10 @@ export class WebViewModel implements ViewModel { } // Otherwise, treat it as a search query - return `https://www.google.com/search?q=${encodeURIComponent(url)}`; + if (searchTemplate == null) { + return `https://www.google.com/search?q=${encodeURIComponent(url)}`; + } + return searchTemplate.replace("{query}", encodeURIComponent(url)); } normalizeUrl(url: string) { @@ -247,7 +274,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 +346,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 +388,9 @@ 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 metaUrl = blockData?.meta?.url || defaultUrl; 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. diff --git a/frontend/types/gotypes.d.ts b/frontend/types/gotypes.d.ts index d58e16d66..fa036c200 100644 --- a/frontend/types/gotypes.d.ts +++ b/frontend/types/gotypes.d.ts @@ -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; diff --git a/pkg/wconfig/defaultconfig/defaultwidgets.json b/pkg/wconfig/defaultconfig/defaultwidgets.json index 3f6ebc863..5711e1f7b 100644 --- a/pkg/wconfig/defaultconfig/defaultwidgets.json +++ b/pkg/wconfig/defaultconfig/defaultwidgets.json @@ -27,8 +27,7 @@ "label": "web", "blockdef": { "meta": { - "view": "web", - "url": "https://waveterm.dev/" + "view": "web" } } }, diff --git a/pkg/wconfig/defaultconfig/settings.json b/pkg/wconfig/defaultconfig/settings.json index fcb805cb1..c23d78731 100644 --- a/pkg/wconfig/defaultconfig/settings.json +++ b/pkg/wconfig/defaultconfig/settings.json @@ -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 } diff --git a/pkg/wconfig/metaconsts.go b/pkg/wconfig/metaconsts.go index b01dbcd2a..2b5c78751 100644 --- a/pkg/wconfig/metaconsts.go +++ b/pkg/wconfig/metaconsts.go @@ -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" diff --git a/pkg/wconfig/settingsconfig.go b/pkg/wconfig/settingsconfig.go index 8a68b591d..d35bb6a70 100644 --- a/pkg/wconfig/settingsconfig.go +++ b/pkg/wconfig/settingsconfig.go @@ -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"`