@weavetab/mcp 2.5.0-beta.5 → 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 (160) hide show
  1. package/AGENTS.md +49 -49
  2. package/ARCHITECTURE.md +590 -590
  3. package/README.md +216 -100
  4. package/SECURITY.md +207 -207
  5. package/TROUBLESHOOTING.md +373 -365
  6. package/dist/anticaptcha/detector.js +395 -395
  7. package/dist/anticaptcha/solver.js +130 -130
  8. package/dist/cdp/bridge.js +73 -73
  9. package/dist/cdp/connector.js +46 -22
  10. package/dist/cdp/extension.js +34 -34
  11. package/dist/cdp/helpers.js +174 -174
  12. package/dist/cdp/pruner.js +195 -195
  13. package/dist/cdp/ref_cache.js +66 -66
  14. package/dist/cdp/security.js +10 -10
  15. package/dist/cdp/verify.js +57 -57
  16. package/dist/cdp/walker.js +563 -525
  17. package/dist/cli/plugin.js +26 -3
  18. package/dist/config/loader.d.ts +0 -3
  19. package/dist/config/loader.js +0 -9
  20. package/dist/index.js +0 -4
  21. package/dist/intelligence/intent.js +81 -81
  22. package/dist/intelligence/sessionMemory.js +2 -2
  23. package/dist/overlay/ghost.js +22 -22
  24. package/dist/overlay/injector.js +89 -89
  25. package/dist/plugin/extension_bridge.js +54 -54
  26. package/dist/plugin/interface.d.ts +7 -0
  27. package/dist/plugin/loader.js +3 -4
  28. package/dist/plugin/schema.d.ts +11 -0
  29. package/dist/plugin/schema.js +59 -0
  30. package/dist/security/guard.js +18 -18
  31. package/dist/sensors/actionability.js +167 -167
  32. package/dist/sensors/guard.js +29 -29
  33. package/dist/sensors/network.js +15 -17
  34. package/dist/server.d.ts +1 -1
  35. package/dist/server.js +173 -144
  36. package/dist/state/delta.js +6 -7
  37. package/dist/tools/automation.js +85 -85
  38. package/dist/tools/canvas.js +48 -25
  39. package/dist/tools/checkpoint.js +94 -35
  40. package/dist/tools/click.js +52 -41
  41. package/dist/tools/clipboard.js +80 -74
  42. package/dist/tools/design_extract.js +441 -441
  43. package/dist/tools/dialog.js +13 -13
  44. package/dist/tools/emulation.d.ts +2 -0
  45. package/dist/tools/emulation.js +7 -0
  46. package/dist/tools/fill.js +344 -347
  47. package/dist/tools/find.js +78 -52
  48. package/dist/tools/highlight.js +67 -67
  49. package/dist/tools/inspect.js +54 -25
  50. package/dist/tools/key.js +42 -35
  51. package/dist/tools/navigate.d.ts +1 -1
  52. package/dist/tools/navigate.js +35 -35
  53. package/dist/tools/office/parse.js +62 -18
  54. package/dist/tools/pdf.d.ts +9 -0
  55. package/dist/tools/pdf.js +7 -4
  56. package/dist/tools/performance.d.ts +11 -4
  57. package/dist/tools/performance.js +94 -42
  58. package/dist/tools/performance_test.d.ts +2 -2
  59. package/dist/tools/performance_test.js +69 -69
  60. package/dist/tools/pointer.js +116 -116
  61. package/dist/tools/read.js +99 -67
  62. package/dist/tools/recording.js +9 -0
  63. package/dist/tools/scrape.js +146 -146
  64. package/dist/tools/screenshot.d.ts +2 -0
  65. package/dist/tools/screenshot.js +46 -4
  66. package/dist/tools/scroll.d.ts +3 -1
  67. package/dist/tools/scroll.js +13 -15
  68. package/dist/tools/select.d.ts +3 -0
  69. package/dist/tools/select.js +131 -124
  70. package/dist/tools/snapshot.js +8 -6
  71. package/dist/tools/storage.d.ts +5 -1
  72. package/dist/tools/storage.js +16 -0
  73. package/dist/tools/tabs.d.ts +7 -5
  74. package/dist/tools/tabs.js +79 -26
  75. package/dist/tools/thought.js +8 -8
  76. package/dist/tools/type.js +274 -274
  77. package/dist/tools/type_secret.js +23 -23
  78. package/dist/tools/upload.js +3 -3
  79. package/dist/tools/video_compiler.js +9 -0
  80. package/dist/tools/wait.js +35 -35
  81. package/dist/version.d.ts +1 -1
  82. package/dist/version.js +1 -1
  83. package/docs/DOCUMENTATION.md +583 -585
  84. package/docs/PLUGINS.md +139 -121
  85. package/docs/TOOLS.md +3 -4
  86. package/docs/architecture/blind-injection.md +83 -83
  87. package/docs/architecture/overview.md +49 -49
  88. package/docs/configuration/configuration.md +39 -27
  89. package/docs/configuration/folders.md +62 -55
  90. package/docs/desktop-automation/electron-apps.md +225 -214
  91. package/docs/get-started/installation.md +1 -1
  92. package/docs/get-started/introduction.md +60 -60
  93. package/docs/get-started/quickstart.md +2 -2
  94. package/docs/get-started/setup-wizard.md +1 -1
  95. package/docs/intelligence/site-profiles.md +151 -151
  96. package/docs/tools/tools.md +582 -631
  97. package/docs/troubleshooting/trouble-loop.md +31 -31
  98. package/ext-dist/background.js +415 -415
  99. package/ext-dist/content.js +2527 -2527
  100. package/ext-dist/dashboard.html +583 -583
  101. package/ext-dist/hud/hud.js +3 -3
  102. package/ext-dist/hud/index.html +20 -20
  103. package/ext-dist/manifest.json +73 -73
  104. package/ext-dist/styles.css +223 -223
  105. package/mcp.json +796 -805
  106. package/package.json +8 -8
  107. package/server.json +23 -23
  108. package/smithery.yaml +7 -11
  109. package/dist/cdp/walker.test.d.ts +0 -2
  110. package/dist/cdp/walker.test.js +0 -172
  111. package/dist/intelligence/sessionMemory.test.d.ts +0 -2
  112. package/dist/intelligence/sessionMemory.test.js +0 -62
  113. package/dist/intelligence/trail.test.d.ts +0 -2
  114. package/dist/intelligence/trail.test.js +0 -110
  115. package/dist/state/cache.test.d.ts +0 -2
  116. package/dist/state/cache.test.js +0 -76
  117. package/dist/state/delta.test.d.ts +0 -2
  118. package/dist/state/delta.test.js +0 -279
  119. package/dist/state/groups.d.ts +0 -15
  120. package/dist/state/groups.js +0 -80
  121. package/dist/tools/accessibility.d.ts +0 -14
  122. package/dist/tools/accessibility.js +0 -32
  123. package/dist/tools/ask.d.ts +0 -14
  124. package/dist/tools/ask.js +0 -49
  125. package/dist/tools/click_and_wait.d.ts +0 -33
  126. package/dist/tools/click_and_wait.js +0 -149
  127. package/dist/tools/drag.d.ts +0 -15
  128. package/dist/tools/drag.js +0 -29
  129. package/dist/tools/geo.d.ts +0 -19
  130. package/dist/tools/geo.js +0 -29
  131. package/dist/tools/github/analyze.d.ts +0 -4
  132. package/dist/tools/github/analyze.js +0 -113
  133. package/dist/tools/github/cache.d.ts +0 -21
  134. package/dist/tools/github/cache.js +0 -28
  135. package/dist/tools/github/index.d.ts +0 -19
  136. package/dist/tools/github/index.js +0 -40
  137. package/dist/tools/github/issues.d.ts +0 -9
  138. package/dist/tools/github/issues.js +0 -88
  139. package/dist/tools/github/pr.d.ts +0 -5
  140. package/dist/tools/github/pr.js +0 -88
  141. package/dist/tools/github/rateLimiter.d.ts +0 -4
  142. package/dist/tools/github/rateLimiter.js +0 -46
  143. package/dist/tools/github/read.d.ts +0 -34
  144. package/dist/tools/github/read.js +0 -74
  145. package/dist/tools/github/repoContext.d.ts +0 -20
  146. package/dist/tools/github/repoContext.js +0 -129
  147. package/dist/tools/hover.d.ts +0 -19
  148. package/dist/tools/hover.js +0 -57
  149. package/dist/tools/integration.test.d.ts +0 -2
  150. package/dist/tools/integration.test.js +0 -96
  151. package/dist/tools/macro_compiler.d.ts +0 -7
  152. package/dist/tools/macro_compiler.js +0 -82
  153. package/dist/tools/mouse.d.ts +0 -21
  154. package/dist/tools/mouse.js +0 -62
  155. package/dist/tools/response.d.ts +0 -129
  156. package/dist/tools/response.js +0 -5
  157. package/dist/tools/wait_for.d.ts +0 -23
  158. package/dist/tools/wait_for.js +0 -30
  159. package/dist/tools/windows.d.ts +0 -22
  160. package/dist/tools/windows.js +0 -57
