gazesight 0.3.5

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 (52) hide show
  1. package/LICENSE +37 -0
  2. package/README.md +102 -0
  3. package/dist/chrome/background-capture-offscreen.html +10 -0
  4. package/dist/chrome/background-capture-offscreen.js +1 -0
  5. package/dist/chrome/background.js +62 -0
  6. package/dist/chrome/content.js +62 -0
  7. package/dist/chrome/diagnostics-page.js +1 -0
  8. package/dist/chrome/fullpage-stitch-offscreen.html +10 -0
  9. package/dist/chrome/fullpage-stitch-offscreen.js +1 -0
  10. package/dist/chrome/icons/icon-128.png +0 -0
  11. package/dist/chrome/icons/icon-16.png +0 -0
  12. package/dist/chrome/icons/icon-32.png +0 -0
  13. package/dist/chrome/icons/icon-48.png +0 -0
  14. package/dist/chrome/icons/icon-64.png +0 -0
  15. package/dist/chrome/icons/icon-96.png +0 -0
  16. package/dist/chrome/logo_sans_background.png +0 -0
  17. package/dist/chrome/manifest.json +70 -0
  18. package/dist/chrome/options.css +66 -0
  19. package/dist/chrome/options.html +76 -0
  20. package/dist/chrome/options.js +82 -0
  21. package/dist/chrome/permission.css +13 -0
  22. package/dist/chrome/permission.html +15 -0
  23. package/dist/chrome/permission.js +1 -0
  24. package/dist/chrome/popup.css +446 -0
  25. package/dist/chrome/popup.html +191 -0
  26. package/dist/chrome/popup.js +1 -0
  27. package/dist/cli/index.js +320 -0
  28. package/dist/firefox/background.js +62 -0
  29. package/dist/firefox/content.js +62 -0
  30. package/dist/firefox/diagnostics-page.js +1 -0
  31. package/dist/firefox/icons/icon-128.png +0 -0
  32. package/dist/firefox/icons/icon-16.png +0 -0
  33. package/dist/firefox/icons/icon-32.png +0 -0
  34. package/dist/firefox/icons/icon-48.png +0 -0
  35. package/dist/firefox/icons/icon-64.png +0 -0
  36. package/dist/firefox/icons/icon-96.png +0 -0
  37. package/dist/firefox/logo_sans_background.png +0 -0
  38. package/dist/firefox/manifest.json +90 -0
  39. package/dist/firefox/options.css +66 -0
  40. package/dist/firefox/options.html +76 -0
  41. package/dist/firefox/options.js +82 -0
  42. package/dist/firefox/permission.css +13 -0
  43. package/dist/firefox/permission.html +15 -0
  44. package/dist/firefox/permission.js +1 -0
  45. package/dist/firefox/popup.css +446 -0
  46. package/dist/firefox/popup.html +191 -0
  47. package/dist/firefox/popup.js +1 -0
  48. package/dist/gazesight-firefox.xpi +0 -0
  49. package/dist/mcp/index.js +1428 -0
  50. package/dist/worker/visual-diff.js +642 -0
  51. package/docs/preview-matrix.csv +6 -0
  52. package/package.json +130 -0
