mirror of
https://github.com/wavetermdev/waveterm
synced 2026-05-23 16:58:30 +00:00
move tsunami views to webviews. also fix git+ssh urls in package-lock.json (#2355)
This commit is contained in:
parent
9dd216aba9
commit
fb4125465b
3 changed files with 78 additions and 28 deletions
|
|
@ -6,28 +6,29 @@ import { atoms, globalStore, WOS } from "@/app/store/global";
|
|||
import { waveEventSubscribe } from "@/app/store/wps";
|
||||
import { RpcApi } from "@/app/store/wshclientapi";
|
||||
import { TabRpcClient } from "@/app/store/wshrpcutil";
|
||||
import { WebView, WebViewModel } from "@/app/view/webview/webview";
|
||||
import * as services from "@/store/services";
|
||||
import * as jotai from "jotai";
|
||||
import { memo, useEffect } from "react";
|
||||
|
||||
class TsunamiViewModel implements ViewModel {
|
||||
viewType: string;
|
||||
blockAtom: jotai.Atom<Block>;
|
||||
blockId: string;
|
||||
viewIcon: jotai.Atom<string>;
|
||||
viewName: jotai.Atom<string>;
|
||||
class TsunamiViewModel extends WebViewModel {
|
||||
shellProcFullStatus: jotai.PrimitiveAtom<BlockControllerRuntimeStatus>;
|
||||
shellProcStatusUnsubFn: () => void;
|
||||
isRestarting: jotai.PrimitiveAtom<boolean>;
|
||||
|
||||
constructor(blockId: string, nodeModel: BlockNodeModel) {
|
||||
super(blockId, nodeModel);
|
||||
this.viewType = "tsunami";
|
||||
this.blockId = blockId;
|
||||
this.blockAtom = WOS.getWaveObjectAtom<Block>(`block:${blockId}`);
|
||||
this.viewIcon = jotai.atom("cube");
|
||||
this.viewName = jotai.atom("Tsunami");
|
||||
this.isRestarting = jotai.atom(false);
|
||||
|
||||
// Hide navigation bar (URL bar, back/forward/home buttons)
|
||||
this.hideNav = jotai.atom(true);
|
||||
|
||||
// Set custom partition for tsunami WebView isolation
|
||||
this.partitionOverride = jotai.atom(`tsunami:${blockId}`);
|
||||
|
||||
this.shellProcFullStatus = jotai.atom(null) as jotai.PrimitiveAtom<BlockControllerRuntimeStatus>;
|
||||
const initialShellProcStatus = services.BlockService.GetControllerStatus(blockId);
|
||||
initialShellProcStatus.then((rts) => {
|
||||
|
|
@ -94,23 +95,32 @@ class TsunamiViewModel implements ViewModel {
|
|||
}
|
||||
|
||||
getSettingsMenuItems(): ContextMenuItem[] {
|
||||
return [];
|
||||
const items = super.getSettingsMenuItems();
|
||||
// Filter out homepage and navigation-related menu items for tsunami view
|
||||
return items.filter((item) => {
|
||||
const label = item.label?.toLowerCase() || "";
|
||||
return (
|
||||
!label.includes("homepage") &&
|
||||
!label.includes("home page") &&
|
||||
!label.includes("navigation") &&
|
||||
!label.includes("nav")
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
type TsunamiViewProps = {
|
||||
model: TsunamiViewModel;
|
||||
};
|
||||
|
||||
const TsunamiView = memo(({ model }: TsunamiViewProps) => {
|
||||
const TsunamiView = memo((props: ViewComponentProps<TsunamiViewModel>) => {
|
||||
const { model } = props;
|
||||
const shellProcFullStatus = jotai.useAtomValue(model.shellProcFullStatus);
|
||||
const blockData = jotai.useAtomValue(model.blockAtom);
|
||||
const isRestarting = jotai.useAtomValue(model.isRestarting);
|
||||
const domReady = jotai.useAtomValue(model.domReady);
|
||||
|
||||
useEffect(() => {
|
||||
model.resyncController();
|
||||
}, [model]);
|
||||
|
||||
|
||||
const appPath = blockData?.meta?.["tsunami:apppath"];
|
||||
const controller = blockData?.meta?.controller;
|
||||
|
||||
|
|
@ -139,15 +149,19 @@ const TsunamiView = memo(({ model }: TsunamiViewProps) => {
|
|||
);
|
||||
}
|
||||
|
||||
// Check if we should show the iframe
|
||||
const shouldShowIframe =
|
||||
// Check if we should show the webview
|
||||
const shouldShowWebView =
|
||||
shellProcFullStatus?.shellprocstatus === "running" &&
|
||||
shellProcFullStatus?.tsunamiport &&
|
||||
shellProcFullStatus.tsunamiport !== 0;
|
||||
|
||||
if (shouldShowIframe) {
|
||||
const iframeUrl = `http://localhost:${shellProcFullStatus.tsunamiport}/?clientid=wave:${model.blockId}`;
|
||||
return <iframe src={iframeUrl} className="w-full h-full border-0" title="Tsunami Application" name={`tsunami:${shellProcFullStatus.tsunamiport}:${model.blockId}`} />;
|
||||
if (shouldShowWebView) {
|
||||
const tsunamiUrl = `http://localhost:${shellProcFullStatus.tsunamiport}/?clientid=wave:${model.blockId}`;
|
||||
return (
|
||||
<div className="w-full h-full">
|
||||
<WebView {...props} initialSrc={tsunamiUrl} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const status = shellProcFullStatus?.shellprocstatus ?? "init";
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import {
|
|||
} from "@/app/suggestion/suggestion";
|
||||
import { WOS, globalStore } from "@/store/global";
|
||||
import { adaptFromReactOrNativeKeyEvent, checkKeyPressed } from "@/util/keyutil";
|
||||
import { fireAndForget } from "@/util/util";
|
||||
import { fireAndForget, useAtomValueSafe } from "@/util/util";
|
||||
import clsx from "clsx";
|
||||
import { WebviewTag } from "electron";
|
||||
import { Atom, PrimitiveAtom, atom, useAtomValue, useSetAtom } from "jotai";
|
||||
|
|
@ -60,6 +60,7 @@ export class WebViewModel implements ViewModel {
|
|||
hideNav: Atom<boolean>;
|
||||
searchAtoms?: SearchAtoms;
|
||||
typeaheadOpen: PrimitiveAtom<boolean>;
|
||||
partitionOverride: PrimitiveAtom<string> | null;
|
||||
|
||||
constructor(blockId: string, nodeModel: BlockNodeModel) {
|
||||
this.nodeModel = nodeModel;
|
||||
|
|
@ -85,6 +86,7 @@ export class WebViewModel implements ViewModel {
|
|||
this.domReady = atom(false);
|
||||
this.hideNav = getBlockMetaKeyAtom(blockId, "web:hidenav");
|
||||
this.typeaheadOpen = atom(false);
|
||||
this.partitionOverride = null;
|
||||
|
||||
this.mediaPlaying = atom(false);
|
||||
this.mediaMuted = atom(false);
|
||||
|
|
@ -398,6 +400,33 @@ export class WebViewModel implements ViewModel {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a new URL in the webview and return a promise.
|
||||
* @param newUrl The new URL to load in the webview.
|
||||
* @param reason The reason for loading the URL.
|
||||
* @returns Promise that resolves when the URL is loaded.
|
||||
*/
|
||||
loadUrlPromise(newUrl: string, reason: string): Promise<void> {
|
||||
const defaultSearchAtom = getSettingsKeyAtom("web:defaultsearch");
|
||||
const searchTemplate = globalStore.get(defaultSearchAtom);
|
||||
const nextUrl = this.ensureUrlScheme(newUrl, searchTemplate);
|
||||
console.log("webview loadUrlPromise", reason, nextUrl, "cur=", this.webviewRef.current?.getURL());
|
||||
|
||||
if (!this.webviewRef.current) {
|
||||
return Promise.reject(new Error("WebView ref not available"));
|
||||
}
|
||||
|
||||
if (newUrl != nextUrl) {
|
||||
globalStore.set(this.url, nextUrl);
|
||||
}
|
||||
|
||||
if (this.webviewRef.current.getURL() != nextUrl) {
|
||||
return this.webviewRef.current.loadURL(nextUrl);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current URL from the state.
|
||||
* @returns The URL from the state.
|
||||
|
|
@ -661,9 +690,10 @@ interface WebViewProps {
|
|||
onFailLoad?: (url: string) => void;
|
||||
blockRef: React.RefObject<HTMLDivElement>;
|
||||
contentRef: React.RefObject<HTMLDivElement>;
|
||||
initialSrc?: string;
|
||||
}
|
||||
|
||||
const WebView = memo(({ model, onFailLoad, blockRef }: WebViewProps) => {
|
||||
const WebView = memo(({ model, onFailLoad, blockRef, initialSrc }: WebViewProps) => {
|
||||
const blockData = useAtomValue(model.blockAtom);
|
||||
const defaultUrl = useAtomValue(model.homepageUrl);
|
||||
const defaultSearchAtom = getSettingsKeyAtom("web:defaultsearch");
|
||||
|
|
@ -672,7 +702,9 @@ const WebView = memo(({ model, onFailLoad, blockRef }: WebViewProps) => {
|
|||
metaUrl = model.ensureUrlScheme(metaUrl, defaultSearch);
|
||||
const metaUrlRef = useRef(metaUrl);
|
||||
const zoomFactor = useAtomValue(getBlockMetaKeyAtom(model.blockId, "web:zoom")) || 1;
|
||||
const webPartition = useAtomValue(getBlockMetaKeyAtom(model.blockId, "web:partition")) || undefined;
|
||||
const partitionOverride = useAtomValueSafe(model.partitionOverride);
|
||||
const metaPartition = useAtomValue(getBlockMetaKeyAtom(model.blockId, "web:partition"));
|
||||
const webPartition = partitionOverride || metaPartition || undefined;
|
||||
|
||||
// Search
|
||||
const searchProps = useSearch({ anchorRef: model.webviewRef, viewModel: model });
|
||||
|
|
@ -718,7 +750,7 @@ const WebView = memo(({ model, onFailLoad, blockRef }: WebViewProps) => {
|
|||
// End Search
|
||||
|
||||
// The initial value of the block metadata URL when the component first renders. Used to set the starting src value for the webview.
|
||||
const [metaUrlInitial] = useState(metaUrl);
|
||||
const [metaUrlInitial] = useState(initialSrc || metaUrl);
|
||||
|
||||
const [webContentsId, setWebContentsId] = useState(null);
|
||||
const domReady = useAtomValue(model.domReady);
|
||||
|
|
@ -774,11 +806,15 @@ const WebView = memo(({ model, onFailLoad, blockRef }: WebViewProps) => {
|
|||
|
||||
// Load a new URL if the block metadata is updated.
|
||||
useEffect(() => {
|
||||
if (initialSrc) {
|
||||
// Skip URL loading if initialSrc is provided (it's already loaded via src attribute)
|
||||
return;
|
||||
}
|
||||
if (metaUrlRef.current != metaUrl) {
|
||||
metaUrlRef.current = metaUrl;
|
||||
model.loadUrl(metaUrl, "meta");
|
||||
}
|
||||
}, [metaUrl]);
|
||||
}, [metaUrl, initialSrc]);
|
||||
|
||||
useEffect(() => {
|
||||
const webview = model.webviewRef.current;
|
||||
|
|
|
|||
8
package-lock.json
generated
8
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "waveterm",
|
||||
"version": "0.11.5",
|
||||
"version": "0.11.6-beta.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "waveterm",
|
||||
"version": "0.11.5",
|
||||
"version": "0.11.6-beta.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"workspaces": [
|
||||
|
|
@ -5045,7 +5045,7 @@
|
|||
},
|
||||
"node_modules/@electron/node-gyp": {
|
||||
"version": "10.2.0-electron.1",
|
||||
"resolved": "git+ssh://git@github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2",
|
||||
"resolved": "git+https://git@github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2",
|
||||
"integrity": "sha512-CrYo6TntjpoMO1SHjl5Pa/JoUsECNqNdB7Kx49WLQpWzPw53eEITJ2Hs9fh/ryUYDn4pxZz11StaBYBrLFJdqg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
|
|
@ -11621,7 +11621,7 @@
|
|||
},
|
||||
"node_modules/@waveterm/docusaurus-og": {
|
||||
"version": "1.0.0-alpha.132",
|
||||
"resolved": "git+ssh://git@github.com/wavetermdev/docusaurus-og.git#2156619012b8970d922c1ef47789d2f14e47e283",
|
||||
"resolved": "git+https://git@github.com/wavetermdev/docusaurus-og.git#2156619012b8970d922c1ef47789d2f14e47e283",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "^3.7.0",
|
||||
|
|
|
|||
Loading…
Reference in a new issue