teamshare-bridge 0.26.13 → 0.26.15

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 (48) hide show
  1. package/dist/cli/commands/research.js +10 -2
  2. package/dist/cli/commands/research.js.map +1 -1
  3. package/dist/cli/commands/run.js +0 -21
  4. package/dist/cli/commands/run.js.map +1 -1
  5. package/dist/lib/browser/batch.d.ts +77 -1
  6. package/dist/lib/browser/batch.js +151 -3
  7. package/dist/lib/browser/batch.js.map +1 -1
  8. package/dist/lib/browser/cdp.d.ts +61 -2
  9. package/dist/lib/browser/cdp.js +235 -14
  10. package/dist/lib/browser/cdp.js.map +1 -1
  11. package/dist/lib/browser/index.d.ts +2 -2
  12. package/dist/lib/browser/index.js +4 -1
  13. package/dist/lib/browser/index.js.map +1 -1
  14. package/dist/lib/browser/login.d.ts +20 -2
  15. package/dist/lib/browser/login.js +125 -88
  16. package/dist/lib/browser/login.js.map +1 -1
  17. package/dist/lib/browser/network-log.d.ts +31 -0
  18. package/dist/lib/browser/network-log.js +94 -12
  19. package/dist/lib/browser/network-log.js.map +1 -1
  20. package/dist/lib/browser/page-context.d.ts +62 -1
  21. package/dist/lib/browser/page-context.js +284 -1
  22. package/dist/lib/browser/page-context.js.map +1 -1
  23. package/dist/lib/browser/refs.d.ts +104 -0
  24. package/dist/lib/browser/refs.js +430 -0
  25. package/dist/lib/browser/refs.js.map +1 -0
  26. package/dist/lib/browser/session.d.ts +188 -7
  27. package/dist/lib/browser/session.js +537 -8
  28. package/dist/lib/browser/session.js.map +1 -1
  29. package/dist/lib/git/git-finalize.js +17 -2
  30. package/dist/lib/git/git-finalize.js.map +1 -1
  31. package/dist/lib/harness/harness-adapter.d.ts +2 -5
  32. package/dist/lib/harness/harness-adapter.js.map +1 -1
  33. package/dist/lib/harness/opencode-control.d.ts +73 -0
  34. package/dist/lib/harness/opencode-control.js +318 -0
  35. package/dist/lib/harness/opencode-control.js.map +1 -1
  36. package/dist/lib/harness/opencode-outcome.d.ts +16 -2
  37. package/dist/lib/harness/opencode-outcome.js +36 -7
  38. package/dist/lib/harness/opencode-outcome.js.map +1 -1
  39. package/dist/lib/harness/spawn.js +112 -48
  40. package/dist/lib/harness/spawn.js.map +1 -1
  41. package/dist/lib/orchestrator/handlers/browser.js +472 -27
  42. package/dist/lib/orchestrator/handlers/browser.js.map +1 -1
  43. package/dist/lib/tools/browser-tool.js +24 -3
  44. package/dist/lib/tools/browser-tool.js.map +1 -1
  45. package/dist/lib/workspace/repo-lock.d.ts +5 -1
  46. package/dist/lib/workspace/repo-lock.js +5 -3
  47. package/dist/lib/workspace/repo-lock.js.map +1 -1
  48. package/package.json +1 -1
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Element references for the debug/MCP browser (2026-10).
3
+ *
4
+ * WHY THIS EXISTS: the QA agent used to identify controls with raw CSS
5
+ * selectors, `getElements` indexes or x/y coordinates. On TeamShare's own
6
+ * shadcn/Radix UI that failed constantly: a dropdown, popover or kebab menu
7
+ * renders into a PORTAL at the end of `<body>`, the selector the model guessed
8
+ * matched nothing, and a coordinate read from a screenshot was device-scale.
9
+ * Most of a QA run was spent re-guessing selectors.
10
+ *
11
+ * A ref is a short opaque handle (`e1`, `e2`, ...) minted by ONE in-page scan:
12
+ *
13
+ * - the scanner walks every interactive element in the document, INCLUDING
14
+ * portalled menus/popovers/dialogs (they are still in the DOM, just not
15
+ * where the markup suggests), filters to what is actually visible, and
16
+ * records role/name/coords plus a direct reference to the live node in
17
+ * `window.__tsRefs.nodes`;
18
+ * - actions resolve a ref to the live node first and its centre point second,
19
+ * so an action is never sent to a coordinate that a re-render invalidated;
20
+ * - the live node is checked with `isConnected` before every use. A dead ref
21
+ * is refused with `REF_STALE` AND, when a unique same-role/same-name match
22
+ * exists in the fresh scan, transparently re-pointed at it.
23
+ *
24
+ * A ref list is invalidated by navigating or by a re-render, exactly like a
25
+ * Playwright locator; the session keeps the last list so it can explain what
26
+ * happened instead of failing mute.
27
+ *
28
+ * The scan deliberately runs in the page (one `Runtime.evaluate`) instead of
29
+ * joining `Accessibility.getFullAXTree` with `DOMSnapshot.captureSnapshot`:
30
+ * the DOM scan gives portal content AND geometry in one round trip, and it is
31
+ * the same document the actions operate on. Roles/names are computed from the
32
+ * explicit ARIA attributes first, then the implicit HTML semantics.
33
+ */
34
+ import { type CdpPage } from './cdp';
35
+ /** A JSON-safe description of one interactive element. */
36
+ export interface RefElement {
37
+ ref: string;
38
+ tag: string;
39
+ role: string;
40
+ name: string;
41
+ disabled: boolean;
42
+ inDialog: boolean;
43
+ inPortal: boolean;
44
+ /** Length only - a field VALUE must never travel through a tool result. */
45
+ valueLength?: number;
46
+ x: number;
47
+ y: number;
48
+ width: number;
49
+ height: number;
50
+ }
51
+ /** Hard cap on refs per scan: keeps the tool result readable and bounded. */
52
+ export declare const MAX_REFS = 300;
53
+ /**
54
+ * The one scan. Returns `{ at, list }` and stores the live nodes on
55
+ * `window.__tsRefs.nodes` for later `isConnected` checks / focus calls.
56
+ */
57
+ export declare function buildRefScanScript(): string;
58
+ /**
59
+ * Human/LLM-readable ref lines. One line per element:
60
+ * `e7 button "Publish draft" (640,412) [in menu]`
61
+ */
62
+ export declare function formatRefs(list: RefElement[], maxLines?: number): string[];
63
+ /** Run a fresh scan and return it (the caller keeps the list). */
64
+ export declare function scanRefs(page: CdpPage): Promise<{
65
+ at: number;
66
+ list: RefElement[];
67
+ }>;
68
+ /** Liveness + geometry of one ref, from the node the scan stored. */
69
+ export declare function buildRefInfoScript(ref: string): string;
70
+ /**
71
+ * Focus a ref'd element, scrolling it to the middle first. Returns whether the
72
+ * element actually took focus plus its tag/role, so `focus` is never a silent
73
+ * no-op: a div without `tabindex` cannot be focused and says so.
74
+ */
75
+ export declare function buildRefFocusScript(ref: string): string;
76
+ /**
77
+ * Dispatch the FULL pointer sequence on a ref'd element, in-page.
78
+ *
79
+ * This is the fallback for controls that ignore a coordinate click: the
80
+ * frontend tours (`frontend/src/modules/tour/utils/acts.ts`) taught us that a
81
+ * bare `.click()` is a SILENT no-op on Radix tabs/menus, which activate on
82
+ * `pointerdown`/`mousedown`. Same recipe, one copy per side of the wire.
83
+ */
84
+ export declare function buildRefPointerScript(ref: string): string;
85
+ /**
86
+ * Draw ref labels over the live page (2026-10).
87
+ *
88
+ * A plain screenshot shows WHAT is on screen; the model still has to guess
89
+ * which button is `e7`. The overlay draws a numbered box on every ref'd
90
+ * element so ONE image both shows the page and names the targets. Removed
91
+ * immediately after the capture - the page is never left modified.
92
+ */
93
+ export declare function buildAnnotateScript(): string;
94
+ /** Remove the ref overlay (always called, even when the capture failed). */
95
+ export declare function buildClearAnnotationScript(): string;
96
+ /** How many popovers/menus are currently open (used by closePopup). */
97
+ export declare function buildPopoverCountScript(): string;
98
+ /**
99
+ * Click OUTSIDE every open layer (the Radix DismissableLayer listens for a
100
+ * document-level `pointerdown`). Coordinate clicks can land on something
101
+ * dangerous in a corner, so this dispatches the pointer sequence on `<body>`
102
+ * at the layer's own outside point instead.
103
+ */
104
+ export declare function buildOutsidePointerScript(): string;
@@ -0,0 +1,430 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MAX_REFS = void 0;
4
+ exports.buildRefScanScript = buildRefScanScript;
5
+ exports.formatRefs = formatRefs;
6
+ exports.scanRefs = scanRefs;
7
+ exports.buildRefInfoScript = buildRefInfoScript;
8
+ exports.buildRefFocusScript = buildRefFocusScript;
9
+ exports.buildRefPointerScript = buildRefPointerScript;
10
+ exports.buildAnnotateScript = buildAnnotateScript;
11
+ exports.buildClearAnnotationScript = buildClearAnnotationScript;
12
+ exports.buildPopoverCountScript = buildPopoverCountScript;
13
+ exports.buildOutsidePointerScript = buildOutsidePointerScript;
14
+ /**
15
+ * Element references for the debug/MCP browser (2026-10).
16
+ *
17
+ * WHY THIS EXISTS: the QA agent used to identify controls with raw CSS
18
+ * selectors, `getElements` indexes or x/y coordinates. On TeamShare's own
19
+ * shadcn/Radix UI that failed constantly: a dropdown, popover or kebab menu
20
+ * renders into a PORTAL at the end of `<body>`, the selector the model guessed
21
+ * matched nothing, and a coordinate read from a screenshot was device-scale.
22
+ * Most of a QA run was spent re-guessing selectors.
23
+ *
24
+ * A ref is a short opaque handle (`e1`, `e2`, ...) minted by ONE in-page scan:
25
+ *
26
+ * - the scanner walks every interactive element in the document, INCLUDING
27
+ * portalled menus/popovers/dialogs (they are still in the DOM, just not
28
+ * where the markup suggests), filters to what is actually visible, and
29
+ * records role/name/coords plus a direct reference to the live node in
30
+ * `window.__tsRefs.nodes`;
31
+ * - actions resolve a ref to the live node first and its centre point second,
32
+ * so an action is never sent to a coordinate that a re-render invalidated;
33
+ * - the live node is checked with `isConnected` before every use. A dead ref
34
+ * is refused with `REF_STALE` AND, when a unique same-role/same-name match
35
+ * exists in the fresh scan, transparently re-pointed at it.
36
+ *
37
+ * A ref list is invalidated by navigating or by a re-render, exactly like a
38
+ * Playwright locator; the session keeps the last list so it can explain what
39
+ * happened instead of failing mute.
40
+ *
41
+ * The scan deliberately runs in the page (one `Runtime.evaluate`) instead of
42
+ * joining `Accessibility.getFullAXTree` with `DOMSnapshot.captureSnapshot`:
43
+ * the DOM scan gives portal content AND geometry in one round trip, and it is
44
+ * the same document the actions operate on. Roles/names are computed from the
45
+ * explicit ARIA attributes first, then the implicit HTML semantics.
46
+ */
47
+ const cdp_1 = require("./cdp");
48
+ /** Hard cap on refs per scan: keeps the tool result readable and bounded. */
49
+ exports.MAX_REFS = 300;
50
+ /** Elements that can be interacted with (and are useful to name). */
51
+ const REF_SELECTOR = [
52
+ 'a[href]',
53
+ 'button',
54
+ 'input:not([type="hidden"])',
55
+ 'select',
56
+ 'textarea',
57
+ 'summary',
58
+ '[role="button"]',
59
+ '[role="link"]',
60
+ '[role="menuitem"]',
61
+ '[role="menuitemcheckbox"]',
62
+ '[role="menuitemradio"]',
63
+ '[role="option"]',
64
+ '[role="tab"]',
65
+ '[role="checkbox"]',
66
+ '[role="radio"]',
67
+ '[role="switch"]',
68
+ '[role="combobox"]',
69
+ '[role="textbox"]',
70
+ '[contenteditable="true"]',
71
+ '[tabindex]:not([tabindex="-1"])',
72
+ ].join(',');
73
+ /**
74
+ * The one scan. Returns `{ at, list }` and stores the live nodes on
75
+ * `window.__tsRefs.nodes` for later `isConnected` checks / focus calls.
76
+ */
77
+ function buildRefScanScript() {
78
+ return `
79
+ (() => {
80
+ const MAX = ${exports.MAX_REFS};
81
+ const SEL = ${JSON.stringify(REF_SELECTOR)};
82
+ const PORTAL_SEL = '[data-radix-popper-content-wrapper],[data-radix-portal],[data-floating-ui-portal]';
83
+ const DIALOG_SEL = 'dialog[open],[role="dialog"],[aria-modal="true"]';
84
+ const vis = (el) => {
85
+ if (!el || el.nodeType !== 1) return false;
86
+ const r = el.getBoundingClientRect();
87
+ if (r.width === 0 || r.height === 0) return false;
88
+ const cs = getComputedStyle(el);
89
+ if (cs.visibility === 'hidden' || cs.display === 'none' || cs.opacity === '0') return false;
90
+ if (cs.pointerEvents === 'none') return false;
91
+ return true;
92
+ };
93
+ const clean = (v, cap) => String(v == null ? '' : v)
94
+ .replace(/\\s+/g, ' ')
95
+ .trim()
96
+ .slice(0, cap || 80);
97
+ const nameOf = (el) => {
98
+ const aria = el.getAttribute('aria-label');
99
+ if (aria) return clean(aria, 80);
100
+ const by = el.getAttribute('aria-labelledby');
101
+ if (by) {
102
+ const t = by.split(/\\s+/).map((id) => {
103
+ const n = document.getElementById(id);
104
+ return n ? (n.innerText || n.textContent || '') : '';
105
+ }).join(' ');
106
+ if (t.trim()) return clean(t, 80);
107
+ }
108
+ const tag = String(el.tagName || '').toLowerCase();
109
+ if (tag === 'input' || tag === 'textarea' || tag === 'select') {
110
+ const id = el.id;
111
+ if (id) {
112
+ try {
113
+ const l = document.querySelector('label[for="' + CSS.escape(id) + '"]');
114
+ if (l) return clean(l.innerText || l.textContent, 80);
115
+ } catch (e) {}
116
+ }
117
+ const wrap = el.closest && el.closest('label');
118
+ if (wrap) return clean(wrap.innerText || wrap.textContent, 80);
119
+ const ph = el.getAttribute('placeholder');
120
+ if (ph) return clean(ph, 80);
121
+ const nm = el.getAttribute('name');
122
+ if (nm) return clean(nm, 80);
123
+ if (el.id) return clean(el.id, 80);
124
+ }
125
+ const txt = el.innerText || el.textContent || '';
126
+ if (txt.trim()) return clean(txt, 80);
127
+ return clean(
128
+ el.getAttribute('title') ||
129
+ el.getAttribute('alt') ||
130
+ el.getAttribute('value') ||
131
+ '',
132
+ 80,
133
+ );
134
+ };
135
+ const roleOf = (el) => {
136
+ const explicit = el.getAttribute('role');
137
+ if (explicit) return clean(explicit, 24);
138
+ const tag = String(el.tagName || '').toLowerCase();
139
+ const type = String(el.type || '').toLowerCase();
140
+ if (tag === 'a') return 'link';
141
+ if (tag === 'button' || tag === 'summary') return 'button';
142
+ if (tag === 'select') return 'combobox';
143
+ if (tag === 'textarea') return 'textbox';
144
+ if (tag === 'input') {
145
+ if (type === 'submit' || type === 'button' || type === 'reset') return 'button';
146
+ if (type === 'checkbox') return 'checkbox';
147
+ if (type === 'radio') return 'radio';
148
+ if (type === 'search') return 'searchbox';
149
+ return 'textbox';
150
+ }
151
+ if (el.isContentEditable === true) return 'textbox';
152
+ return tag || 'element';
153
+ };
154
+ const seen = new Set();
155
+ const nodes = {};
156
+ const list = [];
157
+ const all = [...document.querySelectorAll(SEL)];
158
+ for (const el of all) {
159
+ if (list.length >= MAX) break;
160
+ if (seen.has(el)) continue;
161
+ seen.add(el);
162
+ // Skip an element wholly covered by another candidate we already took
163
+ // only when it IS that element - nested buttons are real (icon spans
164
+ // are not in the selector, so this is only a dedupe guard).
165
+ if (!vis(el)) continue;
166
+ const r = el.getBoundingClientRect();
167
+ const ref = 'e' + (list.length + 1);
168
+ const row = {
169
+ ref,
170
+ tag: clean(String(el.tagName || '').toLowerCase(), 20) || 'element',
171
+ role: roleOf(el),
172
+ name: nameOf(el),
173
+ disabled:
174
+ el.disabled === true || el.getAttribute('aria-disabled') === 'true',
175
+ inDialog: !!(el.closest && el.closest(DIALOG_SEL)),
176
+ inPortal: !!(el.closest && el.closest(PORTAL_SEL)),
177
+ x: Math.round(r.x + r.width / 2),
178
+ y: Math.round(r.y + r.height / 2),
179
+ width: Math.round(r.width),
180
+ height: Math.round(r.height),
181
+ };
182
+ if (
183
+ (row.tag === 'input' || row.tag === 'textarea') &&
184
+ typeof el.value === 'string'
185
+ ) {
186
+ row.valueLength = el.value.length;
187
+ }
188
+ nodes[ref] = el;
189
+ list.push(row);
190
+ }
191
+ window.__tsRefs = { at: Date.now(), list, nodes };
192
+ return { at: window.__tsRefs.at, list };
193
+ })()
194
+ `;
195
+ }
196
+ /**
197
+ * Human/LLM-readable ref lines. One line per element:
198
+ * `e7 button "Publish draft" (640,412) [in menu]`
199
+ */
200
+ function formatRefs(list, maxLines = 300) {
201
+ return list.slice(0, maxLines).map((r) => {
202
+ const parts = [r.ref, r.role];
203
+ if (r.name)
204
+ parts.push(JSON.stringify(r.name.slice(0, 80)));
205
+ if (r.valueLength !== undefined)
206
+ parts.push(`${r.valueLength} chars`);
207
+ parts.push(`(${r.x},${r.y})`);
208
+ if (r.disabled)
209
+ parts.push('[disabled]');
210
+ if (r.inDialog)
211
+ parts.push('[in dialog]');
212
+ if (r.inPortal)
213
+ parts.push('[in menu/popover]');
214
+ return parts.join(' ');
215
+ });
216
+ }
217
+ /** Run a fresh scan and return it (the caller keeps the list). */
218
+ async function scanRefs(page) {
219
+ const raw = await (0, cdp_1.evaluateJs)(page, buildRefScanScript());
220
+ if (!raw || typeof raw !== 'object')
221
+ return { at: 0, list: [] };
222
+ const rec = raw;
223
+ return {
224
+ at: typeof rec.at === 'number' ? rec.at : 0,
225
+ list: Array.isArray(rec.list) ? rec.list : [],
226
+ };
227
+ }
228
+ /** Liveness + geometry of one ref, from the node the scan stored. */
229
+ function buildRefInfoScript(ref) {
230
+ const id = JSON.stringify(ref);
231
+ return `(() => {
232
+ const s = window.__tsRefs;
233
+ const el = s && s.nodes ? s.nodes[${id}] : null;
234
+ if (!el || el.isConnected !== true) return { ref: ${id}, alive: false };
235
+ const r = el.getBoundingClientRect();
236
+ return {
237
+ ref: ${id},
238
+ alive: true,
239
+ x: Math.round(r.x + r.width / 2),
240
+ y: Math.round(r.y + r.height / 2),
241
+ width: Math.round(r.width),
242
+ height: Math.round(r.height),
243
+ inViewport:
244
+ r.top >= 0 && r.left >= 0 &&
245
+ r.bottom <= (window.innerHeight || 0) && r.right <= (window.innerWidth || 0),
246
+ };
247
+ })()`;
248
+ }
249
+ /**
250
+ * Focus a ref'd element, scrolling it to the middle first. Returns whether the
251
+ * element actually took focus plus its tag/role, so `focus` is never a silent
252
+ * no-op: a div without `tabindex` cannot be focused and says so.
253
+ */
254
+ function buildRefFocusScript(ref) {
255
+ const id = JSON.stringify(ref);
256
+ return `(() => {
257
+ const s = window.__tsRefs;
258
+ const el = s && s.nodes ? s.nodes[${id}] : null;
259
+ if (!el || el.isConnected !== true) return { ref: ${id}, ok: false, alive: false };
260
+ try { el.scrollIntoView({ block: 'center', inline: 'center' }); } catch (e) {}
261
+ try { el.focus({ preventScroll: true }); } catch (e) {}
262
+ const a = document.activeElement;
263
+ return {
264
+ ref: ${id},
265
+ ok: a === el,
266
+ alive: true,
267
+ focused: a === el,
268
+ tag: String((el.tagName || '')).toLowerCase(),
269
+ contentEditable: el.isContentEditable === true,
270
+ valueLength: typeof el.value === 'string' ? el.value.length : undefined,
271
+ };
272
+ })()`;
273
+ }
274
+ /**
275
+ * Dispatch the FULL pointer sequence on a ref'd element, in-page.
276
+ *
277
+ * This is the fallback for controls that ignore a coordinate click: the
278
+ * frontend tours (`frontend/src/modules/tour/utils/acts.ts`) taught us that a
279
+ * bare `.click()` is a SILENT no-op on Radix tabs/menus, which activate on
280
+ * `pointerdown`/`mousedown`. Same recipe, one copy per side of the wire.
281
+ */
282
+ function buildRefPointerScript(ref) {
283
+ const id = JSON.stringify(ref);
284
+ return `(() => {
285
+ const s = window.__tsRefs;
286
+ const el = s && s.nodes ? s.nodes[${id}] : null;
287
+ if (!el || el.isConnected !== true) return { ref: ${id}, ok: false, alive: false };
288
+ try { el.scrollIntoView({ block: 'center', inline: 'center' }); } catch (e) {}
289
+ const rect = el.getBoundingClientRect();
290
+ const x = Math.round(rect.x + rect.width / 2);
291
+ const y = Math.round(rect.y + rect.height / 2);
292
+ const base = {
293
+ bubbles: true,
294
+ cancelable: true,
295
+ composed: true,
296
+ clientX: x,
297
+ clientY: y,
298
+ view: window,
299
+ button: 0,
300
+ buttons: 1,
301
+ };
302
+ const pointer = (type) => {
303
+ try {
304
+ return new PointerEvent(type, Object.assign({}, base, {
305
+ pointerId: 1,
306
+ pointerType: 'mouse',
307
+ isPrimary: true,
308
+ }));
309
+ } catch (e) {
310
+ return new MouseEvent(type, base);
311
+ }
312
+ };
313
+ const mouse = (type) => new MouseEvent(type, base);
314
+ el.dispatchEvent(pointer('pointerover'));
315
+ el.dispatchEvent(pointer('pointerenter'));
316
+ el.dispatchEvent(pointer('pointermove'));
317
+ el.dispatchEvent(pointer('pointerdown'));
318
+ el.dispatchEvent(mouse('mousedown'));
319
+ try { el.focus({ preventScroll: true }); } catch (e) {}
320
+ el.dispatchEvent(pointer('pointerup'));
321
+ el.dispatchEvent(mouse('mouseup'));
322
+ el.dispatchEvent(mouse('click'));
323
+ return { ref: ${id}, ok: true, alive: true, clicked: true, x, y };
324
+ })()`;
325
+ }
326
+ /**
327
+ * Draw ref labels over the live page (2026-10).
328
+ *
329
+ * A plain screenshot shows WHAT is on screen; the model still has to guess
330
+ * which button is `e7`. The overlay draws a numbered box on every ref'd
331
+ * element so ONE image both shows the page and names the targets. Removed
332
+ * immediately after the capture - the page is never left modified.
333
+ */
334
+ function buildAnnotateScript() {
335
+ return `(() => {
336
+ const s = window.__tsRefs;
337
+ if (!s || !s.list) return 0;
338
+ const ID = 'ts-ref-overlay';
339
+ const old = document.getElementById(ID);
340
+ if (old) old.remove();
341
+ const root = document.createElement('div');
342
+ root.id = ID;
343
+ root.style.cssText = 'position:fixed;left:0;top:0;width:0;height:0;z-index:2147483647;pointer-events:none;';
344
+ let n = 0;
345
+ for (const item of s.list) {
346
+ const el = s.nodes ? s.nodes[item.ref] : null;
347
+ if (!el || el.isConnected !== true) continue;
348
+ const r = el.getBoundingClientRect();
349
+ if (r.width === 0 || r.height === 0) continue;
350
+ const box = document.createElement('div');
351
+ box.style.cssText =
352
+ 'position:fixed;border:2px solid #e11d48;border-radius:3px;box-sizing:border-box;pointer-events:none;left:' +
353
+ Math.round(r.left) + 'px;top:' + Math.round(r.top) + 'px;width:' + Math.round(r.width) +
354
+ 'px;height:' + Math.round(r.height) + 'px;';
355
+ const tag = document.createElement('div');
356
+ tag.textContent = item.ref;
357
+ tag.style.cssText =
358
+ 'position:fixed;background:#e11d48;color:#ffffff;font:600 11px/1.2 sans-serif;padding:1px 3px;border-radius:2px;pointer-events:none;left:' +
359
+ Math.round(r.left) + 'px;top:' + Math.max(0, Math.round(r.top) - 14) + 'px;';
360
+ root.appendChild(box);
361
+ root.appendChild(tag);
362
+ if (++n >= 120) break;
363
+ }
364
+ document.documentElement.appendChild(root);
365
+ return n;
366
+ })()`;
367
+ }
368
+ /** Remove the ref overlay (always called, even when the capture failed). */
369
+ function buildClearAnnotationScript() {
370
+ return `(() => {
371
+ const el = document.getElementById('ts-ref-overlay');
372
+ if (el) el.remove();
373
+ return true;
374
+ })()`;
375
+ }
376
+ /** How many popovers/menus are currently open (used by closePopup). */
377
+ function buildPopoverCountScript() {
378
+ return `(() => {
379
+ const vis = (el) => {
380
+ if (!el || el.nodeType !== 1) return false;
381
+ const r = el.getBoundingClientRect();
382
+ if (r.width === 0 || r.height === 0) return false;
383
+ const cs = getComputedStyle(el);
384
+ return cs.visibility !== 'hidden' && cs.display !== 'none' && cs.opacity !== '0';
385
+ };
386
+ const n = [...document.querySelectorAll('[data-radix-popper-content-wrapper],[role="menu"],[role="listbox"]')]
387
+ .filter(vis).length;
388
+ return n;
389
+ })()`;
390
+ }
391
+ /**
392
+ * Click OUTSIDE every open layer (the Radix DismissableLayer listens for a
393
+ * document-level `pointerdown`). Coordinate clicks can land on something
394
+ * dangerous in a corner, so this dispatches the pointer sequence on `<body>`
395
+ * at the layer's own outside point instead.
396
+ */
397
+ function buildOutsidePointerScript() {
398
+ return `(() => {
399
+ const target = document.body;
400
+ if (!target) return false;
401
+ const base = {
402
+ bubbles: true,
403
+ cancelable: true,
404
+ composed: true,
405
+ clientX: 4,
406
+ clientY: 4,
407
+ view: window,
408
+ button: 0,
409
+ buttons: 1,
410
+ };
411
+ const pointer = (type) => {
412
+ try {
413
+ return new PointerEvent(type, Object.assign({}, base, {
414
+ pointerId: 1,
415
+ pointerType: 'mouse',
416
+ isPrimary: true,
417
+ }));
418
+ } catch (e) {
419
+ return new MouseEvent(type, base);
420
+ }
421
+ };
422
+ target.dispatchEvent(pointer('pointerdown'));
423
+ target.dispatchEvent(new MouseEvent('mousedown', base));
424
+ target.dispatchEvent(pointer('pointerup'));
425
+ target.dispatchEvent(new MouseEvent('mouseup', base));
426
+ target.dispatchEvent(new MouseEvent('click', base));
427
+ return true;
428
+ })()`;
429
+ }
430
+ //# sourceMappingURL=refs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refs.js","sourceRoot":"","sources":["../../../src/lib/browser/refs.ts"],"names":[],"mappings":"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"}