@@ -0,0 +1,13 @@
1
+ :root { font-family: system-ui, sans-serif; color: #180a69; background: #f4f5ff; }
2
+ body { margin: 0; min-height: 100vh; display: grid; place-items: center; }
3
+ main { width: min(520px, calc(100vw - 48px)); box-sizing: border-box; padding: 38px; border: 1px solid #6848ca; border-radius: 20px; background: #fff; box-shadow: 0 14px 42px #2d15782e; }
4
+ img { width: 54px; height: 54px; object-fit: contain; }
5
+ .eyebrow { margin: 12px 0 4px; color: #645b93; text-transform: uppercase; letter-spacing: 1.2px; font-size: 12px; }
6
+ h1 { margin: 0 0 18px; font-size: 28px; }
7
+ p { line-height: 1.55; color: #4e4778; }
8
+ #status { min-height: 24px; color: #241084; font-weight: 600; }
9
+ .actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 22px; }
10
+ button { padding: 11px 16px; border: 1px solid #c8c6ee; border-radius: 8px; background: #fff; color: #241084; cursor: pointer; font: inherit; }
11
+ button:disabled { opacity: .55; cursor: default; }
12
+ button:focus-visible { outline: 3px solid #7653d6; outline-offset: 3px; }
13
+ .primary { border-color: #4d25b9; background: linear-gradient(110deg, #4d25b9, #4930b3); color: #fff; }
@@ -0,0 +1,15 @@
1
+ <!doctype html>
2
+ <html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>GazeSight permission</title><link rel="stylesheet" href="permission.css" /></head>
3
+ <body><main><img src="logo_sans_background.png" alt="" /><p class="eyebrow">GazeSight permission</p><h1 id="title">Allow screenshot download?</h1>
4
+ <div id="download-copy"><p>Firefox needs the Downloads permission to save this FAST screenshot into your configured Downloads folder.</p><p>GazeSight cannot choose an arbitrary filesystem path.</p></div>
5
+ <div id="background-copy" hidden>
6
+ <p>Firefox requires the optional “Access your data for all websites” permission for its background-tab screenshot API. This Firefox permission is broad; it is not technically limited to one tab.</p>
7
+ <p>GazeSight keeps Background Capture off by default and internally restricts every operation to the development tab and origin you explicitly selected. A different origin requires you to select the tab again.</p>
8
+ <p>Without this optional permission, foreground capture remains available. Disable Background Capture at any time to revoke it. GazeSight sends page content only to your local MCP connection, never to a GazeSight cloud service.</p>
9
+ </div>
10
+ <div id="continuous-copy" hidden>
11
+ <p>Firefox requires the same optional “Access your data for all websites” permission so GazeSight can stay connected to your selected tab across reloads, same-origin navigation and responsive previews, without asking you to click the icon again each time.</p>
12
+ <p>GazeSight still only acts on the one tab and origin you explicitly selected, plus GazeSight-created preview windows derived from it. Navigating to a different origin always requires selecting the tab again.</p>
13
+ <p>Disable Continuous Access at any time to revoke it. GazeSight sends page content only to your local MCP connection, never to a GazeSight cloud service.</p>
14
+ </div>
15
+ <p id="status" role="status" aria-live="polite"></p><div class="actions"><button id="cancel">Cancel</button><button id="allow" class="primary">Allow and continue</button></div></main><script type="module" src="permission.js"></script></body></html>
@@ -0,0 +1 @@
1
+ var c=new URLSearchParams(location.search),a=c.get("action"),e=a==="background"||a==="continuous"?a:"download",s=document.getElementById("allow"),i=document.getElementById("cancel"),n=document.getElementById("status");e==="background"&&(document.getElementById("title").textContent="Enable background capture?",document.getElementById("download-copy").hidden=!0,document.getElementById("background-copy").hidden=!1);e==="continuous"&&(document.getElementById("title").textContent="Enable continuous access?",document.getElementById("download-copy").hidden=!0,document.getElementById("continuous-copy").hidden=!1);i.addEventListener("click",()=>window.close());s.addEventListener("click",async()=>{s.disabled=!0,i.disabled=!0,n.textContent="Waiting for Firefox permission\u2026";try{if(!await browser.permissions.request(e==="background"||e==="continuous"?{origins:["<all_urls>"]}:{permissions:["downloads"]})){e==="background"&&await browser.runtime.sendMessage({type:"gazesight-ui",action:"background-denied"}),e==="continuous"&&await browser.runtime.sendMessage({type:"gazesight-ui",action:"continuous-denied"}),n.textContent="Permission was not granted.";return}let d=e==="background"?"background-enable":e==="continuous"?"continuous-enable":"fast",o=await browser.runtime.sendMessage({type:"gazesight-ui",action:d});if(!o?.ok)throw new Error(o?.error??"Operation failed.");n.textContent=e==="background"?"Background capture enabled.":e==="continuous"?"Continuous access enabled.":`\u2713 Saved to Downloads/${o.value.filename}`,setTimeout(()=>window.close(),1100);return}catch(t){n.textContent=t instanceof Error?t.message:"Operation failed."}finally{s.disabled=!1,i.disabled=!1}});
@@ -0,0 +1,446 @@
1
+ :root {
2
+ font-family: "Segoe UI", Arial, sans-serif;
3
+ color: #160653;
4
+ background: transparent;
5
+ color-scheme: light;
6
+ --muted: #625a94;
7
+ --line: #dddfff;
8
+ --card-radius: 20px;
9
+ }
10
+ * {
11
+ box-sizing: border-box;
12
+ }
13
+ body {
14
+ width: 420px;
15
+ margin: 0;
16
+ padding: 0;
17
+ background: transparent;
18
+ }
19
+ .dashboard {
20
+ border: 1px solid #6848ca;
21
+ border-radius: var(--card-radius);
22
+ background: #fff;
23
+ box-shadow: 0 10px 28px rgba(45, 21, 120, 0.22);
24
+ overflow: hidden;
25
+ }
26
+ header {
27
+ height: 81px;
28
+ display: flex;
29
+ align-items: center;
30
+ gap: 11px;
31
+ padding: 0 24px 10px;
32
+ color: #fff;
33
+ background: linear-gradient(120deg, #4723b0, #38188d);
34
+ box-shadow: inset 0 1px 1px #9f7de9;
35
+ }
36
+ .sight {
37
+ flex: none;
38
+ width: 38px;
39
+ height: 38px;
40
+ object-fit: contain;
41
+ transform: scale(1.18);
42
+ }
43
+ h1 {
44
+ font-size: 25px;
45
+ line-height: 1;
46
+ font-weight: 750;
47
+ letter-spacing: -0.9px;
48
+ margin: 0;
49
+ }
50
+ .connection-pill {
51
+ display: flex;
52
+ align-items: center;
53
+ gap: 6px;
54
+ margin-left: auto;
55
+ padding: 6px 9px;
56
+ border: 1px solid #a480ef40;
57
+ border-radius: 20px;
58
+ font-size: 12px;
59
+ white-space: nowrap;
60
+ background: #7351d01a;
61
+ }
62
+ .connection-dot {
63
+ width: 10px;
64
+ height: 10px;
65
+ border-radius: 50%;
66
+ background: #e52d3d;
67
+ flex: none;
68
+ }
69
+ body[data-connected="true"] .connection-dot {
70
+ background: #00c718;
71
+ }
72
+ .paper {
73
+ position: relative;
74
+ margin-top: -13px;
75
+ border-radius: var(--card-radius);
76
+ background: linear-gradient(115deg, #fff, #fcfcff);
77
+ }
78
+ main {
79
+ padding: 13px 14px 0;
80
+ }
81
+ .status {
82
+ display: grid;
83
+ grid-template-columns: 1fr 1fr;
84
+ gap: 10px;
85
+ margin: 0 0 10px;
86
+ }
87
+ .status-card {
88
+ min-width: 0;
89
+ min-height: 59px;
90
+ display: flex;
91
+ align-items: center;
92
+ gap: 10px;
93
+ padding: 10px;
94
+ border: 1px solid #e3e5ff;
95
+ border-radius: 8px;
96
+ background: #f5f6ffb3;
97
+ }
98
+ .status-card > div {
99
+ display: grid;
100
+ gap: 4px;
101
+ }
102
+ dt,
103
+ .caption {
104
+ color: var(--muted);
105
+ font-size: 9px;
106
+ font-weight: 600;
107
+ letter-spacing: 0.8px;
108
+ text-transform: uppercase;
109
+ }
110
+ dd {
111
+ margin: 0;
112
+ }
113
+ .status dd {
114
+ font-size: 15px;
115
+ font-weight: 700;
116
+ line-height: 1.2;
117
+ }
118
+ .background-capture {
119
+ margin-bottom: 10px;
120
+ padding: 10px 12px;
121
+ border: 1px solid #e3e5ff;
122
+ border-radius: 8px;
123
+ background: #f5f6ffb3;
124
+ }
125
+ .background-heading {
126
+ display: flex;
127
+ align-items: center;
128
+ justify-content: space-between;
129
+ gap: 12px;
130
+ }
131
+ .background-heading > div {
132
+ display: grid;
133
+ gap: 4px;
134
+ }
135
+ .background-heading strong {
136
+ display: flex;
137
+ align-items: center;
138
+ gap: 7px;
139
+ font-size: 14px;
140
+ }
141
+ #background-dot {
142
+ width: 9px;
143
+ height: 9px;
144
+ border: 2px solid #665e94;
145
+ border-radius: 50%;
146
+ }
147
+ body[data-background="enabled"] #background-dot {
148
+ border-color: #00c718;
149
+ background: #00c718;
150
+ }
151
+ #continuous-dot {
152
+ width: 9px;
153
+ height: 9px;
154
+ border: 2px solid #665e94;
155
+ border-radius: 50%;
156
+ }
157
+ body[data-continuous="enabled"] #continuous-dot {
158
+ border-color: #00c718;
159
+ background: #00c718;
160
+ }
161
+ #continuous-toggle {
162
+ min-width: 64px;
163
+ padding: 6px 10px;
164
+ color: #4723b0;
165
+ font-size: 11px;
166
+ font-weight: 700;
167
+ }
168
+ #continuous-description {
169
+ margin: 7px 0 0;
170
+ color: var(--muted);
171
+ font-size: 11px;
172
+ line-height: 1.45;
173
+ }
174
+ #background-toggle {
175
+ min-width: 64px;
176
+ padding: 6px 10px;
177
+ color: #4723b0;
178
+ font-size: 11px;
179
+ font-weight: 700;
180
+ }
181
+ #background-description {
182
+ margin: 7px 0 0;
183
+ color: var(--muted);
184
+ font-size: 11px;
185
+ line-height: 1.45;
186
+ }
187
+ dialog {
188
+ width: 360px;
189
+ padding: 0;
190
+ border: 1px solid #6848ca;
191
+ border-radius: 14px;
192
+ color: #160653;
193
+ box-shadow: 0 14px 40px #210c6250;
194
+ }
195
+ dialog::backdrop {
196
+ background: rgba(20, 6, 83, 0.42);
197
+ }
198
+ dialog form {
199
+ padding: 18px;
200
+ }
201
+ dialog h2 {
202
+ margin: 0 0 10px;
203
+ }
204
+ dialog p {
205
+ color: var(--muted);
206
+ font-size: 12px;
207
+ line-height: 1.5;
208
+ }
209
+ .consent-actions {
210
+ display: flex;
211
+ justify-content: flex-end;
212
+ gap: 8px;
213
+ margin-top: 14px;
214
+ }
215
+ .consent-actions button {
216
+ padding: 8px 10px;
217
+ }
218
+ #background-confirm {
219
+ background: #4723b0;
220
+ border-color: #4723b0;
221
+ color: white;
222
+ }
223
+ .icon {
224
+ width: 20px;
225
+ height: 20px;
226
+ fill: none;
227
+ stroke: currentColor;
228
+ stroke-width: 1.9;
229
+ stroke-linecap: round;
230
+ stroke-linejoin: round;
231
+ flex: none;
232
+ }
233
+ .filled {
234
+ fill: currentColor;
235
+ stroke: none;
236
+ }
237
+ .icon-disc {
238
+ display: flex;
239
+ align-items: center;
240
+ justify-content: center;
241
+ width: 34px;
242
+ height: 34px;
243
+ border-radius: 50%;
244
+ flex: none;
245
+ background: #ebedff;
246
+ }
247
+ .frame {
248
+ padding: 12px 12px 3px;
249
+ border: 1px solid #d7d9ff;
250
+ border-radius: 8px;
251
+ background: #f7f8ffb3;
252
+ }
253
+ .frame .caption {
254
+ display: block;
255
+ line-height: 12px;
256
+ }
257
+ .url-row {
258
+ display: flex;
259
+ align-items: center;
260
+ gap: 8px;
261
+ min-height: 30px;
262
+ margin-top: 2px;
263
+ }
264
+ #url {
265
+ flex: 1;
266
+ min-width: 0;
267
+ margin: 2px 0;
268
+ font-size: 16px;
269
+ font-weight: 600;
270
+ line-height: 1.3;
271
+ overflow-wrap: anywhere;
272
+ }
273
+ #copy-url {
274
+ width: 28px;
275
+ height: 28px;
276
+ padding: 5px;
277
+ border-radius: 6px;
278
+ }
279
+ #copy-url .icon {
280
+ width: 16px;
281
+ height: 16px;
282
+ }
283
+ #viewport {
284
+ margin: 2px 0 0;
285
+ padding-top: 6px;
286
+ border-top: 1px solid #e0e2fc;
287
+ font-size: 12px;
288
+ line-height: 1.5;
289
+ color: var(--muted);
290
+ }
291
+ button {
292
+ font: inherit;
293
+ cursor: pointer;
294
+ border: 1px solid #d6d8ff;
295
+ border-radius: 6px;
296
+ color: inherit;
297
+ background: #fff;
298
+ }
299
+ button:hover {
300
+ background: #f0edff;
301
+ border-color: #ab9adc;
302
+ }
303
+ button:focus-visible {
304
+ outline: 2px solid #7653d6;
305
+ outline-offset: 2px;
306
+ }
307
+ button:disabled {
308
+ cursor: default;
309
+ opacity: 0.55;
310
+ }
311
+ .primary {
312
+ display: flex;
313
+ align-items: center;
314
+ justify-content: center;
315
+ gap: 16px;
316
+ width: 100%;
317
+ height: 45px;
318
+ margin-top: 10px;
319
+ border-color: #6943cb;
320
+ background: linear-gradient(110deg, #4d25b9, #4930b3);
321
+ box-shadow:
322
+ inset 0 1px 1px #9677e0,
323
+ 0 4px 9px #4923af15;
324
+ color: #fff;
325
+ font-weight: 700;
326
+ font-size: 17px;
327
+ }
328
+ .primary .icon {
329
+ width: 24px;
330
+ height: 24px;
331
+ stroke-width: 2.5;
332
+ }
333
+ .primary:hover {
334
+ background: linear-gradient(110deg, #542ac6, #5135c3);
335
+ }
336
+ .hint {
337
+ margin: 9px 0 10px;
338
+ color: var(--muted);
339
+ font-size: 12px;
340
+ line-height: 1.45;
341
+ }
342
+ .capture {
343
+ display: grid;
344
+ grid-template-columns: 1fr 1fr;
345
+ gap: 9px;
346
+ }
347
+ .capture button {
348
+ display: flex;
349
+ align-items: center;
350
+ gap: 10px;
351
+ min-height: 58px;
352
+ padding: 9px;
353
+ text-align: left;
354
+ }
355
+ .capture .icon-disc {
356
+ width: 37px;
357
+ height: 37px;
358
+ }
359
+ .capture .icon {
360
+ width: 23px;
361
+ height: 23px;
362
+ }
363
+ .capture strong {
364
+ font-size: 13px;
365
+ letter-spacing: 0.3px;
366
+ }
367
+ .capture small {
368
+ display: block;
369
+ margin-top: 3px;
370
+ color: var(--muted);
371
+ font-size: 11px;
372
+ white-space: nowrap;
373
+ }
374
+ .history {
375
+ margin: 11px 0 0;
376
+ padding: 4px 0 6px;
377
+ border-top: 1px solid #e1e3fc;
378
+ border-bottom: 1px solid #e1e3fc;
379
+ }
380
+ .history > div {
381
+ display: grid;
382
+ grid-template-columns: 29px 88px minmax(0, 1fr);
383
+ gap: 10px;
384
+ align-items: center;
385
+ min-height: 28px;
386
+ }
387
+ .history .icon-disc {
388
+ width: 28px;
389
+ height: 28px;
390
+ }
391
+ .history .icon {
392
+ width: 15px;
393
+ height: 15px;
394
+ }
395
+ .history dd {
396
+ font-size: 12px;
397
+ overflow-wrap: anywhere;
398
+ line-height: 1.5;
399
+ }
400
+ #feedback {
401
+ font-size: 11px;
402
+ color: var(--muted);
403
+ margin: 8px 0 0;
404
+ }
405
+ #feedback:empty {
406
+ display: none;
407
+ }
408
+ footer {
409
+ display: grid;
410
+ grid-template-columns: 1.05fr 0.9fr 1.05fr;
411
+ gap: 8px;
412
+ padding: 11px 14px 15px;
413
+ }
414
+ footer button {
415
+ min-height: 33px;
416
+ display: flex;
417
+ align-items: center;
418
+ justify-content: center;
419
+ gap: 7px;
420
+ padding: 6px;
421
+ font-size: 11px;
422
+ white-space: nowrap;
423
+ }
424
+ footer .icon {
425
+ width: 16px;
426
+ height: 16px;
427
+ }
428
+ h2 {
429
+ font-size: 14px;
430
+ }
431
+ pre {
432
+ white-space: pre-wrap;
433
+ overflow-wrap: anywhere;
434
+ font:
435
+ 11px/1.5 Consolas,
436
+ monospace;
437
+ max-height: 180px;
438
+ overflow: auto;
439
+ }
440
+ @media (prefers-reduced-motion: no-preference) {
441
+ button {
442
+ transition:
443
+ background 0.12s,
444
+ border-color 0.12s;
445
+ }
446
+ }
@@ -0,0 +1,191 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>GazeSight</title>
7
+ <link rel="stylesheet" href="popup.css?v=9" />
8
+ </head>
9
+ <body>
10
+ <div class="dashboard">
11
+ <header>
12
+ <img class="sight" src="logo_sans_background.png" alt="" />
13
+ <h1>GazeSight</h1>
14
+ <span class="connection-pill"
15
+ ><span class="connection-dot" aria-hidden="true"></span
16
+ ><span id="connection">Disconnected</span></span
17
+ >
18
+ </header>
19
+ <div class="paper">
20
+ <main>
21
+ <dl class="status">
22
+ <div class="status-card">
23
+ <span class="icon-disc"
24
+ ><svg class="icon" viewBox="0 0 24 24" aria-hidden="true">
25
+ <path
26
+ d="M10 13a5 5 0 0 0 7.54 .54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71 M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"
27
+ /></svg
28
+ ></span>
29
+ <div>
30
+ <dt>MCP</dt>
31
+ <dd id="pairing">Not paired</dd>
32
+ </div>
33
+ </div>
34
+ <div class="status-card">
35
+ <span class="icon-disc"
36
+ ><svg class="icon" viewBox="0 0 24 24" aria-hidden="true">
37
+ <path
38
+ d="M12 2c3 2 6 3 9 3v7c0 5-5 8-9 10-4-2-9-5-9-10V5c3 0 6-1 9-3Z"
39
+ /></svg
40
+ ></span>
41
+ <div>
42
+ <dt>Target</dt>
43
+ <dd id="target">None</dd>
44
+ </div>
45
+ </div>
46
+ </dl>
47
+ <section
48
+ class="background-capture"
49
+ aria-labelledby="background-title"
50
+ >
51
+ <div class="background-heading">
52
+ <div>
53
+ <span class="caption" id="background-title"
54
+ >Background capture</span
55
+ >
56
+ <strong
57
+ ><span id="background-dot" aria-hidden="true"></span
58
+ ><span id="background-state">Off</span></strong
59
+ >
60
+ </div>
61
+ <button id="background-toggle" aria-pressed="false">Enable</button>
62
+ </div>
63
+ <p id="background-description">
64
+ Allow GazeSight to keep capturing the selected development tab
65
+ while you work in another tab or application.
66
+ </p>
67
+ </section>
68
+ <section
69
+ class="background-capture"
70
+ aria-labelledby="continuous-title"
71
+ >
72
+ <div class="background-heading">
73
+ <div>
74
+ <span class="caption" id="continuous-title"
75
+ >Continuous access</span
76
+ >
77
+ <strong
78
+ ><span id="continuous-dot" aria-hidden="true"></span
79
+ ><span id="continuous-state">Off</span></strong
80
+ >
81
+ </div>
82
+ <button id="continuous-toggle" aria-pressed="false">Enable</button>
83
+ </div>
84
+ <p id="continuous-description">
85
+ Keep GazeSight connected to this development tab across
86
+ reloads, navigation and responsive previews, so you don't have
87
+ to reauthorize it every time.
88
+ </p>
89
+ </section>
90
+ <section class="frame" aria-label="Selected target">
91
+ <span class="caption">Current URL</span>
92
+ <div class="url-row">
93
+ <p id="url">No target selected</p>
94
+ <button
95
+ id="copy-url"
96
+ aria-label="Copy current URL"
97
+ title="Copy current URL"
98
+ >
99
+ <svg class="icon" viewBox="0 0 24 24" aria-hidden="true">
100
+ <rect x="8" y="7" width="12" height="15" rx="1.5" />
101
+ <path d="M15 3H5a1 1 0 0 0-1 1v13h2" />
102
+ </svg>
103
+ </button>
104
+ </div>
105
+ <p id="viewport">Viewport unavailable</p>
106
+ </section>
107
+ <button id="select" class="primary">
108
+ <svg class="icon" viewBox="0 0 24 24" aria-hidden="true">
109
+ <path d="M2 12s4-7 10-7 10 7 10 7-4 7-10 7S2 12 2 12Z" />
110
+ <circle class="filled" cx="12" cy="12" r="3" /></svg
111
+ ><span id="select-label">Use this tab</span>
112
+ </button>
113
+ <p class="hint">
114
+ Select the page you want your agent to see.
115
+ <span id="shortcut">Ctrl+Shift+Y</span> also selects or reauthorizes
116
+ a tab.
117
+ </p>
118
+ <div class="capture">
119
+ <button id="fast">
120
+ <span class="icon-disc"
121
+ ><svg class="icon" viewBox="0 0 24 24" aria-hidden="true">
122
+ <path
123
+ class="filled"
124
+ d="m14 1-12 14h8l-1 8L22 9h-8Z"
125
+ /></svg></span
126
+ ><span
127
+ ><strong>FAST</strong><small>Immediate capture</small></span
128
+ ></button
129
+ ><button id="stable">
130
+ <span class="icon-disc"
131
+ ><svg class="icon" viewBox="0 0 24 24" aria-hidden="true">
132
+ <circle cx="12" cy="12" r="8" />
133
+ <circle class="filled" cx="12" cy="12" r="2.5" />
134
+ <path d="M12 1v3m0 16v3M1 12h3m16 0h3" /></svg></span
135
+ ><span
136
+ ><strong>STABLE</strong
137
+ ><small>Wait for visual quiet</small></span
138
+ >
139
+ </button>
140
+ </div>
141
+ <dl class="history">
142
+ <div>
143
+ <span class="icon-disc"
144
+ ><svg class="icon" viewBox="0 0 24 24" aria-hidden="true">
145
+ <circle cx="12" cy="12" r="9" />
146
+ <path d="M12 6v6l4 2" /></svg
147
+ ></span>
148
+ <dt>Last capture</dt>
149
+ <dd id="last-capture">None yet</dd>
150
+ </div>
151
+ <div>
152
+ <span class="icon-disc"
153
+ ><svg class="icon" viewBox="0 0 24 24" aria-hidden="true">
154
+ <path d="m12 3 10 18H2Z M12 9v5m0 3v.1" /></svg
155
+ ></span>
156
+ <dt>Last error</dt>
157
+ <dd id="last-error">None</dd>
158
+ </div>
159
+ </dl>
160
+ <p id="feedback" role="status" aria-live="polite"></p>
161
+ <section id="diagnostics-panel" hidden>
162
+ <h2>Diagnostics</h2>
163
+ <pre id="diagnostics-output"></pre>
164
+ </section>
165
+ </main>
166
+ <footer>
167
+ <button id="disconnect">
168
+ <svg class="icon" viewBox="0 0 24 24" aria-hidden="true">
169
+ <path
170
+ d="M10 13a5 5 0 0 0 7.54 .54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71 M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"
171
+ /></svg
172
+ ><span>Disconnect target</span></button
173
+ ><button id="settings">
174
+ <svg class="icon" viewBox="0 0 24 24" aria-hidden="true">
175
+ <path
176
+ d="m9 3 1-2h4l1 2 1 2 3-1 2 3-2 3v4l2 3-2 3-3-1-1 2-1 2h-4l-1-2-1-2-3 1-2-3 2-3v-4L3 7l2-3 3 1Z"
177
+ />
178
+ <circle cx="12" cy="12" r="3" /></svg
179
+ ><span>Open settings</span></button
180
+ ><button id="diagnostics">
181
+ <svg class="icon" viewBox="0 0 24 24" aria-hidden="true">
182
+ <path d="M6 2h9l4 4v16H6Z M14 2v5h5M10 11h5m-5 4h5m-5 4h5" /></svg
183
+ ><span>Open diagnostics</span>
184
+ </button>
185
+ </footer>
186
+ </div>
187
+ </div>
188
+ <script type="module" src="popup.js"></script>
189
+ </body>
190
+ </html>
191
+
@@ -0,0 +1 @@
1
+ function n(e,t){document.getElementById(e).textContent=t}var a=!1,s;function d(e,t=!1){clearTimeout(s),n("feedback",e),t&&(s=setTimeout(()=>n("feedback",""),3500))}async function o(e){let t=await browser.runtime.sendMessage({type:"gazesight-ui",action:e});if(!t?.ok)throw new Error(t?.error??"GazeSight unavailable. Open settings to check pairing.");return t.value}async function i(){let e=await o("status");document.body.dataset.connected=String(e.connected),n("connection",e.connected?"Connected":"Disconnected"),document.body.dataset.background=e.backgroundCapture.enabled?"enabled":"disabled",n("background-state",e.backgroundCapture.enabled?"On":"Off"),n("background-toggle",e.backgroundCapture.enabled?"Disable":"Enable"),document.getElementById("background-toggle").setAttribute("aria-pressed",String(e.backgroundCapture.enabled)),n("background-description",e.backgroundCapture.enabled?"GazeSight can capture this target while you work elsewhere.":e.backgroundCapture.permissionGranted?"Background mode is off. Firefox still reports the optional permission; remove it in Add-ons Manager if needed.":e.backgroundMode==="permission_denied"?"Permission was not granted. Standard foreground capture remains available.":"Allow GazeSight to keep capturing the selected development tab while you work in another tab or application."),document.body.dataset.continuous=e.continuousAuthorization.enabled?"enabled":"disabled",n("continuous-state",e.continuousAuthorization.enabled?"On":"Off"),n("continuous-toggle",e.continuousAuthorization.enabled?"Disable":"Enable"),document.getElementById("continuous-toggle").setAttribute("aria-pressed",String(e.continuousAuthorization.enabled)),n("continuous-description",e.continuousAuthorization.enabled?e.continuousAuthorization.authorizedOrigin?`Staying connected to ${e.continuousAuthorization.authorizedOrigin} across reloads, navigation and previews.`:"Keeps GazeSight connected across reloads, navigation and previews once you select a tab.":"Keep GazeSight connected to this development tab across reloads, navigation and responsive previews, so you don't have to reauthorize it every time."),n("pairing",e.paired?"Paired":"Not paired"),n("target",e.targetState==="authorized"?"Authorized":e.targetState==="none"?"None":"Reauthorize"),n("url",e.url??"No target selected"),n("viewport",e.viewport?`${e.viewport.width} \xD7 ${e.viewport.height} CSS \xB7 DPR ${e.viewport.devicePixelRatio}`:"Viewport unavailable"),n("select-label",e.targetState==="reauthorization_required"?"Reauthorize this tab":"Use this tab"),n("last-capture",e.lastCapture?`${e.lastCapture.mode} \xB7 ${e.lastCapture.width} \xD7 ${e.lastCapture.height} \xB7 ${new Date(e.lastCapture.time).toLocaleTimeString()}`:"None yet"),n("last-error",e.lastError?`${e.lastError.code}: ${e.lastError.message}`:"None");for(let t of["fast","stable"])document.getElementById(t).disabled=a||!e.connected||e.targetState!=="authorized";document.getElementById("disconnect").disabled=a||e.targetState==="none"}for(let e of["select","disconnect","fast","stable","diagnostics"])document.getElementById(e).addEventListener("click",async()=>{if(!a){a=!0,document.querySelectorAll("button").forEach(t=>t.disabled=!0),n("feedback",e==="fast"||e==="stable"?"Capturing\u2026":"Working\u2026");try{if(e==="fast"&&!await browser.permissions.contains({permissions:["downloads"]})){await browser.windows.create({url:browser.runtime.getURL("permission.html?action=download"),type:"popup",focused:!0,width:600,height:700}),window.close();return}let t=await o(e);e==="diagnostics"&&(document.getElementById("diagnostics-panel").hidden=!1,n("diagnostics-output",JSON.stringify(t,null,2))),d(e==="disconnect"?"Target disconnected.":e==="select"?"Target selected.":e==="diagnostics"?"Diagnostics updated.":e==="fast"?`\u2713 Saved to Downloads/${t.filename}`:"Capture completed.",e==="fast")}catch(t){d(t instanceof Error?t.message:"Operation failed.")}finally{a=!1,document.querySelectorAll("button").forEach(t=>t.disabled=!1),await i().catch(t=>n("feedback",String(t)))}}});document.getElementById("copy-url").addEventListener("click",async()=>{let e=document.getElementById("url").textContent;if(!(!e||e==="No target selected"))try{await navigator.clipboard.writeText(e),n("feedback","URL copied.")}catch{n("feedback","Unable to copy URL. Select and copy it manually.")}});document.getElementById("background-toggle").addEventListener("click",async()=>{if((await o("status")).backgroundCapture.enabled){await o("background-disable"),await i();return}await browser.windows.create({url:browser.runtime.getURL("permission.html?action=background"),type:"popup",focused:!0,width:600,height:700}),window.close()});document.getElementById("continuous-toggle").addEventListener("click",async()=>{let e=await o("status");if(e.continuousAuthorization.enabled){await o("continuous-disable"),await i();return}if(e.continuousAuthorization.permissionGranted){await o("continuous-enable"),await i();return}await browser.windows.create({url:browser.runtime.getURL("permission.html?action=continuous"),type:"popup",focused:!0,width:600,height:700}),window.close()});browser.commands.getAll().then(e=>{let t=e.find(r=>r.name==="gazesight-select-target")?.shortcut;t&&n("shortcut",t)}).catch(()=>{});document.getElementById("settings").addEventListener("click",()=>void browser.runtime.openOptionsPage());i().catch(e=>n("feedback",String(e)));var u=setInterval(()=>{a||i().catch(e=>n("feedback",String(e)))},2e3);window.addEventListener("pagehide",()=>clearInterval(u),{once:!0});
Binary file