@weavetab/mcp 2.5.0 → 2.5.1

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.
Files changed (168) hide show
  1. package/AGENTS.md +49 -49
  2. package/ARCHITECTURE.md +590 -590
  3. package/LICENSE +216 -216
  4. package/README.md +269 -269
  5. package/SECURITY.md +207 -207
  6. package/TROUBLESHOOTING.md +373 -365
  7. package/dist/anticaptcha/detector.js +395 -395
  8. package/dist/anticaptcha/solver.js +130 -130
  9. package/dist/cdp/bridge.js +73 -73
  10. package/dist/cdp/connector.js +46 -22
  11. package/dist/cdp/extension.js +34 -34
  12. package/dist/cdp/helpers.js +174 -174
  13. package/dist/cdp/pruner.js +195 -195
  14. package/dist/cdp/ref_cache.js +66 -66
  15. package/dist/cdp/security.js +10 -10
  16. package/dist/cdp/verify.js +57 -57
  17. package/dist/cdp/walker.js +563 -525
  18. package/dist/cli/plugin.js +26 -3
  19. package/dist/config/loader.d.ts +0 -3
  20. package/dist/config/loader.js +0 -9
  21. package/dist/index.js +0 -4
  22. package/dist/intelligence/intent.js +81 -81
  23. package/dist/intelligence/sessionMemory.js +2 -2
  24. package/dist/overlay/ghost.js +22 -22
  25. package/dist/overlay/injector.js +89 -89
  26. package/dist/plugin/extension_bridge.js +54 -54
  27. package/dist/plugin/interface.d.ts +7 -0
  28. package/dist/plugin/loader.js +3 -4
  29. package/dist/plugin/schema.d.ts +11 -0
  30. package/dist/plugin/schema.js +59 -0
  31. package/dist/security/guard.js +18 -18
  32. package/dist/sensors/actionability.js +167 -167
  33. package/dist/sensors/guard.js +29 -29
  34. package/dist/sensors/network.js +15 -17
  35. package/dist/server.d.ts +1 -1
  36. package/dist/server.js +173 -144
  37. package/dist/state/delta.js +6 -7
  38. package/dist/tools/automation.js +85 -85
  39. package/dist/tools/canvas.js +48 -25
  40. package/dist/tools/checkpoint.js +94 -35
  41. package/dist/tools/click.js +52 -41
  42. package/dist/tools/clipboard.js +80 -74
  43. package/dist/tools/design_extract.js +441 -441
  44. package/dist/tools/dialog.js +13 -13
  45. package/dist/tools/emulation.d.ts +2 -0
  46. package/dist/tools/emulation.js +7 -0
  47. package/dist/tools/fill.js +344 -347
  48. package/dist/tools/find.js +78 -52
  49. package/dist/tools/highlight.js +67 -67
  50. package/dist/tools/inspect.js +54 -25
  51. package/dist/tools/key.js +42 -35
  52. package/dist/tools/navigate.d.ts +1 -1
  53. package/dist/tools/navigate.js +35 -35
  54. package/dist/tools/office/parse.js +62 -18
  55. package/dist/tools/pdf.d.ts +9 -0
  56. package/dist/tools/pdf.js +7 -4
  57. package/dist/tools/performance.d.ts +11 -4
  58. package/dist/tools/performance.js +94 -42
  59. package/dist/tools/performance_test.d.ts +2 -2
  60. package/dist/tools/performance_test.js +69 -69
  61. package/dist/tools/pointer.js +116 -116
  62. package/dist/tools/read.js +99 -67
  63. package/dist/tools/recording.js +9 -0
  64. package/dist/tools/scrape.js +146 -146
  65. package/dist/tools/screenshot.d.ts +2 -0
  66. package/dist/tools/screenshot.js +46 -4
  67. package/dist/tools/scroll.d.ts +3 -1
  68. package/dist/tools/scroll.js +13 -15
  69. package/dist/tools/select.d.ts +3 -0
  70. package/dist/tools/select.js +131 -124
  71. package/dist/tools/snapshot.js +8 -6
  72. package/dist/tools/storage.d.ts +5 -1
  73. package/dist/tools/storage.js +16 -0
  74. package/dist/tools/tabs.d.ts +7 -5
  75. package/dist/tools/tabs.js +79 -26
  76. package/dist/tools/thought.js +8 -8
  77. package/dist/tools/type.js +274 -274
  78. package/dist/tools/type_secret.js +23 -23
  79. package/dist/tools/upload.js +3 -3
  80. package/dist/tools/video_compiler.js +9 -0
  81. package/dist/tools/wait.js +35 -35
  82. package/dist/version.d.ts +1 -1
  83. package/dist/version.js +1 -1
  84. package/docs/DOCUMENTATION.md +583 -585
  85. package/docs/PLUGINS.md +139 -121
  86. package/docs/TOOLS.md +3 -4
  87. package/docs/architecture/blind-injection.md +83 -83
  88. package/docs/architecture/overview.md +49 -49
  89. package/docs/architecture/security.md +36 -36
  90. package/docs/architecture/transport.md +37 -37
  91. package/docs/cli/cli-commands.md +50 -50
  92. package/docs/cli/cli-flags.md +28 -28
  93. package/docs/cli/cli-scripting.md +35 -35
  94. package/docs/configuration/config-locations.md +23 -23
  95. package/docs/configuration/configuration.md +39 -27
  96. package/docs/configuration/env-variables.md +19 -19
  97. package/docs/configuration/folders.md +62 -55
  98. package/docs/desktop-automation/electron-apps.md +225 -214
  99. package/docs/get-started/installation.md +1 -1
  100. package/docs/get-started/introduction.md +60 -60
  101. package/docs/get-started/quickstart.md +2 -2
  102. package/docs/get-started/setup-wizard.md +27 -27
  103. package/docs/intelligence/site-profiles.md +151 -151
  104. package/docs/tools/tools.md +582 -631
  105. package/docs/troubleshooting/trouble-browser.md +27 -27
  106. package/docs/troubleshooting/trouble-loop.md +31 -31
  107. package/ext-dist/background.js +415 -415
  108. package/ext-dist/content.js +2527 -2527
  109. package/ext-dist/dashboard.html +583 -583
  110. package/ext-dist/hud/hud.js +3 -3
  111. package/ext-dist/hud/index.html +20 -20
  112. package/ext-dist/manifest.json +73 -73
  113. package/ext-dist/styles.css +223 -223
  114. package/mcp.json +796 -805
  115. package/package.json +118 -118
  116. package/server.json +23 -23
  117. package/dist/cdp/walker.test.d.ts +0 -2
  118. package/dist/cdp/walker.test.js +0 -172
  119. package/dist/intelligence/sessionMemory.test.d.ts +0 -2
  120. package/dist/intelligence/sessionMemory.test.js +0 -62
  121. package/dist/intelligence/trail.test.d.ts +0 -2
  122. package/dist/intelligence/trail.test.js +0 -110
  123. package/dist/state/cache.test.d.ts +0 -2
  124. package/dist/state/cache.test.js +0 -76
  125. package/dist/state/delta.test.d.ts +0 -2
  126. package/dist/state/delta.test.js +0 -279
  127. package/dist/state/groups.d.ts +0 -15
  128. package/dist/state/groups.js +0 -80
  129. package/dist/tools/accessibility.d.ts +0 -14
  130. package/dist/tools/accessibility.js +0 -32
  131. package/dist/tools/ask.d.ts +0 -14
  132. package/dist/tools/ask.js +0 -49
  133. package/dist/tools/click_and_wait.d.ts +0 -33
  134. package/dist/tools/click_and_wait.js +0 -149
  135. package/dist/tools/drag.d.ts +0 -15
  136. package/dist/tools/drag.js +0 -29
  137. package/dist/tools/geo.d.ts +0 -19
  138. package/dist/tools/geo.js +0 -29
  139. package/dist/tools/github/analyze.d.ts +0 -4
  140. package/dist/tools/github/analyze.js +0 -113
  141. package/dist/tools/github/cache.d.ts +0 -21
  142. package/dist/tools/github/cache.js +0 -28
  143. package/dist/tools/github/index.d.ts +0 -19
  144. package/dist/tools/github/index.js +0 -40
  145. package/dist/tools/github/issues.d.ts +0 -9
  146. package/dist/tools/github/issues.js +0 -88
  147. package/dist/tools/github/pr.d.ts +0 -5
  148. package/dist/tools/github/pr.js +0 -88
  149. package/dist/tools/github/rateLimiter.d.ts +0 -4
  150. package/dist/tools/github/rateLimiter.js +0 -46
  151. package/dist/tools/github/read.d.ts +0 -34
  152. package/dist/tools/github/read.js +0 -74
  153. package/dist/tools/github/repoContext.d.ts +0 -20
  154. package/dist/tools/github/repoContext.js +0 -129
  155. package/dist/tools/hover.d.ts +0 -19
  156. package/dist/tools/hover.js +0 -57
  157. package/dist/tools/integration.test.d.ts +0 -2
  158. package/dist/tools/integration.test.js +0 -96
  159. package/dist/tools/macro_compiler.d.ts +0 -7
  160. package/dist/tools/macro_compiler.js +0 -82
  161. package/dist/tools/mouse.d.ts +0 -21
  162. package/dist/tools/mouse.js +0 -62
  163. package/dist/tools/response.d.ts +0 -129
  164. package/dist/tools/response.js +0 -5
  165. package/dist/tools/wait_for.d.ts +0 -23
  166. package/dist/tools/wait_for.js +0 -30
  167. package/dist/tools/windows.d.ts +0 -22
  168. package/dist/tools/windows.js +0 -57
