mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-02 18:39:05 +01:00
wsh web (#358)
This commit is contained in:
parent
a9486852f9
commit
5fe0cae244
@ -18,18 +18,38 @@ var webCmd = &cobra.Command{
|
||||
PersistentPreRunE: preRunSetupRpcClient,
|
||||
}
|
||||
|
||||
var webOpenCommand = &cobra.Command{
|
||||
var webOpenCmd = &cobra.Command{
|
||||
Use: "open url",
|
||||
Short: "open a url a web widget",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: webOpenRun,
|
||||
}
|
||||
|
||||
var webGetCmd = &cobra.Command{
|
||||
Use: "get [--inner] [--all] css-selector",
|
||||
Short: "get the html for a css selector",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Hidden: true,
|
||||
RunE: webGetRun,
|
||||
}
|
||||
|
||||
var webGetInner bool
|
||||
var webGetAll bool
|
||||
var webOpenMagnified bool
|
||||
|
||||
func init() {
|
||||
webCmd.AddCommand(webOpenCommand)
|
||||
webOpenCmd.Flags().BoolVarP(&webOpenMagnified, "magnified", "m", false, "open view in magnified mode")
|
||||
webCmd.AddCommand(webOpenCmd)
|
||||
webGetCmd.Flags().BoolVarP(&webGetInner, "inner", "", false, "get inner html (instead of outer)")
|
||||
webGetCmd.Flags().BoolVarP(&webGetAll, "all", "", false, "get all matches (querySelectorAll)")
|
||||
webCmd.AddCommand(webGetCmd)
|
||||
rootCmd.AddCommand(webCmd)
|
||||
}
|
||||
|
||||
func webGetRun(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func webOpenRun(cmd *cobra.Command, args []string) error {
|
||||
wshCmd := wshrpc.CommandCreateBlockData{
|
||||
BlockDef: &waveobj.BlockDef{
|
||||
@ -38,7 +58,7 @@ func webOpenRun(cmd *cobra.Command, args []string) error {
|
||||
waveobj.MetaKey_Url: args[0],
|
||||
},
|
||||
},
|
||||
Magnified: viewMagnified,
|
||||
Magnified: webOpenMagnified,
|
||||
}
|
||||
oref, err := wshclient.CreateBlockCommand(RpcClient, wshCmd, nil)
|
||||
if err != nil {
|
||||
|
63
emain/emain-web.ts
Normal file
63
emain/emain-web.ts
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { BrowserWindow, ipcMain, webContents, WebContents } from "electron";
|
||||
|
||||
export async function getWebContentsByBlockId(
|
||||
win: BrowserWindow,
|
||||
tabId: string,
|
||||
blockId: string
|
||||
): Promise<WebContents> {
|
||||
const prtn = new Promise<WebContents>((resolve, reject) => {
|
||||
const randId = Math.floor(Math.random() * 1000000000).toString();
|
||||
const respCh = `getWebContentsByBlockId-${randId}`;
|
||||
win.webContents.send("webcontentsid-from-blockid", blockId, respCh);
|
||||
ipcMain.once(respCh, (event, webContentsId) => {
|
||||
if (webContentsId == null) {
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
const wc = webContents.fromId(parseInt(webContentsId));
|
||||
resolve(wc);
|
||||
});
|
||||
setTimeout(() => {
|
||||
reject(new Error("timeout waiting for response"));
|
||||
}, 2000);
|
||||
});
|
||||
return prtn;
|
||||
}
|
||||
|
||||
function escapeSelector(selector: string): string {
|
||||
return selector
|
||||
.replace(/\\/g, "\\\\")
|
||||
.replace(/"/g, '\\"')
|
||||
.replace(/'/g, "\\'")
|
||||
.replace(/\n/g, "\\n")
|
||||
.replace(/\r/g, "\\r")
|
||||
.replace(/\t/g, "\\t");
|
||||
}
|
||||
|
||||
export type WebGetOpts = {
|
||||
all?: boolean;
|
||||
inner?: boolean;
|
||||
};
|
||||
|
||||
export async function webGetSelector(wc: WebContents, selector: string, opts?: WebGetOpts): Promise<string[]> {
|
||||
if (!wc || !selector) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const escapedSelector = escapeSelector(selector);
|
||||
const queryMethod = opts?.all ? "querySelectorAll" : "querySelector";
|
||||
const prop = opts?.inner ? "innerHTML" : "outerHTML";
|
||||
const execExpr = `
|
||||
Array.from(document.${queryMethod}("${escapedSelector}") || []).map(el => el.${prop});
|
||||
`;
|
||||
|
||||
const results = await wc.executeJavaScript(execExpr);
|
||||
return results;
|
||||
} catch (e) {
|
||||
console.error("webGetSelector error", e);
|
||||
return null;
|
||||
}
|
||||
}
|
@ -824,6 +824,12 @@ async function relaunchBrowserWindows(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
process.on("uncaughtException", (error) => {
|
||||
console.error("Uncaught Exception:", error);
|
||||
console.error("Stack Trace:", error.stack);
|
||||
electron.app.quit();
|
||||
});
|
||||
|
||||
async function appMain() {
|
||||
const startTs = Date.now();
|
||||
const instanceLock = electronApp.requestSingleInstanceLock();
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
const { contextBridge, ipcRenderer } = require("electron");
|
||||
import { contextBridge, ipcRenderer, WebviewTag } from "electron";
|
||||
|
||||
contextBridge.exposeInMainWorld("api", {
|
||||
getAuthKey: () => ipcRenderer.sendSync("get-auth-key"),
|
||||
@ -42,3 +42,9 @@ ipcRenderer.on("webview-new-window", (e, webContentsId, details) => {
|
||||
const event = new CustomEvent("new-window", { detail: details });
|
||||
document.getElementById("webview").dispatchEvent(event);
|
||||
});
|
||||
|
||||
ipcRenderer.on("webcontentsid-from-blockid", (e, blockId, responseCh) => {
|
||||
const webviewElem: WebviewTag = document.querySelector("div[data-blockid='" + blockId + "'] webview");
|
||||
const wcId = webviewElem?.dataset?.webcontentsid;
|
||||
ipcRenderer.send(responseCh, wcId);
|
||||
});
|
||||
|
@ -362,6 +362,18 @@ const WebView = memo(({ model }: WebViewProps) => {
|
||||
// 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 [webContentsId, setWebContentsId] = useState(null);
|
||||
const [domReady, setDomReady] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (model.webviewRef.current && domReady) {
|
||||
const wcId = model.webviewRef.current.getWebContentsId?.();
|
||||
if (wcId) {
|
||||
setWebContentsId(wcId);
|
||||
}
|
||||
}
|
||||
}, [model.webviewRef.current, domReady]);
|
||||
|
||||
// Load a new URL if the block metadata is updated.
|
||||
useEffect(() => {
|
||||
if (metaUrlRef.current != metaUrl) {
|
||||
@ -409,6 +421,9 @@ const WebView = memo(({ model }: WebViewProps) => {
|
||||
const webviewBlur = () => {
|
||||
getApi().setWebviewFocus(null);
|
||||
};
|
||||
const handleDomReady = () => {
|
||||
setDomReady(true);
|
||||
};
|
||||
|
||||
webview.addEventListener("did-navigate-in-page", navigateListener);
|
||||
webview.addEventListener("did-navigate", navigateListener);
|
||||
@ -416,9 +431,9 @@ const WebView = memo(({ model }: WebViewProps) => {
|
||||
webview.addEventListener("did-stop-loading", stopLoadingHandler);
|
||||
webview.addEventListener("new-window", newWindowHandler);
|
||||
webview.addEventListener("did-fail-load", failLoadHandler);
|
||||
|
||||
webview.addEventListener("focus", webviewFocus);
|
||||
webview.addEventListener("blur", webviewBlur);
|
||||
webview.addEventListener("dom-ready", handleDomReady);
|
||||
|
||||
// Clean up event listeners on component unmount
|
||||
return () => {
|
||||
@ -428,8 +443,9 @@ const WebView = memo(({ model }: WebViewProps) => {
|
||||
webview.removeEventListener("did-fail-load", failLoadHandler);
|
||||
webview.removeEventListener("did-start-loading", startLoadingHandler);
|
||||
webview.removeEventListener("did-stop-loading", stopLoadingHandler);
|
||||
webview.addEventListener("focus", webviewFocus);
|
||||
webview.addEventListener("blur", webviewBlur);
|
||||
webview.removeEventListener("focus", webviewFocus);
|
||||
webview.removeEventListener("blur", webviewBlur);
|
||||
webview.removeEventListener("dom-ready", handleDomReady);
|
||||
};
|
||||
}
|
||||
}, []);
|
||||
@ -440,6 +456,8 @@ const WebView = memo(({ model }: WebViewProps) => {
|
||||
className="webview"
|
||||
ref={model.webviewRef}
|
||||
src={metaUrlInitial}
|
||||
data-blockid={model.blockId}
|
||||
data-webcontentsid={webContentsId} // needed for emain
|
||||
// @ts-ignore This is a discrepancy between the React typing and the Chromium impl for webviewTag. Chrome webviewTag expects a string, while React expects a boolean.
|
||||
allowpopups="true"
|
||||
></webview>
|
||||
|
Loading…
Reference in New Issue
Block a user