@tangle-network/browser-agent-driver 0.25.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -240,6 +240,56 @@ export const CURSOR_OVERLAY_INIT_SCRIPT = `
240
240
  overflow: 'hidden',
241
241
  });
242
242
 
243
+ // ── Gen 34 Hydra View — fan-out grid (live thumbnails of sub-tabs) ─────
244
+ // Full-viewport dim overlay + grid of live sub-tab screenshot cells.
245
+ // Cells lay out based on count: 1→1, 2→1x2, 3-4→2x2, 5-6→2x3, 7-8→2x4.
246
+ // Each cell background updates via updateFanOutCell(i, dataUrl, meta).
247
+ const hydra = document.createElement('div');
248
+ hydra.id = '__bad_overlay_hydra';
249
+ Object.assign(hydra.style, {
250
+ position: 'fixed',
251
+ inset: '0',
252
+ display: 'none',
253
+ background: 'radial-gradient(ellipse at center, rgba(11,18,32,0.80) 0%, rgba(11,18,32,0.95) 100%)',
254
+ backdropFilter: 'blur(6px)',
255
+ alignItems: 'center',
256
+ justifyContent: 'center',
257
+ flexDirection: 'column',
258
+ gap: '20px',
259
+ transition: 'opacity 400ms ease',
260
+ opacity: '0',
261
+ pointerEvents: 'none',
262
+ });
263
+ const hydraTitle = document.createElement('div');
264
+ Object.assign(hydraTitle.style, {
265
+ fontSize: '11px',
266
+ fontWeight: '700',
267
+ letterSpacing: '0.16em',
268
+ textTransform: 'uppercase',
269
+ color: '#7aa2ff',
270
+ textAlign: 'center',
271
+ });
272
+ hydraTitle.textContent = 'Fan-out · investigating branches in parallel';
273
+ const hydraGrid = document.createElement('div');
274
+ hydraGrid.id = '__bad_overlay_hydra_grid';
275
+ Object.assign(hydraGrid.style, {
276
+ display: 'grid',
277
+ gap: '16px',
278
+ maxWidth: '1200px',
279
+ maxHeight: '70vh',
280
+ padding: '0 40px',
281
+ });
282
+ const hydraCounter = document.createElement('div');
283
+ Object.assign(hydraCounter.style, {
284
+ fontSize: '13px',
285
+ color: '#e6ebf5',
286
+ fontWeight: '600',
287
+ textAlign: 'center',
288
+ });
289
+ hydra.appendChild(hydraTitle);
290
+ hydra.appendChild(hydraGrid);
291
+ hydra.appendChild(hydraCounter);
292
+
243
293
  // Append to documentElement (not body) so the overlay is not affected by
244
294
  // body's stacking context — many modern sites apply transform/will-change
245
295
  // to body which would clip a max-z-index child appended to body.
@@ -262,6 +312,7 @@ export const CURSOR_OVERLAY_INIT_SCRIPT = `
262
312
  root.appendChild(ring);
263
313
  root.appendChild(cursor);
264
314
  root.appendChild(label);
315
+ root.appendChild(hydra);
265
316
  }
266
317
  attach();
267
318
 