@@ -1,583 +1,583 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8">
5
- <title>Weavetab AI Voice Hub</title>
6
- <style>
7
- :root {
8
- --bg: #050506;
9
- --panel-bg: rgba(13, 13, 16, 0.7);
10
- --border: rgba(255, 255, 255, 0.08);
11
- --accent: #8b5cf6;
12
- --accent-glow: rgba(139, 92, 246, 0.4);
13
- --text: #fafafa;
14
- --text-muted: #71717a;
15
- --orb-idle: #3f3f46;
16
- --orb-active: #8b5cf6;
17
- --orb-speaking: #3b82f6;
18
- }
19
- * { margin: 0; padding: 0; box-sizing: border-box; }
20
- body {
21
- background: var(--bg);
22
- color: var(--text);
23
- font-family: 'Inter', system-ui, -apple-system, sans-serif;
24
- overflow-x: hidden;
25
- min-height: 100vh;
26
- display: flex;
27
- flex-direction: column;
28
- }
29
-
30
- .bg-glow {
31
- position: fixed;
32
- top: -20vh;
33
- left: -20vw;
34
- width: 60vw;
35
- height: 60vh;
36
- background: radial-gradient(circle, rgba(139, 92, 246, 0.1) 0%, rgba(0,0,0,0) 70%);
37
- z-index: -1;
38
- pointer-events: none;
39
- }
40
- .bg-glow-2 {
41
- position: fixed;
42
- bottom: -20vh;
43
- right: -20vw;
44
- width: 60vw;
45
- height: 60vh;
46
- background: radial-gradient(circle, rgba(56, 189, 248, 0.05) 0%, rgba(0,0,0,0) 70%);
47
- z-index: -1;
48
- pointer-events: none;
49
- }
50
-
51
- header {
52
- padding: 20px 32px;
53
- display: flex;
54
- justify-content: space-between;
55
- align-items: center;
56
- border-bottom: 1px solid var(--border);
57
- background: rgba(5, 5, 6, 0.8);
58
- backdrop-filter: blur(12px);
59
- -webkit-backdrop-filter: blur(12px);
60
- position: sticky;
61
- top: 0;
62
- z-index: 100;
63
- }
64
- .brand {
65
- display: flex;
66
- align-items: center;
67
- gap: 12px;
68
- font-size: 1.2rem;
69
- font-weight: 600;
70
- letter-spacing: -0.02em;
71
- }
72
- .branch-badge {
73
- background: rgba(139, 92, 246, 0.15);
74
- border: 1px solid rgba(139, 92, 246, 0.3);
75
- color: #c4b5fd;
76
- padding: 4px 12px;
77
- border-radius: 999px;
78
- font-size: 0.75rem;
79
- font-family: monospace;
80
- }
81
-
82
- main {
83
- flex: 1;
84
- padding: 32px;
85
- display: flex;
86
- flex-direction: column;
87
- gap: 32px;
88
- max-width: 1200px;
89
- margin: 0 auto;
90
- width: 100%;
91
- }
92
-
93
- .panel {
94
- background: var(--panel-bg);
95
- border: 1px solid var(--border);
96
- border-radius: 24px;
97
- padding: 32px;
98
- backdrop-filter: blur(24px);
99
- -webkit-backdrop-filter: blur(24px);
100
- box-shadow: 0 20px 50px rgba(0, 0, 0, 0.4);
101
- }
102
- .panel-title {
103
- font-size: 0.85rem;
104
- font-weight: 600;
105
- color: var(--text-muted);
106
- margin-bottom: 24px;
107
- text-transform: uppercase;
108
- letter-spacing: 0.1em;
109
- display: flex;
110
- justify-content: space-between;
111
- align-items: center;
112
- }
113
-
114
- /* --- VOICE SYSTEM UI --- */
115
- .voice-hub {
116
- display: flex;
117
- flex-direction: column;
118
- align-items: center;
119
- justify-content: center;
120
- gap: 40px;
121
- text-align: center;
122
- padding: 60px 0;
123
- }
124
-
125
- .orb-container {
126
- position: relative;
127
- width: 200px;
128
- height: 200px;
129
- display: flex;
130
- align-items: center;
131
- justify-content: center;
132
- cursor: pointer;
133
- transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
134
- }
135
- .orb-container:hover { transform: scale(1.05); }
136
- .orb-container:active { transform: scale(0.95); }
137
-
138
- .orb {
139
- position: absolute;
140
- width: 100px;
141
- height: 100px;
142
- border-radius: 50%;
143
- background: var(--orb-idle);
144
- box-shadow: 0 0 20px rgba(0,0,0,0.5);
145
- transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
146
- z-index: 2;
147
- }
148
- .orb.listening {
149
- background: var(--orb-active);
150
- box-shadow: 0 0 40px var(--accent-glow), 0 0 80px rgba(139, 92, 246, 0.2);
151
- transform: scale(1.2);
152
- }
153
- .orb.speaking {
154
- background: var(--orb-speaking);
155
- box-shadow: 0 0 40px rgba(59, 130, 246, 0.4), 0 0 80px rgba(59, 130, 246, 0.2);
156
- }
157
-
158
- .orb-ring {
159
- position: absolute;
160
- border: 2px solid var(--border);
161
- border-radius: 50%;
162
- transition: all 0.5s ease;
163
- z-index: 1;
164
- }
165
- .ring-1 { width: 140px; height: 140px; animation: rotate 10s linear infinite; }
166
- .ring-2 { width: 180px; height: 180px; animation: rotate 15s linear infinite reverse; border-style: dashed; }
167
-
168
- @keyframes rotate { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
169
-
170
- .voice-status {
171
- font-size: 1.5rem;
172
- font-weight: 500;
173
- color: var(--text);
174
- min-height: 1.5em;
175
- transition: opacity 0.3s;
176
- }
177
- .voice-transcript {
178
- font-size: 1rem;
179
- color: var(--text-muted);
180
- max-width: 600px;
181
- min-height: 1.5em;
182
- font-style: italic;
183
- }
184
-
185
- .voice-controls {
186
- display: flex;
187
- gap: 16px;
188
- justify-content: center;
189
- }
190
- .btn-voice {
191
- background: rgba(255, 255, 255, 0.05);
192
- border: 1px solid var(--border);
193
- color: var(--text);
194
- padding: 12px 24px;
195
- border-radius: 999px;
196
- cursor: pointer;
197
- font-family: inherit;
198
- font-size: 0.9rem;
199
- transition: all 0.2s;
200
- }
201
- .btn-voice:hover { background: rgba(255, 255, 255, 0.1); border-color: rgba(255, 255, 255, 0.2); }
202
- .btn-voice.active { background: var(--accent); border-color: var(--accent); color: white; }
203
-
204
- /* --- Permissions Grid --- */
205
- .permissions-grid {
206
- display: grid;
207
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
208
- gap: 16px;
209
- }
210
- .perm-btn {
211
- background: rgba(255, 255, 255, 0.03);
212
- border: 1px solid var(--border);
213
- color: var(--text);
214
- padding: 16px;
215
- border-radius: 16px;
216
- cursor: pointer;
217
- display: flex;
218
- flex-direction: column;
219
- align-items: flex-start;
220
- gap: 8px;
221
- transition: all 0.2s ease;
222
- font-family: inherit;
223
- text-align: left;
224
- }
225
- .perm-btn:hover { background: rgba(255, 255, 255, 0.07); transform: translateY(-2px); }
226
- .perm-icon { font-size: 1.25rem; margin-bottom: 4px; }
227
- .perm-label { font-weight: 500; font-size: 0.9rem; }
228
- .perm-desc { font-size: 0.8rem; color: var(--text-muted); }
229
-
230
- /* --- Workspace --- */
231
- #workspace-panel { margin-top: 32px; }
232
- #canvas-container { display: flex; flex-direction: column; align-items: center; gap: 16px; width: 100%; }
233
- canvas { background: #000; border: 1px solid var(--border); border-radius: 16px; max-width: 100%; box-shadow: 0 10px 40px rgba(0,0,0,0.5); }
234
- #content-area { display: none; width: 100%; background: #fff; color: #000; border-radius: 16px; min-height: 600px; padding: 24px; overflow: auto; }
235
- </style>
236
- </head>
237
- <body>
238
- <div class="bg-glow"></div>
239
- <div class="bg-glow-2"></div>
240
-
241
- <header>
242
- <div class="brand">
243
- <span style="color: var(--accent);">✦</span> Weavetab <span style="font-weight: 300; opacity: 0.6; margin-left: 4px;">VoiceHub</span>
244
- </div>
245
- <div class="branch-badge" id="branch-display">branch: default</div>
246
- </header>
247
-
248
- <main>
249
- <!-- Voice Interface Panel -->
250
- <div class="panel">
251
- <div class="panel-title">AI Voice Command System</div>
252
- <div class="voice-hub">
253
- <div class="orb-container" id="voice-trigger">
254
- <div class="orb-ring ring-1"></div>
255
- <div class="orb-ring ring-2"></div>
256
- <div class="orb" id="voice-orb"></div>
257
- </div>
258
-
259
- <div class="voice-status" id="status-text">Ready to listen</div>
260
- <div class="voice-transcript" id="transcript-text">"Click the orb or press Space to speak"</div>
261
-
262
- <div class="voice-controls">
263
- <button class="btn-voice" id="btn-listen">Start Listening</button>
264
- <button class="btn-voice" id="btn-voice-settings">Voice Settings</button>
265
- </div>
266
- </div>
267
- </div>
268
-
269
- <!-- Permissions Panel -->
270
- <div class="panel">
271
- <div class="panel-title">System Permissions</div>
272
- <div class="permissions-grid">
273
- <button class="perm-btn" onclick="requestMic()">
274
- <span class="perm-icon">🎤</span>
275
- <span class="perm-label">Microphone</span>
276
- <span class="perm-desc">Required for Realtime Voice</span>
277
- </button>
278
- <button class="perm-btn" onclick="requestCamera()">
279
- <span class="perm-icon">📷</span>
280
- <span class="perm-label">Camera</span>
281
- <span class="perm-desc">Grant video stream access</span>
282
- </button>
283
- <button class="perm-btn" onclick="requestGeolocation()">
284
- <span class="perm-icon">📍</span>
285
- <span class="perm-label">Location</span>
286
- <span class="perm-desc">Allow location services</span>
287
- </button>
288
- <button class="perm-btn" onclick="requestNotifications()">
289
- <span class="perm-icon">🔔</span>
290
- <span class="perm-label">Notifications</span>
291
- <span class="perm-desc">Allow system alerts</span>
292
- </button>
293
- </div>
294
- </div>
295
-
296
- <!-- Workspace Panel -->
297
- <div class="panel" id="workspace-panel">
298
- <div class="panel-title">Workspace</div>
299
- <div id="canvas-container">
300
- <canvas id="wt-canvas" width="800" height="600"></canvas>
301
- </div>
302
- <div id="content-area"></div>
303
- </div>
304
- </main>
305
-
306
- <script>
307
- // --- Infrastructure Setup ---
308
- const urlParams = new URLSearchParams(window.location.search);
309
- const branch = urlParams.get('branch') || 'default';
310
- document.getElementById('branch-display').textContent = `branch: ${branch}`;
311
-
312
- // --- Voice System Implementation ---
313
- class WeavetabVoiceSystem {
314
- constructor() {
315
- this.orb = document.getElementById('voice-orb');
316
- this.status = document.getElementById('status-text');
317
- this.transcript = document.getElementById('transcript-text');
318
- this.btnListen = document.getElementById('btn-listen');
319
- this.trigger = document.getElementById('voice-trigger');
320
-
321
- this.isListening = false;
322
- this.isSpeaking = false;
323
-
324
- // Speech Recognition Setup
325
- const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
326
- if (!SpeechRecognition) {
327
- this.status.textContent = "Speech API not supported";
328
- this.status.style.color = "#ef4444";
329
- return;
330
- }
331
-
332
- this.recognition = new SpeechRecognition();
333
- this.recognition.continuous = false;
334
- this.recognition.interimResults = true;
335
- this.recognition.lang = 'en-US';
336
-
337
- this.initEventListeners();
338
- this.setupRecognition();
339
- }
340
-
341
- initEventListeners() {
342
- this.btnListen.onclick = () => this.toggleListening();
343
- this.trigger.onclick = () => this.toggleListening();
344
-
345
- window.addEventListener('keydown', (e) => {
346
- if (e.code === 'Space' && document.activeElement.tagName !== 'INPUT') {
347
- e.preventDefault();
348
- this.toggleListening();
349
- }
350
- });
351
- }
352
-
353
- setupRecognition() {
354
- this.recognition.onstart = () => {
355
- this.isListening = true;
356
- this.updateUI('listening');
357
- };
358
-
359
- this.recognition.onresult = (event) => {
360
- const result = event.results[0][0].transcript;
361
- this.transcript.textContent = `"${result}"`;
362
-
363
- if (event.results[0].isFinal) {
364
- this.handleVoiceCommand(result);
365
- }
366
- };
367
-
368
- this.recognition.onerror = (e) => {
369
- console.error("Voice Error:", e);
370
- this.stopListening();
371
- this.status.textContent = "Error capturing voice";
372
- };
373
-
374
- this.recognition.onend = () => {
375
- if (this.isListening) this.recognition.start(); // Keep alive if we intend to listen
376
- else this.stopListening();
377
- };
378
- }
379
-
380
- toggleListening() {
381
- if (this.isListening) this.stopListening();
382
- else this.startListening();
383
- }
384
-
385
- startListening() {
386
- try {
387
- this.recognition.start();
388
- this.btnListen.classList.add('active');
389
- } catch (e) {
390
- console.error("Start Error:", e);
391
- }
392
- }
393
-
394
- stopListening() {
395
- this.isListening = false;
396
- this.recognition.stop();
397
- this.btnListen.classList.remove('active');
398
- this.updateUI('idle');
399
- }
400
-
401
- updateUI(state) {
402
- this.orb.className = 'orb';
403
- if (state === 'listening') {
404
- this.orb.classList.add('listening');
405
- this.status.textContent = "I'm listening...";
406
- } else if (state === 'speaking') {
407
- this.orb.classList.add('speaking');
408
- this.status.textContent = "Weavetab is speaking";
409
- } else {
410
- this.status.textContent = "Ready to listen";
411
- }
412
- }
413
-
414
- async handleVoiceCommand(text) {
415
- this.stopListening();
416
- this.status.textContent = "Processing...";
417
-
418
- // Simulate AI Thought
419
- setTimeout(() => {
420
- const response = this.generateAIResponse(text);
421
- this.speak(response);
422
- }, 600);
423
- }
424
-
425
- generateAIResponse(text) {
426
- const t = text.toLowerCase();
427
- if (t.includes('hello') || t.includes('hi')) return "Hello. I am the Weavetab Voice System. How can I assist your workflow today?";
428
- if (t.includes('time')) return `The current system time is ${new Date().toLocaleTimeString()}.`;
429
- if (t.includes('who are you')) return "I am your enterprise AI agent, integrated directly into the Weavetab core for real-time browser orchestration.";
430
- if (t.includes('status')) return "All systems are operational. CDP bridge is active and session is stable.";
431
-
432
- return `You said: ${text}. I am processing this request through the Weavetab neural bridge.`;
433
- }
434
-
435
- speak(text) {
436
- this.isSpeaking = true;
437
- this.updateUI('speaking');
438
-
439
- const utterance = new SpeechSynthesisUtterance(text);
440
-
441
- // Attempt to find a premium "Enterprise" voice
442
- const voices = window.speechSynthesis.getVoices();
443
- const premiumVoice = voices.find(v => v.name.includes('Google US English') || v.name.includes('Samantha') || v.name.includes('Microsoft Aria'));
444
- if (premiumVoice) utterance.voice = premiumVoice;
445
-
446
- utterance.pitch = 0.9; // Slightly deeper for "Enterprise" feel
447
- utterance.rate = 1.0;
448
-
449
- utterance.onend = () => {
450
- this.isSpeaking = false;
451
- this.updateUI('idle');
452
- };
453
-
454
- window.speechSynthesis.speak(utterance);
455
- }
456
- }
457
-
458
- // Initialize Voice System
459
- const voiceSystem = new WeavetabVoiceSystem();
460
-
461
- // --- Go Home Function ---
462
- const browserAPI = typeof browser !== 'undefined' ? browser : chrome;
463
- function goHome() {
464
- // Reset to default branch and clear any error state
465
- const url = new URL(window.location.href);
466
- url.searchParams.set('branch', 'default');
467
- window.location.href = url.toString();
468
- }
469
-
470
- // --- Original Dashboard Logic ---
471
- async function requestMic() {
472
- try {
473
- await navigator.mediaDevices.getUserMedia({ audio: true });
474
- alert('Microphone permission granted!');
475
- } catch (e) {
476
- alert('Microphone permission failed: ' + e.message);
477
- }
478
- }
479
- async function requestCamera() {
480
- try {
481
- const stream = await navigator.mediaDevices.getUserMedia({ video: true });
482
- stream.getTracks().forEach(t => t.stop());
483
- alert('Camera granted!');
484
- } catch (e) { alert('Camera failed: ' + e.message); }
485
- }
486
- function requestGeolocation() {
487
- navigator.geolocation.getCurrentPosition(() => alert('Location granted!'), (e) => alert(e.message));
488
- }
489
- async function requestNotifications() {
490
- const p = await Notification.requestPermission();
491
- alert('Notification: ' + p);
492
- }
493
-
494
- const canvas = document.getElementById('wt-canvas');
495
- const ctx = canvas.getContext('2d');
496
- const contentArea = document.getElementById('content-area');
497
- const container = document.getElementById('canvas-container');
498
-
499
- function drawPlaceholder() {
500
- ctx.fillStyle = '#000';
501
- ctx.fillRect(0, 0, 800, 600);
502
- ctx.fillStyle = '#8b5cf6';
503
- ctx.font = 'bold 36px system-ui, sans-serif';
504
- ctx.textAlign = 'center';
505
- ctx.fillText('Agent Canvas', 400, 260);
506
- ctx.fillStyle = '#a1a1aa';
507
- ctx.font = '18px system-ui, sans-serif';
508
- ctx.fillText('Real-time Voice Active', 400, 310);
509
- }
510
-
511
- // Add error handling for browserAPI.storage access
512
- if (typeof chrome === 'undefined' || !browserAPI.storage || !browserAPI.storage.local) {
513
- console.error('Chrome storage API not available');
514
- document.body.innerHTML = `
515
- <div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100vh;background:#050506;color:#fff;font-family:sans-serif;">
516
- <h2 style="color:#ef4444;margin-bottom:20px;">Extension Not Loaded</h2>
517
- <p style="margin-bottom:20px;">The WeaveTab extension is not properly loaded.</p>
518
- <div style="display:flex;gap:10px;">
519
- <button onclick="location.reload()" style="padding:12px 24px;background:#8b5cf6;color:#fff;border:none;border-radius:8px;cursor:pointer;">Retry</button>
520
- <button onclick="goHome()" style="padding:12px 24px;background:#3b82f6;color:#fff;border:none;border-radius:8px;cursor:pointer;">Go Home</button>
521
- </div>
522
- </div>
523
- `;
524
- drawPlaceholder();
525
- } else {
526
- browserAPI.storage.local.get([`branch_${branch}_html`, `branch_${branch}_canvas`], (result) => {
527
- if (browserAPI.runtime.lastError) {
528
- console.error('Chrome storage error:', browserAPI.runtime.lastError);
529
- document.body.innerHTML = `
530
- <div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100vh;background:#050506;color:#fff;font-family:sans-serif;">
531
- <h2 style="color:#ef4444;margin-bottom:20px;">Storage Error</h2>
532
- <p style="margin-bottom:20px;">${browserAPI.runtime.lastError.message}</p>
533
- <div style="display:flex;gap:10px;">
534
- <button onclick="location.reload()" style="padding:12px 24px;background:#8b5cf6;color:#fff;border:none;border-radius:8px;cursor:pointer;">Retry</button>
535
- <button onclick="goHome()" style="padding:12px 24px;background:#3b82f6;color:#fff;border:none;border-radius:8px;cursor:pointer;">Go Home</button>
536
- </div>
537
- </div>
538
- `;
539
- } else {
540
- if (result[`branch_${branch}_html`]) {
541
- contentArea.textContent = result[`branch_${branch}_html`];
542
- container.style.display = 'none';
543
- contentArea.style.display = 'block';
544
- } else {
545
- drawPlaceholder();
546
- }
547
- }
548
- });
549
- }
550
-
551
- // Add error handling for storage save interval
552
- function safeStorageSave() {
553
- if (typeof chrome === 'undefined' || !browserAPI.storage || !browserAPI.storage.local) {
554
- return;
555
- }
556
- try {
557
- if (contentArea.style.display === 'block') {
558
- browserAPI.storage.local.set({ [`branch_${branch}_html`]: contentArea.innerHTML }).catch(e => {
559
- console.error('Storage save error:', e);
560
- });
561
- } else {
562
- browserAPI.storage.local.set({ [`branch_${branch}_canvas`]: canvas.toDataURL() }).catch(e => {
563
- console.error('Storage save error:', e);
564
- });
565
- }
566
- } catch (e) {
567
- console.error('Storage save error:', e);
568
- }
569
- }
570
-
571
- setInterval(safeStorageSave, 1000);
572
-
573
- window.addEventListener('wt-dashboard-command', (e) => {
574
- const { command, params } = e.detail || {};
575
- if (command === 'show-canvas') { container.style.display = 'flex'; contentArea.style.display = 'none'; }
576
- if (command === 'show-html') {
577
- container.style.display = 'none'; contentArea.style.display = 'block';
578
- if (params?.html) contentArea.innerHTML = params.html;
579
- }
580
- });
581
- </script>
582
- </body>
583
- </html>
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>Weavetab AI Voice Hub</title>
6
+ <style>
7
+ :root {
8
+ --bg: #050506;
9
+ --panel-bg: rgba(13, 13, 16, 0.7);
10
+ --border: rgba(255, 255, 255, 0.08);
11
+ --accent: #8b5cf6;
12
+ --accent-glow: rgba(139, 92, 246, 0.4);
13
+ --text: #fafafa;
14
+ --text-muted: #71717a;
15
+ --orb-idle: #3f3f46;
16
+ --orb-active: #8b5cf6;
17
+ --orb-speaking: #3b82f6;
18
+ }
19
+ * { margin: 0; padding: 0; box-sizing: border-box; }
20
+ body {
21
+ background: var(--bg);
22
+ color: var(--text);
23
+ font-family: 'Inter', system-ui, -apple-system, sans-serif;
24
+ overflow-x: hidden;
25
+ min-height: 100vh;
26
+ display: flex;
27
+ flex-direction: column;
28
+ }
29
+
30
+ .bg-glow {
31
+ position: fixed;
32
+ top: -20vh;
33
+ left: -20vw;
34
+ width: 60vw;
35
+ height: 60vh;
36
+ background: radial-gradient(circle, rgba(139, 92, 246, 0.1) 0%, rgba(0,0,0,0) 70%);
37
+ z-index: -1;
38
+ pointer-events: none;
39
+ }
40
+ .bg-glow-2 {
41
+ position: fixed;
42
+ bottom: -20vh;
43
+ right: -20vw;
44
+ width: 60vw;
45
+ height: 60vh;
46
+ background: radial-gradient(circle, rgba(56, 189, 248, 0.05) 0%, rgba(0,0,0,0) 70%);
47
+ z-index: -1;
48
+ pointer-events: none;
49
+ }
50
+
51
+ header {
52
+ padding: 20px 32px;
53
+ display: flex;
54
+ justify-content: space-between;
55
+ align-items: center;
56
+ border-bottom: 1px solid var(--border);
57
+ background: rgba(5, 5, 6, 0.8);
58
+ backdrop-filter: blur(12px);
59
+ -webkit-backdrop-filter: blur(12px);
60
+ position: sticky;
61
+ top: 0;
62
+ z-index: 100;
63
+ }
64
+ .brand {
65
+ display: flex;
66
+ align-items: center;
67
+ gap: 12px;
68
+ font-size: 1.2rem;
69
+ font-weight: 600;
70
+ letter-spacing: -0.02em;
71
+ }
72
+ .branch-badge {
73
+ background: rgba(139, 92, 246, 0.15);
74
+ border: 1px solid rgba(139, 92, 246, 0.3);
75
+ color: #c4b5fd;
76
+ padding: 4px 12px;
77
+ border-radius: 999px;
78
+ font-size: 0.75rem;
79
+ font-family: monospace;
80
+ }
81
+
82
+ main {
83
+ flex: 1;
84
+ padding: 32px;
85
+ display: flex;
86
+ flex-direction: column;
87
+ gap: 32px;
88
+ max-width: 1200px;
89
+ margin: 0 auto;
90
+ width: 100%;
91
+ }
92
+
93
+ .panel {
94
+ background: var(--panel-bg);
95
+ border: 1px solid var(--border);
96
+ border-radius: 24px;
97
+ padding: 32px;
98
+ backdrop-filter: blur(24px);
99
+ -webkit-backdrop-filter: blur(24px);
100
+ box-shadow: 0 20px 50px rgba(0, 0, 0, 0.4);
101
+ }
102
+ .panel-title {
103
+ font-size: 0.85rem;
104
+ font-weight: 600;
105
+ color: var(--text-muted);
106
+ margin-bottom: 24px;
107
+ text-transform: uppercase;
108
+ letter-spacing: 0.1em;
109
+ display: flex;
110
+ justify-content: space-between;
111
+ align-items: center;
112
+ }
113
+
114
+ /* --- VOICE SYSTEM UI --- */
115
+ .voice-hub {
116
+ display: flex;
117
+ flex-direction: column;
118
+ align-items: center;
119
+ justify-content: center;
120
+ gap: 40px;
121
+ text-align: center;
122
+ padding: 60px 0;
123
+ }
124
+
125
+ .orb-container {
126
+ position: relative;
127
+ width: 200px;
128
+ height: 200px;
129
+ display: flex;
130
+ align-items: center;
131
+ justify-content: center;
132
+ cursor: pointer;
133
+ transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
134
+ }
135
+ .orb-container:hover { transform: scale(1.05); }
136
+ .orb-container:active { transform: scale(0.95); }
137
+
138
+ .orb {
139
+ position: absolute;
140
+ width: 100px;
141
+ height: 100px;
142
+ border-radius: 50%;
143
+ background: var(--orb-idle);
144
+ box-shadow: 0 0 20px rgba(0,0,0,0.5);
145
+ transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
146
+ z-index: 2;
147
+ }
148
+ .orb.listening {
149
+ background: var(--orb-active);
150
+ box-shadow: 0 0 40px var(--accent-glow), 0 0 80px rgba(139, 92, 246, 0.2);
151
+ transform: scale(1.2);
152
+ }
153
+ .orb.speaking {
154
+ background: var(--orb-speaking);
155
+ box-shadow: 0 0 40px rgba(59, 130, 246, 0.4), 0 0 80px rgba(59, 130, 246, 0.2);
156
+ }
157
+
158
+ .orb-ring {
159
+ position: absolute;
160
+ border: 2px solid var(--border);
161
+ border-radius: 50%;
162
+ transition: all 0.5s ease;
163
+ z-index: 1;
164
+ }
165
+ .ring-1 { width: 140px; height: 140px; animation: rotate 10s linear infinite; }
166
+ .ring-2 { width: 180px; height: 180px; animation: rotate 15s linear infinite reverse; border-style: dashed; }
167
+
168
+ @keyframes rotate { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
169
+
170
+ .voice-status {
171
+ font-size: 1.5rem;
172
+ font-weight: 500;
173
+ color: var(--text);
174
+ min-height: 1.5em;
175
+ transition: opacity 0.3s;
176
+ }
177
+ .voice-transcript {
178
+ font-size: 1rem;
179
+ color: var(--text-muted);
180
+ max-width: 600px;
181
+ min-height: 1.5em;
182
+ font-style: italic;
183
+ }
184
+
185
+ .voice-controls {
186
+ display: flex;
187
+ gap: 16px;
188
+ justify-content: center;
189
+ }
190
+ .btn-voice {
191
+ background: rgba(255, 255, 255, 0.05);
192
+ border: 1px solid var(--border);
193
+ color: var(--text);
194
+ padding: 12px 24px;
195
+ border-radius: 999px;
196
+ cursor: pointer;
197
+ font-family: inherit;
198
+ font-size: 0.9rem;
199
+ transition: all 0.2s;
200
+ }
201
+ .btn-voice:hover { background: rgba(255, 255, 255, 0.1); border-color: rgba(255, 255, 255, 0.2); }
202
+ .btn-voice.active { background: var(--accent); border-color: var(--accent); color: white; }
203
+
204
+ /* --- Permissions Grid --- */
205
+ .permissions-grid {
206
+ display: grid;
207
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
208
+ gap: 16px;
209
+ }
210
+ .perm-btn {
211
+ background: rgba(255, 255, 255, 0.03);
212
+ border: 1px solid var(--border);
213
+ color: var(--text);
214
+ padding: 16px;
215
+ border-radius: 16px;
216
+ cursor: pointer;
217
+ display: flex;
218
+ flex-direction: column;
219
+ align-items: flex-start;
220
+ gap: 8px;
221
+ transition: all 0.2s ease;
222
+ font-family: inherit;
223
+ text-align: left;
224
+ }
225
+ .perm-btn:hover { background: rgba(255, 255, 255, 0.07); transform: translateY(-2px); }
226
+ .perm-icon { font-size: 1.25rem; margin-bottom: 4px; }
227
+ .perm-label { font-weight: 500; font-size: 0.9rem; }
228
+ .perm-desc { font-size: 0.8rem; color: var(--text-muted); }
229
+
230
+ /* --- Workspace --- */
231
+ #workspace-panel { margin-top: 32px; }
232
+ #canvas-container { display: flex; flex-direction: column; align-items: center; gap: 16px; width: 100%; }
233
+ canvas { background: #000; border: 1px solid var(--border); border-radius: 16px; max-width: 100%; box-shadow: 0 10px 40px rgba(0,0,0,0.5); }
234
+ #content-area { display: none; width: 100%; background: #fff; color: #000; border-radius: 16px; min-height: 600px; padding: 24px; overflow: auto; }
235
+ </style>
236
+ </head>
237
+ <body>
238
+ <div class="bg-glow"></div>
239
+ <div class="bg-glow-2"></div>
240
+
241
+ <header>
242
+ <div class="brand">
243
+ <span style="color: var(--accent);">✦</span> Weavetab <span style="font-weight: 300; opacity: 0.6; margin-left: 4px;">VoiceHub</span>
244
+ </div>
245
+ <div class="branch-badge" id="branch-display">branch: default</div>
246
+ </header>
247
+
248
+ <main>
249
+ <!-- Voice Interface Panel -->
250
+ <div class="panel">
251
+ <div class="panel-title">AI Voice Command System</div>
252
+ <div class="voice-hub">
253
+ <div class="orb-container" id="voice-trigger">
254
+ <div class="orb-ring ring-1"></div>
255
+ <div class="orb-ring ring-2"></div>
256
+ <div class="orb" id="voice-orb"></div>
257
+ </div>
258
+
259
+ <div class="voice-status" id="status-text">Ready to listen</div>
260
+ <div class="voice-transcript" id="transcript-text">"Click the orb or press Space to speak"</div>
261
+
262
+ <div class="voice-controls">
263
+ <button class="btn-voice" id="btn-listen">Start Listening</button>
264
+ <button class="btn-voice" id="btn-voice-settings">Voice Settings</button>
265
+ </div>
266
+ </div>
267
+ </div>
268
+
269
+ <!-- Permissions Panel -->
270
+ <div class="panel">
271
+ <div class="panel-title">System Permissions</div>
272
+ <div class="permissions-grid">
273
+ <button class="perm-btn" onclick="requestMic()">
274
+ <span class="perm-icon">🎤</span>
275
+ <span class="perm-label">Microphone</span>
276
+ <span class="perm-desc">Required for Realtime Voice</span>
277
+ </button>
278
+ <button class="perm-btn" onclick="requestCamera()">
279
+ <span class="perm-icon">📷</span>
280
+ <span class="perm-label">Camera</span>
281
+ <span class="perm-desc">Grant video stream access</span>
282
+ </button>
283
+ <button class="perm-btn" onclick="requestGeolocation()">
284
+ <span class="perm-icon">📍</span>
285
+ <span class="perm-label">Location</span>
286
+ <span class="perm-desc">Allow location services</span>
287
+ </button>
288
+ <button class="perm-btn" onclick="requestNotifications()">
289
+ <span class="perm-icon">🔔</span>
290
+ <span class="perm-label">Notifications</span>
291
+ <span class="perm-desc">Allow system alerts</span>
292
+ </button>
293
+ </div>
294
+ </div>
295
+
296
+ <!-- Workspace Panel -->
297
+ <div class="panel" id="workspace-panel">
298
+ <div class="panel-title">Workspace</div>
299
+ <div id="canvas-container">
300
+ <canvas id="wt-canvas" width="800" height="600"></canvas>
301
+ </div>
302
+ <div id="content-area"></div>
303
+ </div>
304
+ </main>
305
+
306
+ <script>
307
+ // --- Infrastructure Setup ---
308
+ const urlParams = new URLSearchParams(window.location.search);
309
+ const branch = urlParams.get('branch') || 'default';
310
+ document.getElementById('branch-display').textContent = `branch: ${branch}`;
311
+
312
+ // --- Voice System Implementation ---
313
+ class WeavetabVoiceSystem {
314
+ constructor() {
315
+ this.orb = document.getElementById('voice-orb');
316
+ this.status = document.getElementById('status-text');
317
+ this.transcript = document.getElementById('transcript-text');
318
+ this.btnListen = document.getElementById('btn-listen');
319
+ this.trigger = document.getElementById('voice-trigger');
320
+
321
+ this.isListening = false;
322
+ this.isSpeaking = false;
323
+
324
+ // Speech Recognition Setup
325
+ const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
326
+ if (!SpeechRecognition) {
327
+ this.status.textContent = "Speech API not supported";
328
+ this.status.style.color = "#ef4444";
329
+ return;
330
+ }
331
+
332
+ this.recognition = new SpeechRecognition();
333
+ this.recognition.continuous = false;
334
+ this.recognition.interimResults = true;
335
+ this.recognition.lang = 'en-US';
336
+
337
+ this.initEventListeners();
338
+ this.setupRecognition();
339
+ }
340
+
341
+ initEventListeners() {
342
+ this.btnListen.onclick = () => this.toggleListening();
343
+ this.trigger.onclick = () => this.toggleListening();
344
+
345
+ window.addEventListener('keydown', (e) => {
346
+ if (e.code === 'Space' && document.activeElement.tagName !== 'INPUT') {
347
+ e.preventDefault();
348
+ this.toggleListening();
349
+ }
350
+ });
351
+ }
352
+
353
+ setupRecognition() {
354
+ this.recognition.onstart = () => {
355
+ this.isListening = true;
356
+ this.updateUI('listening');
357
+ };
358
+
359
+ this.recognition.onresult = (event) => {
360
+ const result = event.results[0][0].transcript;
361
+ this.transcript.textContent = `"${result}"`;
362
+
363
+ if (event.results[0].isFinal) {
364
+ this.handleVoiceCommand(result);
365
+ }
366
+ };
367
+
368
+ this.recognition.onerror = (e) => {
369
+ console.error("Voice Error:", e);
370
+ this.stopListening();
371
+ this.status.textContent = "Error capturing voice";
372
+ };
373
+
374
+ this.recognition.onend = () => {
375
+ if (this.isListening) this.recognition.start(); // Keep alive if we intend to listen
376
+ else this.stopListening();
377
+ };
378
+ }
379
+
380
+ toggleListening() {
381
+ if (this.isListening) this.stopListening();
382
+ else this.startListening();
383
+ }
384
+
385
+ startListening() {
386
+ try {
387
+ this.recognition.start();
388
+ this.btnListen.classList.add('active');
389
+ } catch (e) {
390
+ console.error("Start Error:", e);
391
+ }
392
+ }
393
+
394
+ stopListening() {
395
+ this.isListening = false;
396
+ this.recognition.stop();
397
+ this.btnListen.classList.remove('active');
398
+ this.updateUI('idle');
399
+ }
400
+
401
+ updateUI(state) {
402
+ this.orb.className = 'orb';
403
+ if (state === 'listening') {
404
+ this.orb.classList.add('listening');
405
+ this.status.textContent = "I'm listening...";
406
+ } else if (state === 'speaking') {
407
+ this.orb.classList.add('speaking');
408
+ this.status.textContent = "Weavetab is speaking";
409
+ } else {
410
+ this.status.textContent = "Ready to listen";
411
+ }
412
+ }
413
+
414
+ async handleVoiceCommand(text) {
415
+ this.stopListening();
416
+ this.status.textContent = "Processing...";
417
+
418
+ // Simulate AI Thought
419
+ setTimeout(() => {
420
+ const response = this.generateAIResponse(text);
421
+ this.speak(response);
422
+ }, 600);
423
+ }
424
+
425
+ generateAIResponse(text) {
426
+ const t = text.toLowerCase();
427
+ if (t.includes('hello') || t.includes('hi')) return "Hello. I am the Weavetab Voice System. How can I assist your workflow today?";
428
+ if (t.includes('time')) return `The current system time is ${new Date().toLocaleTimeString()}.`;
429
+ if (t.includes('who are you')) return "I am your enterprise AI agent, integrated directly into the Weavetab core for real-time browser orchestration.";
430
+ if (t.includes('status')) return "All systems are operational. CDP bridge is active and session is stable.";
431
+
432
+ return `You said: ${text}. I am processing this request through the Weavetab neural bridge.`;
433
+ }
434
+
435
+ speak(text) {
436
+ this.isSpeaking = true;
437
+ this.updateUI('speaking');
438
+
439
+ const utterance = new SpeechSynthesisUtterance(text);
440
+
441
+ // Attempt to find a premium "Enterprise" voice
442
+ const voices = window.speechSynthesis.getVoices();
443
+ const premiumVoice = voices.find(v => v.name.includes('Google US English') || v.name.includes('Samantha') || v.name.includes('Microsoft Aria'));
444
+ if (premiumVoice) utterance.voice = premiumVoice;
445
+
446
+ utterance.pitch = 0.9; // Slightly deeper for "Enterprise" feel
447
+ utterance.rate = 1.0;
448
+
449
+ utterance.onend = () => {
450
+ this.isSpeaking = false;
451
+ this.updateUI('idle');
452
+ };
453
+
454
+ window.speechSynthesis.speak(utterance);
455
+ }
456
+ }
457
+
458
+ // Initialize Voice System
459
+ const voiceSystem = new WeavetabVoiceSystem();
460
+
461
+ // --- Go Home Function ---
462
+ const browserAPI = typeof browser !== 'undefined' ? browser : chrome;
463
+ function goHome() {
464
+ // Reset to default branch and clear any error state
465
+ const url = new URL(window.location.href);
466
+ url.searchParams.set('branch', 'default');
467
+ window.location.href = url.toString();
468
+ }
469
+
470
+ // --- Original Dashboard Logic ---
471
+ async function requestMic() {
472
+ try {
473
+ await navigator.mediaDevices.getUserMedia({ audio: true });
474
+ alert('Microphone permission granted!');
475
+ } catch (e) {
476
+ alert('Microphone permission failed: ' + e.message);
477
+ }
478
+ }
479
+ async function requestCamera() {
480
+ try {
481
+ const stream = await navigator.mediaDevices.getUserMedia({ video: true });
482
+ stream.getTracks().forEach(t => t.stop());
483
+ alert('Camera granted!');
484
+ } catch (e) { alert('Camera failed: ' + e.message); }
485
+ }
486
+ function requestGeolocation() {
487
+ navigator.geolocation.getCurrentPosition(() => alert('Location granted!'), (e) => alert(e.message));
488
+ }
489
+ async function requestNotifications() {
490
+ const p = await Notification.requestPermission();
491
+ alert('Notification: ' + p);
492
+ }
493
+
494
+ const canvas = document.getElementById('wt-canvas');
495
+ const ctx = canvas.getContext('2d');
496
+ const contentArea = document.getElementById('content-area');
497
+ const container = document.getElementById('canvas-container');
498
+
499
+ function drawPlaceholder() {
500
+ ctx.fillStyle = '#000';
501
+ ctx.fillRect(0, 0, 800, 600);
502
+ ctx.fillStyle = '#8b5cf6';
503
+ ctx.font = 'bold 36px system-ui, sans-serif';
504
+ ctx.textAlign = 'center';
505
+ ctx.fillText('Agent Canvas', 400, 260);
506
+ ctx.fillStyle = '#a1a1aa';
507
+ ctx.font = '18px system-ui, sans-serif';
508
+ ctx.fillText('Real-time Voice Active', 400, 310);
509
+ }
510
+
511
+ // Add error handling for browserAPI.storage access
512
+ if (typeof chrome === 'undefined' || !browserAPI.storage || !browserAPI.storage.local) {
513
+ console.error('Chrome storage API not available');
514
+ document.body.innerHTML = `
515
+ <div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100vh;background:#050506;color:#fff;font-family:sans-serif;">
516
+ <h2 style="color:#ef4444;margin-bottom:20px;">Extension Not Loaded</h2>
517
+ <p style="margin-bottom:20px;">The WeaveTab extension is not properly loaded.</p>
518
+ <div style="display:flex;gap:10px;">
519
+ <button onclick="location.reload()" style="padding:12px 24px;background:#8b5cf6;color:#fff;border:none;border-radius:8px;cursor:pointer;">Retry</button>
520
+ <button onclick="goHome()" style="padding:12px 24px;background:#3b82f6;color:#fff;border:none;border-radius:8px;cursor:pointer;">Go Home</button>
521
+ </div>
522
+ </div>
523
+ `;
524
+ drawPlaceholder();
525
+ } else {
526
+ browserAPI.storage.local.get([`branch_${branch}_html`, `branch_${branch}_canvas`], (result) => {
527
+ if (browserAPI.runtime.lastError) {
528
+ console.error('Chrome storage error:', browserAPI.runtime.lastError);
529
+ document.body.innerHTML = `
530
+ <div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100vh;background:#050506;color:#fff;font-family:sans-serif;">
531
+ <h2 style="color:#ef4444;margin-bottom:20px;">Storage Error</h2>
532
+ <p style="margin-bottom:20px;">${browserAPI.runtime.lastError.message}</p>
533
+ <div style="display:flex;gap:10px;">
534
+ <button onclick="location.reload()" style="padding:12px 24px;background:#8b5cf6;color:#fff;border:none;border-radius:8px;cursor:pointer;">Retry</button>
535
+ <button onclick="goHome()" style="padding:12px 24px;background:#3b82f6;color:#fff;border:none;border-radius:8px;cursor:pointer;">Go Home</button>
536
+ </div>
537
+ </div>
538
+ `;
539
+ } else {
540
+ if (result[`branch_${branch}_html`]) {
541
+ contentArea.textContent = result[`branch_${branch}_html`];
542
+ container.style.display = 'none';
543
+ contentArea.style.display = 'block';
544
+ } else {
545
+ drawPlaceholder();
546
+ }
547
+ }
548
+ });
549
+ }
550
+
551
+ // Add error handling for storage save interval
552
+ function safeStorageSave() {
553
+ if (typeof chrome === 'undefined' || !browserAPI.storage || !browserAPI.storage.local) {
554
+ return;
555
+ }
556
+ try {
557
+ if (contentArea.style.display === 'block') {
558
+ browserAPI.storage.local.set({ [`branch_${branch}_html`]: contentArea.innerHTML }).catch(e => {
559
+ console.error('Storage save error:', e);
560
+ });
561
+ } else {
562
+ browserAPI.storage.local.set({ [`branch_${branch}_canvas`]: canvas.toDataURL() }).catch(e => {
563
+ console.error('Storage save error:', e);
564
+ });
565
+ }
566
+ } catch (e) {
567
+ console.error('Storage save error:', e);
568
+ }
569
+ }
570
+
571
+ setInterval(safeStorageSave, 1000);
572
+
573
+ window.addEventListener('wt-dashboard-command', (e) => {
574
+ const { command, params } = e.detail || {};
575
+ if (command === 'show-canvas') { container.style.display = 'flex'; contentArea.style.display = 'none'; }
576
+ if (command === 'show-html') {
577
+ container.style.display = 'none'; contentArea.style.display = 'block';
578
+ if (params?.html) contentArea.innerHTML = params.html;
579
+ }
580
+ });
581
+ </script>
582
+ </body>
583
+ </html>