@@ -64,200 +64,200 @@ function buildEvalExpression(rawIds, structuralIdioms = []) {
64
64
  // We JSON-encode the id list so the script can build a lookup Set.
65
65
  const idsJson = JSON.stringify(rawIds);
66
66
  const idiomsJson = JSON.stringify(structuralIdioms);
67
- return `(function() {
68
- var t0 = performance.now();
69
-
70
- // ── Interactive selector (mirrors walker.ts) ─────────────────────────────
71
- var INTERACTIVE_SEL = [
72
- 'a', 'button', 'input', 'textarea', 'select',
73
- '[role="button"]', '[role="link"]', '[role="textbox"]',
74
- '[role="checkbox"]', '[role="radio"]', '[role="combobox"]',
75
- '[role="searchbox"]', '[role="menuitem"]', '[role="tab"]',
76
- '[tabindex]:not([tabindex="-1"])'
77
- ].join(', ');
78
-
79
- // SVG ancestor selector (includes menuitem + tab for Jira/AWS patterns)
80
- var SVG_ANCESTOR_SEL = 'button, a, [role="button"], [role="menuitem"], [role="tab"]';
81
-
82
- var targetIds = new Set(${idsJson});
83
-
84
- // ── Assign synthetic ids to any element missing data-wt-id ──
85
- // data-wt-secret guard fires on the ATTRIBUTE (not the id), so secret fields
86
- // are safe even when running on a synthetic id.
87
- var syntheticPrefix = '__prune_idx_';
88
- var syntheticEls = [];
89
- var allEls = document.querySelectorAll('[data-wt-id], [data-wt-secret]');
90
- var synIdx = 0;
91
- for (var si = 0; si < allEls.length; si++) {
92
- var sel = allEls[si];
93
- if (!sel.getAttribute('data-wt-id')) {
94
- var synthId = syntheticPrefix + (synIdx++);
95
- sel.setAttribute('data-wt-id', synthId);
96
- syntheticEls.push(sel);
97
- targetIds.add(synthId);
98
- }
99
- }
100
-
101
- // ── Pass 1: Document-level controls map ──────────────────────────────────
102
- // Built from the WHOLE document — stale or unrelated aria-controls values
103
- // can force-keep unrelated targets. Accepted over-keep bias: prefer recall
104
- // over precision so the agent never loses a toggle target it needs.
105
- var controlsMap = new Set();
106
- var CONTROLS_ATTRS = ['aria-controls', 'aria-owns', 'data-target', 'data-bs-target'];
107
- var extraIdioms = ${idiomsJson};
108
- for (var ei = 0; ei < extraIdioms.length; ei++) {
109
- if (CONTROLS_ATTRS.indexOf(extraIdioms[ei]) === -1) {
110
- CONTROLS_ATTRS.push(extraIdioms[ei]);
111
- }
112
- }
113
- try {
114
- var allNodes = document.querySelectorAll(CONTROLS_ATTRS.map(function(a) { return '[' + a + ']'; }).join(', '));
115
- for (var ci = 0; ci < allNodes.length; ci++) {
116
- for (var ai = 0; ai < CONTROLS_ATTRS.length; ai++) {
117
- var val = allNodes[ci].getAttribute(CONTROLS_ATTRS[ai]);
118
- if (!val) continue;
119
- var tokens = val.split(/\\s+/);
120
- for (var ti = 0; ti < tokens.length; ti++) {
121
- if (tokens[ti]) controlsMap.add(tokens[ti]);
122
- }
123
- }
124
- }
125
- } catch(e) {}
126
-
127
- // ── Pass 2: Post-order hasInteractiveDescendant annotation ───────────────
128
- // Single bottom-up pass — O(n). Each hidden-element check is then O(1).
129
- try {
130
- var walker = document.createTreeWalker(document.body || document, NodeFilter.SHOW_ELEMENT, null, false);
131
- var nodeStack = [];
132
- var node = walker.nextNode();
133
- while (node) {
134
- nodeStack.push(node);
135
- node = walker.nextNode();
136
- }
137
- // Process in reverse (post-order)
138
- for (var ni = nodeStack.length - 1; ni >= 0; ni--) {
139
- var n = nodeStack[ni];
140
- var isInteractive = false;
141
- try { isInteractive = n.matches(INTERACTIVE_SEL); } catch(e) {}
142
- var childHas = false;
143
- try {
144
- var children = n.children;
145
- for (var ki = 0; ki < children.length; ki++) {
146
- if (children[ki].__wtHasInteractive) { childHas = true; break; }
147
- }
148
- } catch(e) {}
149
- n.__wtHasInteractive = isInteractive || childHas;
150
- }
151
- } catch(e) {}
152
-
153
- // ── Pass 3: Per-element prune decision ───────────────────────────────────
154
- var keptIds = [];
155
- var svgLabels = {};
156
- var prunedCount = 0;
157
- var svgCount = 0;
158
-
159
- targetIds.forEach(function(wtId) {
160
- var el;
161
- try {
162
- el = document.querySelector('[data-wt-id="' + wtId + '"]');
163
- } catch(e) {}
164
- if (!el) { prunedCount++; return; }
165
-
166
- // Secret guard — fires on ATTRIBUTE, independent of whether id is synthetic
167
- if (el.hasAttribute('data-wt-secret')) {
168
- keptIds.push(String(wtId));
169
- return;
170
- }
171
-
172
- // Visibility check
173
- var hidden = false;
174
- try {
175
- var rect = el.getBoundingClientRect();
176
- if (rect.width === 0 && rect.height === 0) {
177
- hidden = true;
178
- } else {
179
- var cs = window.getComputedStyle(el);
180
- if (cs.visibility === 'hidden' || cs.display === 'none') hidden = true;
181
- }
182
- } catch(e) {}
183
-
184
- if (!hidden) {
185
- // Visible — keep; handle SVG label replacement
186
- keptIds.push(String(wtId));
187
- if (el.tagName && el.tagName.toLowerCase() === 'svg') {
188
- var label = resolveSvgLabel(el, SVG_ANCESTOR_SEL);
189
- svgLabels[String(wtId)] = label;
190
- svgCount++;
191
- }
192
- return;
193
- }
194
-
195
- // Hidden — keep if has interactive descendant
196
- if (el.__wtHasInteractive) {
197
- keptIds.push(String(wtId));
198
- return;
199
- }
200
-
201
- // Hidden — keep if referenced by a known toggle trigger (controlsMap)
202
- var elId = el.getAttribute('id');
203
- if (elId && controlsMap.has(elId)) {
204
- keptIds.push(String(wtId));
205
- return;
206
- }
207
-
208
- // Drop
209
- prunedCount++;
210
- });
211
-
212
- // ── Strip synthetic ids from DOM ─────────────────────────────────────────
213
- for (var ri = 0; ri < syntheticEls.length; ri++) {
214
- try { syntheticEls[ri].removeAttribute('data-wt-id'); } catch(e) {}
215
- }
216
-
217
- // ── SVG label resolution helper ──────────────────────────────────────────
218
- function resolveSvgLabel(svg, ancestorSel) {
219
- // 1. SVG's own aria-label
220
- var label = svg.getAttribute('aria-label');
221
- if (label && label.trim()) return '[SVG Icon: ' + label.trim() + ']';
222
-
223
- // 2. SVG's own <title> child
224
- try {
225
- var title = svg.querySelector('title');
226
- if (title && title.textContent && title.textContent.trim()) {
227
- return '[SVG Icon: ' + title.textContent.trim() + ']';
228
- }
229
- } catch(e) {}
230
-
231
- // 3. Closest interactive ancestor's aria-label
232
- // Covers: <button aria-label="Delete"><svg/></button>
233
- // <a role="menuitem" aria-label="More actions"><svg/></a> (Jira)
234
- // <div role="tab" aria-label="Overview"><svg/></div> (AWS)
235
- try {
236
- var ancestor = svg.closest(ancestorSel);
237
- if (ancestor) {
238
- var aLabel = ancestor.getAttribute('aria-label');
239
- if (aLabel && aLabel.trim()) return '[SVG Icon: ' + aLabel.trim() + ']';
240
- }
241
- } catch(e) {}
242
-
243
- return '[SVG Icon]';
244
- }
245
-
246
- var elapsedMs = performance.now() - t0;
247
-
248
- // ── Perf-gate: if this took > 5ms, flag it — caller will decide on split ─
249
- // (In this single-evaluate path we can only report, not split retroactively.
250
- // The split path would be invoked by the caller on a second evaluate call.)
251
- return JSON.stringify({
252
- keptIds: keptIds,
253
- svgLabels: svgLabels,
254
- stats: {
255
- total: targetIds.size,
256
- pruned: prunedCount,
257
- svgReplaced: svgCount,
258
- elapsedMs: elapsedMs,
259
- splitPath: false
260
- }
261
- });
67
+ return `(function() {
68
+ var t0 = performance.now();
69
+
70
+ // ── Interactive selector (mirrors walker.ts) ─────────────────────────────
71
+ var INTERACTIVE_SEL = [
72
+ 'a', 'button', 'input', 'textarea', 'select',
73
+ '[role="button"]', '[role="link"]', '[role="textbox"]',
74
+ '[role="checkbox"]', '[role="radio"]', '[role="combobox"]',
75
+ '[role="searchbox"]', '[role="menuitem"]', '[role="tab"]',
76
+ '[tabindex]:not([tabindex="-1"])'
77
+ ].join(', ');
78
+
79
+ // SVG ancestor selector (includes menuitem + tab for Jira/AWS patterns)
80
+ var SVG_ANCESTOR_SEL = 'button, a, [role="button"], [role="menuitem"], [role="tab"]';
81
+
82
+ var targetIds = new Set(${idsJson});
83
+
84
+ // ── Assign synthetic ids to any element missing data-wt-id ──
85
+ // data-wt-secret guard fires on the ATTRIBUTE (not the id), so secret fields
86
+ // are safe even when running on a synthetic id.
87
+ var syntheticPrefix = '__prune_idx_';
88
+ var syntheticEls = [];
89
+ var allEls = document.querySelectorAll('[data-wt-id], [data-wt-secret]');
90
+ var synIdx = 0;
91
+ for (var si = 0; si < allEls.length; si++) {
92
+ var sel = allEls[si];
93
+ if (!sel.getAttribute('data-wt-id')) {
94
+ var synthId = syntheticPrefix + (synIdx++);
95
+ sel.setAttribute('data-wt-id', synthId);
96
+ syntheticEls.push(sel);
97
+ targetIds.add(synthId);
98
+ }
99
+ }
100
+
101
+ // ── Pass 1: Document-level controls map ──────────────────────────────────
102
+ // Built from the WHOLE document — stale or unrelated aria-controls values
103
+ // can force-keep unrelated targets. Accepted over-keep bias: prefer recall
104
+ // over precision so the agent never loses a toggle target it needs.
105
+ var controlsMap = new Set();
106
+ var CONTROLS_ATTRS = ['aria-controls', 'aria-owns', 'data-target', 'data-bs-target'];
107
+ var extraIdioms = ${idiomsJson};
108
+ for (var ei = 0; ei < extraIdioms.length; ei++) {
109
+ if (CONTROLS_ATTRS.indexOf(extraIdioms[ei]) === -1) {
110
+ CONTROLS_ATTRS.push(extraIdioms[ei]);
111
+ }
112
+ }
113
+ try {
114
+ var allNodes = document.querySelectorAll(CONTROLS_ATTRS.map(function(a) { return '[' + a + ']'; }).join(', '));
115
+ for (var ci = 0; ci < allNodes.length; ci++) {
116
+ for (var ai = 0; ai < CONTROLS_ATTRS.length; ai++) {
117
+ var val = allNodes[ci].getAttribute(CONTROLS_ATTRS[ai]);
118
+ if (!val) continue;
119
+ var tokens = val.split(/\\s+/);
120
+ for (var ti = 0; ti < tokens.length; ti++) {
121
+ if (tokens[ti]) controlsMap.add(tokens[ti]);
122
+ }
123
+ }
124
+ }
125
+ } catch(e) {}
126
+
127
+ // ── Pass 2: Post-order hasInteractiveDescendant annotation ───────────────
128
+ // Single bottom-up pass — O(n). Each hidden-element check is then O(1).
129
+ try {
130
+ var walker = document.createTreeWalker(document.body || document, NodeFilter.SHOW_ELEMENT, null, false);
131
+ var nodeStack = [];
132
+ var node = walker.nextNode();
133
+ while (node) {
134
+ nodeStack.push(node);
135
+ node = walker.nextNode();
136
+ }
137
+ // Process in reverse (post-order)
138
+ for (var ni = nodeStack.length - 1; ni >= 0; ni--) {
139
+ var n = nodeStack[ni];
140
+ var isInteractive = false;
141
+ try { isInteractive = n.matches(INTERACTIVE_SEL); } catch(e) {}
142
+ var childHas = false;
143
+ try {
144
+ var children = n.children;
145
+ for (var ki = 0; ki < children.length; ki++) {
146
+ if (children[ki].__wtHasInteractive) { childHas = true; break; }
147
+ }
148
+ } catch(e) {}
149
+ n.__wtHasInteractive = isInteractive || childHas;
150
+ }
151
+ } catch(e) {}
152
+
153
+ // ── Pass 3: Per-element prune decision ───────────────────────────────────
154
+ var keptIds = [];
155
+ var svgLabels = {};
156
+ var prunedCount = 0;
157
+ var svgCount = 0;
158
+
159
+ targetIds.forEach(function(wtId) {
160
+ var el;
161
+ try {
162
+ el = document.querySelector('[data-wt-id="' + wtId + '"]');
163
+ } catch(e) {}
164
+ if (!el) { prunedCount++; return; }
165
+
166
+ // Secret guard — fires on ATTRIBUTE, independent of whether id is synthetic
167
+ if (el.hasAttribute('data-wt-secret')) {
168
+ keptIds.push(String(wtId));
169
+ return;
170
+ }
171
+
172
+ // Visibility check
173
+ var hidden = false;
174
+ try {
175
+ var rect = el.getBoundingClientRect();
176
+ if (rect.width === 0 && rect.height === 0) {
177
+ hidden = true;
178
+ } else {
179
+ var cs = window.getComputedStyle(el);
180
+ if (cs.visibility === 'hidden' || cs.display === 'none') hidden = true;
181
+ }
182
+ } catch(e) {}
183
+
184
+ if (!hidden) {
185
+ // Visible — keep; handle SVG label replacement
186
+ keptIds.push(String(wtId));
187
+ if (el.tagName && el.tagName.toLowerCase() === 'svg') {
188
+ var label = resolveSvgLabel(el, SVG_ANCESTOR_SEL);
189
+ svgLabels[String(wtId)] = label;
190
+ svgCount++;
191
+ }
192
+ return;
193
+ }
194
+
195
+ // Hidden — keep if has interactive descendant
196
+ if (el.__wtHasInteractive) {
197
+ keptIds.push(String(wtId));
198
+ return;
199
+ }
200
+
201
+ // Hidden — keep if referenced by a known toggle trigger (controlsMap)
202
+ var elId = el.getAttribute('id');
203
+ if (elId && controlsMap.has(elId)) {
204
+ keptIds.push(String(wtId));
205
+ return;
206
+ }
207
+
208
+ // Drop
209
+ prunedCount++;
210
+ });
211
+
212
+ // ── Strip synthetic ids from DOM ─────────────────────────────────────────
213
+ for (var ri = 0; ri < syntheticEls.length; ri++) {
214
+ try { syntheticEls[ri].removeAttribute('data-wt-id'); } catch(e) {}
215
+ }
216
+
217
+ // ── SVG label resolution helper ──────────────────────────────────────────
218
+ function resolveSvgLabel(svg, ancestorSel) {
219
+ // 1. SVG's own aria-label
220
+ var label = svg.getAttribute('aria-label');
221
+ if (label && label.trim()) return '[SVG Icon: ' + label.trim() + ']';
222
+
223
+ // 2. SVG's own <title> child
224
+ try {
225
+ var title = svg.querySelector('title');
226
+ if (title && title.textContent && title.textContent.trim()) {
227
+ return '[SVG Icon: ' + title.textContent.trim() + ']';
228
+ }
229
+ } catch(e) {}
230
+
231
+ // 3. Closest interactive ancestor's aria-label
232
+ // Covers: <button aria-label="Delete"><svg/></button>
233
+ // <a role="menuitem" aria-label="More actions"><svg/></a> (Jira)
234
+ // <div role="tab" aria-label="Overview"><svg/></div> (AWS)
235
+ try {
236
+ var ancestor = svg.closest(ancestorSel);
237
+ if (ancestor) {
238
+ var aLabel = ancestor.getAttribute('aria-label');
239
+ if (aLabel && aLabel.trim()) return '[SVG Icon: ' + aLabel.trim() + ']';
240
+ }
241
+ } catch(e) {}
242
+
243
+ return '[SVG Icon]';
244
+ }
245
+
246
+ var elapsedMs = performance.now() - t0;
247
+
248
+ // ── Perf-gate: if this took > 5ms, flag it — caller will decide on split ─
249
+ // (In this single-evaluate path we can only report, not split retroactively.
250
+ // The split path would be invoked by the caller on a second evaluate call.)
251
+ return JSON.stringify({
252
+ keptIds: keptIds,
253
+ svgLabels: svgLabels,
254
+ stats: {
255
+ total: targetIds.size,
256
+ pruned: prunedCount,
257
+ svgReplaced: svgCount,
258
+ elapsedMs: elapsedMs,
259
+ splitPath: false
260
+ }
261
+ });
262
262
  })()`;
263
263
  }
@@ -125,72 +125,72 @@ export function urcPopulate(targetId, elements) {
125
125
  export async function atomicResolve(session, selector, fingerprint, hostname) {
126
126
  try {
127
127
  const { result } = await session.Runtime.evaluate({
128
- expression: `(function(sel, fp) {
129
- try {
130
- // Primary: standard querySelector
131
- var el = document.querySelector(sel);
132
-
133
- // Shadow DOM fallback
134
- if (!el && typeof window.__Weavetab_shadowQuery === 'function') {
135
- el = window.__Weavetab_shadowQuery(sel);
136
- }
137
-
138
- // WeakMap registry fallback: re-derive selector from stored data
139
- if (!el && window.__wt_registry) {
140
- // Walk the registry to find the element whose stored id matches
141
- // (only feasible for small pages — skip for large DOMs by checking size)
142
- try {
143
- var allEls = document.querySelectorAll('[data-wt-id]');
144
- for (var i = 0; i < allEls.length; i++) {
145
- var candidate = allEls[i];
146
- var entry = window.__wt_registry.get(candidate);
147
- if (entry && ('[data-wt-id="' + entry.id + '"]' === sel ||
148
- entry.selector === sel)) {
149
- el = candidate;
150
- break;
151
- }
152
- }
153
- } catch (e) {}
154
- }
155
-
156
- if (!el) return { status: 'NOT_FOUND' };
157
-
158
- // Fingerprint check: "role:type:labelPrefix"
159
- if (fp) {
160
- var parts = fp.split(':');
161
- var fpRole = parts[0];
162
- var fpType = parts[1];
163
- var elRole = el.getAttribute('role') || el.tagName.toLowerCase();
164
- var elType = (el.type || '').toLowerCase();
165
- if (fpRole && fpRole !== '' && elRole !== fpRole && elType !== fpRole) {
166
- return { status: 'STALE' };
167
- }
168
- }
169
-
170
- var rect = el.getBoundingClientRect();
171
- if (!rect.width || !rect.height) return { status: 'INVISIBLE' };
172
-
173
- // Scroll into view if out of viewport
174
- if (rect.top < 0 || rect.bottom > window.innerHeight ||
175
- rect.left < 0 || rect.right > window.innerWidth) {
176
- el.scrollIntoView({ block: 'center', inline: 'center', behavior: 'instant' });
177
- rect = el.getBoundingClientRect();
178
- }
179
-
180
- // Re-hydrate data-wt-id attribute if stripped by SPA reconciliation
181
- var idMatch = sel.match(/\\[data-wt-id="([^"]+)"\\]/);
182
- if (idMatch && !el.hasAttribute('data-wt-id')) {
183
- try { el.setAttribute('data-wt-id', idMatch[1]); } catch(e) {}
184
- }
185
-
186
- return {
187
- status: 'OK',
188
- x: Math.round(rect.left + rect.width / 2),
189
- y: Math.round(rect.top + rect.height / 2)
190
- };
191
- } catch (e) {
192
- return { status: 'NOT_FOUND' };
193
- }
128
+ expression: `(function(sel, fp) {
129
+ try {
130
+ // Primary: standard querySelector
131
+ var el = document.querySelector(sel);
132
+
133
+ // Shadow DOM fallback
134
+ if (!el && typeof window.__Weavetab_shadowQuery === 'function') {
135
+ el = window.__Weavetab_shadowQuery(sel);
136
+ }
137
+
138
+ // WeakMap registry fallback: re-derive selector from stored data
139
+ if (!el && window.__wt_registry) {
140
+ // Walk the registry to find the element whose stored id matches
141
+ // (only feasible for small pages — skip for large DOMs by checking size)
142
+ try {
143
+ var allEls = document.querySelectorAll('[data-wt-id]');
144
+ for (var i = 0; i < allEls.length; i++) {
145
+ var candidate = allEls[i];
146
+ var entry = window.__wt_registry.get(candidate);
147
+ if (entry && ('[data-wt-id="' + entry.id + '"]' === sel ||
148
+ entry.selector === sel)) {
149
+ el = candidate;
150
+ break;
151
+ }
152
+ }
153
+ } catch (e) {}
154
+ }
155
+
156
+ if (!el) return { status: 'NOT_FOUND' };
157
+
158
+ // Fingerprint check: "role:type:labelPrefix"
159
+ if (fp) {
160
+ var parts = fp.split(':');
161
+ var fpRole = parts[0];
162
+ var fpType = parts[1];
163
+ var elRole = el.getAttribute('role') || el.tagName.toLowerCase();
164
+ var elType = (el.type || '').toLowerCase();
165
+ if (fpRole && fpRole !== '' && elRole !== fpRole && elType !== fpRole) {
166
+ return { status: 'STALE' };
167
+ }
168
+ }
169
+
170
+ var rect = el.getBoundingClientRect();
171
+ if (!rect.width || !rect.height) return { status: 'INVISIBLE' };
172
+
173
+ // Scroll into view if out of viewport
174
+ if (rect.top < 0 || rect.bottom > window.innerHeight ||
175
+ rect.left < 0 || rect.right > window.innerWidth) {
176
+ el.scrollIntoView({ block: 'center', inline: 'center', behavior: 'instant' });
177
+ rect = el.getBoundingClientRect();
178
+ }
179
+
180
+ // Re-hydrate data-wt-id attribute if stripped by SPA reconciliation
181
+ var idMatch = sel.match(/\\[data-wt-id="([^"]+)"\\]/);
182
+ if (idMatch && !el.hasAttribute('data-wt-id')) {
183
+ try { el.setAttribute('data-wt-id', idMatch[1]); } catch(e) {}
184
+ }
185
+
186
+ return {
187
+ status: 'OK',
188
+ x: Math.round(rect.left + rect.width / 2),
189
+ y: Math.round(rect.top + rect.height / 2)
190
+ };
191
+ } catch (e) {
192
+ return { status: 'NOT_FOUND' };
193
+ }
194
194
  })(${JSON.stringify(selector)}, ${JSON.stringify(fingerprint ?? null)})`,
195
195
  returnByValue: true,
196
196
  awaitPromise: false,
@@ -216,16 +216,16 @@ async function getProfileFromProcess(session, config) {
216
216
  try {
217
217
  // Use extension-based method to check profile directory
218
218
  const result = await session.Runtime.evaluate({
219
- expression: `
220
- (() => {
221
- try {
222
- // Check if we can read the user data directory from browser internals
223
- if (window.__wt_profile_dir) return window.__wt_profile_dir;
224
- return null;
225
- } catch (e) {
226
- return null;
227
- }
228
- })()
219
+ expression: `
220
+ (() => {
221
+ try {
222
+ // Check if we can read the user data directory from browser internals
223
+ if (window.__wt_profile_dir) return window.__wt_profile_dir;
224
+ return null;
225
+ } catch (e) {
226
+ return null;
227
+ }
228
+ })()
229
229
  `,
230
230
  returnByValue: true,
231
231
  });