@weavetab/mcp 2.5.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +48 -0
- package/ARCHITECTURE.md +590 -0
- package/LICENSE +631 -0
- package/README.md +909 -0
- package/SECURITY.md +196 -0
- package/TROUBLESHOOTING.md +365 -0
- package/dist/anticaptcha/detector.d.ts +49 -0
- package/dist/anticaptcha/detector.js +465 -0
- package/dist/anticaptcha/integration.d.ts +36 -0
- package/dist/anticaptcha/integration.js +123 -0
- package/dist/anticaptcha/solver.d.ts +27 -0
- package/dist/anticaptcha/solver.js +552 -0
- package/dist/audit/logger.d.ts +13 -0
- package/dist/audit/logger.js +177 -0
- package/dist/audit/telemetry.d.ts +36 -0
- package/dist/audit/telemetry.js +162 -0
- package/dist/cdp/bridge.d.ts +16 -0
- package/dist/cdp/bridge.js +826 -0
- package/dist/cdp/confirm.d.ts +26 -0
- package/dist/cdp/confirm.js +79 -0
- package/dist/cdp/connector.d.ts +37 -0
- package/dist/cdp/connector.js +661 -0
- package/dist/cdp/extension.d.ts +36 -0
- package/dist/cdp/extension.js +118 -0
- package/dist/cdp/finder.d.ts +1 -0
- package/dist/cdp/finder.js +2 -0
- package/dist/cdp/helpers.d.ts +11 -0
- package/dist/cdp/helpers.js +195 -0
- package/dist/cdp/label_cache.d.ts +12 -0
- package/dist/cdp/label_cache.js +51 -0
- package/dist/cdp/lock.d.ts +33 -0
- package/dist/cdp/lock.js +206 -0
- package/dist/cdp/omni_broker.d.ts +17 -0
- package/dist/cdp/omni_broker.js +119 -0
- package/dist/cdp/profiles.d.ts +1 -0
- package/dist/cdp/profiles.js +87 -0
- package/dist/cdp/pruner.d.ts +41 -0
- package/dist/cdp/pruner.js +263 -0
- package/dist/cdp/ref_cache.d.ts +101 -0
- package/dist/cdp/ref_cache.js +234 -0
- package/dist/cdp/retry.d.ts +5 -0
- package/dist/cdp/retry.js +39 -0
- package/dist/cdp/retry_enhanced.d.ts +44 -0
- package/dist/cdp/retry_enhanced.js +102 -0
- package/dist/cdp/security.d.ts +50 -0
- package/dist/cdp/security.js +237 -0
- package/dist/cdp/verify.d.ts +39 -0
- package/dist/cdp/verify.js +143 -0
- package/dist/cdp/walker.d.ts +91 -0
- package/dist/cdp/walker.js +896 -0
- package/dist/cdp/walker.test.d.ts +2 -0
- package/dist/cdp/walker.test.js +172 -0
- package/dist/cdp/websocket_transport.d.ts +15 -0
- package/dist/cdp/websocket_transport.js +118 -0
- package/dist/cli/plugin.d.ts +9 -0
- package/dist/cli/plugin.js +1134 -0
- package/dist/cli/updater.d.ts +1 -0
- package/dist/cli/updater.js +72 -0
- package/dist/config/loader.d.ts +120 -0
- package/dist/config/loader.js +704 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1248 -0
- package/dist/intelligence/hints.d.ts +11 -0
- package/dist/intelligence/hints.js +39 -0
- package/dist/intelligence/intent.d.ts +33 -0
- package/dist/intelligence/intent.js +171 -0
- package/dist/intelligence/sessionMemory.d.ts +66 -0
- package/dist/intelligence/sessionMemory.js +284 -0
- package/dist/intelligence/sessionMemory.test.d.ts +2 -0
- package/dist/intelligence/sessionMemory.test.js +62 -0
- package/dist/intelligence/trail.d.ts +64 -0
- package/dist/intelligence/trail.js +233 -0
- package/dist/intelligence/trail.test.d.ts +2 -0
- package/dist/intelligence/trail.test.js +110 -0
- package/dist/intelligence/vision.d.ts +24 -0
- package/dist/intelligence/vision.js +151 -0
- package/dist/lifecycle/mission_manager.d.ts +16 -0
- package/dist/lifecycle/mission_manager.js +44 -0
- package/dist/overlay/bubble.d.ts +16 -0
- package/dist/overlay/bubble.js +37 -0
- package/dist/overlay/ghost.d.ts +35 -0
- package/dist/overlay/ghost.js +508 -0
- package/dist/overlay/injector.d.ts +22 -0
- package/dist/overlay/injector.js +198 -0
- package/dist/plugin/interface.d.ts +134 -0
- package/dist/plugin/interface.js +1 -0
- package/dist/plugin/loader.d.ts +18 -0
- package/dist/plugin/loader.js +155 -0
- package/dist/plugin/registry.d.ts +50 -0
- package/dist/plugin/registry.js +234 -0
- package/dist/security/blacklist.d.ts +24 -0
- package/dist/security/blacklist.js +80 -0
- package/dist/security/filter.d.ts +2 -0
- package/dist/security/filter.js +20 -0
- package/dist/security/guard.d.ts +8 -0
- package/dist/security/guard.js +35 -0
- package/dist/security/ratelimit.d.ts +11 -0
- package/dist/security/ratelimit.js +57 -0
- package/dist/security/secrets.d.ts +75 -0
- package/dist/security/secrets.js +365 -0
- package/dist/sensors/actionability.d.ts +81 -0
- package/dist/sensors/actionability.js +303 -0
- package/dist/sensors/guard.d.ts +25 -0
- package/dist/sensors/guard.js +168 -0
- package/dist/sensors/mutations.d.ts +23 -0
- package/dist/sensors/mutations.js +93 -0
- package/dist/sensors/network.d.ts +67 -0
- package/dist/sensors/network.js +193 -0
- package/dist/sensors/operation_monitor.d.ts +80 -0
- package/dist/sensors/operation_monitor.js +203 -0
- package/dist/sensors/pacing.d.ts +44 -0
- package/dist/sensors/pacing.js +111 -0
- package/dist/sensors/thoughts.d.ts +2 -0
- package/dist/sensors/thoughts.js +59 -0
- package/dist/sensors/wait.d.ts +12 -0
- package/dist/sensors/wait.js +44 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.js +1687 -0
- package/dist/state/agent.d.ts +36 -0
- package/dist/state/agent.js +101 -0
- package/dist/state/cache.d.ts +16 -0
- package/dist/state/cache.js +92 -0
- package/dist/state/cache.test.d.ts +2 -0
- package/dist/state/cache.test.js +76 -0
- package/dist/state/delta.d.ts +71 -0
- package/dist/state/delta.js +234 -0
- package/dist/state/delta.test.d.ts +2 -0
- package/dist/state/delta.test.js +279 -0
- package/dist/state/groups.d.ts +15 -0
- package/dist/state/groups.js +80 -0
- package/dist/state/session.d.ts +30 -0
- package/dist/state/session.js +155 -0
- package/dist/tools/accessibility.d.ts +14 -0
- package/dist/tools/accessibility.js +32 -0
- package/dist/tools/ask.d.ts +14 -0
- package/dist/tools/ask.js +49 -0
- package/dist/tools/automation.d.ts +36 -0
- package/dist/tools/automation.js +218 -0
- package/dist/tools/browser_detect.d.ts +13 -0
- package/dist/tools/browser_detect.js +73 -0
- package/dist/tools/burst.d.ts +35 -0
- package/dist/tools/burst.js +344 -0
- package/dist/tools/canvas.d.ts +25 -0
- package/dist/tools/canvas.js +132 -0
- package/dist/tools/captcha.d.ts +38 -0
- package/dist/tools/captcha.js +209 -0
- package/dist/tools/click.d.ts +36 -0
- package/dist/tools/click.js +561 -0
- package/dist/tools/click_and_wait.d.ts +33 -0
- package/dist/tools/click_and_wait.js +149 -0
- package/dist/tools/clipboard.d.ts +29 -0
- package/dist/tools/clipboard.js +279 -0
- package/dist/tools/console.d.ts +21 -0
- package/dist/tools/console.js +107 -0
- package/dist/tools/cookies.d.ts +67 -0
- package/dist/tools/cookies.js +121 -0
- package/dist/tools/design_extract.d.ts +109 -0
- package/dist/tools/design_extract.js +641 -0
- package/dist/tools/dialog.d.ts +24 -0
- package/dist/tools/dialog.js +109 -0
- package/dist/tools/drag.d.ts +15 -0
- package/dist/tools/drag.js +29 -0
- package/dist/tools/emulation.d.ts +33 -0
- package/dist/tools/emulation.js +69 -0
- package/dist/tools/eval.d.ts +20 -0
- package/dist/tools/eval.js +92 -0
- package/dist/tools/fill.d.ts +41 -0
- package/dist/tools/fill.js +667 -0
- package/dist/tools/find.d.ts +30 -0
- package/dist/tools/find.js +60 -0
- package/dist/tools/geo.d.ts +19 -0
- package/dist/tools/geo.js +29 -0
- package/dist/tools/github/analyze.d.ts +4 -0
- package/dist/tools/github/analyze.js +113 -0
- package/dist/tools/github/cache.d.ts +21 -0
- package/dist/tools/github/cache.js +28 -0
- package/dist/tools/github/index.d.ts +4 -0
- package/dist/tools/github/index.js +4 -0
- package/dist/tools/github/issues.d.ts +9 -0
- package/dist/tools/github/issues.js +88 -0
- package/dist/tools/github/pr.d.ts +5 -0
- package/dist/tools/github/pr.js +88 -0
- package/dist/tools/github/rateLimiter.d.ts +4 -0
- package/dist/tools/github/rateLimiter.js +46 -0
- package/dist/tools/github/read.d.ts +34 -0
- package/dist/tools/github/read.js +74 -0
- package/dist/tools/github/repoContext.d.ts +20 -0
- package/dist/tools/github/repoContext.js +129 -0
- package/dist/tools/highlight.d.ts +20 -0
- package/dist/tools/highlight.js +140 -0
- package/dist/tools/hover.d.ts +19 -0
- package/dist/tools/hover.js +57 -0
- package/dist/tools/inspect.d.ts +17 -0
- package/dist/tools/inspect.js +125 -0
- package/dist/tools/integration.test.d.ts +2 -0
- package/dist/tools/integration.test.js +96 -0
- package/dist/tools/key.d.ts +25 -0
- package/dist/tools/key.js +173 -0
- package/dist/tools/macro_compiler.d.ts +7 -0
- package/dist/tools/macro_compiler.js +82 -0
- package/dist/tools/mouse.d.ts +21 -0
- package/dist/tools/mouse.js +62 -0
- package/dist/tools/navigate.d.ts +16 -0
- package/dist/tools/navigate.js +188 -0
- package/dist/tools/network_intercept.d.ts +17 -0
- package/dist/tools/network_intercept.js +151 -0
- package/dist/tools/office/index.d.ts +9 -0
- package/dist/tools/office/index.js +7 -0
- package/dist/tools/office/list.d.ts +15 -0
- package/dist/tools/office/list.js +41 -0
- package/dist/tools/office/parse.d.ts +20 -0
- package/dist/tools/office/parse.js +172 -0
- package/dist/tools/pdf.d.ts +22 -0
- package/dist/tools/pdf.js +42 -0
- package/dist/tools/performance.d.ts +12 -0
- package/dist/tools/performance.js +47 -0
- package/dist/tools/performance_test.d.ts +70 -0
- package/dist/tools/performance_test.js +128 -0
- package/dist/tools/plan.d.ts +39 -0
- package/dist/tools/plan.js +161 -0
- package/dist/tools/pointer.d.ts +35 -0
- package/dist/tools/pointer.js +522 -0
- package/dist/tools/read.d.ts +30 -0
- package/dist/tools/read.js +488 -0
- package/dist/tools/recording.d.ts +45 -0
- package/dist/tools/recording.js +124 -0
- package/dist/tools/response.d.ts +129 -0
- package/dist/tools/response.js +5 -0
- package/dist/tools/scrape.d.ts +24 -0
- package/dist/tools/scrape.js +140 -0
- package/dist/tools/screenshot.d.ts +40 -0
- package/dist/tools/screenshot.js +79 -0
- package/dist/tools/scroll.d.ts +23 -0
- package/dist/tools/scroll.js +196 -0
- package/dist/tools/select.d.ts +13 -0
- package/dist/tools/select.js +217 -0
- package/dist/tools/snapshot.d.ts +27 -0
- package/dist/tools/snapshot.js +99 -0
- package/dist/tools/storage.d.ts +18 -0
- package/dist/tools/storage.js +29 -0
- package/dist/tools/tabs.d.ts +77 -0
- package/dist/tools/tabs.js +342 -0
- package/dist/tools/thought.d.ts +16 -0
- package/dist/tools/thought.js +79 -0
- package/dist/tools/type.d.ts +65 -0
- package/dist/tools/type.js +1061 -0
- package/dist/tools/type_secret.d.ts +23 -0
- package/dist/tools/type_secret.js +229 -0
- package/dist/tools/upload.d.ts +19 -0
- package/dist/tools/upload.js +103 -0
- package/dist/tools/video_compiler.d.ts +24 -0
- package/dist/tools/video_compiler.js +199 -0
- package/dist/tools/viewport.d.ts +44 -0
- package/dist/tools/viewport.js +230 -0
- package/dist/tools/wait.d.ts +19 -0
- package/dist/tools/wait.js +202 -0
- package/dist/tools/wait_for.d.ts +23 -0
- package/dist/tools/wait_for.js +30 -0
- package/dist/tools/windows.d.ts +22 -0
- package/dist/tools/windows.js +57 -0
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.js +12 -0
- package/docs/DEV_MODE.md +68 -0
- package/docs/DOCUMENTATION.md +532 -0
- package/docs/PLUGINS.md +538 -0
- package/docs/TOOLS.md +104 -0
- package/docs/dev_plugin.md +310 -0
- package/ext-dist/background.js +467 -0
- package/ext-dist/content.js +3089 -0
- package/ext-dist/dashboard.html +583 -0
- package/ext-dist/hud/hud.js +73 -0
- package/ext-dist/hud/index.html +20 -0
- package/ext-dist/manifest.json +77 -0
- package/ext-dist/sidepanel/sidepanel.js +25 -0
- package/ext-dist/sidepanel.html +547 -0
- package/ext-dist/styles.css +218 -0
- package/icon.png +0 -0
- package/mcp.json +796 -0
- package/package.json +113 -0
- package/server.json +23 -0
- package/smithery.yaml +11 -0
|
@@ -0,0 +1,3089 @@
|
|
|
1
|
+
const browserAPI = typeof browser !== 'undefined' ? browser : chrome;
|
|
2
|
+
// Register this tab with background (skip about:blank and chrome:// pages)
|
|
3
|
+
if (location.href !== 'about:blank' && !location.protocol.startsWith('chrome')) {
|
|
4
|
+
try { browserAPI.runtime.sendMessage({ type: 'REGISTER_TAB' }).catch(function(){}); } catch(e) {}
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// ── STABLE EXTENSION MARKER ────────────────────────────────────────
|
|
8
|
+
// Unconditionally injected at document root so MCP server can detect the extension reliably.
|
|
9
|
+
if (!document.getElementById('wt-extension-marker')) {
|
|
10
|
+
const marker = document.createElement('meta');
|
|
11
|
+
marker.id = 'wt-extension-marker';
|
|
12
|
+
marker.setAttribute('name', 'weavetab-extension');
|
|
13
|
+
if (document.head) {
|
|
14
|
+
document.head.appendChild(marker);
|
|
15
|
+
} else if (document.documentElement) {
|
|
16
|
+
document.documentElement.appendChild(marker);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ── SAFE MESSAGE BRIDGE ────────────────────────────────────────
|
|
21
|
+
// Wraps browserAPI.runtime.sendMessage to survive "Extension context invalidated"
|
|
22
|
+
// errors that occur when the extension is reloaded/updated mid-session.
|
|
23
|
+
var __wt_extAlive = true;
|
|
24
|
+
function __wt_safeSendMessage(msg) {
|
|
25
|
+
try {
|
|
26
|
+
if (!browserAPI.runtime || !browserAPI.runtime.id) { __wt_extAlive = false; return; }
|
|
27
|
+
browserAPI.runtime.sendMessage(msg).catch(function(){ __wt_extAlive = false; });
|
|
28
|
+
} catch(e) {
|
|
29
|
+
__wt_extAlive = false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function __wt_safeAppendToBody(el) {
|
|
34
|
+
if (document.body) {
|
|
35
|
+
document.body.appendChild(el);
|
|
36
|
+
} else {
|
|
37
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
38
|
+
if (document.body) document.body.appendChild(el);
|
|
39
|
+
}, { once: true });
|
|
40
|
+
// Backup for SPAs that might fire DOMContentLoaded early or clear body
|
|
41
|
+
const obs = new MutationObserver((m, observer) => {
|
|
42
|
+
if (document.body) {
|
|
43
|
+
if (!el.parentNode || !document.body.contains(el)) document.body.appendChild(el);
|
|
44
|
+
observer.disconnect();
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
obs.observe(document.documentElement, { childList: true });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ── HUD MOUNT LAYER (Phase 1: Closed Shadow DOM + Iframe Architecture) ─────
|
|
52
|
+
// Reload-proof, agent-invisible, real-time visual system
|
|
53
|
+
(function mountHUD() {
|
|
54
|
+
// Guard: exit gracefully on chrome:// pages (content scripts blocked)
|
|
55
|
+
if (location.protocol.startsWith('chrome')) return;
|
|
56
|
+
|
|
57
|
+
let host = null;
|
|
58
|
+
let shadow = null;
|
|
59
|
+
let iframe = null;
|
|
60
|
+
let hudState = {};
|
|
61
|
+
|
|
62
|
+
// Create mount point with closed shadow DOM
|
|
63
|
+
function createMount() {
|
|
64
|
+
if (host && document.documentElement.contains(host)) return;
|
|
65
|
+
|
|
66
|
+
host = document.createElement('weavetab-host');
|
|
67
|
+
host.setAttribute('data-wt-internal', 'true');
|
|
68
|
+
host.setAttribute('aria-hidden', 'true');
|
|
69
|
+
host.style.cssText = 'all:initial !important;position:fixed !important;left:0 !important;top:0 !important;width:0 !important;height:0 !important;z-index:2147483647 !important;pointer-events:none !important;background:transparent !important;opacity:1 !important;transform:none !important;filter:none !important;';
|
|
70
|
+
__wt_safeAppendToBody(host);
|
|
71
|
+
|
|
72
|
+
// Closed shadow root — ref lives ONLY here, never on window
|
|
73
|
+
shadow = host.attachShadow({ mode: 'closed' });
|
|
74
|
+
|
|
75
|
+
iframe = document.createElement('iframe');
|
|
76
|
+
iframe.src = browserAPI.runtime.getURL('hud/index.html');
|
|
77
|
+
iframe.id = 'wt-hud-iframe';
|
|
78
|
+
iframe.setAttribute('allowtransparency', 'true');
|
|
79
|
+
// FIX: Start hidden. Expand only after receiving state to prevent white flash.
|
|
80
|
+
Object.assign(iframe.style, {
|
|
81
|
+
position: 'fixed',
|
|
82
|
+
top: '0',
|
|
83
|
+
left: '0',
|
|
84
|
+
width: '0',
|
|
85
|
+
height: '0',
|
|
86
|
+
border: 'none',
|
|
87
|
+
background: 'transparent',
|
|
88
|
+
pointerEvents: 'none',
|
|
89
|
+
zIndex: '2147483647',
|
|
90
|
+
visibility: 'hidden',
|
|
91
|
+
opacity: '0',
|
|
92
|
+
colorScheme: 'light dark'
|
|
93
|
+
});
|
|
94
|
+
shadow.appendChild(iframe);
|
|
95
|
+
|
|
96
|
+
// REHYDRATE: send state to iframe, but DO NOT make visible yet to avoid white flash.
|
|
97
|
+
iframe.onload = function() {
|
|
98
|
+
browserAPI.storage.local.get('wt-hud-state', function(result) {
|
|
99
|
+
hudState = result['wt-hud-state'] || {};
|
|
100
|
+
if (iframe.contentWindow) {
|
|
101
|
+
iframe.contentWindow.postMessage({ type: 'REHYDRATE', state: hudState }, browserAPI.runtime.getURL(''));
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// Listen for HUD state changes from the iframe to toggle visibility
|
|
107
|
+
window.addEventListener('message', function(e) {
|
|
108
|
+
if (e.data && e.data.type === 'HUD_STATE_CHANGED' && e.source === iframe.contentWindow) {
|
|
109
|
+
const visible = e.data.state && e.data.state.hudVisible !== false;
|
|
110
|
+
if (visible) {
|
|
111
|
+
iframe.style.width = '100vw';
|
|
112
|
+
iframe.style.height = '100vh';
|
|
113
|
+
iframe.style.visibility = 'visible';
|
|
114
|
+
iframe.style.opacity = '1';
|
|
115
|
+
} else {
|
|
116
|
+
iframe.style.width = '0';
|
|
117
|
+
iframe.style.height = '0';
|
|
118
|
+
iframe.style.visibility = 'hidden';
|
|
119
|
+
iframe.style.opacity = '0';
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Navigation guard: re-mount if host is removed (SPA/bfcache safe)
|
|
126
|
+
const observer = new MutationObserver(function() {
|
|
127
|
+
if (!document.documentElement.contains(host)) {
|
|
128
|
+
createMount();
|
|
129
|
+
}
|
|
130
|
+
if (window.__wt_visualComponents && window.__wt_visualComponents.cursor && !document.getElementById('__Weavetab_cursor_host')) {
|
|
131
|
+
if (typeof __wt_recoverVisuals === 'function') __wt_recoverVisuals();
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
observer.observe(document.documentElement, { childList: true });
|
|
135
|
+
|
|
136
|
+
// Real-time event relays (Agent only)
|
|
137
|
+
// Removed physical mousemove, click, and keydown listeners to prevent lag and interference with agent visuals.
|
|
138
|
+
|
|
139
|
+
let scrollTicking = false;
|
|
140
|
+
document.addEventListener('scroll', function() {
|
|
141
|
+
if (!scrollTicking && iframe && iframe.contentWindow) {
|
|
142
|
+
window.requestAnimationFrame(function() {
|
|
143
|
+
iframe.contentWindow.postMessage({
|
|
144
|
+
type: 'RT_SCROLL',
|
|
145
|
+
y: window.scrollY,
|
|
146
|
+
ratio: window.scrollY / document.body.scrollHeight
|
|
147
|
+
}, '*');
|
|
148
|
+
scrollTicking = false;
|
|
149
|
+
});
|
|
150
|
+
scrollTicking = true;
|
|
151
|
+
}
|
|
152
|
+
}, { passive: true });
|
|
153
|
+
|
|
154
|
+
// ── MASTER VISUALS TOGGLE SYSTEM ───────────────────────────
|
|
155
|
+
let __wt_masterVisualsEnabled = true;
|
|
156
|
+
|
|
157
|
+
function __wt_applyMasterVisuals(enabled) {
|
|
158
|
+
__wt_masterVisualsEnabled = !!enabled;
|
|
159
|
+
window.__wt_masterVisualsEnabled = __wt_masterVisualsEnabled;
|
|
160
|
+
|
|
161
|
+
const cursorHost = document.getElementById('__Weavetab_cursor_host');
|
|
162
|
+
if (cursorHost) {
|
|
163
|
+
cursorHost.style.display = __wt_masterVisualsEnabled ? 'block' : 'none';
|
|
164
|
+
}
|
|
165
|
+
if (typeof __weaveCursor !== 'undefined' && __weaveCursor) {
|
|
166
|
+
__weaveCursor.setVisible(__wt_masterVisualsEnabled);
|
|
167
|
+
if (__weaveCursor._canvas) {
|
|
168
|
+
__weaveCursor._canvas.style.display = __wt_masterVisualsEnabled ? 'block' : 'none';
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const hudEl = document.getElementById('Weavetab-hud');
|
|
173
|
+
if (hudEl) {
|
|
174
|
+
hudEl.style.display = 'none';
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const hudHost = document.querySelector('weavetab-host');
|
|
178
|
+
if (hudHost) {
|
|
179
|
+
hudHost.style.display = __wt_masterVisualsEnabled ? 'block' : 'none';
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const indicatorEl = document.getElementById('wt-controlled-indicator') || document.querySelector('.wt-controlled-indicator') || document.getElementById('__Weavetab_controlled_indicator');
|
|
183
|
+
if (indicatorEl) {
|
|
184
|
+
indicatorEl.style.display = __wt_masterVisualsEnabled ? 'block' : 'none';
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (!__wt_masterVisualsEnabled) {
|
|
188
|
+
if (typeof clearXRay === 'function') clearXRay();
|
|
189
|
+
document.querySelectorAll('.Weavetab-ripple, .wt-xray-spotlight, .wt-bubble, .__Weavetab_xray').forEach(function(el){
|
|
190
|
+
el.remove();
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
try {
|
|
196
|
+
browserAPI.storage.local.get(['wt_master_visuals'], function(res) {
|
|
197
|
+
if (res && res.wt_master_visuals === false) {
|
|
198
|
+
__wt_applyMasterVisuals(false);
|
|
199
|
+
} else {
|
|
200
|
+
__wt_applyMasterVisuals(true);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
browserAPI.storage.onChanged.addListener(function(changes, area) {
|
|
205
|
+
if (area === 'local' && changes.wt_master_visuals !== undefined) {
|
|
206
|
+
__wt_applyMasterVisuals(changes.wt_master_visuals.newValue !== false);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
} catch(e) {}
|
|
210
|
+
|
|
211
|
+
// Ghost CDP cursor relay from background
|
|
212
|
+
browserAPI.runtime.onMessage.addListener(function(msg) {
|
|
213
|
+
if (msg.type === 'WT_TOGGLE_MASTER_VISUALS') {
|
|
214
|
+
__wt_applyMasterVisuals(msg.enabled);
|
|
215
|
+
}
|
|
216
|
+
if (msg.type === 'GHOST_CURSOR_MOVE' && iframe && iframe.contentWindow) {
|
|
217
|
+
iframe.contentWindow.postMessage({ type: 'RT_CURSOR_MOVE', x: msg.x, y: msg.y, phase: msg.phase }, '*');
|
|
218
|
+
}
|
|
219
|
+
if (msg.type === 'TOOL_CALL_EVENT' && iframe && iframe.contentWindow) {
|
|
220
|
+
iframe.contentWindow.postMessage({ type: 'TOOL_CALL_EVENT', payload: msg.payload }, '*');
|
|
221
|
+
}
|
|
222
|
+
// Relay sidepanel open/close state to HUD iframe so it can gate browser_thought pill
|
|
223
|
+
if (msg.type === 'WT_SIDEPANEL_STATE' && iframe && iframe.contentWindow) {
|
|
224
|
+
iframe.contentWindow.postMessage({ type: 'WT_SIDEPANEL_STATE', open: msg.open }, '*');
|
|
225
|
+
}
|
|
226
|
+
// Background tells us to suppress the thought pill for this specific event
|
|
227
|
+
if (msg.type === 'WT_SUPPRESS_THOUGHT_HUD' && iframe && iframe.contentWindow) {
|
|
228
|
+
iframe.contentWindow.postMessage({ type: 'WT_SUPPRESS_THOUGHT_HUD' }, '*');
|
|
229
|
+
}
|
|
230
|
+
// Scripted pill toggle from sidepanel settings — relay to HUD iframe
|
|
231
|
+
if (msg.type === 'WT_SCRIPTED_PILL_TOGGLE' && iframe && iframe.contentWindow) {
|
|
232
|
+
iframe.contentWindow.postMessage({ type: 'WT_SCRIPTED_PILL_TOGGLE', enabled: msg.enabled }, '*');
|
|
233
|
+
}
|
|
234
|
+
// Handle incoming user notes sent from the sidepanel input
|
|
235
|
+
if (msg.type === 'WT_USER_NOTE_INJECT' || msg.type === 'WT_USER_NOTE') {
|
|
236
|
+
window.__wt_chat_queue = window.__wt_chat_queue || [];
|
|
237
|
+
let formattedMsg = msg.text || '';
|
|
238
|
+
if (msg.snipe) {
|
|
239
|
+
formattedMsg = `[USER AREA ANNOTATION ATTACHED (${msg.snipe.mode})]: ` + formattedMsg;
|
|
240
|
+
}
|
|
241
|
+
window.__wt_chat_queue.push(formattedMsg);
|
|
242
|
+
|
|
243
|
+
// Unblock pending ask_bridge polling loops immediately
|
|
244
|
+
window.__wt_ask_bridge = window.__wt_ask_bridge || {};
|
|
245
|
+
if (window.__wt_ask_bridge.resolve) {
|
|
246
|
+
window.__wt_ask_bridge.resolve({
|
|
247
|
+
success: true,
|
|
248
|
+
response: formattedMsg,
|
|
249
|
+
status: 'answered'
|
|
250
|
+
});
|
|
251
|
+
window.__wt_ask_bridge = {};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Display on-page glassmorphism notification banner if master visuals are enabled
|
|
255
|
+
if (window.__wt_masterVisualsEnabled !== false) {
|
|
256
|
+
__wt_showUserNoteVisualBanner(formattedMsg);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
function __wt_showUserNoteVisualBanner(text) {
|
|
262
|
+
let banner = document.getElementById('wt-user-note-banner');
|
|
263
|
+
if (!banner) {
|
|
264
|
+
banner = document.createElement('div');
|
|
265
|
+
banner.id = 'wt-user-note-banner';
|
|
266
|
+
banner.setAttribute('data-wt-ignore', 'true');
|
|
267
|
+
banner.style.cssText = `
|
|
268
|
+
position: fixed; top: 16px; right: 20px; z-index: 2147483647;
|
|
269
|
+
background: rgba(24, 24, 27, 0.94); backdrop-filter: blur(12px);
|
|
270
|
+
border: 1px solid #38bdf8; color: #f4f4f5; font-weight: 500;
|
|
271
|
+
font-size: 11px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
272
|
+
padding: 8px 16px; border-radius: 20px;
|
|
273
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6), 0 0 14px rgba(56, 189, 248, 0.4);
|
|
274
|
+
display: flex; align-items: center; gap: 8px;
|
|
275
|
+
transition: opacity 0.3s ease, transform 0.3s ease;
|
|
276
|
+
opacity: 0; transform: translateY(-10px); pointer-events: none;
|
|
277
|
+
`;
|
|
278
|
+
document.documentElement.appendChild(banner);
|
|
279
|
+
}
|
|
280
|
+
const cleanText = text.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').slice(0, 90);
|
|
281
|
+
banner.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#38bdf8" stroke-width="2"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg><span style="color:#38bdf8;font-weight:600;">Message Received:</span> <span>${cleanText}</span>`;
|
|
282
|
+
requestAnimationFrame(() => {
|
|
283
|
+
banner.style.opacity = '1';
|
|
284
|
+
banner.style.transform = 'translateY(0)';
|
|
285
|
+
});
|
|
286
|
+
setTimeout(() => {
|
|
287
|
+
banner.style.opacity = '0';
|
|
288
|
+
banner.style.transform = 'translateY(-10px)';
|
|
289
|
+
setTimeout(() => banner.remove(), 300);
|
|
290
|
+
}, 4000);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// State persistence bridge: listen for state changes from iframe
|
|
294
|
+
window.addEventListener('message', function(e) {
|
|
295
|
+
// Forward relevant events to the extension background/sidepanel
|
|
296
|
+
if (e.data && (
|
|
297
|
+
e.data.type === 'WT_TOOL_CALL' ||
|
|
298
|
+
e.data.type === 'WT_PLAN_UPDATE' ||
|
|
299
|
+
e.data.type === 'WT_THOUGHT' ||
|
|
300
|
+
e.data.type === 'TOOL_CALL_EVENT' ||
|
|
301
|
+
e.data.type === 'Weavetab_CONFIG' ||
|
|
302
|
+
e.data.type === 'WT_CONFIG_UPDATE'
|
|
303
|
+
)) {
|
|
304
|
+
__wt_safeSendMessage(e.data);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
if (e.origin !== browserAPI.runtime.getURL('').slice(0, -1)) return;
|
|
309
|
+
if (e.data.type === 'HUD_STATE_CHANGED') {
|
|
310
|
+
hudState = { ...hudState, ...e.data.state };
|
|
311
|
+
browserAPI.storage.local.set({ 'wt-hud-state': hudState });
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
// Listen for toggle command from background
|
|
316
|
+
browserAPI.runtime.onMessage.addListener(function(msg) {
|
|
317
|
+
console.log('[Weavetab Content] Received message:', msg.type);
|
|
318
|
+
if (msg.type === 'TOGGLE_HUD' && iframe && iframe.contentWindow) {
|
|
319
|
+
iframe.contentWindow.postMessage({ type: 'TOGGLE_HUD' }, '*');
|
|
320
|
+
}
|
|
321
|
+
if (msg.type === 'TOGGLE_DEBUG' && iframe && iframe.contentWindow) {
|
|
322
|
+
console.log('[Weavetab Content] Forwarding TOGGLE_DEBUG to iframe');
|
|
323
|
+
iframe.contentWindow.postMessage({ type: 'TOGGLE_DEBUG' }, '*');
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
// Initial mount
|
|
328
|
+
createMount();
|
|
329
|
+
})();
|
|
330
|
+
|
|
331
|
+
// ── UNIFIED VISUAL SYSTEM INITIALIZATION ─────────────────────────
|
|
332
|
+
// Ensures all visual components initialize in correct order and persist across reloads
|
|
333
|
+
window.__wt_visualsReady = false;
|
|
334
|
+
window.__wt_visualComponents = {
|
|
335
|
+
cursor: false,
|
|
336
|
+
feedback: false,
|
|
337
|
+
debug: false
|
|
338
|
+
};
|
|
339
|
+
window.__wt_zIndexCounter = 2147483640; // Start near max z-index
|
|
340
|
+
|
|
341
|
+
function __wt_initVisuals() {
|
|
342
|
+
if (window.__wt_visualsReady) return;
|
|
343
|
+
|
|
344
|
+
// Initialize cursor first
|
|
345
|
+
if (!window.__wt_visualComponents.cursor && window.__weaveCursor_instance) {
|
|
346
|
+
window.__wt_visualComponents.cursor = true;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Check for WeaveTab OS panel (todo list + chat)
|
|
350
|
+
if (!window.__wt_visualComponents.feedback && document.getElementById('wt-main-container')) {
|
|
351
|
+
window.__wt_visualComponents.feedback = true;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Check for debug overlay
|
|
355
|
+
if (!window.__wt_visualComponents.debug && document.getElementById('wt-debug-overlay')) {
|
|
356
|
+
window.__wt_visualComponents.debug = true;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Mark as ready when cursor and panel are initialized
|
|
360
|
+
if (window.__wt_visualComponents.cursor && window.__wt_visualComponents.feedback) {
|
|
361
|
+
window.__wt_visualsReady = true;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Health check every 5 seconds to recover missing components
|
|
366
|
+
setInterval(function() {
|
|
367
|
+
__wt_initVisuals();
|
|
368
|
+
__wt_recoverVisuals();
|
|
369
|
+
}, 5000);
|
|
370
|
+
|
|
371
|
+
// Visual recovery - detect and fix missing/broken components
|
|
372
|
+
function __wt_recoverVisuals() {
|
|
373
|
+
// Recover cursor if missing
|
|
374
|
+
if (window.__wt_visualComponents && window.__wt_visualComponents.cursor && !document.getElementById('__Weavetab_cursor_host')) {
|
|
375
|
+
console.warn('[Weavetab] Cursor host missing, reinitializing...');
|
|
376
|
+
if (window.__weaveCursor_instance && window.__weaveCursor_instance.destroy) {
|
|
377
|
+
window.__weaveCursor_instance.destroy();
|
|
378
|
+
}
|
|
379
|
+
__weaveCursor = new WeaveCursor();
|
|
380
|
+
window.__weaveCursor_instance = __weaveCursor;
|
|
381
|
+
window.__wt_visualComponents.cursor = true;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Recover WeaveTab OS panel if missing
|
|
385
|
+
if (window.__wt_visualComponents.feedback && !document.getElementById('wt-main-container')) {
|
|
386
|
+
console.warn('[Weavetab] WeaveTab OS panel missing, this is handled by page reload');
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Fix z-index conflicts
|
|
390
|
+
var wtElements = document.querySelectorAll('[data-wt-ignore]');
|
|
391
|
+
wtElements.forEach(function(el) {
|
|
392
|
+
if (el.style.zIndex && parseInt(el.style.zIndex) < 2147483640) {
|
|
393
|
+
el.style.zIndex = ++window.__wt_zIndexCounter;
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// ── STALE ELEMENT CLEANUP ─────────────────────────────────────
|
|
399
|
+
// Removes old UI elements from previous server/extension sessions.
|
|
400
|
+
// Observer stays active for the full page lifetime (SPA-safe).
|
|
401
|
+
(function cleanupOldUI() {
|
|
402
|
+
var OLD_IDS = [
|
|
403
|
+
'wt-ghost-cursor','wt-ghost-bubble','wt-ask-panel','wt-cur-canvas',
|
|
404
|
+
'wt-ask-container','wt-msg-panel','wt-ask-title','wt-msg-bar','wt-msg-pill'
|
|
405
|
+
];
|
|
406
|
+
function removeOld() {
|
|
407
|
+
for (var i = 0; i < OLD_IDS.length; i++) {
|
|
408
|
+
var el = document.getElementById(OLD_IDS[i]);
|
|
409
|
+
if (el) el.remove();
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
removeOld();
|
|
413
|
+
// Persistent observer — never disconnects so SPA re-renders stay clean
|
|
414
|
+
var obs = new MutationObserver(function() { removeOld(); });
|
|
415
|
+
if (document.documentElement) {
|
|
416
|
+
obs.observe(document.documentElement, { childList: true, subtree: true });
|
|
417
|
+
} else {
|
|
418
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
419
|
+
if (document.documentElement) obs.observe(document.documentElement, { childList: true, subtree: true });
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
})();
|
|
423
|
+
|
|
424
|
+
// ── UI DESIGN SYSTEM (inject once, styles.css handles visuals) ─
|
|
425
|
+
(function injectWeavetabUI() {
|
|
426
|
+
if (document.getElementById('wt-ui-styles')) return;
|
|
427
|
+
// Guard marker so we only run once
|
|
428
|
+
const m = document.createElement('meta');
|
|
429
|
+
m.id = 'wt-ui-styles';
|
|
430
|
+
(document.head || document.documentElement || document.body).appendChild(m);
|
|
431
|
+
})();
|
|
432
|
+
|
|
433
|
+
// ── THOUGHT BUBBLE ─────────────────────────────────────────────
|
|
434
|
+
let __wt_activeBubble = null;
|
|
435
|
+
let __wt_bubbleTimer = null;
|
|
436
|
+
let __wt_typewriterTimer = null;
|
|
437
|
+
let __wt_feedbackSticky = false;
|
|
438
|
+
let __wt_thoughtBirth = 0;
|
|
439
|
+
let __wt_thoughtQueue = [];
|
|
440
|
+
let __wt_lastRemoveTime = 0;
|
|
441
|
+
let __wt_toolActive = false;
|
|
442
|
+
|
|
443
|
+
function __wt_fadeOutBubble(callback) {
|
|
444
|
+
if (!__wt_activeBubble) { if (callback) callback(); return; }
|
|
445
|
+
var bub = __wt_activeBubble;
|
|
446
|
+
bub.style.transition = 'opacity 0.15s ease-out, transform 0.15s ease-out';
|
|
447
|
+
bub.style.opacity = '0';
|
|
448
|
+
bub.style.transform = 'translateX(-50%) translateY(6px)';
|
|
449
|
+
setTimeout(function() {
|
|
450
|
+
bub.remove();
|
|
451
|
+
if (callback) callback();
|
|
452
|
+
}, 150);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function __wt_destroyBubble() {
|
|
456
|
+
if (__wt_bubbleTimer) { clearTimeout(__wt_bubbleTimer); __wt_bubbleTimer = null; }
|
|
457
|
+
if (__wt_typewriterTimer) { clearInterval(__wt_typewriterTimer); __wt_typewriterTimer = null; }
|
|
458
|
+
var current = document.querySelector('.wt-bubble[data-wt-thought]');
|
|
459
|
+
if (current) {
|
|
460
|
+
current.style.transition = 'opacity 0.15s ease-out, transform 0.15s ease-out';
|
|
461
|
+
current.style.opacity = '0';
|
|
462
|
+
current.style.transform = 'translateX(-50%) translateY(6px)';
|
|
463
|
+
setTimeout(function() {
|
|
464
|
+
if (current && current.parentNode) current.remove();
|
|
465
|
+
__wt_activeBubble = null;
|
|
466
|
+
}, 150);
|
|
467
|
+
} else {
|
|
468
|
+
__wt_activeBubble = null;
|
|
469
|
+
}
|
|
470
|
+
__wt_lastRemoveTime = Date.now();
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function __wt_removeThought() {
|
|
474
|
+
if (__wt_feedbackSticky) return;
|
|
475
|
+
if (__wt_thoughtBirth && Date.now() - __wt_thoughtBirth < 300) return;
|
|
476
|
+
__wt_destroyBubble();
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function __wt_showThought(text, options) {
|
|
480
|
+
// On-page thought bubbles removed — all tool calls/thoughts display inside sidepanel logs
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function __wt_renderThought(text, options) {
|
|
485
|
+
// On-page thought bubbles removed — all tool calls/thoughts display inside sidepanel logs
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
function __wt_processThoughtQueue() {
|
|
490
|
+
if (__wt_thoughtQueue.length > 0) {
|
|
491
|
+
var next = __wt_thoughtQueue.shift();
|
|
492
|
+
setTimeout(function() { __wt_renderThought(next.text, next.options); }, 200);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// ── QUESTION INPUT (merged into thought bubble system) ────────
|
|
497
|
+
let __wt_askContainer = null;
|
|
498
|
+
let __wt_askTimer = null;
|
|
499
|
+
let __wt_askBridge = null;
|
|
500
|
+
|
|
501
|
+
function __wt_showQuestion(text, inputType, timeoutSeconds) {
|
|
502
|
+
__wt_removeAsk();
|
|
503
|
+
__wt_feedbackSticky = true;
|
|
504
|
+
var ts = timeoutSeconds || 120;
|
|
505
|
+
|
|
506
|
+
// DOM bridge
|
|
507
|
+
__wt_askBridge = document.getElementById('__wt_ask_bridge');
|
|
508
|
+
if (!__wt_askBridge) {
|
|
509
|
+
__wt_askBridge = document.createElement('div');
|
|
510
|
+
__wt_askBridge.id = '__wt_ask_bridge';
|
|
511
|
+
__wt_askBridge.style.display = 'none';
|
|
512
|
+
document.body.appendChild(__wt_askBridge);
|
|
513
|
+
}
|
|
514
|
+
__wt_askBridge.dataset.status = 'pending';
|
|
515
|
+
__wt_askBridge.dataset.result = '';
|
|
516
|
+
|
|
517
|
+
// Update HUD to "waiting..." so the top bar doesn't show question text twice
|
|
518
|
+
updateHUD('waiting for your input...');
|
|
519
|
+
|
|
520
|
+
// Render green thought bubble (same as sticky feedback — 0ms delay)
|
|
521
|
+
__wt_renderThought(text, { active: false, sticky: true });
|
|
522
|
+
|
|
523
|
+
// Countdown timer
|
|
524
|
+
var remaining = ts;
|
|
525
|
+
__wt_askTimer = setInterval(function() {
|
|
526
|
+
remaining--;
|
|
527
|
+
if (remaining <= 0) {
|
|
528
|
+
clearInterval(__wt_askTimer);
|
|
529
|
+
__wt_askTimer = null;
|
|
530
|
+
if (__wt_askBridge) { __wt_askBridge.dataset.status = 'timeout'; __wt_askBridge.dataset.result = ''; }
|
|
531
|
+
__wt_removeAsk();
|
|
532
|
+
}
|
|
533
|
+
}, 1000);
|
|
534
|
+
|
|
535
|
+
// Smooth input row — delays slightly so bubble appears first, then input slides up
|
|
536
|
+
setTimeout(function() {
|
|
537
|
+
if (!__wt_askBridge) return;
|
|
538
|
+
|
|
539
|
+
var row = document.createElement('div');
|
|
540
|
+
row.setAttribute('data-wt-ignore', '');
|
|
541
|
+
row.style.cssText = 'position:fixed;bottom:16px;left:50%;transform:translateX(-50%) translateY(8px);z-index:2147483640;display:flex;align-items:center;gap:0;pointer-events:auto;opacity:0;transition:opacity 0.25s ease-out, transform 0.25s ease-out;';
|
|
542
|
+
|
|
543
|
+
var input;
|
|
544
|
+
if (inputType === 'paragraph') {
|
|
545
|
+
input = document.createElement('textarea');
|
|
546
|
+
input.rows = 3;
|
|
547
|
+
input.style.cssText = 'min-width:280px;max-width:480px;font-size:11px;padding:6px 8px;border-radius:4px 0 0 4px;background:#042f2e;color:#e4e4e7;border:1px solid #059669;border-right:none;outline:none;resize:vertical;font-family:"IBM Plex Mono",monospace;';
|
|
548
|
+
input.placeholder = 'Type your response...';
|
|
549
|
+
} else {
|
|
550
|
+
input = document.createElement('input');
|
|
551
|
+
input.style.cssText = 'min-width:200px;font-size:11px;padding:4px 8px;border-radius:4px 0 0 4px;background:#042f2e;color:#e4e4e7;border:1px solid #059669;border-right:none;outline:none;caret-color:#10b981;font-family:"IBM Plex Mono",monospace;';
|
|
552
|
+
input.placeholder = 'Your answer...';
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
var btn = document.createElement('button');
|
|
556
|
+
btn.style.cssText = 'padding:4px 10px;background:#059670;border:1px solid #059670;border-left:none;border-radius:0 4px 4px 0;color:#fff;cursor:pointer;font-size:10px;font-family:"IBM Plex Mono",monospace;';
|
|
557
|
+
btn.textContent = 'Send';
|
|
558
|
+
|
|
559
|
+
row.appendChild(input);
|
|
560
|
+
row.appendChild(btn);
|
|
561
|
+
document.body.appendChild(row);
|
|
562
|
+
__wt_askContainer = row;
|
|
563
|
+
|
|
564
|
+
// Fade in
|
|
565
|
+
requestAnimationFrame(function() {
|
|
566
|
+
row.style.opacity = '1';
|
|
567
|
+
row.style.transform = 'translateX(-50%) translateY(0)';
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
setTimeout(function() { input.focus(); }, 150);
|
|
571
|
+
|
|
572
|
+
function submit() {
|
|
573
|
+
if (__wt_askBridge) { __wt_askBridge.dataset.status = 'submitted'; __wt_askBridge.dataset.result = input.value; }
|
|
574
|
+
__wt_removeAsk();
|
|
575
|
+
}
|
|
576
|
+
function cancel() {
|
|
577
|
+
if (__wt_askBridge) { __wt_askBridge.dataset.status = 'cancelled'; __wt_askBridge.dataset.result = ''; }
|
|
578
|
+
__wt_removeAsk();
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
btn.onclick = submit;
|
|
582
|
+
input.onkeydown = function(e) {
|
|
583
|
+
if (e.key === 'Enter') {
|
|
584
|
+
if (inputType === 'paragraph' && !e.ctrlKey && !e.metaKey) return;
|
|
585
|
+
submit();
|
|
586
|
+
}
|
|
587
|
+
if (e.key === 'Escape') cancel();
|
|
588
|
+
};
|
|
589
|
+
}, 400);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
function __wt_removeAsk() {
|
|
593
|
+
if (__wt_askTimer) { clearInterval(__wt_askTimer); __wt_askTimer = null; }
|
|
594
|
+
if (__wt_askContainer) { __wt_askContainer.remove(); __wt_askContainer = null; }
|
|
595
|
+
__wt_askBridge = null;
|
|
596
|
+
__wt_feedbackSticky = false;
|
|
597
|
+
clearTimeout(window.__wt_feedbackStickyTimer);
|
|
598
|
+
__wt_thoughtQueue = [];
|
|
599
|
+
__wt_destroyBubble();
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// ── SHARED OVERLAY CONTAINER ────────────────────────────────────
|
|
603
|
+
const __wt_shadowStyles = `
|
|
604
|
+
.Weavetab-ripple {
|
|
605
|
+
width: 40px; height: 40px; margin: -20px 0 0 -20px; border-radius: 50%;
|
|
606
|
+
background: rgba(79,142,247,0.35); border: 2px solid #4F8EF7;
|
|
607
|
+
animation: __wt_ripple 0.55s ease-out forwards;
|
|
608
|
+
}
|
|
609
|
+
@keyframes __wt_ripple { from { transform: scale(0.3); opacity: 1; } to { transform: scale(2.2); opacity: 0; } }
|
|
610
|
+
@keyframes __wt_xray_pulse {
|
|
611
|
+
0% { opacity: 0; transform: scale(0.94); }
|
|
612
|
+
18% { opacity: 1; transform: scale(1.02); }
|
|
613
|
+
70% { opacity: 0.85; transform: scale(1); }
|
|
614
|
+
100% { opacity: 0; transform: scale(0.97); }
|
|
615
|
+
}
|
|
616
|
+
`;
|
|
617
|
+
|
|
618
|
+
function getWeavetabRoot() {
|
|
619
|
+
let host = document.getElementById('Weavetab-root');
|
|
620
|
+
if (!host) {
|
|
621
|
+
host = document.createElement('div');
|
|
622
|
+
host.id = 'Weavetab-root';
|
|
623
|
+
host.setAttribute('data-wt-ignore', '');
|
|
624
|
+
Object.assign(host.style, {
|
|
625
|
+
position: 'fixed', left: '0', top: '0',
|
|
626
|
+
width: '0', height: '0',
|
|
627
|
+
zIndex: '2147483640', pointerEvents: 'none',
|
|
628
|
+
});
|
|
629
|
+
var root = document.documentElement || document.body;
|
|
630
|
+
if (root) root.appendChild(host);
|
|
631
|
+
}
|
|
632
|
+
// Reuse existing shadow root (open mode, set by CDP injector or ourselves)
|
|
633
|
+
if (host.shadowRoot) {
|
|
634
|
+
return host.shadowRoot;
|
|
635
|
+
}
|
|
636
|
+
// Guard against NotSupportedError when another context already attached a closed shadow root
|
|
637
|
+
try {
|
|
638
|
+
const sr = host.attachShadow({ mode: 'open' });
|
|
639
|
+
const style = document.createElement('style');
|
|
640
|
+
style.textContent = __wt_shadowStyles;
|
|
641
|
+
sr.appendChild(style);
|
|
642
|
+
return sr;
|
|
643
|
+
} catch (e) {
|
|
644
|
+
// FIX: fallback must be invisible and non-blocking so it can't
|
|
645
|
+
// cover page content or cause opacity bleeding on sites like example.com.
|
|
646
|
+
const fallback = document.createElement('div');
|
|
647
|
+
fallback.setAttribute('data-wt-ignore', '');
|
|
648
|
+
fallback.style.cssText = 'position:fixed;left:0;top:0;width:0;height:0;pointer-events:none;overflow:visible;z-index:2147483640;';
|
|
649
|
+
__wt_safeAppendToBody(fallback);
|
|
650
|
+
return fallback;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// ── COORDINATE ORACLE ───────────────────────────────────────────
|
|
655
|
+
function shadowQuery(selector) {
|
|
656
|
+
function searchRoot(root) {
|
|
657
|
+
const found = root.querySelector(selector);
|
|
658
|
+
if (found) return found;
|
|
659
|
+
|
|
660
|
+
for (const el of root.querySelectorAll('*')) {
|
|
661
|
+
if (el.shadowRoot) {
|
|
662
|
+
const result = searchRoot(el.shadowRoot);
|
|
663
|
+
if (result) return result;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
return null;
|
|
667
|
+
}
|
|
668
|
+
return searchRoot(document);
|
|
669
|
+
}
|
|
670
|
+
window.__Weavetab = {
|
|
671
|
+
version: '2.5.0',
|
|
672
|
+
suppressVisuals: false,
|
|
673
|
+
shadowQuery: shadowQuery,
|
|
674
|
+
|
|
675
|
+
// ── CDP Co-Processor Helpers ──────────────────────────────────────────────
|
|
676
|
+
// Call these via Runtime.evaluate instead of doing expensive CDP-side work.
|
|
677
|
+
// All return plain JSON-serializable values.
|
|
678
|
+
|
|
679
|
+
/** Scroll position, viewport size, and page dimensions in one call. */
|
|
680
|
+
getScrollInfo: function() {
|
|
681
|
+
var vv = window.visualViewport || {};
|
|
682
|
+
var body = document.body || document.documentElement;
|
|
683
|
+
return {
|
|
684
|
+
scrollX: Math.round(window.scrollX),
|
|
685
|
+
scrollY: Math.round(window.scrollY),
|
|
686
|
+
viewportW: Math.round(vv.width || window.innerWidth),
|
|
687
|
+
viewportH: Math.round(vv.height || window.innerHeight),
|
|
688
|
+
pageW: Math.round(body.scrollWidth),
|
|
689
|
+
pageH: Math.round(body.scrollHeight),
|
|
690
|
+
maxScrollX: Math.round(Math.max(0, body.scrollWidth - window.innerWidth)),
|
|
691
|
+
maxScrollY: Math.round(Math.max(0, body.scrollHeight - window.innerHeight)),
|
|
692
|
+
scrollRatio: body.scrollHeight > 0 ? +(window.scrollY / body.scrollHeight).toFixed(3) : 0
|
|
693
|
+
};
|
|
694
|
+
},
|
|
695
|
+
|
|
696
|
+
/**
|
|
697
|
+
* Wait until no DOM mutations occur for `quietMs` milliseconds.
|
|
698
|
+
* Resolves to true when stable, false on timeout (default 5000ms).
|
|
699
|
+
* CDP calls this and awaits — replaces dumb sleep() calls.
|
|
700
|
+
*/
|
|
701
|
+
waitForStable: function(quietMs, timeoutMs) {
|
|
702
|
+
quietMs = quietMs || 300;
|
|
703
|
+
timeoutMs = timeoutMs || 5000;
|
|
704
|
+
return new Promise(function(resolve) {
|
|
705
|
+
var timer = null;
|
|
706
|
+
var deadline = setTimeout(function() {
|
|
707
|
+
obs.disconnect();
|
|
708
|
+
resolve(false);
|
|
709
|
+
}, timeoutMs);
|
|
710
|
+
function reset() {
|
|
711
|
+
if (timer) clearTimeout(timer);
|
|
712
|
+
timer = setTimeout(function() {
|
|
713
|
+
clearTimeout(deadline);
|
|
714
|
+
obs.disconnect();
|
|
715
|
+
resolve(true);
|
|
716
|
+
}, quietMs);
|
|
717
|
+
}
|
|
718
|
+
var obs = new MutationObserver(reset);
|
|
719
|
+
obs.observe(document.documentElement, { childList: true, subtree: true, attributes: true, characterData: true });
|
|
720
|
+
reset(); // start the quiet timer immediately
|
|
721
|
+
});
|
|
722
|
+
},
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* Resolve a data-wt-id directly from the live index — much faster than
|
|
726
|
+
* doing a full accessibility tree walk.
|
|
727
|
+
* Returns { id, x, y, rect, tag, label, viewport, isVisible } or null.
|
|
728
|
+
*/
|
|
729
|
+
getElementByWtId: function(id) {
|
|
730
|
+
var idx = window.__wt_liveIndex;
|
|
731
|
+
if (!idx) return null;
|
|
732
|
+
var entry = null;
|
|
733
|
+
for (var i = 0; i < idx.length; i++) {
|
|
734
|
+
if (idx[i].id === String(id)) { entry = idx[i]; break; }
|
|
735
|
+
}
|
|
736
|
+
if (!entry) return null;
|
|
737
|
+
// Refresh coordinates from DOM if element still exists
|
|
738
|
+
var el = document.querySelector('[data-wt-id="' + entry.id + '"]');
|
|
739
|
+
if (el) {
|
|
740
|
+
var r = el.getBoundingClientRect();
|
|
741
|
+
entry.x = Math.round(r.left + r.width / 2);
|
|
742
|
+
entry.y = Math.round(r.top + r.height / 2);
|
|
743
|
+
entry.rect = { left: Math.round(r.left), top: Math.round(r.top), width: Math.round(r.width), height: Math.round(r.height) };
|
|
744
|
+
}
|
|
745
|
+
return entry;
|
|
746
|
+
},
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* Dump all form field current values — lets agent verify form state cheaply.
|
|
750
|
+
* Returns array of { name, id, type, value, tag }.
|
|
751
|
+
*/
|
|
752
|
+
getFormValues: function() {
|
|
753
|
+
var results = [];
|
|
754
|
+
var els = document.querySelectorAll('input, select, textarea');
|
|
755
|
+
for (var i = 0; i < els.length; i++) {
|
|
756
|
+
var el = els[i];
|
|
757
|
+
if (el.hasAttribute('data-wt-ignore')) continue;
|
|
758
|
+
var val = el.type === 'checkbox' || el.type === 'radio' ? el.checked : el.value;
|
|
759
|
+
results.push({
|
|
760
|
+
name: el.name || el.id || null,
|
|
761
|
+
id: el.id || null,
|
|
762
|
+
wtId: el.getAttribute('data-wt-id') || null,
|
|
763
|
+
type: el.type || el.tagName.toLowerCase(),
|
|
764
|
+
tag: el.tagName.toLowerCase(),
|
|
765
|
+
value: val,
|
|
766
|
+
placeholder: el.placeholder || null
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
return results;
|
|
770
|
+
},
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* Check visibility of a selector — uses the live IntersectionObserver data
|
|
774
|
+
* maintained by the MutationObserver. Much faster than a full DOM query.
|
|
775
|
+
* Returns { found, visible, occluded, x, y, rect } or null.
|
|
776
|
+
*/
|
|
777
|
+
checkVisible: function(selector) {
|
|
778
|
+
var el = document.querySelector(selector) || shadowQuery(selector);
|
|
779
|
+
if (!el) return { found: false };
|
|
780
|
+
var r = el.getBoundingClientRect();
|
|
781
|
+
var vv = window.visualViewport || {};
|
|
782
|
+
var vw = vv.width || window.innerWidth;
|
|
783
|
+
var vh = vv.height || window.innerHeight;
|
|
784
|
+
var inViewport = r.width > 0 && r.height > 0 && r.top < vh && r.bottom > 0 && r.left < vw && r.right > 0;
|
|
785
|
+
var topEl = document.elementFromPoint(r.left + r.width / 2, r.top + r.height / 2);
|
|
786
|
+
var occluded = topEl !== el && !el.contains(topEl);
|
|
787
|
+
return {
|
|
788
|
+
found: true,
|
|
789
|
+
visible: inViewport,
|
|
790
|
+
occluded: occluded,
|
|
791
|
+
x: Math.round(r.left + r.width / 2),
|
|
792
|
+
y: Math.round(r.top + r.height / 2),
|
|
793
|
+
rect: { left: Math.round(r.left), top: Math.round(r.top), width: Math.round(r.width), height: Math.round(r.height) }
|
|
794
|
+
};
|
|
795
|
+
},
|
|
796
|
+
|
|
797
|
+
/**
|
|
798
|
+
* Return the live element index, optionally filtered.
|
|
799
|
+
* filter: 'visible' | 'all' (default 'visible')
|
|
800
|
+
* Much faster than browser_map for simple lookups.
|
|
801
|
+
*/
|
|
802
|
+
getLiveIndex: function(filter) {
|
|
803
|
+
var idx = window.__wt_liveIndex || [];
|
|
804
|
+
if (filter === 'all') return idx;
|
|
805
|
+
// Default: visible viewport only
|
|
806
|
+
return idx.filter(function(e) { return e.viewport === 'visible' || e.isVisible; });
|
|
807
|
+
},
|
|
808
|
+
|
|
809
|
+
/**
|
|
810
|
+
* Pop and return the next user feedback message from the queue.
|
|
811
|
+
* MCP calls this between tool calls to check for user interrupts.
|
|
812
|
+
* Returns { text, ts, tabId } or null if queue is empty.
|
|
813
|
+
* Non-destructive — call popUserFeedback() to consume, peekUserFeedback() to inspect.
|
|
814
|
+
*/
|
|
815
|
+
popUserFeedback: function() {
|
|
816
|
+
// Read from localStorage queue (written by submitFeedback + persisted by background)
|
|
817
|
+
try {
|
|
818
|
+
var raw = localStorage.getItem('__wt_chat_queue_persist');
|
|
819
|
+
if (!raw) return null;
|
|
820
|
+
var arr = JSON.parse(raw);
|
|
821
|
+
if (!arr || !arr.length) return null;
|
|
822
|
+
var item = arr.shift();
|
|
823
|
+
localStorage.setItem('__wt_chat_queue_persist', JSON.stringify(arr));
|
|
824
|
+
return item;
|
|
825
|
+
} catch(e) { return null; }
|
|
826
|
+
},
|
|
827
|
+
|
|
828
|
+
/**
|
|
829
|
+
* Check if a blocking dialog (alert/confirm/prompt) is pending on this tab.
|
|
830
|
+
* Agent should check this before clicking if it suspects a dialog might block.
|
|
831
|
+
* Returns { pending: bool, ts: number } — reads from extension storage via background relay.
|
|
832
|
+
*/
|
|
833
|
+
getAlertState: function() {
|
|
834
|
+
// Sync check via a DOM marker set by alert wrapper
|
|
835
|
+
return { pending: !!(window.__wt_alertPending), ts: Date.now() };
|
|
836
|
+
},
|
|
837
|
+
};
|
|
838
|
+
window.__Weavetab_shadowQuery = shadowQuery;
|
|
839
|
+
|
|
840
|
+
function isOccluded(el) {
|
|
841
|
+
const rect = el.getBoundingClientRect();
|
|
842
|
+
const cx = rect.left + rect.width / 2;
|
|
843
|
+
const cy = rect.top + rect.height / 2;
|
|
844
|
+
const topEl = document.elementFromPoint(cx, cy);
|
|
845
|
+
return topEl !== el && !el.contains(topEl);
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
window.__Weavetab_getCoords = function(selector) {
|
|
849
|
+
const el = document.querySelector(selector) || shadowQuery(selector);
|
|
850
|
+
|
|
851
|
+
if (!el) return null;
|
|
852
|
+
|
|
853
|
+
const rect = el.getBoundingClientRect();
|
|
854
|
+
const vv = window.visualViewport;
|
|
855
|
+
|
|
856
|
+
return {
|
|
857
|
+
x: rect.left + rect.width / 2 + vv.offsetLeft,
|
|
858
|
+
y: rect.top + rect.height / 2 + vv.offsetTop,
|
|
859
|
+
raw: { left: rect.left, top: rect.top, w: rect.width, h: rect.height },
|
|
860
|
+
occluded: isOccluded(el),
|
|
861
|
+
visible: rect.width > 0 && rect.height > 0
|
|
862
|
+
&& rect.top < vv.height && rect.bottom > 0
|
|
863
|
+
};
|
|
864
|
+
};
|
|
865
|
+
|
|
866
|
+
function getStableSelector(el) {
|
|
867
|
+
if (el.id) return `#${el.id}`;
|
|
868
|
+
const path = [];
|
|
869
|
+
while (el.parentNode) {
|
|
870
|
+
let index = 1;
|
|
871
|
+
let sibling = el.previousElementSibling;
|
|
872
|
+
while (sibling) {
|
|
873
|
+
if (sibling.tagName === el.tagName) index++;
|
|
874
|
+
sibling = sibling.previousElementSibling;
|
|
875
|
+
}
|
|
876
|
+
path.unshift(`${el.tagName.toLowerCase()}:nth-of-type(${index})`);
|
|
877
|
+
el = el.parentNode;
|
|
878
|
+
}
|
|
879
|
+
return path.join(' > ');
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
window.__Weavetab_fullMap = function() {
|
|
883
|
+
const map = [];
|
|
884
|
+
let idCounter = 1;
|
|
885
|
+
function crawl(root, depth = 0) {
|
|
886
|
+
for (const el of root.querySelectorAll(
|
|
887
|
+
'a,button,input,select,textarea,[role],[onclick],[tabindex]'
|
|
888
|
+
)) {
|
|
889
|
+
if (el.hasAttribute('data-wt-ignore')) continue;
|
|
890
|
+
const rect = el.getBoundingClientRect();
|
|
891
|
+
// Always include form elements (input/textarea/select/button) regardless of rect size
|
|
892
|
+
// — canvas-heavy pages can occlude them and getBoundingClientRect returns 0
|
|
893
|
+
const isEssential = ['INPUT', 'TEXTAREA', 'SELECT', 'BUTTON'].includes(el.tagName);
|
|
894
|
+
if (rect.width === 0 && !isEssential) continue;
|
|
895
|
+
let id = el.getAttribute('data-wt-id');
|
|
896
|
+
if (!id) {
|
|
897
|
+
id = String(idCounter++);
|
|
898
|
+
el.setAttribute('data-wt-id', id);
|
|
899
|
+
}
|
|
900
|
+
map.push({
|
|
901
|
+
id: id,
|
|
902
|
+
tag: el.tagName.toLowerCase(),
|
|
903
|
+
text: (el.innerText || el.value || el.getAttribute('aria-label') || '').trim().slice(0,80),
|
|
904
|
+
role: el.getAttribute('role'),
|
|
905
|
+
selector: `[data-wt-id="${id}"]`,
|
|
906
|
+
coords: { x: rect.left + rect.width/2, y: rect.top + rect.height/2 },
|
|
907
|
+
inShadow: depth > 0
|
|
908
|
+
});
|
|
909
|
+
if (el.shadowRoot) crawl(el.shadowRoot, depth + 1);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
crawl(document);
|
|
913
|
+
return map;
|
|
914
|
+
};
|
|
915
|
+
|
|
916
|
+
// ── CONSOLE BUFFERING ───────────────────────────────────────────
|
|
917
|
+
const __consoleBuffer = [];
|
|
918
|
+
let __consoleCursor = 0;
|
|
919
|
+
|
|
920
|
+
['log','warn','error','info','debug'].forEach(level => {
|
|
921
|
+
const orig = console[level];
|
|
922
|
+
console[level] = (...args) => {
|
|
923
|
+
__consoleBuffer.push({ level, time: Date.now(), text: args.join(' ') });
|
|
924
|
+
orig.apply(console, args);
|
|
925
|
+
};
|
|
926
|
+
});
|
|
927
|
+
|
|
928
|
+
window.__Weavetab_readConsole = function({ since = 'last', level = null } = {}) {
|
|
929
|
+
const from = since === 'last' ? __consoleCursor : 0;
|
|
930
|
+
const entries = __consoleBuffer.slice(from);
|
|
931
|
+
__consoleCursor = __consoleBuffer.length;
|
|
932
|
+
return level ? entries.filter(e => e.level === level) : entries;
|
|
933
|
+
};
|
|
934
|
+
|
|
935
|
+
// ── PREMIUM WEAVE CURSOR (Phase 5.1 / 5.3) ─────────────────────
|
|
936
|
+
// Shadow-DOM-isolated, canvas-based, with cognitive-load speed scaling
|
|
937
|
+
// and state-driven visual transitions: idle, moving, reading, typing, clicking, thinking
|
|
938
|
+
class WeaveCursor {
|
|
939
|
+
constructor() {
|
|
940
|
+
// Attach a shadow host that sits above all page content
|
|
941
|
+
this._host = document.createElement('div');
|
|
942
|
+
this._host.id = '__Weavetab_cursor_host';
|
|
943
|
+
Object.assign(this._host.style, {
|
|
944
|
+
position: 'fixed',
|
|
945
|
+
top: '0',
|
|
946
|
+
left: '0',
|
|
947
|
+
width: '0',
|
|
948
|
+
height: '0',
|
|
949
|
+
zIndex: '2147483640',
|
|
950
|
+
pointerEvents: 'none',
|
|
951
|
+
overflow: 'visible',
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
const shadow = this._host.attachShadow({ mode: 'closed' });
|
|
955
|
+
|
|
956
|
+
// Canvas layer
|
|
957
|
+
this._canvas = document.createElement('canvas');
|
|
958
|
+
this._canvas.width = 64;
|
|
959
|
+
this._canvas.height = 64;
|
|
960
|
+
Object.assign(this._canvas.style, {
|
|
961
|
+
position: 'fixed',
|
|
962
|
+
top: '0',
|
|
963
|
+
left: '0',
|
|
964
|
+
width: '64px',
|
|
965
|
+
height: '64px',
|
|
966
|
+
pointerEvents: 'none',
|
|
967
|
+
transform: 'translate(-9999px,-9999px)',
|
|
968
|
+
willChange: 'transform',
|
|
969
|
+
transition: 'opacity 0.15s ease-out',
|
|
970
|
+
});
|
|
971
|
+
shadow.appendChild(this._canvas);
|
|
972
|
+
|
|
973
|
+
this._ctx = this._canvas.getContext('2d');
|
|
974
|
+
this._x = -9999;
|
|
975
|
+
this._y = -9999;
|
|
976
|
+
this._targetX = -9999;
|
|
977
|
+
this._targetY = -9999;
|
|
978
|
+
this._lerpX = -9999;
|
|
979
|
+
this._lerpY = -9999;
|
|
980
|
+
this._state = 'idle';
|
|
981
|
+
this._visible = false;
|
|
982
|
+
this._animFrame = null;
|
|
983
|
+
this._lastMoveTime = 0;
|
|
984
|
+
this._lastTime = performance.now();
|
|
985
|
+
this._waypointQueue = [];
|
|
986
|
+
this._playingQueue = false;
|
|
987
|
+
|
|
988
|
+
this._hotX = 0;
|
|
989
|
+
this._hotY = 0;
|
|
990
|
+
|
|
991
|
+
// Sharp precision arrow pointer path (tip at 0,0)
|
|
992
|
+
this._outerPath = new Path2D("M 0 0 L 0 16 L 4.2 12.2 L 7.5 19 L 10.2 17.6 L 6.8 10.8 L 11.8 10.8 Z");
|
|
993
|
+
this._innerPath = new Path2D("M 1.2 2.5 L 1.2 13.5 L 3.8 10.8 L 6.6 16.5 L 8.2 15.6 L 5.4 9.8 L 9.8 9.8 Z");
|
|
994
|
+
|
|
995
|
+
// Vivid linear gradient (#06B6D4 -> #3B82F6)
|
|
996
|
+
this._blueGrad = this._ctx.createLinearGradient(0, 0, 12, 19);
|
|
997
|
+
this._blueGrad.addColorStop(0, '#06B6D4');
|
|
998
|
+
this._blueGrad.addColorStop(1, '#3B82F6');
|
|
999
|
+
|
|
1000
|
+
__wt_safeAppendToBody(this._host);
|
|
1001
|
+
this._startLoop();
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
destroy() {
|
|
1005
|
+
if (this._animFrame) {
|
|
1006
|
+
cancelAnimationFrame(this._animFrame);
|
|
1007
|
+
this._animFrame = null;
|
|
1008
|
+
}
|
|
1009
|
+
if (this._host && this._host.parentNode) {
|
|
1010
|
+
this._host.parentNode.removeChild(this._host);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
setState(state) {
|
|
1015
|
+
this._state = state || 'idle';
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
setVisible(visible) {
|
|
1019
|
+
if (typeof __wt_masterVisualsEnabled !== 'undefined' && !__wt_masterVisualsEnabled) {
|
|
1020
|
+
this._visible = false;
|
|
1021
|
+
this._canvas.style.opacity = '0';
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
1024
|
+
this._visible = visible;
|
|
1025
|
+
this._canvas.style.opacity = visible ? '1' : '0';
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
moveTo(x, y) {
|
|
1029
|
+
if (typeof __wt_masterVisualsEnabled !== 'undefined' && !__wt_masterVisualsEnabled) return;
|
|
1030
|
+
if (this._playingQueue) return;
|
|
1031
|
+
this._targetX = x;
|
|
1032
|
+
this._targetY = y;
|
|
1033
|
+
if (this._lerpX === -9999) { this._lerpX = x; this._lerpY = y; }
|
|
1034
|
+
this._lastMoveTime = Date.now();
|
|
1035
|
+
if (!this._visible) {
|
|
1036
|
+
this._visible = true;
|
|
1037
|
+
this._canvas.style.opacity = '1';
|
|
1038
|
+
}
|
|
1039
|
+
if (!this._animFrame) {
|
|
1040
|
+
this._startLoop();
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
setWaypoints(waypoints, durationMs) {
|
|
1045
|
+
if (typeof __wt_masterVisualsEnabled !== 'undefined' && !__wt_masterVisualsEnabled) return;
|
|
1046
|
+
this._waypointQueue = waypoints;
|
|
1047
|
+
this._playingQueue = true;
|
|
1048
|
+
this._waypointStartTime = performance.now();
|
|
1049
|
+
this._waypointDuration = durationMs;
|
|
1050
|
+
if (waypoints.length > 0 && this._lerpX === -9999) {
|
|
1051
|
+
this._lerpX = waypoints[0].x;
|
|
1052
|
+
this._lerpY = waypoints[0].y;
|
|
1053
|
+
}
|
|
1054
|
+
if (!this._visible) { this._visible = true; this._canvas.style.opacity = '1'; }
|
|
1055
|
+
if (!this._animFrame) this._startLoop();
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
_startLoop() {
|
|
1059
|
+
this._idleFrames = 0;
|
|
1060
|
+
this._lastTime = performance.now();
|
|
1061
|
+
const draw = (currentTime) => {
|
|
1062
|
+
const dt = Math.min((currentTime - this._lastTime) / 1000, 0.1);
|
|
1063
|
+
this._lastTime = currentTime;
|
|
1064
|
+
|
|
1065
|
+
if (this._playingQueue && this._waypointQueue.length > 0) {
|
|
1066
|
+
const elapsed = currentTime - this._waypointStartTime;
|
|
1067
|
+
const progress = Math.min(elapsed / this._waypointDuration, 1);
|
|
1068
|
+
const idx = Math.min(Math.floor(progress * this._waypointQueue.length), this._waypointQueue.length - 1);
|
|
1069
|
+
const pt = this._waypointQueue[idx];
|
|
1070
|
+
if (pt) {
|
|
1071
|
+
this._targetX = pt.x;
|
|
1072
|
+
this._targetY = pt.y;
|
|
1073
|
+
this._lastMoveTime = Date.now();
|
|
1074
|
+
}
|
|
1075
|
+
if (progress >= 1) {
|
|
1076
|
+
this._playingQueue = false;
|
|
1077
|
+
this._waypointQueue = [];
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
// Smooth lerp (k = 14)
|
|
1082
|
+
const lerpFactor = 1 - Math.exp(-14 * dt);
|
|
1083
|
+
if (this._targetX !== -9999) {
|
|
1084
|
+
this._lerpX += (this._targetX - this._lerpX) * lerpFactor;
|
|
1085
|
+
this._lerpY += (this._targetY - this._lerpY) * lerpFactor;
|
|
1086
|
+
if (Math.abs(this._targetX - this._lerpX) < 0.3) this._lerpX = this._targetX;
|
|
1087
|
+
if (Math.abs(this._targetY - this._lerpY) < 0.3) this._lerpY = this._targetY;
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
const hasRecentMove = (Date.now() - this._lastMoveTime) < 1200;
|
|
1091
|
+
if (hasRecentMove && (typeof __wt_masterVisualsEnabled === 'undefined' || __wt_masterVisualsEnabled)) {
|
|
1092
|
+
if (!this._visible) {
|
|
1093
|
+
this._visible = true;
|
|
1094
|
+
this._canvas.style.opacity = '1';
|
|
1095
|
+
}
|
|
1096
|
+
this._idleFrames = 0;
|
|
1097
|
+
} else {
|
|
1098
|
+
this._idleFrames++;
|
|
1099
|
+
if (this._idleFrames > 90 || !__wt_masterVisualsEnabled) {
|
|
1100
|
+
if (this._visible) {
|
|
1101
|
+
this._visible = false;
|
|
1102
|
+
this._canvas.style.opacity = '0';
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
this._render();
|
|
1108
|
+
this._animFrame = requestAnimationFrame(draw);
|
|
1109
|
+
};
|
|
1110
|
+
this._animFrame = requestAnimationFrame(draw);
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
_render() {
|
|
1114
|
+
if (typeof __wt_masterVisualsEnabled !== 'undefined' && !__wt_masterVisualsEnabled) {
|
|
1115
|
+
this._canvas.style.opacity = '0';
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
const { _ctx: ctx, _lerpX: x, _lerpY: y } = this;
|
|
1120
|
+
|
|
1121
|
+
this._canvas.style.transform = `translate(${x - this._hotX}px, ${y - this._hotY}px)`;
|
|
1122
|
+
ctx.clearRect(0, 0, 64, 64);
|
|
1123
|
+
|
|
1124
|
+
ctx.save();
|
|
1125
|
+
ctx.translate(2, 2);
|
|
1126
|
+
|
|
1127
|
+
// Sharp outer stroke — White
|
|
1128
|
+
ctx.fillStyle = '#FFFFFF';
|
|
1129
|
+
ctx.strokeStyle = '#FFFFFF';
|
|
1130
|
+
ctx.lineWidth = 2;
|
|
1131
|
+
ctx.lineJoin = 'miter';
|
|
1132
|
+
ctx.miterLimit = 3;
|
|
1133
|
+
ctx.fill(this._outerPath);
|
|
1134
|
+
ctx.stroke(this._outerPath);
|
|
1135
|
+
|
|
1136
|
+
// Sharp inner core — Gradient
|
|
1137
|
+
ctx.fillStyle = this._blueGrad;
|
|
1138
|
+
ctx.fill(this._innerPath);
|
|
1139
|
+
|
|
1140
|
+
ctx.restore();
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
// Instantiate the premium cursor
|
|
1145
|
+
let __weaveCursor = { setState: function(){}, moveTo: function(){}, destroy: function(){}, setVisible: function(){}, setWaypoints: function(){} };
|
|
1146
|
+
window.__weaveCursor_instance = __weaveCursor;
|
|
1147
|
+
|
|
1148
|
+
function moveCursor(x, y, state) {
|
|
1149
|
+
// Cursor visuals disabled
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
|
|
1153
|
+
// Click ripple (kept as page-level effect, not inside shadow DOM)
|
|
1154
|
+
function ripple(x, y) {
|
|
1155
|
+
if (typeof __wt_masterVisualsEnabled !== 'undefined' && !__wt_masterVisualsEnabled) return;
|
|
1156
|
+
const el = document.createElement('div');
|
|
1157
|
+
el.className = 'Weavetab-ripple';
|
|
1158
|
+
el.setAttribute('data-wt-ignore', '');
|
|
1159
|
+
el.style.cssText = `position:fixed;left:${x}px;top:${y}px;pointer-events:none;z-index:2147483646`;
|
|
1160
|
+
getWeavetabRoot().appendChild(el);
|
|
1161
|
+
el.addEventListener('animationend', () => el.remove());
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
// ── X-RAY SPOTLIGHT OVERLAY (Phase 5.2) ────────────────────────
|
|
1165
|
+
// Flashes glowing halos around top interactive elements when the
|
|
1166
|
+
// semantic map is scanned (GET_SEMANTIC_MAP message).
|
|
1167
|
+
let __xrayActive = false;
|
|
1168
|
+
const __xrayOverlays = [];
|
|
1169
|
+
|
|
1170
|
+
function clearXRay() {
|
|
1171
|
+
for (const el of __xrayOverlays) el.remove();
|
|
1172
|
+
__xrayOverlays.length = 0;
|
|
1173
|
+
__xrayActive = false;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
function activateXRay(elements) {
|
|
1177
|
+
clearXRay();
|
|
1178
|
+
__xrayActive = true;
|
|
1179
|
+
|
|
1180
|
+
// Colour spectrum for visual richness
|
|
1181
|
+
const colours = [
|
|
1182
|
+
'#4F8EF7', '#7C3AED', '#10B981', '#F59E0B',
|
|
1183
|
+
'#EF4444', '#06B6D4', '#F97316', '#8B5CF6',
|
|
1184
|
+
];
|
|
1185
|
+
|
|
1186
|
+
// Architecture change: Use live index for visual overlay
|
|
1187
|
+
// The live index maintains up-to-date coordinates and visibility info
|
|
1188
|
+
const liveIndex = window.__wt_liveIndex || [];
|
|
1189
|
+
const top = elements.slice(0, 12); // highlight top 12 nodes
|
|
1190
|
+
|
|
1191
|
+
top.forEach((item, i) => {
|
|
1192
|
+
// Try to get element from live index first for accurate coordinates
|
|
1193
|
+
const liveEntry = liveIndex.find(e => e.id === item.id || e.selector === item.selector);
|
|
1194
|
+
|
|
1195
|
+
let rect;
|
|
1196
|
+
let el;
|
|
1197
|
+
|
|
1198
|
+
if (liveEntry && liveEntry.rect) {
|
|
1199
|
+
// Use coordinates from live index (more accurate, already computed)
|
|
1200
|
+
rect = liveEntry.rect;
|
|
1201
|
+
el = document.querySelector(item.selector) || shadowQuery(item.selector);
|
|
1202
|
+
} else {
|
|
1203
|
+
// Fallback to querying DOM
|
|
1204
|
+
el = document.querySelector(item.selector) || shadowQuery(item.selector);
|
|
1205
|
+
if (!el) return;
|
|
1206
|
+
rect = el.getBoundingClientRect();
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
if (rect.width === 0 || rect.height === 0) return;
|
|
1210
|
+
|
|
1211
|
+
const colour = colours[i % colours.length];
|
|
1212
|
+
const halo = document.createElement('div');
|
|
1213
|
+
halo.className = '__Weavetab_xray';
|
|
1214
|
+
halo.setAttribute('data-wt-ignore', '');
|
|
1215
|
+
|
|
1216
|
+
Object.assign(halo.style, {
|
|
1217
|
+
position: 'fixed',
|
|
1218
|
+
left: `${rect.left - 4}px`,
|
|
1219
|
+
top: `${rect.top - 4}px`,
|
|
1220
|
+
width: `${rect.width + 8}px`,
|
|
1221
|
+
height: `${rect.height + 8}px`,
|
|
1222
|
+
border: `2px solid ${colour}`,
|
|
1223
|
+
borderRadius: '6px',
|
|
1224
|
+
boxShadow: `0 0 12px 4px ${colour}88, inset 0 0 8px ${colour}33`,
|
|
1225
|
+
background: `${colour}0d`,
|
|
1226
|
+
pointerEvents: 'none',
|
|
1227
|
+
zIndex: '2147483645',
|
|
1228
|
+
animation: '__wt_xray_pulse 1.4s ease-in-out forwards',
|
|
1229
|
+
animationDelay: `${i * 55}ms`,
|
|
1230
|
+
});
|
|
1231
|
+
|
|
1232
|
+
// Rank label badge
|
|
1233
|
+
const badge = document.createElement('span');
|
|
1234
|
+
Object.assign(badge.style, {
|
|
1235
|
+
position: 'absolute',
|
|
1236
|
+
top: '-10px',
|
|
1237
|
+
left: '4px',
|
|
1238
|
+
background: colour,
|
|
1239
|
+
color: '#fff',
|
|
1240
|
+
fontSize: '9px',
|
|
1241
|
+
fontFamily: 'system-ui, sans-serif',
|
|
1242
|
+
fontWeight: '700',
|
|
1243
|
+
padding: '1px 5px',
|
|
1244
|
+
borderRadius: '3px',
|
|
1245
|
+
lineHeight: '14px',
|
|
1246
|
+
whiteSpace: 'nowrap',
|
|
1247
|
+
letterSpacing: '0.03em',
|
|
1248
|
+
boxShadow: `0 1px 4px ${colour}99`,
|
|
1249
|
+
});
|
|
1250
|
+
const label = (item.text || item.role || item.tag || '').slice(0, 22) || `#${i + 1}`;
|
|
1251
|
+
badge.textContent = `${i + 1} ${label}`;
|
|
1252
|
+
halo.appendChild(badge);
|
|
1253
|
+
|
|
1254
|
+
getWeavetabRoot().appendChild(halo);
|
|
1255
|
+
__xrayOverlays.push(halo);
|
|
1256
|
+
});
|
|
1257
|
+
|
|
1258
|
+
// Auto-clear after 2.8 s
|
|
1259
|
+
setTimeout(clearXRay, 2800);
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
// Inject keyframe animation for X-Ray
|
|
1263
|
+
(function injectXRayStyles() {
|
|
1264
|
+
if (document.getElementById('__Weavetab_xray_styles')) return;
|
|
1265
|
+
const style = document.createElement('style');
|
|
1266
|
+
style.id = '__Weavetab_xray_styles';
|
|
1267
|
+
style.textContent = `
|
|
1268
|
+
@keyframes __wt_xray_pulse {
|
|
1269
|
+
0% { opacity: 0; transform: scale(0.94); }
|
|
1270
|
+
18% { opacity: 1; transform: scale(1.02); }
|
|
1271
|
+
70% { opacity: 0.85; transform: scale(1); }
|
|
1272
|
+
100% { opacity: 0; transform: scale(0.97); }
|
|
1273
|
+
}
|
|
1274
|
+
.Weavetab-ripple {
|
|
1275
|
+
width: 40px; height: 40px;
|
|
1276
|
+
margin: -20px 0 0 -20px;
|
|
1277
|
+
border-radius: 50%;
|
|
1278
|
+
background: rgba(79,142,247,0.35);
|
|
1279
|
+
border: 2px solid #4F8EF7;
|
|
1280
|
+
animation: __wt_ripple 0.55s ease-out forwards;
|
|
1281
|
+
}
|
|
1282
|
+
@keyframes __wt_ripple {
|
|
1283
|
+
from { transform: scale(0.3); opacity: 1; }
|
|
1284
|
+
to { transform: scale(2.2); opacity: 0; }
|
|
1285
|
+
}
|
|
1286
|
+
@keyframes wt-reading-pulse {
|
|
1287
|
+
0% { opacity: 0; box-shadow: inset 0 0 0 rgba(59,130,246,0); }
|
|
1288
|
+
15% { opacity: 1; box-shadow: inset 0 0 60px rgba(59,130,246,0.15); }
|
|
1289
|
+
85% { opacity: 1; box-shadow: inset 0 0 60px rgba(59,130,246,0.15); }
|
|
1290
|
+
100% { opacity: 0; box-shadow: inset 0 0 0 rgba(59,130,246,0); }
|
|
1291
|
+
}
|
|
1292
|
+
`;
|
|
1293
|
+
(document.head || document.documentElement).appendChild(style);
|
|
1294
|
+
})();
|
|
1295
|
+
|
|
1296
|
+
// ── READING GLOW ────────────────────────────────────────────────
|
|
1297
|
+
let __wt_readingGlow = null;
|
|
1298
|
+
|
|
1299
|
+
function __wt_showReadingGlow() {
|
|
1300
|
+
if (__wt_readingGlow) return;
|
|
1301
|
+
var el = document.createElement('div');
|
|
1302
|
+
el.id = 'wt-reading-glow';
|
|
1303
|
+
el.setAttribute('data-wt-ignore', '');
|
|
1304
|
+
el.style.cssText = 'position:fixed;inset:0;pointer-events:none;z-index:2147483646;border:4px solid;border-image:linear-gradient(135deg, #3b82f6, #8b5cf6, #10b981) 1;animation:wt-reading-pulse 3.5s ease-in-out forwards;';
|
|
1305
|
+
__wt_safeAppendToBody(el);
|
|
1306
|
+
__wt_readingGlow = el;
|
|
1307
|
+
setTimeout(__wt_hideReadingGlow, 3500);
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
function __wt_hideReadingGlow() {
|
|
1311
|
+
if (__wt_readingGlow) { __wt_readingGlow.remove(); __wt_readingGlow = null; }
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
// ── SEMANTIC DOM MAP ────────────────────────────────────────────
|
|
1315
|
+
// Architecture change: Passive/extension-side ID assignment with live index
|
|
1316
|
+
// The MutationObserver continuously assigns data-wt-id to interactive elements
|
|
1317
|
+
// and maintains a live index that CDP can read lazily
|
|
1318
|
+
|
|
1319
|
+
// Live index - always up-to-date, accessible via window.__wt_liveIndex
|
|
1320
|
+
window.__wt_liveIndex = [];
|
|
1321
|
+
window.__wt_nextId = 1;
|
|
1322
|
+
|
|
1323
|
+
// IntersectionObserver for real occlusion detection (not just display:none)
|
|
1324
|
+
const intersectionObserver = new IntersectionObserver((entries) => {
|
|
1325
|
+
for (const entry of entries) {
|
|
1326
|
+
const el = entry.target;
|
|
1327
|
+
const id = el.getAttribute('data-wt-id');
|
|
1328
|
+
if (id) {
|
|
1329
|
+
const indexEntry = window.__wt_liveIndex.find(e => e.id === id);
|
|
1330
|
+
if (indexEntry) {
|
|
1331
|
+
indexEntry.isVisible = entry.isIntersecting;
|
|
1332
|
+
indexEntry.intersectionRatio = entry.intersectionRatio;
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
}, { threshold: [0, 0.25, 0.5, 0.75, 1.0] });
|
|
1337
|
+
|
|
1338
|
+
// Function to assign data-wt-id to an element and add to live index
|
|
1339
|
+
function assignIdAndIndex(el) {
|
|
1340
|
+
if (el.hasAttribute('data-wt-ignore')) return;
|
|
1341
|
+
if (el.hasAttribute('data-wt-id')) return; // Already assigned
|
|
1342
|
+
|
|
1343
|
+
const id = String(window.__wt_nextId++);
|
|
1344
|
+
el.setAttribute('data-wt-id', id);
|
|
1345
|
+
el.__wt_id = id;
|
|
1346
|
+
|
|
1347
|
+
const rect = el.getBoundingClientRect();
|
|
1348
|
+
const inViewport = (
|
|
1349
|
+
rect.bottom > 0 && rect.right > 0 &&
|
|
1350
|
+
rect.top < window.innerHeight && rect.left < window.innerWidth
|
|
1351
|
+
);
|
|
1352
|
+
const viewportPos = inViewport ? "visible" : (rect.top >= window.innerHeight ? "below" : "above");
|
|
1353
|
+
|
|
1354
|
+
// Check for event listeners
|
|
1355
|
+
const hasClickListener = el.onclick !== null ||
|
|
1356
|
+
el.addEventListener !== undefined &&
|
|
1357
|
+
(el._listeners?.click?.length > 0);
|
|
1358
|
+
|
|
1359
|
+
const indexEntry = {
|
|
1360
|
+
id: id,
|
|
1361
|
+
tag: el.tagName.toLowerCase(),
|
|
1362
|
+
role: el.getAttribute('role') || el.tagName.toLowerCase(),
|
|
1363
|
+
label: el.getAttribute('aria-label') || el.textContent?.trim().slice(0, 60) || '',
|
|
1364
|
+
placeholder: el.getAttribute('placeholder') || '',
|
|
1365
|
+
selector: getStableSelector(el),
|
|
1366
|
+
x: Math.round(rect.left + rect.width / 2),
|
|
1367
|
+
y: Math.round(rect.top + rect.height / 2),
|
|
1368
|
+
viewport: viewportPos,
|
|
1369
|
+
href: el.href || null,
|
|
1370
|
+
isVisible: inViewport,
|
|
1371
|
+
intersectionRatio: inViewport ? 1.0 : 0.0,
|
|
1372
|
+
hasClickListener: hasClickListener,
|
|
1373
|
+
rect: { left: rect.left, top: rect.top, width: rect.width, height: rect.height }
|
|
1374
|
+
};
|
|
1375
|
+
|
|
1376
|
+
window.__wt_liveIndex.push(indexEntry);
|
|
1377
|
+
|
|
1378
|
+
// Observe for intersection changes
|
|
1379
|
+
try {
|
|
1380
|
+
intersectionObserver.observe(el);
|
|
1381
|
+
} catch (e) {
|
|
1382
|
+
// Element might not be observable
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
// Function to update an existing entry in the live index
|
|
1387
|
+
function updateIndexEntry(el) {
|
|
1388
|
+
const id = el.getAttribute('data-wt-id');
|
|
1389
|
+
if (!id) return;
|
|
1390
|
+
|
|
1391
|
+
const indexEntry = window.__wt_liveIndex.find(e => e.id === id);
|
|
1392
|
+
if (!indexEntry) return;
|
|
1393
|
+
|
|
1394
|
+
const rect = el.getBoundingClientRect();
|
|
1395
|
+
const inViewport = (
|
|
1396
|
+
rect.bottom > 0 && rect.right > 0 &&
|
|
1397
|
+
rect.top < window.innerHeight && rect.left < window.innerWidth
|
|
1398
|
+
);
|
|
1399
|
+
const viewportPos = inViewport ? "visible" : (rect.top >= window.innerHeight ? "below" : "above");
|
|
1400
|
+
|
|
1401
|
+
indexEntry.x = Math.round(rect.left + rect.width / 2);
|
|
1402
|
+
indexEntry.y = Math.round(rect.top + rect.height / 2);
|
|
1403
|
+
indexEntry.viewport = viewportPos;
|
|
1404
|
+
indexEntry.isVisible = inViewport;
|
|
1405
|
+
indexEntry.rect = { left: rect.left, top: rect.top, width: rect.width, height: rect.height };
|
|
1406
|
+
indexEntry.label = el.getAttribute('aria-label') || el.textContent?.trim().slice(0, 60) || '';
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
// Function to remove an entry from the live index
|
|
1410
|
+
function removeIndexEntry(el) {
|
|
1411
|
+
const id = el.getAttribute('data-wt-id');
|
|
1412
|
+
if (!id) return;
|
|
1413
|
+
|
|
1414
|
+
const index = window.__wt_liveIndex.findIndex(e => e.id === id);
|
|
1415
|
+
if (index !== -1) {
|
|
1416
|
+
window.__wt_liveIndex.splice(index, 1);
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
try {
|
|
1420
|
+
intersectionObserver.unobserve(el);
|
|
1421
|
+
} catch (e) {
|
|
1422
|
+
// Element might not be observable
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
// MutationObserver for continuous ID assignment and index maintenance
|
|
1427
|
+
const idAssignmentObserver = new MutationObserver((mutations) => {
|
|
1428
|
+
for (const mutation of mutations) {
|
|
1429
|
+
// Handle added nodes
|
|
1430
|
+
for (const node of mutation.addedNodes) {
|
|
1431
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
1432
|
+
// Check if the node itself is interactive
|
|
1433
|
+
const interactiveSelector = 'a, button, input, select, textarea, [role="button"], [role="link"], [role="textbox"], [role="checkbox"], [role="radio"], [role="combobox"], [role="searchbox"], [role="menuitem"], [role="tab"], [tabindex]:not([tabindex="-1"])';
|
|
1434
|
+
if (node.matches && node.matches(interactiveSelector)) {
|
|
1435
|
+
assignIdAndIndex(node);
|
|
1436
|
+
}
|
|
1437
|
+
// Check descendants
|
|
1438
|
+
const descendants = node.querySelectorAll ? node.querySelectorAll(interactiveSelector) : [];
|
|
1439
|
+
for (const desc of descendants) {
|
|
1440
|
+
assignIdAndIndex(desc);
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
// Handle removed nodes
|
|
1446
|
+
for (const node of mutation.removedNodes) {
|
|
1447
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
1448
|
+
if (node.getAttribute && node.getAttribute('data-wt-id')) {
|
|
1449
|
+
removeIndexEntry(node);
|
|
1450
|
+
}
|
|
1451
|
+
const descendants = node.querySelectorAll ? node.querySelectorAll('[data-wt-id]') : [];
|
|
1452
|
+
for (const desc of descendants) {
|
|
1453
|
+
removeIndexEntry(desc);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
// Handle attribute changes (position, visibility, etc.)
|
|
1459
|
+
if (mutation.type === 'attributes' && mutation.target.getAttribute) {
|
|
1460
|
+
const target = mutation.target;
|
|
1461
|
+
if (target.getAttribute('data-wt-id')) {
|
|
1462
|
+
updateIndexEntry(target);
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
});
|
|
1467
|
+
|
|
1468
|
+
// Start observing when DOM is ready
|
|
1469
|
+
function startIdAssignment() {
|
|
1470
|
+
const interactiveSelector = 'a, button, input, select, textarea, [role="button"], [role="link"], [role="textbox"], [role="checkbox"], [role="radio"], [role="combobox"], [role="searchbox"], [role="menuitem"], [role="tab"], [tabindex]:not([tabindex="-1"])';
|
|
1471
|
+
|
|
1472
|
+
// Assign IDs to existing interactive elements
|
|
1473
|
+
const existing = document.querySelectorAll(interactiveSelector);
|
|
1474
|
+
for (const el of existing) {
|
|
1475
|
+
assignIdAndIndex(el);
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
// Start observing for changes
|
|
1479
|
+
if (document.body) {
|
|
1480
|
+
idAssignmentObserver.observe(document.body, {
|
|
1481
|
+
childList: true,
|
|
1482
|
+
subtree: true,
|
|
1483
|
+
attributes: true,
|
|
1484
|
+
attributeFilter: ['style', 'class', 'hidden', 'aria-hidden']
|
|
1485
|
+
});
|
|
1486
|
+
} else {
|
|
1487
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
1488
|
+
const existing = document.querySelectorAll(interactiveSelector);
|
|
1489
|
+
for (const el of existing) {
|
|
1490
|
+
assignIdAndIndex(el);
|
|
1491
|
+
}
|
|
1492
|
+
idAssignmentObserver.observe(document.body, {
|
|
1493
|
+
childList: true,
|
|
1494
|
+
subtree: true,
|
|
1495
|
+
attributes: true,
|
|
1496
|
+
attributeFilter: ['style', 'class', 'hidden', 'aria-hidden']
|
|
1497
|
+
});
|
|
1498
|
+
});
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
// Start the ID assignment system
|
|
1503
|
+
if (document.readyState === 'loading') {
|
|
1504
|
+
document.addEventListener('DOMContentLoaded', startIdAssignment);
|
|
1505
|
+
} else {
|
|
1506
|
+
startIdAssignment();
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
// Legacy buildSemanticMap function for backward compatibility
|
|
1510
|
+
// Now reads from the live index instead of rebuilding
|
|
1511
|
+
let cachedMap = null;
|
|
1512
|
+
const legacyObserver = new MutationObserver(() => {
|
|
1513
|
+
cachedMap = null;
|
|
1514
|
+
});
|
|
1515
|
+
// FIX: document.body may be null at document_start; defer observe until body exists
|
|
1516
|
+
if (document.body) {
|
|
1517
|
+
legacyObserver.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class', 'hidden'] });
|
|
1518
|
+
} else {
|
|
1519
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
1520
|
+
if (document.body) legacyObserver.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class', 'hidden'] });
|
|
1521
|
+
}, { once: true });
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
function buildSemanticMap() {
|
|
1525
|
+
if (cachedMap) return cachedMap;
|
|
1526
|
+
|
|
1527
|
+
// Read from the live index maintained by the MutationObserver
|
|
1528
|
+
cachedMap = window.__wt_liveIndex.map(entry => ({
|
|
1529
|
+
id: entry.id,
|
|
1530
|
+
tag: entry.tag,
|
|
1531
|
+
role: entry.role,
|
|
1532
|
+
label: entry.label,
|
|
1533
|
+
placeholder: entry.placeholder,
|
|
1534
|
+
selector: entry.selector,
|
|
1535
|
+
x: entry.x,
|
|
1536
|
+
y: entry.y,
|
|
1537
|
+
viewport: entry.viewport,
|
|
1538
|
+
href: entry.href,
|
|
1539
|
+
coords: { x: entry.x, y: entry.y }
|
|
1540
|
+
}));
|
|
1541
|
+
|
|
1542
|
+
return cachedMap;
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
// ── HUD ─────────────────────────────────────────────────────────
|
|
1546
|
+
const hudHistory = [];
|
|
1547
|
+
const hud = (() => {
|
|
1548
|
+
const el = document.createElement('div');
|
|
1549
|
+
el.id = 'Weavetab-hud';
|
|
1550
|
+
el.setAttribute('data-wt-ignore', '');
|
|
1551
|
+
Object.assign(el.style, {
|
|
1552
|
+
position: 'fixed', bottom: '24px', right: '24px',
|
|
1553
|
+
transform: 'translateY(8px)',
|
|
1554
|
+
color: '#a1a1aa',
|
|
1555
|
+
font: '11px/1.3 -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif',
|
|
1556
|
+
borderRadius: '8px',
|
|
1557
|
+
opacity: '0', pointerEvents: 'none',
|
|
1558
|
+
zIndex: '2147483640',
|
|
1559
|
+
maxWidth: '400px',
|
|
1560
|
+
display: 'flex', flexDirection: 'column-reverse', gap: '4px',
|
|
1561
|
+
transition: 'opacity 0.2s, transform 0.2s',
|
|
1562
|
+
textAlign: 'right',
|
|
1563
|
+
});
|
|
1564
|
+
__wt_safeAppendToBody(el);
|
|
1565
|
+
return el;
|
|
1566
|
+
})();
|
|
1567
|
+
|
|
1568
|
+
function updateHUD(action, detail) {
|
|
1569
|
+
if (window.self !== window.top) return;
|
|
1570
|
+
// Tool call entries display strictly inside the Sidepanel TOOL CALLS section.
|
|
1571
|
+
// Suppress on-page bottom-right HUD popup overlay.
|
|
1572
|
+
if (hud) {
|
|
1573
|
+
hud.style.display = 'none';
|
|
1574
|
+
hud.style.opacity = '0';
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
// ── ACTIVE TAB INDICATOR ─────────────────────────────────────────
|
|
1579
|
+
class ControlledTabIndicator {
|
|
1580
|
+
constructor() {
|
|
1581
|
+
this._el = document.createElement('div');
|
|
1582
|
+
this._el.id = '__Weavetab_controlled_indicator';
|
|
1583
|
+
this._el.setAttribute('data-wt-ignore', '');
|
|
1584
|
+
Object.assign(this._el.style, {
|
|
1585
|
+
position: 'fixed',
|
|
1586
|
+
top: '0',
|
|
1587
|
+
left: '0',
|
|
1588
|
+
right: '0',
|
|
1589
|
+
height: '3px',
|
|
1590
|
+
zIndex: '2147483646',
|
|
1591
|
+
background: '#3B82F6',
|
|
1592
|
+
boxShadow: '0 0 12px #3B82F6, 0 0 4px #60a5fa',
|
|
1593
|
+
opacity: '0',
|
|
1594
|
+
transition: 'opacity 0.25s ease-in-out',
|
|
1595
|
+
pointerEvents: 'none',
|
|
1596
|
+
});
|
|
1597
|
+
// Animated shimmer
|
|
1598
|
+
this._shimmer = document.createElement('div');
|
|
1599
|
+
Object.assign(this._shimmer.style, {
|
|
1600
|
+
position: 'absolute',
|
|
1601
|
+
top: '0', left: '-60%', width: '60%', height: '100%',
|
|
1602
|
+
background: 'linear-gradient(90deg,transparent,rgba(255,255,255,0.45),transparent)',
|
|
1603
|
+
animation: 'wt-shimmer 1.6s infinite',
|
|
1604
|
+
});
|
|
1605
|
+
// Inject animation keyframes into shadow root
|
|
1606
|
+
var style = document.createElement('style');
|
|
1607
|
+
style.textContent = '@keyframes wt-shimmer{0%{left:-60%}100%{left:120%}}';
|
|
1608
|
+
try { getWeavetabRoot().appendChild(style); } catch(e) {}
|
|
1609
|
+
this._el.appendChild(this._shimmer);
|
|
1610
|
+
getWeavetabRoot().appendChild(this._el);
|
|
1611
|
+
this._hideTimer = null;
|
|
1612
|
+
this._persistent = false;
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
// Show persistently (while agent is weaving)
|
|
1616
|
+
showPersistent(color) {
|
|
1617
|
+
this._persistent = true;
|
|
1618
|
+
clearTimeout(this._hideTimer);
|
|
1619
|
+
if (color) {
|
|
1620
|
+
this._el.style.background = color;
|
|
1621
|
+
this._el.style.boxShadow = '0 0 12px ' + color + ', 0 0 4px ' + color;
|
|
1622
|
+
}
|
|
1623
|
+
this._el.style.opacity = '1';
|
|
1624
|
+
this._shimmer.style.display = 'block';
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
// Show briefly (1.5s flash for single actions)
|
|
1628
|
+
show(color) {
|
|
1629
|
+
if (this._persistent) return; // Don't override persistent mode
|
|
1630
|
+
if (color) this._el.style.background = color;
|
|
1631
|
+
this._el.style.opacity = '1';
|
|
1632
|
+
clearTimeout(this._hideTimer);
|
|
1633
|
+
this._hideTimer = setTimeout(() => this.hide(), 1500);
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
hide() {
|
|
1637
|
+
this._persistent = false;
|
|
1638
|
+
this._el.style.opacity = '0';
|
|
1639
|
+
this._shimmer.style.display = 'none';
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
destroy() {
|
|
1643
|
+
clearTimeout(this._hideTimer);
|
|
1644
|
+
this._el.remove();
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
const controlledIndicator = new ControlledTabIndicator();
|
|
1649
|
+
|
|
1650
|
+
// ── MESSAGE BRIDGE ──────────────────────────────────────────────
|
|
1651
|
+
// Shared handler for both CustomEvent (MAIN world dispatch) and
|
|
1652
|
+
// postMessage (cross-world: MAIN → ISOLATED) communication channels.
|
|
1653
|
+
var __wt_lastCursor = { x: -9999, y: -9999 };
|
|
1654
|
+
var __wt_processedMsgIds = new Set();
|
|
1655
|
+
|
|
1656
|
+
function __wt_handleWeavetabMsg(msg) {
|
|
1657
|
+
if (!msg) return;
|
|
1658
|
+
if (Array.isArray(msg)) { msg.forEach(m => __wt_handleWeavetabMsg(m)); return; }
|
|
1659
|
+
if (msg._id) { if (__wt_processedMsgIds.has(msg._id)) return; __wt_processedMsgIds.add(msg._id); if (__wt_processedMsgIds.size > 100) __wt_processedMsgIds.clear(); }
|
|
1660
|
+
|
|
1661
|
+
if (msg.type === 'Weavetab_STATE') {
|
|
1662
|
+
if (msg.cursor) {
|
|
1663
|
+
__wt_lastCursor = { x: msg.cursor.x, y: msg.cursor.y };
|
|
1664
|
+
if (msg.cursor.waypoints && msg.cursor.duration_ms) {
|
|
1665
|
+
__weaveCursor.setWaypoints(msg.cursor.waypoints, msg.cursor.duration_ms);
|
|
1666
|
+
}
|
|
1667
|
+
moveCursor(msg.cursor.x, msg.cursor.y, msg.state);
|
|
1668
|
+
}
|
|
1669
|
+
else if (msg.state) { moveCursor(__wt_lastCursor.x, __wt_lastCursor.y, msg.state); }
|
|
1670
|
+
if (msg.ripple) ripple(msg.ripple.x, msg.ripple.y);
|
|
1671
|
+
|
|
1672
|
+
if (msg.userFeedback === true && msg.feedbackText) {
|
|
1673
|
+
__wt_feedbackSticky = true;
|
|
1674
|
+
clearTimeout(window.__wt_feedbackStickyTimer);
|
|
1675
|
+
window.__wt_feedbackStickyTimer = setTimeout(function(){ __wt_feedbackSticky = false; }, 6000);
|
|
1676
|
+
} else if (msg.userFeedback === false) {
|
|
1677
|
+
setTimeout(function(){ __wt_feedbackSticky = false; }, 2000);
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
// stickyFeedback=true: fired by Weavetab_logs when agent answers a user question.
|
|
1681
|
+
// Pin the bubble for 10s so tool chatter (moving to..., typing...) cannot override it.
|
|
1682
|
+
if (msg.stickyFeedback === true && msg.hud && msg.hud.action) {
|
|
1683
|
+
__wt_thoughtQueue = [];
|
|
1684
|
+
clearTimeout(window.__wt_feedbackStickyTimer);
|
|
1685
|
+
|
|
1686
|
+
if (msg.ask) {
|
|
1687
|
+
window.__wt_feedbackStickyTimer = setTimeout(function(){
|
|
1688
|
+
__wt_feedbackSticky = false;
|
|
1689
|
+
__wt_removeAsk();
|
|
1690
|
+
}, (msg.ask.timeout || 120) * 1000 + 2000);
|
|
1691
|
+
__wt_showQuestion(msg.hud.action, msg.ask.input || 'sentence', msg.ask.timeout || 120);
|
|
1692
|
+
} else {
|
|
1693
|
+
__wt_feedbackSticky = true;
|
|
1694
|
+
window.__wt_feedbackStickyTimer = setTimeout(function(){
|
|
1695
|
+
__wt_feedbackSticky = false;
|
|
1696
|
+
__wt_destroyBubble();
|
|
1697
|
+
}, 10000);
|
|
1698
|
+
__wt_renderThought(msg.hud.action, { active: false, sticky: true });
|
|
1699
|
+
if (msg.hud) updateHUD(msg.hud.action, msg.hud.detail);
|
|
1700
|
+
}
|
|
1701
|
+
if (msg.cursor) {
|
|
1702
|
+
__wt_lastCursor = { x: msg.cursor.x, y: msg.cursor.y };
|
|
1703
|
+
if (msg.cursor.waypoints && msg.cursor.duration_ms) {
|
|
1704
|
+
__weaveCursor.setWaypoints(msg.cursor.waypoints, msg.cursor.duration_ms);
|
|
1705
|
+
}
|
|
1706
|
+
moveCursor(msg.cursor.x, msg.cursor.y, msg.state);
|
|
1707
|
+
}
|
|
1708
|
+
else if (msg.state) { moveCursor(__wt_lastCursor.x, __wt_lastCursor.y, msg.state); }
|
|
1709
|
+
return;
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
if (msg.hud) {
|
|
1713
|
+
updateHUD(msg.hud.action, msg.hud.detail);
|
|
1714
|
+
var a = (msg.hud.action || '').trim();
|
|
1715
|
+
if (a === 'reading the page...') {
|
|
1716
|
+
__wt_showReadingGlow();
|
|
1717
|
+
__wt_showThought('Reading the page...', { active: true });
|
|
1718
|
+
__weaveCursor.setState('reading');
|
|
1719
|
+
} else if (a === 'Mission Running') {
|
|
1720
|
+
// Tool call is starting — switch to eval animation and show tool bubble
|
|
1721
|
+
__wt_toolActive = true;
|
|
1722
|
+
__weaveCursor.setState('eval');
|
|
1723
|
+
__wt_showThought('Executing tool...', { active: true });
|
|
1724
|
+
} else if (a.startsWith('[OK]')) {
|
|
1725
|
+
// Tool completed successfully — revert to idle and instantly hide bubble
|
|
1726
|
+
__weaveCursor.setState('idle');
|
|
1727
|
+
__wt_toolActive = false;
|
|
1728
|
+
if (!__wt_feedbackSticky) {
|
|
1729
|
+
__wt_destroyBubble(); // Instant removal
|
|
1730
|
+
__wt_processThoughtQueue();
|
|
1731
|
+
}
|
|
1732
|
+
} else if (a.startsWith('[ERR]')) {
|
|
1733
|
+
// Tool errored — show stuck cursor briefly
|
|
1734
|
+
__weaveCursor.setState('stuck');
|
|
1735
|
+
__wt_toolActive = false;
|
|
1736
|
+
setTimeout(function(){ __weaveCursor.setState('idle'); }, 3000);
|
|
1737
|
+
if (!__wt_feedbackSticky) {
|
|
1738
|
+
__wt_destroyBubble(); // Instant removal
|
|
1739
|
+
__wt_processThoughtQueue();
|
|
1740
|
+
}
|
|
1741
|
+
} else if (a && a.length > 3) {
|
|
1742
|
+
__wt_hideReadingGlow();
|
|
1743
|
+
if (!__wt_feedbackSticky) {
|
|
1744
|
+
__wt_showThought(a, { active: true });
|
|
1745
|
+
}
|
|
1746
|
+
} else {
|
|
1747
|
+
if (!__wt_feedbackSticky) {
|
|
1748
|
+
__wt_hideReadingGlow();
|
|
1749
|
+
__wt_destroyBubble(); // Instant removal
|
|
1750
|
+
__wt_processThoughtQueue();
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
if (msg.darkMode !== undefined) document.body.classList.toggle('Weavetab-dark', msg.darkMode);
|
|
1755
|
+
if (msg.cursor || msg.hud) {
|
|
1756
|
+
controlledIndicator.show('#3B82F6');
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
// Handle plan updates via sendToExtension channel
|
|
1761
|
+
if (msg.type === 'Weavetab_STATE' && msg.plan) {
|
|
1762
|
+
const p = msg.plan;
|
|
1763
|
+
if (p.action === 'hide') {
|
|
1764
|
+
if (window.__wt_plan) window.__wt_plan('hide', '', []);
|
|
1765
|
+
} else {
|
|
1766
|
+
if (window.__wt_plan) window.__wt_plan(p.action || 'create', p.goal || '', p.tasks || []);
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
|
|
1771
|
+
if (msg.type === 'WT_USER_NOTE_INJECT') {
|
|
1772
|
+
window.__wt_user_notes = window.__wt_user_notes || [];
|
|
1773
|
+
window.__wt_user_notes.push({ text: msg.text, time: Date.now() });
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1776
|
+
if (msg.type === 'GET_SEMANTIC_MAP') {
|
|
1777
|
+
const map = window.__Weavetab_fullMap();
|
|
1778
|
+
if (window.__wt_user_notes && window.__wt_user_notes.length > 0) {
|
|
1779
|
+
// Keep notes for 2 minutes
|
|
1780
|
+
window.__wt_user_notes = window.__wt_user_notes.filter(n => Date.now() - n.time < 120000);
|
|
1781
|
+
const notesMap = window.__wt_user_notes.map((note, i) => ({
|
|
1782
|
+
id: `user-msg-${i}`,
|
|
1783
|
+
role: 'alert',
|
|
1784
|
+
tag: 'div',
|
|
1785
|
+
text: `URGENT MESSAGE FROM USER: "${note.text}"`,
|
|
1786
|
+
coords: [0, 0, 100, 100],
|
|
1787
|
+
interactive: false
|
|
1788
|
+
}));
|
|
1789
|
+
map.unshift(...notesMap);
|
|
1790
|
+
}
|
|
1791
|
+
activateXRay(map.filter(m => m.coords && m.text).slice(0, 12));
|
|
1792
|
+
window.dispatchEvent(new CustomEvent('Weavetab-msg-out', { detail: { id: msg.id, data: map } }));
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
if (msg.type === 'RESOLVE_EXTENSION_URL') {
|
|
1796
|
+
var url = '';
|
|
1797
|
+
try { url = browserAPI.runtime.getURL((msg.page || 'dashboard') + '.html'); } catch(e) {}
|
|
1798
|
+
window.dispatchEvent(new CustomEvent('Weavetab-msg-out', { detail: { id: msg.id, data: url } }));
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
if (msg.type === 'Weavetab_CONFIG') {
|
|
1802
|
+
window.__wt_devMode = !!msg.devMode;
|
|
1803
|
+
window.dispatchEvent(new CustomEvent('wt-config-sync'));
|
|
1804
|
+
__wt_safeSendMessage(msg);
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
if (msg.type === 'Weavetab_GROUP_STATE') {
|
|
1808
|
+
__wt_safeSendMessage(msg);
|
|
1809
|
+
// Only ever blue dot — no yellow. Idle state = no dot.
|
|
1810
|
+
const dotColor = (msg.state === 'idle') ? null : '#3B82F6';
|
|
1811
|
+
if (dotColor) {
|
|
1812
|
+
controlledIndicator.show(dotColor);
|
|
1813
|
+
}
|
|
1814
|
+
updateFaviconDot(msg.state, msg.isActive);
|
|
1815
|
+
// Parked tabs: clear thought bubble and reading glow
|
|
1816
|
+
if (msg.isActive === false) {
|
|
1817
|
+
__wt_removeThought();
|
|
1818
|
+
__wt_hideReadingGlow();
|
|
1819
|
+
}
|
|
1820
|
+
if (msg.state === 'idle') {
|
|
1821
|
+
__wt_hideReadingGlow();
|
|
1822
|
+
__wt_destroyBubble();
|
|
1823
|
+
__wt_feedbackSticky = false;
|
|
1824
|
+
__wt_processThoughtQueue();
|
|
1825
|
+
restoreOriginalFavicon();
|
|
1826
|
+
// Hide the persistent indicator bar when idle
|
|
1827
|
+
controlledIndicator.hide();
|
|
1828
|
+
__weaveCursor.setState('idle');
|
|
1829
|
+
} else if (msg.state === 'weaving') {
|
|
1830
|
+
// Show persistent blue bar while agent works
|
|
1831
|
+
controlledIndicator.showPersistent('#3B82F6');
|
|
1832
|
+
__weaveCursor.setState('eval');
|
|
1833
|
+
} else if (msg.state === 'stuck') {
|
|
1834
|
+
// Still show blue bar (not red) when stuck
|
|
1835
|
+
controlledIndicator.showPersistent('#3B82F6');
|
|
1836
|
+
__weaveCursor.setState('stuck');
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
|
|
1841
|
+
if (msg.type === 'Weavetab_THOUGHT') {
|
|
1842
|
+
__wt_showThought(msg.text, { active: msg.active });
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
if (msg.type === 'Weavetab_ASK') {
|
|
1846
|
+
// Legacy support — route to new unified question system
|
|
1847
|
+
console.log('[Weavetab] Received ASK request (legacy):', msg.question);
|
|
1848
|
+
__wt_showQuestion(msg.question, 'sentence', msg.timeout || 120);
|
|
1849
|
+
window.dispatchEvent(new CustomEvent('Weavetab-msg-out', { detail: { id: msg.id, data: { status: 'shown' } } }));
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
if (msg.type === 'Weavetab_CLEAR_THOUGHT') {
|
|
1853
|
+
__wt_removeThought();
|
|
1854
|
+
__wt_hideReadingGlow();
|
|
1855
|
+
__wt_processThoughtQueue();
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
if (msg.type === 'Weavetab_RELOAD_EXTENSION') {
|
|
1859
|
+
__wt_safeSendMessage({ type: 'RELOAD_EXTENSION' });
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
// Channel 1: CustomEvent from MAIN world (used by fallback + sendToExtension)
|
|
1864
|
+
window.addEventListener('Weavetab-msg-in', (e) => {
|
|
1865
|
+
__wt_handleWeavetabMsg(e.detail);
|
|
1866
|
+
});
|
|
1867
|
+
|
|
1868
|
+
// Channel 2: postMessage from MAIN world (cross-world MV3 bridge)
|
|
1869
|
+
window.addEventListener('message', (event) => {
|
|
1870
|
+
if (event.data && event.data.type === 'Weavetab-msg-in' && event.data.data) {
|
|
1871
|
+
__wt_handleWeavetabMsg(event.data.data);
|
|
1872
|
+
}
|
|
1873
|
+
// ── User feedback relay: WeaveTab OS panel → background storage queue ────
|
|
1874
|
+
// When the user types in the chat panel and hits Send, submitFeedback() fires
|
|
1875
|
+
// a __WT_FEEDBACK postMessage. We relay it to the background so it lands in
|
|
1876
|
+
// chrome.storage.local and window.__Weavetab.popUserFeedback() can return it.
|
|
1877
|
+
if (event.data && event.data.type === '__WT_FEEDBACK' && event.data.text) {
|
|
1878
|
+
try {
|
|
1879
|
+
if (browserAPI.runtime && browserAPI.runtime.id) {
|
|
1880
|
+
browserAPI.runtime.sendMessage({ type: 'USER_FEEDBACK', text: event.data.text }).catch(function(){});
|
|
1881
|
+
}
|
|
1882
|
+
} catch(e) {}
|
|
1883
|
+
}
|
|
1884
|
+
});
|
|
1885
|
+
|
|
1886
|
+
// ── FAVICON STATUS DOT INDICATOR ───────────────────────────────
|
|
1887
|
+
let wtFaviconObserver = null;
|
|
1888
|
+
|
|
1889
|
+
let __wt_faviconStaleTimer = null;
|
|
1890
|
+
|
|
1891
|
+
function updateFaviconDot(state, isActive) {
|
|
1892
|
+
if (__wt_faviconStaleTimer) { clearTimeout(__wt_faviconStaleTimer); __wt_faviconStaleTimer = null; }
|
|
1893
|
+
|
|
1894
|
+
if (state === 'idle' || !state || isActive === false) {
|
|
1895
|
+
if (wtFaviconObserver) {
|
|
1896
|
+
wtFaviconObserver.disconnect();
|
|
1897
|
+
wtFaviconObserver = null;
|
|
1898
|
+
}
|
|
1899
|
+
restoreOriginalFavicon();
|
|
1900
|
+
return;
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
// Always use blue — never red or yellow for favicon dot
|
|
1904
|
+
const color = '#3B82F6';
|
|
1905
|
+
|
|
1906
|
+
if (!wtFaviconObserver) {
|
|
1907
|
+
wtFaviconObserver = new MutationObserver(() => {
|
|
1908
|
+
wtFaviconObserver.disconnect();
|
|
1909
|
+
applyFaviconDot(color);
|
|
1910
|
+
wtFaviconObserver.observe(document.head, { childList: true, subtree: true });
|
|
1911
|
+
});
|
|
1912
|
+
} else {
|
|
1913
|
+
wtFaviconObserver.disconnect();
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
applyFaviconDot(color);
|
|
1917
|
+
wtFaviconObserver.observe(document.head, { childList: true, subtree: true });
|
|
1918
|
+
|
|
1919
|
+
// Set stale timer: clear dot + hide glow if no message for 30s
|
|
1920
|
+
__wt_faviconStaleTimer = setTimeout(function() {
|
|
1921
|
+
restoreOriginalFavicon();
|
|
1922
|
+
__wt_hideReadingGlow();
|
|
1923
|
+
__wt_destroyBubble();
|
|
1924
|
+
}, 30000);
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
function restoreOriginalFavicon() {
|
|
1928
|
+
const customLink = document.getElementById('wt-custom-favicon');
|
|
1929
|
+
if (customLink) customLink.remove();
|
|
1930
|
+
|
|
1931
|
+
const links = document.querySelectorAll('link[rel*="icon"]');
|
|
1932
|
+
links.forEach(link => {
|
|
1933
|
+
if (link.dataset.wtOrigHref) {
|
|
1934
|
+
link.href = link.dataset.wtOrigHref;
|
|
1935
|
+
delete link.dataset.wtOrigHref;
|
|
1936
|
+
}
|
|
1937
|
+
});
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
function applyFaviconDot(color) {
|
|
1941
|
+
const links = Array.from(document.querySelectorAll('link[rel*="icon"]')).filter(l => l.id !== 'wt-custom-favicon');
|
|
1942
|
+
let originalHref = '';
|
|
1943
|
+
|
|
1944
|
+
if (links.length > 0) {
|
|
1945
|
+
const link = links[0];
|
|
1946
|
+
if (!link.dataset.wtOrigHref) {
|
|
1947
|
+
link.dataset.wtOrigHref = link.href;
|
|
1948
|
+
}
|
|
1949
|
+
originalHref = link.dataset.wtOrigHref;
|
|
1950
|
+
} else {
|
|
1951
|
+
originalHref = '/favicon.ico';
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
const canvas = document.createElement('canvas');
|
|
1955
|
+
canvas.width = 32;
|
|
1956
|
+
canvas.height = 32;
|
|
1957
|
+
const ctx = canvas.getContext('2d');
|
|
1958
|
+
|
|
1959
|
+
const img = new Image();
|
|
1960
|
+
img.crossOrigin = 'anonymous';
|
|
1961
|
+
img.onload = () => {
|
|
1962
|
+
try {
|
|
1963
|
+
ctx.drawImage(img, 0, 0, 32, 32);
|
|
1964
|
+
drawDot(ctx, color);
|
|
1965
|
+
setFaviconHref(canvas.toDataURL('image/png'));
|
|
1966
|
+
} catch (e) {
|
|
1967
|
+
drawFallbackIcon(ctx, color);
|
|
1968
|
+
setFaviconHref(canvas.toDataURL('image/png'));
|
|
1969
|
+
}
|
|
1970
|
+
};
|
|
1971
|
+
img.onerror = () => {
|
|
1972
|
+
drawFallbackIcon(ctx, color);
|
|
1973
|
+
setFaviconHref(canvas.toDataURL('image/png'));
|
|
1974
|
+
};
|
|
1975
|
+
img.src = originalHref;
|
|
1976
|
+
}
|
|
1977
|
+
|
|
1978
|
+
function drawDot(ctx, color) {
|
|
1979
|
+
// Solid dot
|
|
1980
|
+
ctx.beginPath();
|
|
1981
|
+
ctx.arc(24, 24, 5.5, 0, 2 * Math.PI);
|
|
1982
|
+
ctx.fillStyle = color;
|
|
1983
|
+
ctx.fill();
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
function drawFallbackIcon(ctx, color) {
|
|
1987
|
+
ctx.clearRect(0, 0, 32, 32);
|
|
1988
|
+
ctx.beginPath();
|
|
1989
|
+
ctx.arc(16, 16, 11, 0, 2 * Math.PI);
|
|
1990
|
+
ctx.fillStyle = '#1E1F22';
|
|
1991
|
+
ctx.fill();
|
|
1992
|
+
ctx.strokeStyle = '#3C3F45';
|
|
1993
|
+
ctx.lineWidth = 1.5;
|
|
1994
|
+
ctx.stroke();
|
|
1995
|
+
|
|
1996
|
+
ctx.beginPath();
|
|
1997
|
+
ctx.arc(16, 16, 6, 0, 2 * Math.PI);
|
|
1998
|
+
ctx.fillStyle = '#7C3AED';
|
|
1999
|
+
ctx.fill();
|
|
2000
|
+
|
|
2001
|
+
drawDot(ctx, color);
|
|
2002
|
+
}
|
|
2003
|
+
|
|
2004
|
+
function setFaviconHref(dataUrl) {
|
|
2005
|
+
let customLink = document.getElementById('wt-custom-favicon');
|
|
2006
|
+
if (!customLink) {
|
|
2007
|
+
customLink = document.createElement('link');
|
|
2008
|
+
customLink.id = 'wt-custom-favicon';
|
|
2009
|
+
customLink.rel = 'icon';
|
|
2010
|
+
customLink.type = 'image/png';
|
|
2011
|
+
(document.head || document.documentElement).appendChild(customLink);
|
|
2012
|
+
}
|
|
2013
|
+
customLink.href = dataUrl;
|
|
2014
|
+
|
|
2015
|
+
const links = document.querySelectorAll('link[rel*="icon"]');
|
|
2016
|
+
links.forEach(link => {
|
|
2017
|
+
if (link.id !== 'wt-custom-favicon') {
|
|
2018
|
+
if (!link.dataset.wtOrigHref) {
|
|
2019
|
+
link.dataset.wtOrigHref = link.href;
|
|
2020
|
+
}
|
|
2021
|
+
link.href = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
|
|
2022
|
+
}
|
|
2023
|
+
});
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
|
|
2027
|
+
|
|
2028
|
+
// ── PERSISTENT MESSAGING PANEL (WeaveTab OS v2.5.0) ───────────────
|
|
2029
|
+
// Design inspired by designs/elements.html
|
|
2030
|
+
(function initWeaveTabUI() {
|
|
2031
|
+
if (window.self !== window.top) return;
|
|
2032
|
+
if (document.getElementById('wt-main-container')) return;
|
|
2033
|
+
|
|
2034
|
+
const version = "2.5.0";
|
|
2035
|
+
const isBeta = true;
|
|
2036
|
+
|
|
2037
|
+
// ── CSS Injection ─────────────────────────────────────────────
|
|
2038
|
+
const style = document.createElement('style');
|
|
2039
|
+
style.id = 'wt-ui-styles';
|
|
2040
|
+
style.textContent = `
|
|
2041
|
+
/* ── Sidepanel Tokens ────────────────────────────────────── */
|
|
2042
|
+
:host, .wt-sp-root { all: initial; }
|
|
2043
|
+
.wt-sp-root, .wt-sp-root * { box-sizing: border-box; margin: 0; padding: 0; }
|
|
2044
|
+
|
|
2045
|
+
.wt-sp-root {
|
|
2046
|
+
--sp-bg: #ffffff;
|
|
2047
|
+
--sp-bg-panel: #f9f9fb;
|
|
2048
|
+
--sp-bg-item: #f4f4f5;
|
|
2049
|
+
--sp-bg-hover: #e8e8ec;
|
|
2050
|
+
--sp-border: #e4e4e7;
|
|
2051
|
+
--sp-border-hi: #d4d4d8;
|
|
2052
|
+
--sp-text: #18181b;
|
|
2053
|
+
--sp-text-secondary: #52525b;
|
|
2054
|
+
--sp-text-muted: #a1a1aa;
|
|
2055
|
+
--sp-blue: #3b82f6;
|
|
2056
|
+
--sp-blue-light: #60a5fa;
|
|
2057
|
+
--sp-green: #16a34a;
|
|
2058
|
+
--sp-green-bg: #f0fdf4;
|
|
2059
|
+
--sp-red: #ef4444;
|
|
2060
|
+
--sp-red-bg: #fef2f2;
|
|
2061
|
+
--sp-yellow: #f59e0b;
|
|
2062
|
+
--sp-shadow: 0 1px 3px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.06);
|
|
2063
|
+
--sp-font: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', system-ui, sans-serif;
|
|
2064
|
+
--sp-font-mono: 'SF Mono', 'Cascadia Code', 'Consolas', monospace;
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
.wt-sp-root[data-theme='dark'] {
|
|
2068
|
+
--sp-bg: #09090b;
|
|
2069
|
+
--sp-bg-panel: #111113;
|
|
2070
|
+
--sp-bg-item: #1c1c1f;
|
|
2071
|
+
--sp-bg-hover: #27272a;
|
|
2072
|
+
--sp-border: #27272a;
|
|
2073
|
+
--sp-border-hi: #3f3f46;
|
|
2074
|
+
--sp-text: #fafafa;
|
|
2075
|
+
--sp-text-secondary: #a1a1aa;
|
|
2076
|
+
--sp-text-muted: #52525b;
|
|
2077
|
+
--sp-green-bg: #052e16;
|
|
2078
|
+
--sp-red-bg: #450a0a;
|
|
2079
|
+
--sp-shadow: 0 1px 3px rgba(0,0,0,0.4), 0 4px 12px rgba(0,0,0,0.3);
|
|
2080
|
+
}
|
|
2081
|
+
|
|
2082
|
+
/* ── Panel Shell ─────────────────────────────────────────── */
|
|
2083
|
+
.wt-sp-root {
|
|
2084
|
+
position: fixed;
|
|
2085
|
+
top: 0; bottom: 0; right: 0;
|
|
2086
|
+
width: 360px;
|
|
2087
|
+
display: none !important;
|
|
2088
|
+
flex-direction: column;
|
|
2089
|
+
background: var(--sp-bg);
|
|
2090
|
+
border-left: 1px solid var(--sp-border);
|
|
2091
|
+
z-index: 2147483646;
|
|
2092
|
+
pointer-events: auto;
|
|
2093
|
+
font-family: var(--sp-font);
|
|
2094
|
+
color: var(--sp-text);
|
|
2095
|
+
|
|
2096
|
+
transition: transform 0.3s cubic-bezier(0.32, 0.72, 0, 1);
|
|
2097
|
+
overflow: hidden;
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
.wt-sp-root.minimized {
|
|
2101
|
+
transform: translateX(100%);
|
|
2102
|
+
pointer-events: none;
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
/* ── Header ──────────────────────────────────────────────── */
|
|
2106
|
+
.wt-sp-header {
|
|
2107
|
+
display: flex;
|
|
2108
|
+
justify-content: space-between;
|
|
2109
|
+
align-items: center;
|
|
2110
|
+
padding: 10px 14px;
|
|
2111
|
+
border-bottom: 1px solid var(--sp-border);
|
|
2112
|
+
flex-shrink: 0;
|
|
2113
|
+
-webkit-app-region: drag;
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2116
|
+
.wt-sp-brand {
|
|
2117
|
+
font-size: 13px;
|
|
2118
|
+
font-weight: 600;
|
|
2119
|
+
color: var(--sp-text);
|
|
2120
|
+
letter-spacing: -0.01em;
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
.wt-sp-controls {
|
|
2124
|
+
display: flex;
|
|
2125
|
+
align-items: center;
|
|
2126
|
+
gap: 2px;
|
|
2127
|
+
-webkit-app-region: no-drag;
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
.wt-sp-ctrl-btn {
|
|
2131
|
+
width: 28px; height: 28px;
|
|
2132
|
+
display: flex; align-items: center; justify-content: center;
|
|
2133
|
+
background: transparent;
|
|
2134
|
+
border: none;
|
|
2135
|
+
border-radius: 6px;
|
|
2136
|
+
color: var(--sp-text-muted);
|
|
2137
|
+
cursor: pointer;
|
|
2138
|
+
transition: background 0.15s, color 0.15s;
|
|
2139
|
+
font-size: 14px;
|
|
2140
|
+
}
|
|
2141
|
+
.wt-sp-ctrl-btn:hover { background: var(--sp-bg-hover); color: var(--sp-text); }
|
|
2142
|
+
|
|
2143
|
+
/* ── Badge Row ───────────────────────────────────────────── */
|
|
2144
|
+
.wt-sp-badges {
|
|
2145
|
+
display: flex;
|
|
2146
|
+
align-items: center;
|
|
2147
|
+
gap: 6px;
|
|
2148
|
+
padding: 6px 14px;
|
|
2149
|
+
border-bottom: 1px solid var(--sp-border);
|
|
2150
|
+
flex-shrink: 0;
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2153
|
+
.wt-sp-badge {
|
|
2154
|
+
font-size: 9px;
|
|
2155
|
+
font-weight: 600;
|
|
2156
|
+
padding: 2px 6px;
|
|
2157
|
+
border-radius: 4px;
|
|
2158
|
+
text-transform: uppercase;
|
|
2159
|
+
letter-spacing: 0.03em;
|
|
2160
|
+
}
|
|
2161
|
+
.wt-sp-badge-dev { background: var(--sp-blue); color: #fff; }
|
|
2162
|
+
.wt-sp-badge-beta { background: var(--sp-yellow); color: #fff; }
|
|
2163
|
+
|
|
2164
|
+
/* ── Body (scrollable) ───────────────────────────────────── */
|
|
2165
|
+
.wt-sp-body {
|
|
2166
|
+
flex: 1;
|
|
2167
|
+
overflow-y: auto;
|
|
2168
|
+
display: flex;
|
|
2169
|
+
flex-direction: column;
|
|
2170
|
+
}
|
|
2171
|
+
|
|
2172
|
+
.wt-sp-body::-webkit-scrollbar { width: 5px; }
|
|
2173
|
+
.wt-sp-body::-webkit-scrollbar-track { background: transparent; }
|
|
2174
|
+
.wt-sp-body::-webkit-scrollbar-thumb { background: var(--sp-border-hi); border-radius: 3px; }
|
|
2175
|
+
|
|
2176
|
+
/* ── Section ─────────────────────────────────────────────── */
|
|
2177
|
+
.wt-sp-section {
|
|
2178
|
+
border-bottom: 1px solid var(--sp-border);
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
.wt-sp-section-hdr {
|
|
2182
|
+
display: flex;
|
|
2183
|
+
align-items: center;
|
|
2184
|
+
gap: 6px;
|
|
2185
|
+
padding: 8px 14px;
|
|
2186
|
+
font-size: 11px;
|
|
2187
|
+
font-weight: 600;
|
|
2188
|
+
color: var(--sp-text-secondary);
|
|
2189
|
+
text-transform: uppercase;
|
|
2190
|
+
letter-spacing: 0.04em;
|
|
2191
|
+
cursor: pointer;
|
|
2192
|
+
user-select: none;
|
|
2193
|
+
transition: color 0.15s;
|
|
2194
|
+
}
|
|
2195
|
+
.wt-sp-section-hdr:hover { color: var(--sp-text); }
|
|
2196
|
+
|
|
2197
|
+
.wt-sp-chevron {
|
|
2198
|
+
font-size: 10px;
|
|
2199
|
+
transition: transform 0.2s ease;
|
|
2200
|
+
display: inline-block;
|
|
2201
|
+
color: var(--sp-text-muted);
|
|
2202
|
+
}
|
|
2203
|
+
.wt-sp-section.closed .wt-sp-chevron { transform: rotate(-90deg); }
|
|
2204
|
+
|
|
2205
|
+
.wt-sp-section-body {
|
|
2206
|
+
overflow: hidden;
|
|
2207
|
+
transition: max-height 0.25s cubic-bezier(0.32, 0.72, 0, 1);
|
|
2208
|
+
max-height: 500px;
|
|
2209
|
+
}
|
|
2210
|
+
.wt-sp-section.closed .wt-sp-section-body { max-height: 0; }
|
|
2211
|
+
|
|
2212
|
+
.wt-sp-section-grow { flex: 1; display: flex; flex-direction: column; }
|
|
2213
|
+
.wt-sp-section-grow .wt-sp-section-body { flex: 1; overflow-y: auto; max-height: none; }
|
|
2214
|
+
|
|
2215
|
+
/* ── Tasks ────────────────────────────────────────────────── */
|
|
2216
|
+
.wt-sp-tasks {
|
|
2217
|
+
display: flex;
|
|
2218
|
+
flex-direction: column;
|
|
2219
|
+
padding: 0 14px 10px;
|
|
2220
|
+
gap: 2px;
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
.wt-sp-task {
|
|
2224
|
+
display: flex;
|
|
2225
|
+
align-items: flex-start;
|
|
2226
|
+
gap: 8px;
|
|
2227
|
+
padding: 6px 8px;
|
|
2228
|
+
border-radius: 6px;
|
|
2229
|
+
font-size: 12px;
|
|
2230
|
+
line-height: 1.4;
|
|
2231
|
+
cursor: pointer;
|
|
2232
|
+
transition: background 0.15s;
|
|
2233
|
+
}
|
|
2234
|
+
.wt-sp-task:hover { background: var(--sp-bg-hover); }
|
|
2235
|
+
|
|
2236
|
+
.wt-sp-task.done { color: var(--sp-green); }
|
|
2237
|
+
.wt-sp-task.active { color: var(--sp-text); background: var(--sp-bg-item); border-left: 2px solid var(--sp-blue); padding-left: 6px; }
|
|
2238
|
+
.wt-sp-task.pending { color: var(--sp-text-muted); }
|
|
2239
|
+
|
|
2240
|
+
.wt-sp-task-icon { flex-shrink: 0; width: 16px; text-align: center; font-size: 11px; margin-top: 1px; }
|
|
2241
|
+
.wt-sp-task.done .wt-sp-task-icon { color: var(--sp-green); }
|
|
2242
|
+
.wt-sp-task.active .wt-sp-task-icon { color: var(--sp-blue); }
|
|
2243
|
+
|
|
2244
|
+
.wt-sp-task-desc { flex: 1; word-break: break-word; }
|
|
2245
|
+
.wt-sp-task.done .wt-sp-task-desc { text-decoration: line-through; opacity: 0.7; }
|
|
2246
|
+
|
|
2247
|
+
.wt-sp-task-empty {
|
|
2248
|
+
font-size: 11px;
|
|
2249
|
+
color: var(--sp-text-muted);
|
|
2250
|
+
padding: 8px;
|
|
2251
|
+
text-align: center;
|
|
2252
|
+
}
|
|
2253
|
+
|
|
2254
|
+
/* ── Activity Log ────────────────────────────────────────── */
|
|
2255
|
+
.wt-sp-log {
|
|
2256
|
+
display: flex;
|
|
2257
|
+
flex-direction: column;
|
|
2258
|
+
gap: 1px;
|
|
2259
|
+
padding: 0 14px 10px;
|
|
2260
|
+
font-family: var(--sp-font-mono);
|
|
2261
|
+
}
|
|
2262
|
+
|
|
2263
|
+
.wt-sp-log-entry {
|
|
2264
|
+
display: flex;
|
|
2265
|
+
align-items: flex-start;
|
|
2266
|
+
gap: 8px;
|
|
2267
|
+
padding: 5px 8px;
|
|
2268
|
+
border-radius: 4px;
|
|
2269
|
+
font-size: 11px;
|
|
2270
|
+
line-height: 1.4;
|
|
2271
|
+
transition: background 0.1s;
|
|
2272
|
+
}
|
|
2273
|
+
.wt-sp-log-entry:hover { background: var(--sp-bg-hover); }
|
|
2274
|
+
|
|
2275
|
+
.wt-sp-log-icon { flex-shrink: 0; width: 14px; text-align: center; margin-top: 1px; font-size: 10px; }
|
|
2276
|
+
.wt-sp-log-icon.running { color: var(--sp-blue); }
|
|
2277
|
+
.wt-sp-log-icon.done { color: var(--sp-green); }
|
|
2278
|
+
.wt-sp-log-icon.error { color: var(--sp-red); }
|
|
2279
|
+
|
|
2280
|
+
.wt-sp-log-name {
|
|
2281
|
+
font-weight: 500;
|
|
2282
|
+
color: var(--sp-text);
|
|
2283
|
+
white-space: nowrap;
|
|
2284
|
+
overflow: hidden;
|
|
2285
|
+
text-overflow: ellipsis;
|
|
2286
|
+
max-width: 140px;
|
|
2287
|
+
}
|
|
2288
|
+
|
|
2289
|
+
.wt-sp-log-badge {
|
|
2290
|
+
font-size: 9px;
|
|
2291
|
+
font-weight: 600;
|
|
2292
|
+
padding: 1px 5px;
|
|
2293
|
+
border-radius: 3px;
|
|
2294
|
+
text-transform: uppercase;
|
|
2295
|
+
letter-spacing: 0.02em;
|
|
2296
|
+
flex-shrink: 0;
|
|
2297
|
+
}
|
|
2298
|
+
.wt-sp-log-badge.running { background: rgba(59,130,246,0.12); color: var(--sp-blue); }
|
|
2299
|
+
.wt-sp-log-badge.done { background: rgba(22,163,74,0.12); color: var(--sp-green); }
|
|
2300
|
+
.wt-sp-log-badge.error { background: rgba(239,68,68,0.12); color: var(--sp-red); }
|
|
2301
|
+
.wt-sp-log-badge.info { background: var(--sp-bg-item); color: var(--sp-text-muted); }
|
|
2302
|
+
|
|
2303
|
+
.wt-sp-log-time {
|
|
2304
|
+
font-size: 10px;
|
|
2305
|
+
color: var(--sp-text-muted);
|
|
2306
|
+
margin-left: auto;
|
|
2307
|
+
flex-shrink: 0;
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
.wt-sp-log-empty {
|
|
2311
|
+
font-size: 11px;
|
|
2312
|
+
color: var(--sp-text-muted);
|
|
2313
|
+
padding: 8px;
|
|
2314
|
+
text-align: center;
|
|
2315
|
+
font-family: var(--sp-font);
|
|
2316
|
+
}
|
|
2317
|
+
|
|
2318
|
+
/* ── Chat ─────────────────────────────────────────────────── */
|
|
2319
|
+
.wt-sp-chat {
|
|
2320
|
+
padding: 10px 14px;
|
|
2321
|
+
border-top: 1px solid var(--sp-border);
|
|
2322
|
+
flex-shrink: 0;
|
|
2323
|
+
}
|
|
2324
|
+
|
|
2325
|
+
.wt-sp-chat-row {
|
|
2326
|
+
display: flex;
|
|
2327
|
+
gap: 6px;
|
|
2328
|
+
align-items: flex-end;
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2331
|
+
.wt-sp-chat-input {
|
|
2332
|
+
flex: 1;
|
|
2333
|
+
padding: 8px 12px;
|
|
2334
|
+
background: var(--sp-bg-item);
|
|
2335
|
+
border: 1px solid var(--sp-border);
|
|
2336
|
+
border-radius: 8px;
|
|
2337
|
+
color: var(--sp-text);
|
|
2338
|
+
font-family: var(--sp-font);
|
|
2339
|
+
font-size: 12px;
|
|
2340
|
+
outline: none;
|
|
2341
|
+
resize: none;
|
|
2342
|
+
min-height: 36px;
|
|
2343
|
+
max-height: 100px;
|
|
2344
|
+
transition: border-color 0.15s;
|
|
2345
|
+
line-height: 1.4;
|
|
2346
|
+
}
|
|
2347
|
+
.wt-sp-chat-input::placeholder { color: var(--sp-text-muted); }
|
|
2348
|
+
.wt-sp-chat-input:focus { border-color: var(--sp-blue); }
|
|
2349
|
+
|
|
2350
|
+
.wt-sp-send-btn {
|
|
2351
|
+
width: 36px; height: 36px;
|
|
2352
|
+
display: flex; align-items: center; justify-content: center;
|
|
2353
|
+
background: var(--sp-blue);
|
|
2354
|
+
border: none;
|
|
2355
|
+
border-radius: 8px;
|
|
2356
|
+
cursor: pointer;
|
|
2357
|
+
flex-shrink: 0;
|
|
2358
|
+
transition: background 0.15s, transform 0.1s;
|
|
2359
|
+
}
|
|
2360
|
+
.wt-sp-send-btn:hover { background: #2563eb; }
|
|
2361
|
+
.wt-sp-send-btn:active { transform: scale(0.95); }
|
|
2362
|
+
.wt-sp-send-btn.sent { background: var(--sp-green); }
|
|
2363
|
+
.wt-sp-send-btn svg { width: 16px; height: 16px; stroke: #fff; fill: none; }
|
|
2364
|
+
|
|
2365
|
+
/* ── Debug Section ───────────────────────────────────────── */
|
|
2366
|
+
.wt-sp-debug-area {
|
|
2367
|
+
padding: 0 14px 10px;
|
|
2368
|
+
position: relative;
|
|
2369
|
+
min-height: 200px;
|
|
2370
|
+
}
|
|
2371
|
+
|
|
2372
|
+
.wt-sp-f3-btn {
|
|
2373
|
+
margin-left: auto;
|
|
2374
|
+
font-size: 9px;
|
|
2375
|
+
font-weight: 600;
|
|
2376
|
+
padding: 2px 6px;
|
|
2377
|
+
border-radius: 4px;
|
|
2378
|
+
background: var(--sp-bg-item);
|
|
2379
|
+
border: 1px solid var(--sp-border);
|
|
2380
|
+
color: var(--sp-text-muted);
|
|
2381
|
+
cursor: pointer;
|
|
2382
|
+
text-transform: uppercase;
|
|
2383
|
+
transition: all 0.15s;
|
|
2384
|
+
}
|
|
2385
|
+
.wt-sp-f3-btn:hover { color: var(--sp-text); border-color: var(--sp-border-hi); }
|
|
2386
|
+
|
|
2387
|
+
/* ── System Icon (minimized state) ───────────────────────── */
|
|
2388
|
+
#wt-system-icon {
|
|
2389
|
+
position: fixed;
|
|
2390
|
+
bottom: 20px;
|
|
2391
|
+
right: 20px;
|
|
2392
|
+
width: 40px;
|
|
2393
|
+
height: 40px;
|
|
2394
|
+
background: var(--sp-bg);
|
|
2395
|
+
border: 1px solid var(--sp-border);
|
|
2396
|
+
border-radius: 10px;
|
|
2397
|
+
display: none;
|
|
2398
|
+
justify-content: center;
|
|
2399
|
+
align-items: center;
|
|
2400
|
+
cursor: pointer;
|
|
2401
|
+
z-index: 2147483640;
|
|
2402
|
+
box-shadow: var(--sp-shadow);
|
|
2403
|
+
transition: background 0.15s, transform 0.15s;
|
|
2404
|
+
pointer-events: auto;
|
|
2405
|
+
}
|
|
2406
|
+
#wt-system-icon:hover { background: var(--sp-bg-hover); transform: scale(1.05); }
|
|
2407
|
+
#wt-system-icon svg { width: 18px; height: 18px; fill: var(--sp-text-muted); }
|
|
2408
|
+
|
|
2409
|
+
/* ── Utilities ────────────────────────────────────────────── */
|
|
2410
|
+
.wt-sp-root .hidden, .wt-sp-root.hidden, .wt-hidden { display: none !important; }
|
|
2411
|
+
|
|
2412
|
+
@keyframes wt-sp-spin {
|
|
2413
|
+
from { transform: rotate(0deg); }
|
|
2414
|
+
to { transform: rotate(360deg); }
|
|
2415
|
+
}
|
|
2416
|
+
.wt-sp-spinner {
|
|
2417
|
+
display: inline-block;
|
|
2418
|
+
width: 10px; height: 10px;
|
|
2419
|
+
border: 1.5px solid var(--sp-border-hi);
|
|
2420
|
+
border-top-color: var(--sp-blue);
|
|
2421
|
+
border-radius: 50%;
|
|
2422
|
+
animation: wt-sp-spin 0.8s linear infinite;
|
|
2423
|
+
}
|
|
2424
|
+
`;
|
|
2425
|
+
(document.head || document.documentElement).appendChild(style);
|
|
2426
|
+
|
|
2427
|
+
// ── HTML Construction ─────────────────────────────────────────
|
|
2428
|
+
const container = document.createElement('div');
|
|
2429
|
+
container.id = 'wt-main-container';
|
|
2430
|
+
container.className = 'wt-sp-root';
|
|
2431
|
+
container.setAttribute('data-wt-ignore', 'true');
|
|
2432
|
+
container.setAttribute('data-theme', 'dark');
|
|
2433
|
+
|
|
2434
|
+
container.innerHTML = `
|
|
2435
|
+
<div class="wt-sp-header">
|
|
2436
|
+
<span class="wt-sp-brand">Weavetab</span>
|
|
2437
|
+
<div class="wt-sp-controls">
|
|
2438
|
+
<button class="wt-sp-ctrl-btn" id="wt-theme-toggle" title="Toggle theme">
|
|
2439
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="5"/><path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/></svg>
|
|
2440
|
+
</button>
|
|
2441
|
+
<button class="wt-sp-ctrl-btn" id="wt-minimize-btn" title="Minimize">
|
|
2442
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 12h14"/></svg>
|
|
2443
|
+
</button>
|
|
2444
|
+
</div>
|
|
2445
|
+
</div>
|
|
2446
|
+
<div class="wt-sp-badges">
|
|
2447
|
+
<span class="wt-sp-badge wt-sp-badge-dev hidden" id="wt-badge-dev">DEV</span>
|
|
2448
|
+
${isBeta ? '<span class="wt-sp-badge wt-sp-badge-beta">BETA</span>' : ''}
|
|
2449
|
+
</div>
|
|
2450
|
+
<div class="wt-sp-body">
|
|
2451
|
+
<div class="wt-sp-section" id="wt-sec-tasks">
|
|
2452
|
+
<div class="wt-sp-section-hdr" id="wt-header-tasks">
|
|
2453
|
+
<span class="wt-sp-chevron">›</span>
|
|
2454
|
+
<span>Tasks</span>
|
|
2455
|
+
</div>
|
|
2456
|
+
<div class="wt-sp-section-body">
|
|
2457
|
+
<div class="wt-sp-tasks" id="wt-todo-list">
|
|
2458
|
+
<div class="wt-sp-task-empty">No active mission</div>
|
|
2459
|
+
</div>
|
|
2460
|
+
</div>
|
|
2461
|
+
</div>
|
|
2462
|
+
|
|
2463
|
+
<div class="wt-sp-section wt-sp-section-grow" id="wt-sec-logs">
|
|
2464
|
+
<div class="wt-sp-section-hdr" id="wt-header-logs">
|
|
2465
|
+
<span class="wt-sp-chevron">›</span>
|
|
2466
|
+
<span>Activity</span>
|
|
2467
|
+
</div>
|
|
2468
|
+
<div class="wt-sp-section-body">
|
|
2469
|
+
<div class="wt-sp-log" id="wt-log-container">
|
|
2470
|
+
<div class="wt-sp-log-empty">Waiting for activity...</div>
|
|
2471
|
+
</div>
|
|
2472
|
+
</div>
|
|
2473
|
+
</div>
|
|
2474
|
+
|
|
2475
|
+
<div class="wt-sp-section closed" id="wt-sec-debug">
|
|
2476
|
+
<div class="wt-sp-section-hdr" id="wt-header-debug">
|
|
2477
|
+
<span class="wt-sp-chevron">›</span>
|
|
2478
|
+
<span>Debug</span>
|
|
2479
|
+
</div>
|
|
2480
|
+
<div class="wt-sp-section-body">
|
|
2481
|
+
<div class="wt-sp-debug-area" id="wt-debug-content-area"></div>
|
|
2482
|
+
</div>
|
|
2483
|
+
</div>
|
|
2484
|
+
</div>
|
|
2485
|
+
|
|
2486
|
+
<div class="wt-sp-chat">
|
|
2487
|
+
<div class="wt-sp-chat-row">
|
|
2488
|
+
<textarea class="wt-sp-chat-input" id="wt-msg-input" placeholder="Send feedback..." rows="1"></textarea>
|
|
2489
|
+
<button class="wt-sp-send-btn" id="wt-send-btn">
|
|
2490
|
+
<svg viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
|
|
2491
|
+
</button>
|
|
2492
|
+
</div>
|
|
2493
|
+
</div>
|
|
2494
|
+
`;
|
|
2495
|
+
__wt_safeAppendToBody(container);
|
|
2496
|
+
|
|
2497
|
+
const systemIcon = document.createElement('div');
|
|
2498
|
+
systemIcon.id = 'wt-system-icon';
|
|
2499
|
+
systemIcon.title = 'Open Weavetab';
|
|
2500
|
+
systemIcon.innerHTML = `
|
|
2501
|
+
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
2502
|
+
<path d="M4 4h16v16H4V4zm2 2v12h12V6H6zm2 3h3v2H8V9zm4 4h4v2h-4v-2z" />
|
|
2503
|
+
</svg>
|
|
2504
|
+
`;
|
|
2505
|
+
__wt_safeAppendToBody(systemIcon);
|
|
2506
|
+
|
|
2507
|
+
// ── PERSISTENT ACTIVITY LOG ──────────────────────────────────
|
|
2508
|
+
// Logs never clear until browser close — append-only like Claude Chrome
|
|
2509
|
+
window.__wt_logEntries = [];
|
|
2510
|
+
const logContainer = document.getElementById('wt-log-container');
|
|
2511
|
+
|
|
2512
|
+
function __wt_addLogEntry(name, status, detail) {
|
|
2513
|
+
const entry = {
|
|
2514
|
+
name: name || 'unknown',
|
|
2515
|
+
status: status || 'info',
|
|
2516
|
+
detail: detail || '',
|
|
2517
|
+
time: Date.now()
|
|
2518
|
+
};
|
|
2519
|
+
window.__wt_logEntries.push(entry);
|
|
2520
|
+
__wt_renderLogEntry(entry);
|
|
2521
|
+
// Auto-scroll to bottom
|
|
2522
|
+
if (logContainer) {
|
|
2523
|
+
logContainer.scrollTop = logContainer.scrollHeight;
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
|
|
2527
|
+
function __wt_renderLogEntry(entry) {
|
|
2528
|
+
if (!logContainer) return;
|
|
2529
|
+
// Remove empty placeholder on first real entry
|
|
2530
|
+
const empty = logContainer.querySelector('.wt-sp-log-empty');
|
|
2531
|
+
if (empty) empty.remove();
|
|
2532
|
+
|
|
2533
|
+
const el = document.createElement('div');
|
|
2534
|
+
el.className = 'wt-sp-log-entry';
|
|
2535
|
+
|
|
2536
|
+
const iconClass = entry.status === 'done' ? 'done' : entry.status === 'error' ? 'error' : entry.status === 'running' ? 'running' : '';
|
|
2537
|
+
const icon = entry.status === 'running' ? '<span class="wt-sp-spinner"></span>'
|
|
2538
|
+
: entry.status === 'done' ? '✓'
|
|
2539
|
+
: entry.status === 'error' ? '✗'
|
|
2540
|
+
: '●';
|
|
2541
|
+
|
|
2542
|
+
const badge = entry.status === 'running' ? 'RUNNING'
|
|
2543
|
+
: entry.status === 'done' ? 'OK'
|
|
2544
|
+
: entry.status === 'error' ? 'ERR'
|
|
2545
|
+
: '';
|
|
2546
|
+
|
|
2547
|
+
const timeStr = new Date(entry.time).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
|
2548
|
+
|
|
2549
|
+
el.innerHTML = `
|
|
2550
|
+
<span class="wt-sp-log-icon ${iconClass}">${icon}</span>
|
|
2551
|
+
<span class="wt-sp-log-name">${__wt_escapeHtml(entry.name)}</span>
|
|
2552
|
+
${badge ? `<span class="wt-sp-log-badge ${entry.status}">${badge}</span>` : ''}
|
|
2553
|
+
<span class="wt-sp-log-time">${timeStr}</span>
|
|
2554
|
+
`;
|
|
2555
|
+
|
|
2556
|
+
if (entry.detail) {
|
|
2557
|
+
const detailEl = document.createElement('div');
|
|
2558
|
+
detailEl.style.cssText = 'width:100%;font-size:10px;color:var(--sp-text-muted);white-space:pre-wrap;word-break:break-all;margin-top:2px;';
|
|
2559
|
+
detailEl.textContent = entry.detail;
|
|
2560
|
+
el.appendChild(detailEl);
|
|
2561
|
+
}
|
|
2562
|
+
|
|
2563
|
+
logContainer.appendChild(el);
|
|
2564
|
+
}
|
|
2565
|
+
|
|
2566
|
+
function __wt_escapeHtml(str) {
|
|
2567
|
+
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
2568
|
+
}
|
|
2569
|
+
|
|
2570
|
+
// ── THEME SYSTEM ──────────────────────────────────────────────
|
|
2571
|
+
function __wt_applyTheme(theme) {
|
|
2572
|
+
container.setAttribute('data-theme', theme);
|
|
2573
|
+
document.getElementById('wt-system-icon') && document.getElementById('wt-system-icon').setAttribute('data-theme', theme);
|
|
2574
|
+
try { browserAPI.storage.local.set({ wt_theme: theme }); } catch(e) {}
|
|
2575
|
+
}
|
|
2576
|
+
|
|
2577
|
+
function __wt_toggleTheme() {
|
|
2578
|
+
const current = container.getAttribute('data-theme');
|
|
2579
|
+
__wt_applyTheme(current === 'dark' ? 'light' : 'dark');
|
|
2580
|
+
}
|
|
2581
|
+
|
|
2582
|
+
// Restore persisted theme
|
|
2583
|
+
try {
|
|
2584
|
+
browserAPI.storage.local.get(['wt_theme'], function(res) {
|
|
2585
|
+
if (res.wt_theme) {
|
|
2586
|
+
__wt_applyTheme(res.wt_theme);
|
|
2587
|
+
}
|
|
2588
|
+
});
|
|
2589
|
+
} catch(e) {}
|
|
2590
|
+
|
|
2591
|
+
|
|
2592
|
+
|
|
2593
|
+
// ── HUD STATUS INTERCEPTION (for activity log) ────────────────
|
|
2594
|
+
// Capture Weavetab_STATE hud updates and add to log
|
|
2595
|
+
window.addEventListener('message', function(e) {
|
|
2596
|
+
if (e.data && e.data.type === 'Weavetab-msg-in' && e.data.data && e.data.data.type === 'Weavetab_STATE' && e.data.data.hud) {
|
|
2597
|
+
const hud = e.data.data.hud;
|
|
2598
|
+
if (hud.action && hud.action.length > 2) {
|
|
2599
|
+
__wt_addLogEntry(hud.action, 'info', hud.detail || '');
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
2602
|
+
});
|
|
2603
|
+
|
|
2604
|
+
|
|
2605
|
+
|
|
2606
|
+
// ── Panel State Persistence ────────────────────────────────────
|
|
2607
|
+
// Sidepanel always opens on browser launch (background clears state on startup).
|
|
2608
|
+
// Persists minimized state across page navigations within a session.
|
|
2609
|
+
function minimizePanel() {
|
|
2610
|
+
container.classList.add('minimized');
|
|
2611
|
+
systemIcon.style.display = 'flex';
|
|
2612
|
+
try { browserAPI.storage.local.set({ wt_panel_minimized: true }); } catch(e) {}
|
|
2613
|
+
}
|
|
2614
|
+
|
|
2615
|
+
function restorePanel() {
|
|
2616
|
+
systemIcon.style.display = 'none';
|
|
2617
|
+
container.classList.remove('minimized');
|
|
2618
|
+
try { browserAPI.storage.local.set({ wt_panel_minimized: false }); } catch(e) {}
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2621
|
+
// Restore minimized state from storage (respects user's last action)
|
|
2622
|
+
try {
|
|
2623
|
+
browserAPI.storage.local.get(['wt_panel_minimized'], function(res) {
|
|
2624
|
+
if (res.wt_panel_minimized) {
|
|
2625
|
+
container.classList.add('minimized');
|
|
2626
|
+
systemIcon.style.display = 'flex';
|
|
2627
|
+
}
|
|
2628
|
+
});
|
|
2629
|
+
} catch(e) {}
|
|
2630
|
+
|
|
2631
|
+
function toggleSection(id) {
|
|
2632
|
+
document.getElementById(id).classList.toggle('closed');
|
|
2633
|
+
}
|
|
2634
|
+
|
|
2635
|
+
|
|
2636
|
+
|
|
2637
|
+
function syncDevMode() {
|
|
2638
|
+
const active = !!window.__wt_devMode;
|
|
2639
|
+
const devBadge = document.getElementById('wt-badge-dev');
|
|
2640
|
+
|
|
2641
|
+
if (devBadge) devBadge.classList.toggle('hidden', !active);
|
|
2642
|
+
}
|
|
2643
|
+
|
|
2644
|
+
window.addEventListener('wt-config-sync', syncDevMode);
|
|
2645
|
+
|
|
2646
|
+
container.querySelector('#wt-minimize-btn').onclick = minimizePanel;
|
|
2647
|
+
systemIcon.onclick = restorePanel;
|
|
2648
|
+
container.querySelector('#wt-theme-toggle').onclick = __wt_toggleTheme;
|
|
2649
|
+
container.querySelector('#wt-header-tasks').onclick = () => toggleSection('wt-sec-tasks');
|
|
2650
|
+
container.querySelector('#wt-header-logs').onclick = () => toggleSection('wt-sec-logs');
|
|
2651
|
+
container.querySelector('#wt-header-debug').onclick = () => toggleSection('wt-sec-debug');
|
|
2652
|
+
|
|
2653
|
+
|
|
2654
|
+
syncDevMode();
|
|
2655
|
+
|
|
2656
|
+
const msgInput = container.querySelector('#wt-msg-input');
|
|
2657
|
+
const sendBtn = container.querySelector('#wt-send-btn');
|
|
2658
|
+
|
|
2659
|
+
// Auto-resize textarea
|
|
2660
|
+
msgInput.addEventListener('input', function() {
|
|
2661
|
+
this.style.height = 'auto';
|
|
2662
|
+
this.style.height = Math.min(this.scrollHeight, 100) + 'px';
|
|
2663
|
+
});
|
|
2664
|
+
|
|
2665
|
+
// ── Plan Rendering Integration ───────────────────────────────
|
|
2666
|
+
function renderPlan(action, goal, tasks) {
|
|
2667
|
+
const list = document.getElementById('wt-todo-list');
|
|
2668
|
+
if (!list) return;
|
|
2669
|
+
|
|
2670
|
+
if (action === 'hide' || (!goal && (!tasks || tasks.length === 0))) {
|
|
2671
|
+
list.innerHTML = '<div class="wt-sp-task-empty">No active mission</div>';
|
|
2672
|
+
return;
|
|
2673
|
+
}
|
|
2674
|
+
|
|
2675
|
+
list.innerHTML = '';
|
|
2676
|
+
if (goal) {
|
|
2677
|
+
const goalEl = document.createElement('div');
|
|
2678
|
+
goalEl.className = 'wt-sp-task active';
|
|
2679
|
+
goalEl.style.cssText = 'margin-bottom:4px;font-weight:500;';
|
|
2680
|
+
goalEl.innerHTML = '<span class="wt-sp-task-icon">▶</span><span class="wt-sp-task-desc">' + __wt_escapeHtml(goal) + '</span>';
|
|
2681
|
+
list.appendChild(goalEl);
|
|
2682
|
+
}
|
|
2683
|
+
if (tasks && tasks.length > 0) {
|
|
2684
|
+
tasks.forEach(task => {
|
|
2685
|
+
const item = document.createElement('div');
|
|
2686
|
+
const statusClass = task.status === 'done' ? 'done' : (task.status === 'in-progress' ? 'active' : 'pending');
|
|
2687
|
+
const icon = task.status === 'done' ? '✓' : (task.status === 'in-progress' ? '<span class="wt-sp-spinner"></span>' : '○');
|
|
2688
|
+
|
|
2689
|
+
item.className = 'wt-sp-task ' + statusClass;
|
|
2690
|
+
item.setAttribute('data-task-id', task.id);
|
|
2691
|
+
item.innerHTML = '<span class="wt-sp-task-icon">' + icon + '</span><span class="wt-sp-task-desc">' + __wt_escapeHtml(task.description) + '</span>';
|
|
2692
|
+
|
|
2693
|
+
list.appendChild(item);
|
|
2694
|
+
});
|
|
2695
|
+
}
|
|
2696
|
+
|
|
2697
|
+
// Log the plan update
|
|
2698
|
+
__wt_addLogEntry('Plan updated', 'info', (tasks ? tasks.length : 0) + ' tasks');
|
|
2699
|
+
}
|
|
2700
|
+
|
|
2701
|
+
window.__wt_plan = renderPlan;
|
|
2702
|
+
|
|
2703
|
+
window.addEventListener('message', (e) => {
|
|
2704
|
+
if (e.data && e.data.type === '__WT_PLAN') {
|
|
2705
|
+
renderPlan(e.data.action, e.data.goal, e.data.tasks);
|
|
2706
|
+
}
|
|
2707
|
+
syncDevMode();
|
|
2708
|
+
});
|
|
2709
|
+
|
|
2710
|
+
setInterval(syncDevMode, 2000);
|
|
2711
|
+
|
|
2712
|
+
try {
|
|
2713
|
+
browserAPI.storage.local.get(['wt_plan_collapsed'], function(res) {});
|
|
2714
|
+
} catch(e) {}
|
|
2715
|
+
|
|
2716
|
+
// Persist draft
|
|
2717
|
+
msgInput.addEventListener('input', function() {
|
|
2718
|
+
try { browserAPI.storage.local.set({ wt_chat_draft: msgInput.value }); } catch(e) {}
|
|
2719
|
+
});
|
|
2720
|
+
|
|
2721
|
+
// Submit
|
|
2722
|
+
function submitFeedback() {
|
|
2723
|
+
var text = msgInput.value.trim();
|
|
2724
|
+
if (!text) return;
|
|
2725
|
+
|
|
2726
|
+
window.postMessage({
|
|
2727
|
+
type: '__WT_FEEDBACK',
|
|
2728
|
+
text: text
|
|
2729
|
+
}, '*');
|
|
2730
|
+
|
|
2731
|
+
window.dispatchEvent(new CustomEvent('Weavetab-msg-in', {
|
|
2732
|
+
detail: {
|
|
2733
|
+
type: 'USER_FEEDBACK',
|
|
2734
|
+
text: text
|
|
2735
|
+
}
|
|
2736
|
+
}));
|
|
2737
|
+
|
|
2738
|
+
try {
|
|
2739
|
+
var existing = JSON.parse(localStorage.getItem('__wt_chat_queue_persist') || '[]');
|
|
2740
|
+
existing.push({ text: text, ts: Date.now() });
|
|
2741
|
+
if (existing.length > 20) existing.splice(0, existing.length - 20);
|
|
2742
|
+
localStorage.setItem('__wt_chat_queue_persist', JSON.stringify(existing));
|
|
2743
|
+
} catch(e) {}
|
|
2744
|
+
|
|
2745
|
+
__wt_addLogEntry('Feedback sent', 'done', text.length > 60 ? text.substring(0, 60) + '...' : text);
|
|
2746
|
+
|
|
2747
|
+
msgInput.value = '';
|
|
2748
|
+
msgInput.style.height = 'auto';
|
|
2749
|
+
try { browserAPI.storage.local.remove('wt_chat_draft'); } catch(e) {}
|
|
2750
|
+
sendBtn.classList.add('sent');
|
|
2751
|
+
setTimeout(function(){ sendBtn.classList.remove('sent'); }, 1200);
|
|
2752
|
+
}
|
|
2753
|
+
|
|
2754
|
+
sendBtn.onclick = submitFeedback;
|
|
2755
|
+
msgInput.addEventListener('keydown', function(e) {
|
|
2756
|
+
if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); submitFeedback(); }
|
|
2757
|
+
if (e.key === 'Escape') { minimizePanel(); }
|
|
2758
|
+
});
|
|
2759
|
+
|
|
2760
|
+
})();
|
|
2761
|
+
|
|
2762
|
+
// ── GEMINI-STYLE AREA SNIPE & DIRECT ANNOTATION ENGINE ─────────────────────
|
|
2763
|
+
(function initAreaSnipeEngine() {
|
|
2764
|
+
let isSnipeActive = false;
|
|
2765
|
+
let currentSnipeMode = 'dom_target';
|
|
2766
|
+
let overlayEl = null;
|
|
2767
|
+
let marqueeBox = null;
|
|
2768
|
+
let sizeBadge = null;
|
|
2769
|
+
let headerPill = null;
|
|
2770
|
+
let startX = 0, startY = 0;
|
|
2771
|
+
let isDragging = false;
|
|
2772
|
+
|
|
2773
|
+
function createOverlay(mode) {
|
|
2774
|
+
if (overlayEl) overlayEl.remove();
|
|
2775
|
+
currentSnipeMode = mode || 'dom_target';
|
|
2776
|
+
|
|
2777
|
+
overlayEl = document.createElement('div');
|
|
2778
|
+
overlayEl.id = 'wt-snipe-overlay';
|
|
2779
|
+
overlayEl.setAttribute('data-wt-ignore', 'true');
|
|
2780
|
+
overlayEl.style.cssText = `
|
|
2781
|
+
position: fixed; inset: 0; z-index: 2147483647;
|
|
2782
|
+
background: rgba(0, 0, 0, 0.35); backdrop-filter: blur(2px);
|
|
2783
|
+
cursor: crosshair; user-select: none;
|
|
2784
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
|
|
2785
|
+
`;
|
|
2786
|
+
|
|
2787
|
+
headerPill = document.createElement('div');
|
|
2788
|
+
headerPill.style.cssText = `
|
|
2789
|
+
position: fixed; top: 16px; left: 50%; transform: translateX(-50%);
|
|
2790
|
+
background: #18181b; border: 1px solid #3f3f46; color: #f4f4f5;
|
|
2791
|
+
font-size: 11px; font-weight: 500; padding: 6px 16px; border-radius: 20px;
|
|
2792
|
+
box-shadow: 0 4px 20px rgba(0,0,0,0.6); pointer-events: none;
|
|
2793
|
+
display: flex; align-items: center; gap: 8px; z-index: 2147483647;
|
|
2794
|
+
`;
|
|
2795
|
+
const modeLabel = currentSnipeMode === 'visual_crop' ? 'Vision Crop' : 'DOM Target';
|
|
2796
|
+
headerPill.innerHTML = `<span style="display:inline-flex;align-items:center;gap:6px;"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#38bdf8" stroke-width="2"><path d="M3 7V5a2 2 0 0 1 2-2h2"/><path d="M17 3h2a2 2 0 0 1 2 2v2"/><path d="M21 17v2a2 2 0 0 1-2 2h-2"/><path d="M7 21H5a2 2 0 0 1-2-2v-2"/><rect x="7" y="7" width="10" height="10" rx="1"/></svg>Drag box to select area</span> <span style="color:#06b6d4;font-weight:600;">[${modeLabel}]</span> <span style="color:#71717a;">(Esc to cancel)</span>`;
|
|
2797
|
+
|
|
2798
|
+
// Marquee selection box matching img1 Gemini/YouTube Chrome UI style
|
|
2799
|
+
marqueeBox = document.createElement('div');
|
|
2800
|
+
marqueeBox.style.cssText = `
|
|
2801
|
+
position: fixed; border: 1px solid rgba(255, 255, 255, 0.45);
|
|
2802
|
+
border-radius: 12px;
|
|
2803
|
+
box-shadow: 0 0 24px 3px rgba(147, 197, 253, 0.45), inset 0 0 20px rgba(147, 197, 253, 0.2);
|
|
2804
|
+
pointer-events: auto; display: none; z-index: 2147483647;
|
|
2805
|
+
background: rgba(0, 0, 0, 0.1);
|
|
2806
|
+
`;
|
|
2807
|
+
|
|
2808
|
+
// Four thick white rounded corner reticles (matching img1)
|
|
2809
|
+
const corners = [
|
|
2810
|
+
'top: -2px; left: -2px; border-top: 4px solid #ffffff; border-left: 4px solid #ffffff; border-top-left-radius: 10px;',
|
|
2811
|
+
'top: -2px; right: -2px; border-top: 4px solid #ffffff; border-right: 4px solid #ffffff; border-top-right-radius: 10px;',
|
|
2812
|
+
'bottom: -2px; left: -2px; border-bottom: 4px solid #ffffff; border-left: 4px solid #ffffff; border-bottom-left-radius: 10px;',
|
|
2813
|
+
'bottom: -2px; right: -2px; border-bottom: 4px solid #ffffff; border-right: 4px solid #ffffff; border-bottom-right-radius: 10px;'
|
|
2814
|
+
];
|
|
2815
|
+
corners.forEach(styleCss => {
|
|
2816
|
+
const c = document.createElement('div');
|
|
2817
|
+
c.style.cssText = `position: absolute; width: 18px; height: 18px; pointer-events: none; ${styleCss}`;
|
|
2818
|
+
marqueeBox.appendChild(c);
|
|
2819
|
+
});
|
|
2820
|
+
|
|
2821
|
+
// Circular close X button on top-right corner
|
|
2822
|
+
const closeBtn = document.createElement('button');
|
|
2823
|
+
closeBtn.style.cssText = `
|
|
2824
|
+
position: absolute; top: -11px; right: -11px;
|
|
2825
|
+
width: 22px; height: 22px; border-radius: 50%;
|
|
2826
|
+
background: rgba(24, 24, 27, 0.95); border: 1px solid rgba(255, 255, 255, 0.25);
|
|
2827
|
+
color: #f4f4f5; cursor: pointer; display: flex; align-items: center; justify-content: center;
|
|
2828
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.6); z-index: 2147483647; transition: transform 0.15s, background 0.15s;
|
|
2829
|
+
`;
|
|
2830
|
+
closeBtn.innerHTML = `<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>`;
|
|
2831
|
+
closeBtn.onclick = function(e) {
|
|
2832
|
+
e.stopPropagation();
|
|
2833
|
+
destroyOverlay();
|
|
2834
|
+
};
|
|
2835
|
+
marqueeBox.appendChild(closeBtn);
|
|
2836
|
+
|
|
2837
|
+
sizeBadge = document.createElement('div');
|
|
2838
|
+
sizeBadge.style.cssText = `
|
|
2839
|
+
position: absolute; bottom: -28px; left: 50%; transform: translateX(-50%);
|
|
2840
|
+
background: rgba(15, 23, 42, 0.9); backdrop-filter: blur(8px); color: #e2e8f0; font-weight: 600;
|
|
2841
|
+
font-size: 10px; font-family: -apple-system, BlinkMacSystemFont, sans-serif; padding: 2px 8px; border-radius: 12px;
|
|
2842
|
+
border: 1px solid rgba(255,255,255,0.15); box-shadow: 0 4px 12px rgba(0,0,0,0.5); pointer-events: none;
|
|
2843
|
+
white-space: nowrap;
|
|
2844
|
+
`;
|
|
2845
|
+
marqueeBox.appendChild(sizeBadge);
|
|
2846
|
+
|
|
2847
|
+
overlayEl.appendChild(headerPill);
|
|
2848
|
+
overlayEl.appendChild(marqueeBox);
|
|
2849
|
+
document.documentElement.appendChild(overlayEl);
|
|
2850
|
+
|
|
2851
|
+
isSnipeActive = true;
|
|
2852
|
+
}
|
|
2853
|
+
|
|
2854
|
+
function destroyOverlay() {
|
|
2855
|
+
if (overlayEl) {
|
|
2856
|
+
overlayEl.remove();
|
|
2857
|
+
overlayEl = null;
|
|
2858
|
+
}
|
|
2859
|
+
isSnipeActive = false;
|
|
2860
|
+
isDragging = false;
|
|
2861
|
+
}
|
|
2862
|
+
|
|
2863
|
+
function handleMouseDown(e) {
|
|
2864
|
+
if (!isSnipeActive || e.button !== 0) return;
|
|
2865
|
+
e.preventDefault();
|
|
2866
|
+
e.stopPropagation();
|
|
2867
|
+
|
|
2868
|
+
isDragging = true;
|
|
2869
|
+
startX = e.clientX;
|
|
2870
|
+
startY = e.clientY;
|
|
2871
|
+
|
|
2872
|
+
marqueeBox.style.left = startX + 'px';
|
|
2873
|
+
marqueeBox.style.top = startY + 'px';
|
|
2874
|
+
marqueeBox.style.width = '0px';
|
|
2875
|
+
marqueeBox.style.height = '0px';
|
|
2876
|
+
marqueeBox.style.display = 'block';
|
|
2877
|
+
}
|
|
2878
|
+
|
|
2879
|
+
function handleMouseMove(e) {
|
|
2880
|
+
if (!isSnipeActive || !isDragging) return;
|
|
2881
|
+
e.preventDefault();
|
|
2882
|
+
e.stopPropagation();
|
|
2883
|
+
|
|
2884
|
+
const currentX = e.clientX;
|
|
2885
|
+
const currentY = e.clientY;
|
|
2886
|
+
|
|
2887
|
+
const left = Math.min(startX, currentX);
|
|
2888
|
+
const top = Math.min(startY, currentY);
|
|
2889
|
+
const width = Math.abs(currentX - startX);
|
|
2890
|
+
const height = Math.abs(currentY - startY);
|
|
2891
|
+
|
|
2892
|
+
marqueeBox.style.left = left + 'px';
|
|
2893
|
+
marqueeBox.style.top = top + 'px';
|
|
2894
|
+
marqueeBox.style.width = width + 'px';
|
|
2895
|
+
marqueeBox.style.height = height + 'px';
|
|
2896
|
+
|
|
2897
|
+
sizeBadge.textContent = `${width} × ${height} px`;
|
|
2898
|
+
}
|
|
2899
|
+
|
|
2900
|
+
function handleMouseUp(e) {
|
|
2901
|
+
if (!isSnipeActive || !isDragging) return;
|
|
2902
|
+
e.preventDefault();
|
|
2903
|
+
e.stopPropagation();
|
|
2904
|
+
|
|
2905
|
+
isDragging = false;
|
|
2906
|
+
const currentX = e.clientX;
|
|
2907
|
+
const currentY = e.clientY;
|
|
2908
|
+
|
|
2909
|
+
const left = Math.min(startX, currentX);
|
|
2910
|
+
const top = Math.min(startY, currentY);
|
|
2911
|
+
const width = Math.abs(currentX - startX);
|
|
2912
|
+
const height = Math.abs(currentY - startY);
|
|
2913
|
+
|
|
2914
|
+
if (width < 10 || height < 10) {
|
|
2915
|
+
destroyOverlay();
|
|
2916
|
+
return;
|
|
2917
|
+
}
|
|
2918
|
+
|
|
2919
|
+
if (currentSnipeMode === 'visual_crop') {
|
|
2920
|
+
captureVisualCrop(left, top, width, height);
|
|
2921
|
+
} else {
|
|
2922
|
+
captureDOMTargets(left, top, width, height);
|
|
2923
|
+
}
|
|
2924
|
+
|
|
2925
|
+
destroyOverlay();
|
|
2926
|
+
}
|
|
2927
|
+
|
|
2928
|
+
function handleKeyDown(e) {
|
|
2929
|
+
if (e.key === 'Escape' && isSnipeActive) {
|
|
2930
|
+
destroyOverlay();
|
|
2931
|
+
}
|
|
2932
|
+
if (e.altKey && (e.key === 's' || e.key === 'S')) {
|
|
2933
|
+
e.preventDefault();
|
|
2934
|
+
createOverlay('dom_target');
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2937
|
+
|
|
2938
|
+
window.addEventListener('mousedown', handleMouseDown, true);
|
|
2939
|
+
window.addEventListener('mousemove', handleMouseMove, true);
|
|
2940
|
+
window.addEventListener('mouseup', handleMouseUp, true);
|
|
2941
|
+
window.addEventListener('keydown', handleKeyDown, true);
|
|
2942
|
+
|
|
2943
|
+
// ── Mode 1: DOM Element Intersector ──────────────────────────────────────
|
|
2944
|
+
function captureDOMTargets(left, top, width, height) {
|
|
2945
|
+
const right = left + width;
|
|
2946
|
+
const bottom = top + height;
|
|
2947
|
+
const elements = [];
|
|
2948
|
+
|
|
2949
|
+
const allEls = document.querySelectorAll('button, input, select, textarea, a, h1, h2, h3, h4, h5, h6, p, table, tr, td, th, div, span, [role]');
|
|
2950
|
+
allEls.forEach((el) => {
|
|
2951
|
+
if (el.hasAttribute('data-wt-ignore') || el.closest('#wt-snipe-overlay')) return;
|
|
2952
|
+
const r = el.getBoundingClientRect();
|
|
2953
|
+
if (r.width === 0 || r.height === 0) return;
|
|
2954
|
+
|
|
2955
|
+
const overlaps = !(r.right < left || r.left > right || r.bottom < top || r.top > bottom);
|
|
2956
|
+
if (overlaps) {
|
|
2957
|
+
const text = (el.innerText || el.value || el.getAttribute('aria-label') || '').trim().slice(0, 100);
|
|
2958
|
+
if (text || ['INPUT', 'BUTTON', 'A', 'SELECT', 'TEXTAREA'].includes(el.tagName)) {
|
|
2959
|
+
elements.push({
|
|
2960
|
+
tag: el.tagName.toLowerCase(),
|
|
2961
|
+
selector: getStableSelector(el),
|
|
2962
|
+
ref: el.getAttribute('data-wt-id') || null,
|
|
2963
|
+
text: text,
|
|
2964
|
+
role: el.getAttribute('role') || null,
|
|
2965
|
+
rect: { x: Math.round(r.left), y: Math.round(r.top), w: Math.round(r.width), h: Math.round(r.height) }
|
|
2966
|
+
});
|
|
2967
|
+
}
|
|
2968
|
+
}
|
|
2969
|
+
});
|
|
2970
|
+
|
|
2971
|
+
const uniqueElements = elements.slice(0, 15);
|
|
2972
|
+
|
|
2973
|
+
__wt_safeSendMessage({
|
|
2974
|
+
type: 'WT_SNIPE_COMPLETED',
|
|
2975
|
+
payload: {
|
|
2976
|
+
mode: 'dom_target',
|
|
2977
|
+
elements: uniqueElements,
|
|
2978
|
+
boundingRect: { x: left, y: top, width, height }
|
|
2979
|
+
}
|
|
2980
|
+
});
|
|
2981
|
+
}
|
|
2982
|
+
|
|
2983
|
+
// ── Mode 2: Visual WebP Crop ─────────────────────────────────────────────
|
|
2984
|
+
function captureVisualCrop(left, top, width, height) {
|
|
2985
|
+
try {
|
|
2986
|
+
const canvas = document.createElement('canvas');
|
|
2987
|
+
canvas.width = width;
|
|
2988
|
+
canvas.height = height;
|
|
2989
|
+
const ctx = canvas.getContext('2d');
|
|
2990
|
+
|
|
2991
|
+
const htmlContent = document.documentElement.outerHTML;
|
|
2992
|
+
const dataSvg = `
|
|
2993
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="${window.innerWidth}" height="${window.innerHeight}">
|
|
2994
|
+
<foreignObject width="100%" height="100%">
|
|
2995
|
+
<div xmlns="http://www.w3.org/1999/xhtml">
|
|
2996
|
+
${htmlContent}
|
|
2997
|
+
</div>
|
|
2998
|
+
</foreignObject>
|
|
2999
|
+
</svg>
|
|
3000
|
+
`;
|
|
3001
|
+
|
|
3002
|
+
const img = new Image();
|
|
3003
|
+
const blob = new Blob([dataSvg], { type: 'image/svg+xml;charset=utf-8' });
|
|
3004
|
+
const url = URL.createObjectURL(blob);
|
|
3005
|
+
|
|
3006
|
+
img.onload = function() {
|
|
3007
|
+
ctx.drawImage(img, left, top, width, height, 0, 0, width, height);
|
|
3008
|
+
URL.revokeObjectURL(url);
|
|
3009
|
+
const webpBase64 = canvas.toDataURL('image/webp', 0.75);
|
|
3010
|
+
|
|
3011
|
+
__wt_safeSendMessage({
|
|
3012
|
+
type: 'WT_SNIPE_COMPLETED',
|
|
3013
|
+
payload: {
|
|
3014
|
+
mode: 'visual_crop',
|
|
3015
|
+
cropImage: webpBase64,
|
|
3016
|
+
boundingRect: { x: left, y: top, width, height }
|
|
3017
|
+
}
|
|
3018
|
+
});
|
|
3019
|
+
};
|
|
3020
|
+
img.onerror = function() {
|
|
3021
|
+
captureDOMTargets(left, top, width, height);
|
|
3022
|
+
};
|
|
3023
|
+
img.src = url;
|
|
3024
|
+
} catch(e) {
|
|
3025
|
+
captureDOMTargets(left, top, width, height);
|
|
3026
|
+
}
|
|
3027
|
+
}
|
|
3028
|
+
|
|
3029
|
+
// Listen for start snipe message from sidepanel/background
|
|
3030
|
+
browserAPI.runtime.onMessage.addListener((msg) => {
|
|
3031
|
+
if (msg.type === 'WT_START_SNIPE') {
|
|
3032
|
+
createOverlay(msg.mode || 'dom_target');
|
|
3033
|
+
}
|
|
3034
|
+
if (msg.type === 'WT_SNIPE_INJECT' && msg.entry) {
|
|
3035
|
+
const noteStr = typeof msg.entry === 'string' ? msg.entry : JSON.stringify(msg.entry);
|
|
3036
|
+
try {
|
|
3037
|
+
if (!window.__wt_chat_queue) window.__wt_chat_queue = [];
|
|
3038
|
+
window.__wt_chat_queue.push(noteStr);
|
|
3039
|
+
} catch(e) {}
|
|
3040
|
+
|
|
3041
|
+
try {
|
|
3042
|
+
const existing = JSON.parse(localStorage.getItem('__wt_chat_queue_persist') || '[]');
|
|
3043
|
+
existing.push(msg.entry);
|
|
3044
|
+
localStorage.setItem('__wt_chat_queue_persist', JSON.stringify(existing));
|
|
3045
|
+
} catch(e) {}
|
|
3046
|
+
|
|
3047
|
+
const bridge = document.getElementById('__wt_ask_bridge');
|
|
3048
|
+
if (bridge) {
|
|
3049
|
+
bridge.dataset.status = 'submitted';
|
|
3050
|
+
bridge.dataset.result = msg.entry.text || 'Annotation submitted';
|
|
3051
|
+
}
|
|
3052
|
+
}
|
|
3053
|
+
});
|
|
3054
|
+
|
|
3055
|
+
})();
|
|
3056
|
+
|
|
3057
|
+
// ── ALERT / ACTION-REQUIRED TAB INDICATOR ─────────────────────
|
|
3058
|
+
(function initAlertDetector() {
|
|
3059
|
+
if (window.self !== window.top) return;
|
|
3060
|
+
// Expose on window so window.__Weavetab.getAlertState() can read it from CDP MAIN world
|
|
3061
|
+
window.__wt_alertPending = false;
|
|
3062
|
+
var _alert = window.alert;
|
|
3063
|
+
window.alert = function(msg) {
|
|
3064
|
+
window.__wt_alertPending = true;
|
|
3065
|
+
__wt_safeSendMessage({ type: 'TAB_ALERT_WAITING' });
|
|
3066
|
+
var r = _alert.apply(this, arguments);
|
|
3067
|
+
window.__wt_alertPending = false;
|
|
3068
|
+
__wt_safeSendMessage({ type: 'TAB_ALERT_CLEARED' });
|
|
3069
|
+
return r;
|
|
3070
|
+
};
|
|
3071
|
+
var _confirm = window.confirm;
|
|
3072
|
+
window.confirm = function(msg) {
|
|
3073
|
+
window.__wt_alertPending = true;
|
|
3074
|
+
__wt_safeSendMessage({ type: 'TAB_ALERT_WAITING' });
|
|
3075
|
+
var r = _confirm.apply(this, arguments);
|
|
3076
|
+
window.__wt_alertPending = false;
|
|
3077
|
+
__wt_safeSendMessage({ type: 'TAB_ALERT_CLEARED' });
|
|
3078
|
+
return r;
|
|
3079
|
+
};
|
|
3080
|
+
var _prompt = window.prompt;
|
|
3081
|
+
window.prompt = function(msg, def) {
|
|
3082
|
+
window.__wt_alertPending = true;
|
|
3083
|
+
__wt_safeSendMessage({ type: 'TAB_ALERT_WAITING' });
|
|
3084
|
+
var r = _prompt.apply(this, arguments);
|
|
3085
|
+
window.__wt_alertPending = false;
|
|
3086
|
+
__wt_safeSendMessage({ type: 'TAB_ALERT_CLEARED' });
|
|
3087
|
+
return r;
|
|
3088
|
+
};
|
|
3089
|
+
})();
|