@@ -418,6 +469,219 @@ export const CURSOR_OVERLAY_INIT_SCRIPT = `
418
469
  } catch { /* cosmetic */ }
419
470
  },
420
471
 
472
+ // ── Gen 34 Hydra API ─────────────────────────────────────────────
473
+ /**
474
+ * Begin a fan-out: render the dim overlay + grid of N labeled cells.
475
+ * Cells start in "queued" state (empty). Call updateFanOutCell(i, ...)
476
+ * as sub-agent screenshots stream in.
477
+ *
478
+ * labels: human-readable label per cell, one per sub-agent.
479
+ * originX/originY: optional viewport coord where the fan-out was
480
+ * initiated — cells burst outward from this point (default: center).
481
+ */
482
+ fanOutStart(labels, originX, originY) {
483
+ try {
484
+ const n = Math.max(1, Math.min(8, labels.length));
485
+ // Layout: 1→1, 2→1x2, 3-4→2x2, 5-6→3x2, 7-8→4x2
486
+ const cols = n === 1 ? 1 : n === 2 ? 2 : n <= 4 ? 2 : n <= 6 ? 3 : 4;
487
+ const rows = Math.ceil(n / cols);
488
+ hydraGrid.style.gridTemplateColumns = 'repeat(' + cols + ', 1fr)';
489
+ hydraGrid.style.gridTemplateRows = 'repeat(' + rows + ', 1fr)';
490
+ // Cell size: scale to fit. For a 1200x600 viewport budget:
491
+ const cellW = Math.floor(1120 / cols) - 10;
492
+ const cellH = Math.floor(Math.min(520 / rows, cellW * 0.7));
493
+ // Clear prior cells
494
+ while (hydraGrid.firstChild) hydraGrid.removeChild(hydraGrid.firstChild);
495
+ const originPx = typeof originX === 'number' && typeof originY === 'number'
496
+ ? { x: originX, y: originY }
497
+ : { x: window.innerWidth / 2, y: window.innerHeight / 2 };
498
+ for (let i = 0; i < n; i++) {
499
+ const cell = document.createElement('div');
500
+ cell.className = '__bad_hydra_cell';
501
+ cell.dataset.index = String(i);
502
+ Object.assign(cell.style, {
503
+ position: 'relative',
504
+ width: cellW + 'px',
505
+ height: cellH + 'px',
506
+ background: '#0b1220',
507
+ backgroundSize: 'cover',
508
+ backgroundPosition: 'center top',
509
+ border: '2px solid rgba(122,162,255,0.55)',
510
+ borderRadius: '10px',
511
+ overflow: 'hidden',
512
+ boxShadow: '0 10px 30px rgba(0,0,0,0.55), inset 0 0 0 1px rgba(122,162,255,0.2)',
513
+ opacity: '0',
514
+ transform: 'translate(' + (originPx.x - window.innerWidth / 2) + 'px, ' + (originPx.y - window.innerHeight / 2) + 'px) scale(0.15)',
515
+ transition: 'transform 480ms cubic-bezier(0.34, 1.56, 0.64, 1), opacity 300ms ease, border-color 300ms ease, box-shadow 300ms ease',
516
+ });
517
+ // Header strip: label + live elapsed
518
+ const header = document.createElement('div');
519
+ Object.assign(header.style, {
520
+ position: 'absolute',
521
+ top: '0', left: '0', right: '0',
522
+ padding: '6px 10px',
523
+ background: 'linear-gradient(180deg, rgba(11,18,32,0.95) 0%, rgba(11,18,32,0.0) 100%)',
524
+ color: '#e6ebf5',
525
+ fontSize: '11px',
526
+ fontWeight: '700',
527
+ letterSpacing: '0.04em',
528
+ display: 'flex',
529
+ justifyContent: 'space-between',
530
+ alignItems: 'center',
531
+ });
532
+ const headerLabel = document.createElement('span');
533
+ headerLabel.textContent = labels[i] || ('branch-' + (i + 1));
534
+ const headerTimer = document.createElement('span');
535
+ headerTimer.className = '__bad_hydra_timer';
536
+ headerTimer.style.cssText = 'font-family:SF Mono,Menlo,monospace;font-size:10px;color:#9aa3b8;font-weight:500;';
537
+ headerTimer.textContent = '0.0s';
538
+ header.appendChild(headerLabel);
539
+ header.appendChild(headerTimer);
540
+ cell.appendChild(header);
541
+ // Verdict chip (hidden until complete)
542
+ const chip = document.createElement('div');
543
+ chip.className = '__bad_hydra_chip';
544
+ Object.assign(chip.style, {
545
+ position: 'absolute',
546
+ bottom: '8px',
547
+ left: '8px',
548
+ padding: '3px 10px',
549
+ background: 'rgba(11,18,32,0.92)',
550
+ color: '#ffffff',
551
+ fontSize: '10px',
552
+ fontWeight: '700',
553
+ letterSpacing: '0.05em',
554
+ textTransform: 'uppercase',
555
+ borderRadius: '6px',
556
+ opacity: '0',
557
+ transition: 'opacity 220ms ease',
558
+ });
559
+ cell.appendChild(chip);
560
+ hydraGrid.appendChild(cell);
561
+ }
562
+ hydra.style.display = 'flex';
563
+ hydraCounter.textContent = '0 / ' + n + ' branches complete';
564
+ // Next frame: fade in + animate cells to their grid positions
565
+ requestAnimationFrame(() => {
566
+ hydra.style.opacity = '1';
567
+ const cells = hydraGrid.children;
568
+ for (let i = 0; i < cells.length; i++) {
569
+ const c = cells[i];
570
+ // Staggered burst: 0ms, 60ms, 120ms, ...
571
+ setTimeout(function () {
572
+ c.style.opacity = '1';
573
+ c.style.transform = 'translate(0, 0) scale(1)';
574
+ }, i * 60);
575
+ }
576
+ });
577
+ // Start the elapsed-time ticker
578
+ hydra.__tickerStart = Date.now();
579
+ if (hydra.__ticker) clearInterval(hydra.__ticker);
580
+ hydra.__ticker = setInterval(function () {
581
+ const elapsed = (Date.now() - hydra.__tickerStart) / 1000;
582
+ const cells = hydraGrid.children;
583
+ for (let i = 0; i < cells.length; i++) {
584
+ const cell = cells[i];
585
+ if (cell.__completedAt) continue;
586
+ const timer = cell.querySelector('.__bad_hydra_timer');
587
+ if (timer) timer.textContent = elapsed.toFixed(1) + 's';
588
+ }
589
+ }, 100);
590
+ } catch { /* cosmetic */ }
591
+ },
592
+
593
+ /**
594
+ * Update cell N with a fresh screenshot (data URL) and/or metadata.
595
+ * Call this at 2-5 FPS from the runner's screenshot streamer.
596
+ * meta: { action?: string, turn?: number }
597
+ */
598
+ fanOutUpdateCell(index, dataUrl, meta) {
599
+ try {
600
+ const cell = hydraGrid.children[index];
601
+ if (!cell) return;
602
+ if (dataUrl) {
603
+ cell.style.backgroundImage = 'url("' + dataUrl + '")';
604
+ }
605
+ // eslint-disable-next-line no-empty
606
+ if (meta) { /* reserved for future overlays inside the cell */ }
607
+ } catch { /* cosmetic */ }
608
+ },
609
+
610
+ /**
611
+ * Mark cell N as complete with a verdict. kind is one of
612
+ * 'positive' | 'cleared' | 'review' | 'info'. Final state: border
613
+ * recolors, chip fades in, timer freezes.
614
+ */
615
+ fanOutCompleteCell(index, kind, verdictText) {
616
+ try {
617
+ const cell = hydraGrid.children[index];
618
+ if (!cell) return;
619
+ cell.__completedAt = Date.now();
620
+ const palette = (kind === 'positive' ? { c: '#fb7185', bg: 'rgba(251,113,133,0.25)' }
621
+ : kind === 'cleared' ? { c: '#4ade80', bg: 'rgba(74,222,128,0.25)' }
622
+ : kind === 'review' ? { c: '#fbbf24', bg: 'rgba(251,191,36,0.25)' }
623
+ : { c: '#7aa2ff', bg: 'rgba(122,162,255,0.25)' });
624
+ cell.style.borderColor = palette.c;
625
+ cell.style.boxShadow = '0 10px 30px rgba(0,0,0,0.55), inset 0 0 0 1px ' + palette.c + ', 0 0 24px ' + palette.bg;
626
+ const chip = cell.querySelector('.__bad_hydra_chip');
627
+ if (chip) {
628
+ chip.style.background = palette.bg;
629
+ chip.style.color = palette.c;
630
+ chip.style.border = '1px solid ' + palette.c;
631
+ chip.textContent = (verdictText || kind).slice(0, 32);
632
+ chip.style.opacity = '1';
633
+ }
634
+ // Update global counter
635
+ const total = hydraGrid.children.length;
636
+ let done = 0;
637
+ for (let i = 0; i < total; i++) if (hydraGrid.children[i].__completedAt) done++;
638
+ hydraCounter.textContent = done + ' / ' + total + ' branches complete';
639
+ } catch { /* cosmetic */ }
640
+ },
641
+
642
+ /**
643
+ * Collapse the grid: cells fly inward to center, merge into a result
644
+ * tile, then dissolve. Returns after ~800ms when the animation is
645
+ * done (but does NOT hide the overlay — caller invokes fanOutDismiss).
646
+ */
647
+ fanOutCollapse() {
648
+ try {
649
+ if (hydra.__ticker) { clearInterval(hydra.__ticker); hydra.__ticker = null; }
650
+ const cells = hydraGrid.children;
651
+ const cx = window.innerWidth / 2;
652
+ const cy = window.innerHeight / 2;
653
+ for (let i = 0; i < cells.length; i++) {
654
+ const c = cells[i];
655
+ const rect = c.getBoundingClientRect();
656
+ const dx = cx - (rect.left + rect.width / 2);
657
+ const dy = cy - (rect.top + rect.height / 2);
658
+ c.style.transition = 'transform 520ms cubic-bezier(0.6, 0, 0.4, 1), opacity 400ms ease';
659
+ c.style.transform = 'translate(' + dx + 'px, ' + dy + 'px) scale(0.2)';
660
+ c.style.opacity = '0';
661
+ }
662
+ hydraTitle.style.transition = 'opacity 400ms ease';
663
+ hydraCounter.style.transition = 'opacity 400ms ease';
664
+ setTimeout(function () { hydraTitle.style.opacity = '0'; hydraCounter.style.opacity = '0'; }, 200);
665
+ } catch { /* cosmetic */ }
666
+ },
667
+
668
+ /**
669
+ * Hide the Hydra overlay entirely. Call after fanOutCollapse has
670
+ * finished + the parent agent has resumed.
671
+ */
672
+ fanOutDismiss() {
673
+ try {
674
+ hydra.style.opacity = '0';
675
+ setTimeout(function () {
676
+ hydra.style.display = 'none';
677
+ hydraTitle.style.opacity = '1';
678
+ hydraCounter.style.opacity = '1';
679
+ while (hydraGrid.firstChild) hydraGrid.removeChild(hydraGrid.firstChild);
680
+ if (hydra.__ticker) { clearInterval(hydra.__ticker); hydra.__ticker = null; }
681
+ }, 400);
682
+ } catch { /* cosmetic */ }
683
+ },
684
+
421
685
  hide() {
422
686
  box.style.opacity = '0';
423
687
  label.style.opacity = '0';
@@ -1 +1 @@
1
- {"version":3,"file":"cursor-overlay.js","sourceRoot":"","sources":["../../src/drivers/cursor-overlay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoZzC,CAAA"}
1
+ {"version":3,"file":"cursor-overlay.js","sourceRoot":"","sources":["../../src/drivers/cursor-overlay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4pBzC,CAAA"}
@@ -2,18 +2,26 @@
2
2
  * Cursor-overlay label builder.
3
3
  *
4
4
  * The overlay renders a single short label string next to the animated
5
- * cursor. A bare verb (`click`, `type`) leaves 90% of the agent's intent
6
- * invisible to anyone watching the recording. Rich labels turn the cursor
7
- * into a readable narrative `click · Search`, `type · "IVANOV ALEKSANDR"`,
8
- * `press · Enter`, `nav · sanctionssearch.ofac.treas.gov`.
5
+ * cursor. The label is the PRIMARY channel of agent intent visible to
6
+ * anyone watching a recorded video it needs to read like a human
7
+ * narrator, not like a debug log.
8
+ *
9
+ * Gen 34 rewrite: natural-English sentences.
10
+ *
11
+ * Before: `type "JOHN SMITH" · Last Name: insert criteria`
12
+ * After: `Typing "JOHN SMITH" into Last Name`
9
13
  *
10
14
  * Design rules:
11
- * - Labels MUST fit on one line at ~13px. Target max 48 chars rendered.
15
+ * - Labels MUST fit on one line at ~13px. Target max 56 chars rendered.
12
16
  * - Truncate with an ellipsis; never wrap.
13
17
  * - Verb-only is the SAFE FALLBACK — never throw, never return empty.
14
- * - Target accessible name goes in the label only when it's short and
15
- * informative. Skip generic names ('button', 'link', 'textbox') that
16
- * restate the role the viewer already sees from context.
18
+ * - Target names get aggressive cleanup: strip trailing punctuation,
19
+ * drop generic role-restatements, drop verbose suffixes like
20
+ * "insert criteria" / "search input" that ARIA labels carry but
21
+ * viewers don't need.
22
+ * - Start every action with a verb-in-progress ("Typing", "Clicking",
23
+ * "Pressing") for readability. Status-bar shorthand ("click · X")
24
+ * was good for devs; bad for anyone else.
17
25
  */
18
26
  import type { Action } from '../types.js';
19
27
  export interface OverlayLabelContext {
@@ -26,8 +34,8 @@ export interface OverlayLabelContext {
26
34
  * Build a single-line label string for the cursor overlay given an action
27
35
  * and (optionally) the resolved target element's accessible name.
28
36
  *
29
- * Always returns a non-empty string. Falls back to the bare verb on any
30
- * missing context.
37
+ * Always returns a non-empty string. Falls back to a bare verb-phrase
38
+ * ("Clicking", "Typing") on any missing context. Never throws.
31
39
  */
32
40
  export declare function formatOverlayLabel(action: Action, ctx?: OverlayLabelContext): string;
33
41
  //# sourceMappingURL=overlay-label.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"overlay-label.d.ts","sourceRoot":"","sources":["../../src/drivers/overlay-label.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AA0CzC,MAAM,WAAW,mBAAmB;IAClC,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,GAAE,mBAAwB,GAAG,MAAM,CAsCxF"}
1
+ {"version":3,"file":"overlay-label.d.ts","sourceRoot":"","sources":["../../src/drivers/overlay-label.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAyFzC,MAAM,WAAW,mBAAmB;IAClC,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,GAAE,mBAAwB,GAAG,MAAM,CAsCxF"}
@@ -1,17 +1,31 @@
1
- const MAX_LABEL_LEN = 48;
2
- const MAX_TEXT_PREVIEW = 30;
3
- const MAX_NAME_PREVIEW = 30;
1
+ const MAX_LABEL_LEN = 56;
2
+ const MAX_TEXT_PREVIEW = 32;
3
+ const MAX_NAME_PREVIEW = 28;
4
4
  /**
5
5
  * Role names that are generic; including them in the label adds no info.
6
- * NOTE: "Search" is NOT here — a button labeled "Search" is informative
7
- * (OFAC, Google, etc.). "searchbox" IS here — that's the role spilling
8
- * through as a name when an input has no label, which just restates what
9
- * the viewer already sees from the cursor's position on the textbox.
6
+ * NOTE: "Search" is NOT here — a button labeled "Search" is informative.
10
7
  */
11
8
  const GENERIC_NAMES = new Set([
12
9
  'button', 'link', 'textbox', 'input', 'field', 'searchbox',
13
10
  'combobox', 'listbox', 'option', 'checkbox', 'radio', 'switch',
14
11
  ]);
12
+ /**
13
+ * Suffixes ARIA labels / placeholders often carry that bloat the label
14
+ * without adding meaning. "Last Name: insert criteria" → "Last Name".
15
+ * Matching is case-insensitive and trims any trailing punctuation.
16
+ */
17
+ const NOISE_SUFFIXES = [
18
+ /\s*[:,]?\s*insert (?:criteria|name|value|text)\s*$/i,
19
+ /\s*[:,]?\s*search input\s*$/i,
20
+ /\s*[:,]?\s*search field\s*$/i,
21
+ /\s*[:,]?\s*input field\s*$/i,
22
+ /\s*[:,]?\s*text input\s*$/i,
23
+ /\s*[:,]?\s*enter (?:text|value|name|keyword|query)\s*$/i,
24
+ /\s*[:,]?\s*type (?:here|name|text)\s*$/i,
25
+ /\s*[:,]?\s*required\s*$/i,
26
+ /\s*[*]\s*$/, // trailing asterisks from "Required *" fields
27
+ /[:;,]\s*$/, // any trailing punctuation
28
+ ];
15
29
  function truncate(s, max) {
16
30
  const collapsed = s.replace(/\s+/g, ' ').trim();
17
31
  if (collapsed.length <= max)
@@ -21,7 +35,22 @@ function truncate(s, max) {
21
35
  function cleanName(name) {
22
36
  if (!name)
23
37
  return undefined;
24
- const c = name.replace(/\s+/g, ' ').trim();
38
+ let c = name.replace(/\s+/g, ' ').trim();
39
+ if (!c)
40
+ return undefined;
41
+ // Strip noise suffixes iteratively — an ARIA name like
42
+ // "Last Name: insert criteria, required *" has three layers to peel.
43
+ let changed = true;
44
+ while (changed) {
45
+ changed = false;
46
+ for (const re of NOISE_SUFFIXES) {
47
+ const stripped = c.replace(re, '').trim();
48
+ if (stripped !== c && stripped.length > 0) {
49
+ c = stripped;
50
+ changed = true;
51
+ }
52
+ }
53
+ }
25
54
  if (!c)
26
55
  return undefined;
27
56
  if (GENERIC_NAMES.has(c.toLowerCase()))
@@ -38,52 +67,69 @@ function displayHost(url) {
38
67
  return url.replace(/^https?:\/\//, '').replace(/\/.*$/, '').slice(0, MAX_NAME_PREVIEW);
39
68
  }
40
69
  }
70
+ /** Press keys get friendlier names — "Enter" / "Tab" / arrow glyphs. */
71
+ function friendlyKey(key) {
72
+ const k = key.trim();
73
+ const map = {
74
+ 'Enter': 'Enter',
75
+ 'Return': 'Enter',
76
+ 'Tab': 'Tab',
77
+ 'Escape': 'Esc',
78
+ 'Backspace': '⌫',
79
+ 'Delete': 'Del',
80
+ 'ArrowUp': '↑',
81
+ 'ArrowDown': '↓',
82
+ 'ArrowLeft': '←',
83
+ 'ArrowRight': '→',
84
+ };
85
+ return map[k] ?? k;
86
+ }
41
87
  /**
42
88
  * Build a single-line label string for the cursor overlay given an action
43
89
  * and (optionally) the resolved target element's accessible name.
44
90
  *
45
- * Always returns a non-empty string. Falls back to the bare verb on any
46
- * missing context.
91
+ * Always returns a non-empty string. Falls back to a bare verb-phrase
92
+ * ("Clicking", "Typing") on any missing context. Never throws.
47
93
  */
48
94
  export function formatOverlayLabel(action, ctx = {}) {
49
95
  const name = cleanName(ctx.targetName);
50
96
  switch (action.action) {
51
97
  case 'click': {
52
- return truncate(name ? `click · ${name}` : 'click', MAX_LABEL_LEN);
98
+ return truncate(name ? `Clicking ${name}` : 'Clicking', MAX_LABEL_LEN);
53
99
  }
54
100
  case 'type': {
55
101
  const text = truncate(action.text ?? '', MAX_TEXT_PREVIEW);
56
102
  if (text && name)
57
- return truncate(`type "${text}" · ${name}`, MAX_LABEL_LEN);
103
+ return truncate(`Typing “${text} into ${name}`, MAX_LABEL_LEN);
58
104
  if (text)
59
- return truncate(`type · "${text}"`, MAX_LABEL_LEN);
60
- return name ? truncate(`type · ${name}`, MAX_LABEL_LEN) : 'type';
105
+ return truncate(`Typing “${text}”`, MAX_LABEL_LEN);
106
+ return name ? truncate(`Typing into ${name}`, MAX_LABEL_LEN) : 'Typing';
61
107
  }
62
108
  case 'press': {
63
- const key = action.key ?? '';
109
+ const key = action.key ? friendlyKey(action.key) : '';
64
110
  if (key && name)
65
- return truncate(`press ${key} · ${name}`, MAX_LABEL_LEN);
66
- return truncate(key ? `press · ${key}` : 'press', MAX_LABEL_LEN);
111
+ return truncate(`Pressing ${key} in ${name}`, MAX_LABEL_LEN);
112
+ return truncate(key ? `Pressing ${key}` : 'Pressing a key', MAX_LABEL_LEN);
67
113
  }
68
114
  case 'hover': {
69
- return truncate(name ? `hover · ${name}` : 'hover', MAX_LABEL_LEN);
115
+ return truncate(name ? `Hovering ${name}` : 'Hovering', MAX_LABEL_LEN);
70
116
  }
71
117
  case 'select': {
72
118
  const val = truncate(action.value ?? '', MAX_TEXT_PREVIEW);
73
119
  if (val && name)
74
- return truncate(`select ${val} · ${name}`, MAX_LABEL_LEN);
120
+ return truncate(`Selecting “${val} in ${name}`, MAX_LABEL_LEN);
75
121
  if (val)
76
- return truncate(`select · ${val}`, MAX_LABEL_LEN);
77
- return name ? truncate(`select · ${name}`, MAX_LABEL_LEN) : 'select';
122
+ return truncate(`Selecting “${val}”`, MAX_LABEL_LEN);
123
+ return name ? truncate(`Selecting in ${name}`, MAX_LABEL_LEN) : 'Selecting';
78
124
  }
79
125
  case 'scroll': {
80
- return truncate(`scroll ${action.direction}`, MAX_LABEL_LEN);
126
+ return truncate(`Scrolling ${action.direction}`, MAX_LABEL_LEN);
81
127
  }
82
128
  case 'navigate': {
83
- return truncate(`nav · ${displayHost(action.url)}`, MAX_LABEL_LEN);
129
+ return truncate(`Navigating to ${displayHost(action.url)}`, MAX_LABEL_LEN);
84
130
  }
85
131
  case 'wait': {
86
- return truncate(`wait ${action.ms}ms`, MAX_LABEL_LEN);
132
+ return truncate(`Waiting ${action.ms}ms`, MAX_LABEL_LEN);
87
133
  }
88
134
  default:
89
135
  return action.action;
@@ -1 +1 @@
1
- {"version":3,"file":"overlay-label.js","sourceRoot":"","sources":["../../src/drivers/overlay-label.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"overlay-label.js","sourceRoot":"","sources":["../../src/drivers/overlay-label.ts"],"names":[],"mappings":"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"}
@@ -121,6 +121,16 @@ export declare class PlaywrightDriver implements Driver {
121
121
  * to celebrate the moment.
122
122
  */
123
123
  pushOverlayBadge(kind: 'positive' | 'cleared' | 'review' | 'info', text: string): Promise<void>;
124
+ /** Start the fan-out overlay. Renders the dim + grid of N labeled cells. */
125
+ fanOutStart(labels: string[]): Promise<void>;
126
+ /** Stream a JPEG dataURL into cell N. Call at 2-5 FPS during fan-out. */
127
+ fanOutUpdateCell(index: number, dataUrl?: string): Promise<void>;
128
+ /** Mark cell N complete with a verdict kind + chip text. */
129
+ fanOutCompleteCell(index: number, kind: 'positive' | 'cleared' | 'review' | 'info', verdictText?: string): Promise<void>;
130
+ /** Collapse the grid (cells fly to center, merge). ~500ms animation. */
131
+ fanOutCollapse(): Promise<void>;
132
+ /** Dismiss the fan-out overlay (fade out). Call after collapse finishes. */
133
+ fanOutDismiss(): Promise<void>;
124
134
  getPage(): Page;
125
135
  getUrl(): string;
126
136
  inspectSelectorHref(selector: string): Promise<string | undefined>;
@@ -1 +1 @@
1
- {"version":3,"file":"playwright.d.ts","sourceRoot":"","sources":["../../src/drivers/playwright.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"playwright.d.ts","sourceRoot":"","sources":["../../src/drivers/playwright.ts"],"names":[],"mappings":"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"}
@@ -328,6 +328,84 @@ export class PlaywrightDriver {
328
328
  }
329
329
  catch { /* cosmetic */ }
330
330
  }
331
+ // ── Gen 34 Hydra API ───────────────────────────────────────────────
332
+ /** Start the fan-out overlay. Renders the dim + grid of N labeled cells. */
333
+ async fanOutStart(labels) {
334
+ if (!this.options.showCursor)
335
+ return;
336
+ if (this.cursorInstallPromise)
337
+ await this.cursorInstallPromise.catch(() => undefined);
338
+ try {
339
+ const ls = labels.slice(0, 8);
340
+ await this.page.evaluate((l) => {
341
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
342
+ const ov = window.__bad_overlay;
343
+ if (ov && typeof ov.fanOutStart === 'function')
344
+ ov.fanOutStart(l);
345
+ }, ls).catch(() => { });
346
+ }
347
+ catch { /* cosmetic */ }
348
+ }
349
+ /** Stream a JPEG dataURL into cell N. Call at 2-5 FPS during fan-out. */
350
+ async fanOutUpdateCell(index, dataUrl) {
351
+ if (!this.options.showCursor)
352
+ return;
353
+ if (this.cursorInstallPromise)
354
+ await this.cursorInstallPromise.catch(() => undefined);
355
+ try {
356
+ await this.page.evaluate(({ i, d }) => {
357
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
358
+ const ov = window.__bad_overlay;
359
+ if (ov && typeof ov.fanOutUpdateCell === 'function')
360
+ ov.fanOutUpdateCell(i, d);
361
+ }, { i: index, ...(dataUrl ? { d: dataUrl } : {}) }).catch(() => { });
362
+ }
363
+ catch { /* cosmetic */ }
364
+ }
365
+ /** Mark cell N complete with a verdict kind + chip text. */
366
+ async fanOutCompleteCell(index, kind, verdictText) {
367
+ if (!this.options.showCursor)
368
+ return;
369
+ if (this.cursorInstallPromise)
370
+ await this.cursorInstallPromise.catch(() => undefined);
371
+ try {
372
+ await this.page.evaluate(({ i, k, t }) => {
373
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
374
+ const ov = window.__bad_overlay;
375
+ if (ov && typeof ov.fanOutCompleteCell === 'function')
376
+ ov.fanOutCompleteCell(i, k, t);
377
+ }, { i: index, k: kind, ...(verdictText ? { t: verdictText } : {}) }).catch(() => { });
378
+ }
379
+ catch { /* cosmetic */ }
380
+ }
381
+ /** Collapse the grid (cells fly to center, merge). ~500ms animation. */
382
+ async fanOutCollapse() {
383
+ if (!this.options.showCursor)
384
+ return;
385
+ try {
386
+ await this.page.evaluate(() => {
387
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
388
+ const ov = window.__bad_overlay;
389
+ if (ov && typeof ov.fanOutCollapse === 'function')
390
+ ov.fanOutCollapse();
391
+ }).catch(() => { });
392
+ }
393
+ catch { /* cosmetic */ }
394
+ }
395
+ /** Dismiss the fan-out overlay (fade out). Call after collapse finishes. */
396
+ async fanOutDismiss() {
397
+ if (!this.options.showCursor)
398
+ return;
399
+ try {
400
+ await this.page.evaluate(() => {
401
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
402
+ const ov = window.__bad_overlay;
403
+ if (ov && typeof ov.fanOutDismiss === 'function')
404
+ ov.fanOutDismiss();
405
+ }).catch(() => { });
406
+ }
407
+ catch { /* cosmetic */ }
408
+ }
331
409
  getPage() {
332
410
  return this.page;
333
411
  }