main
js 104 lines 3.41 KB
Raw
1 import { store as browserStore } from "/plugins/_browser/webui/browser-store.js";
2
3 function waitForElement(selector, timeoutMs = 3000) {
4 const found = document.querySelector(selector);
5 if (found) return Promise.resolve(found);
6 return new Promise((resolve) => {
7 const timeout = globalThis.setTimeout(() => {
8 observer.disconnect();
9 resolve(null);
10 }, timeoutMs);
11 const observer = new MutationObserver(() => {
12 const element = document.querySelector(selector);
13 if (!element) return;
14 globalThis.clearTimeout(timeout);
15 observer.disconnect();
16 resolve(element);
17 });
18 observer.observe(document.body, { childList: true, subtree: true });
19 });
20 }
21
22 function nextAnimationFrame() {
23 return new Promise((resolve) => {
24 const schedule = globalThis.requestAnimationFrame || ((callback) => globalThis.setTimeout(callback, 16));
25 schedule(() => resolve());
26 });
27 }
28
29 function isVisibleCanvasPanel(panel) {
30 if (!panel?.isConnected) return false;
31 const surface = panel.closest(".browser-canvas-surface");
32 const stage = panel.querySelector(".browser-stage") || panel;
33 const surfaceStyle = surface ? globalThis.getComputedStyle?.(surface) : null;
34 const panelStyle = globalThis.getComputedStyle?.(panel);
35 if (surfaceStyle?.display === "none" || surfaceStyle?.visibility === "hidden") return false;
36 if (panelStyle?.display === "none" || panelStyle?.visibility === "hidden") return false;
37 const rect = stage.getBoundingClientRect?.();
38 return Boolean(rect && Math.round(rect.width || 0) >= 80 && Math.round(rect.height || 0) >= 80);
39 }
40
41 async function waitForVisibleCanvasPanel(selector, timeoutMs = 3000) {
42 const deadline = Date.now() + timeoutMs;
43 let stableKey = "";
44 let stableCount = 0;
45
46 while (Date.now() <= deadline) {
47 const panel = document.querySelector(selector);
48 const visible = isVisibleCanvasPanel(panel);
49 if (visible) {
50 const stage = panel.querySelector(".browser-stage") || panel;
51 const rect = stage.getBoundingClientRect();
52 const key = `${Math.round(rect.width || 0)}x${Math.round(rect.height || 0)}`;
53 if (key === stableKey) {
54 stableCount += 1;
55 if (stableCount >= 2) {
56 return panel;
57 }
58 } else {
59 stableKey = key;
60 stableCount = 0;
61 }
62 } else {
63 stableKey = "";
64 stableCount = 0;
65 }
66 await nextAnimationFrame();
67 }
68
69 return document.querySelector(selector);
70 }
71
72 export default async function registerBrowserSurface(canvas) {
73 canvas.registerSurface({
74 id: "browser",
75 title: "Browser",
76 icon: "language",
77 order: 10,
78 modalPath: "/plugins/_browser/webui/main.html",
79 beginDockHandoff() {
80 browserStore.beginSurfaceHandoff?.();
81 },
82 finishDockHandoff() {
83 browserStore.finishSurfaceHandoff?.();
84 },
85 cancelDockHandoff() {
86 browserStore.cancelSurfaceHandoff?.();
87 },
88 async open(payload = {}) {
89 await waitForElement('[data-surface-id="browser"] .browser-panel');
90 const panel = await waitForVisibleCanvasPanel('[data-surface-id="browser"] .browser-panel');
91 const browser = browserStore;
92 if (panel && browser?.onOpen) {
93 await browser.onOpen(panel, {
94 mode: "canvas",
95 browserId: payload.browserId || payload.browser_id || null,
96 contextId: payload.contextId || payload.context_id || null,
97 });
98 }
99 },
100 async close() {
101 await browserStore.cleanup?.();
102 },
103 });
104 }