@browserbridge/bbx 1.0.0 → 1.0.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 (52) hide show
  1. package/README.md +3 -1
  2. package/docs/api-reference.md +33 -33
  3. package/docs/mcp-vs-cli.md +104 -104
  4. package/docs/publishing.md +1 -3
  5. package/docs/quickstart.md +6 -6
  6. package/docs/unpacked-extension.md +72 -0
  7. package/manifest.json +3 -17
  8. package/package.json +44 -42
  9. package/packages/agent-client/src/cli-helpers.js +10 -5
  10. package/packages/agent-client/src/cli.js +65 -135
  11. package/packages/agent-client/src/client.js +37 -17
  12. package/packages/agent-client/src/command-registry.js +101 -69
  13. package/packages/agent-client/src/detect.js +3 -6
  14. package/packages/agent-client/src/install.js +10 -27
  15. package/packages/agent-client/src/mcp-config.js +11 -30
  16. package/packages/agent-client/src/runtime.js +41 -20
  17. package/packages/agent-client/src/setup-status.js +13 -28
  18. package/packages/extension/src/background-helpers.js +51 -36
  19. package/packages/extension/src/background-routing.js +11 -13
  20. package/packages/extension/src/background.js +562 -299
  21. package/packages/extension/src/content-script-helpers.js +17 -16
  22. package/packages/extension/src/content-script.js +175 -109
  23. package/packages/extension/src/sidepanel-helpers.js +3 -1
  24. package/packages/extension/ui/popup.js +39 -20
  25. package/packages/extension/ui/sidepanel.js +108 -191
  26. package/packages/extension/ui/ui.css +2 -1
  27. package/packages/mcp-server/src/handlers.js +546 -250
  28. package/packages/mcp-server/src/server.js +558 -257
  29. package/packages/native-host/bin/bridge-daemon.js +6 -2
  30. package/packages/native-host/bin/install-manifest.js +2 -2
  31. package/packages/native-host/bin/postinstall.js +4 -2
  32. package/packages/native-host/src/config.js +11 -7
  33. package/packages/native-host/src/daemon.js +143 -92
  34. package/packages/native-host/src/install-manifest.js +73 -22
  35. package/packages/native-host/src/native-host.js +55 -40
  36. package/packages/protocol/src/budget.js +3 -7
  37. package/packages/protocol/src/capabilities.js +3 -3
  38. package/packages/protocol/src/errors.js +11 -11
  39. package/packages/protocol/src/protocol.js +104 -71
  40. package/packages/protocol/src/registry.js +300 -45
  41. package/packages/protocol/src/summary.js +249 -106
  42. package/packages/protocol/src/types.js +1 -1
  43. package/skills/browser-bridge/SKILL.md +1 -1
  44. package/skills/browser-bridge/agents/openai.yaml +3 -3
  45. package/skills/browser-bridge/references/interaction.md +33 -11
  46. package/skills/browser-bridge/references/patch-workflow.md +3 -0
  47. package/skills/browser-bridge/references/protocol.md +125 -70
  48. package/skills/browser-bridge/references/tailwind.md +12 -11
  49. package/skills/browser-bridge/references/token-efficiency.md +23 -22
  50. package/skills/browser-bridge/references/ui-workflows.md +8 -0
  51. package/packages/extension/ui/offscreen.html +0 -6
  52. package/packages/extension/ui/offscreen.js +0 -61
@@ -1,7 +1,10 @@
1
1
  // @ts-check
2
2
 
3
3
  (() => {
4
- const globalState = /** @type {typeof globalThis & { __BBX_CONTENT_HELPERS__?: Record<string, unknown> }} */ (globalThis);
4
+ const globalState =
5
+ /** @type {typeof globalThis & { __BBX_CONTENT_HELPERS__?: Record<string, unknown> }} */ (
6
+ globalThis
7
+ );
5
8
 
6
9
  if (globalState.__BBX_CONTENT_HELPERS__) {
7
10
  return;
@@ -27,7 +30,7 @@
27
30
  'radio',
28
31
  'range',
29
32
  'reset',
30
- 'submit'
33
+ 'submit',
31
34
  ]);
32
35
 
33
36
  /**
@@ -49,11 +52,7 @@
49
52
  return [];
50
53
  }
51
54
 
52
- return [
53
- ...new Set(
54
- value.filter((item) => typeof item === 'string' && item.trim())
55
- )
56
- ];
55
+ return [...new Set(value.filter((item) => typeof item === 'string' && item.trim()))];
57
56
  }
58
57
 
59
58
  /**
@@ -66,7 +65,7 @@
66
65
  maxDepth: clamp(options.maxDepth ?? 4, 1, 20),
67
66
  textBudget: clamp(options.textBudget ?? 600, 32, 10000),
68
67
  includeBbox: options.includeBbox !== false,
69
- attributeAllowlist: normalizeList(options.attributeAllowlist)
68
+ attributeAllowlist: normalizeList(options.attributeAllowlist),
70
69
  };
71
70
  }
72
71
 
@@ -87,7 +86,7 @@
87
86
  return {
88
87
  value: `${value.slice(0, Math.max(0, budget - 1))}\u2026`,
89
88
  truncated: true,
90
- omitted: value.length - budget
89
+ omitted: value.length - budget,
91
90
  };
92
91
  }
93
92
 
@@ -118,7 +117,7 @@
118
117
  search: 'searchbox',
119
118
  submit: 'button',
120
119
  reset: 'button',
121
- image: 'button'
120
+ image: 'button',
122
121
  };
123
122
  return map[type] || 'textbox';
124
123
  }
@@ -160,7 +159,7 @@
160
159
  textarea: 'textbox',
161
160
  th: 'columnheader',
162
161
  tr: 'row',
163
- ul: 'list'
162
+ ul: 'list',
164
163
  };
165
164
  return roleMap[tag] || '';
166
165
  }
@@ -175,14 +174,16 @@
175
174
  link: 'a[href]',
176
175
  article: 'article',
177
176
  complementary: 'aside',
178
- button: 'button, input[type=button], input[type=submit], input[type=reset], input[type=image]',
177
+ button:
178
+ 'button, input[type=button], input[type=submit], input[type=reset], input[type=image]',
179
179
  dialog: 'dialog',
180
180
  contentinfo: 'footer',
181
181
  form: 'form',
182
182
  heading: 'h1, h2, h3, h4, h5, h6',
183
183
  banner: 'header',
184
184
  img: 'img',
185
- textbox: 'input:not([type=button]):not([type=checkbox]):not([type=radio]):not([type=range]):not([type=submit]):not([type=reset]):not([type=image]):not([type=hidden]), textarea',
185
+ textbox:
186
+ 'input:not([type=button]):not([type=checkbox]):not([type=radio]):not([type=range]):not([type=submit]):not([type=reset]):not([type=image]):not([type=hidden]), textarea',
186
187
  listitem: 'li',
187
188
  main: 'main',
188
189
  navigation: 'nav',
@@ -198,7 +199,7 @@
198
199
  checkbox: 'input[type=checkbox]',
199
200
  radio: 'input[type=radio]',
200
201
  slider: 'input[type=range]',
201
- searchbox: 'input[type=search]'
202
+ searchbox: 'input[type=search]',
202
203
  };
203
204
  return map[role] || '';
204
205
  }
@@ -212,7 +213,7 @@
212
213
  x: rect.x + window.scrollX,
213
214
  y: rect.y + window.scrollY,
214
215
  width: rect.width,
215
- height: rect.height
216
+ height: rect.height,
216
217
  };
217
218
  }
218
219
 
@@ -276,6 +277,6 @@
276
277
  getInputImplicitRole,
277
278
  normalizeList,
278
279
  toRect,
279
- truncateText
280
+ truncateText,
280
281
  });
281
282
  })();
@@ -1,7 +1,10 @@
1
1
  // @ts-check
2
2
 
3
3
  (() => {
4
- const contentScriptGlobal = /** @type {typeof globalThis & { __chromeCodexBridgeContentScriptLoaded?: boolean }} */ (globalThis);
4
+ const contentScriptGlobal =
5
+ /** @type {typeof globalThis & { __chromeCodexBridgeContentScriptLoaded?: boolean }} */ (
6
+ globalThis
7
+ );
5
8
  if (contentScriptGlobal.__chromeCodexBridgeContentScriptLoaded) {
6
9
  return;
7
10
  }
@@ -43,7 +46,8 @@
43
46
  const MAX_REGISTRY_SIZE = 5000;
44
47
  const MAX_PATCH_REGISTRY_SIZE = 2000;
45
48
  let registryPruned = false;
46
- const contentHelpers = /** @type {typeof globalThis & { __BBX_CONTENT_HELPERS__?: {
49
+ const contentHelpers =
50
+ /** @type {typeof globalThis & { __BBX_CONTENT_HELPERS__?: {
47
51
  NON_TEXT_INPUT_TYPES: Set<string>,
48
52
  applyBudget: (options?: Record<string, any>) => Budget,
49
53
  clamp: (value: number | string | null | undefined, minimum: number, maximum: number) => number,
@@ -66,7 +70,7 @@
66
70
  getImplicitRole,
67
71
  getImplicitRoleSelector,
68
72
  toRect,
69
- truncateText
73
+ truncateText,
70
74
  } = contentHelpers;
71
75
 
72
76
  chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
@@ -81,11 +85,17 @@
81
85
 
82
86
  try {
83
87
  const result = handleCommand(message.method, message.params);
84
- Promise.resolve(result).then(sendResponse).catch((err) => {
85
- sendResponse({ error: err instanceof Error ? err.message : String(err) });
86
- });
88
+ Promise.resolve(result)
89
+ .then(sendResponse)
90
+ .catch((err) => {
91
+ sendResponse({
92
+ error: err instanceof Error ? err.message : String(err),
93
+ });
94
+ });
87
95
  } catch (error) {
88
- sendResponse({ error: error instanceof Error ? error.message : String(error) });
96
+ sendResponse({
97
+ error: error instanceof Error ? error.message : String(error),
98
+ });
89
99
  }
90
100
 
91
101
  return true;
@@ -128,7 +138,7 @@
128
138
  case 'dom.get_html':
129
139
  return getHtml({
130
140
  ...params,
131
- elementRef: resolveElementRefFromParams(params)
141
+ elementRef: resolveElementRefFromParams(params),
132
142
  });
133
143
  case 'layout.get_box_model':
134
144
  return getBoxModel(resolveElementRefFromParams(params));
@@ -194,8 +204,7 @@
194
204
  * }}
195
205
  */
196
206
  function getPageState() {
197
- const scrollingElement =
198
- document.scrollingElement || document.documentElement || document.body;
207
+ const scrollingElement = document.scrollingElement || document.documentElement || document.body;
199
208
  const selection = document.getSelection?.()?.toString() || '';
200
209
 
201
210
  return {
@@ -215,22 +224,22 @@
215
224
  maxX: Math.max(
216
225
  0,
217
226
  (scrollingElement?.scrollWidth || document.documentElement.scrollWidth || 0) -
218
- window.innerWidth,
227
+ window.innerWidth
219
228
  ),
220
229
  maxY: Math.max(
221
230
  0,
222
231
  (scrollingElement?.scrollHeight || document.documentElement.scrollHeight || 0) -
223
- window.innerHeight,
232
+ window.innerHeight
224
233
  ),
225
234
  },
226
235
  activeElement:
227
236
  document.activeElement instanceof Element
228
237
  ? summarizeNode(
229
- document.activeElement,
230
- ['id', 'class', 'name', 'type', 'href', 'role'],
231
- 120,
232
- true,
233
- ).node
238
+ document.activeElement,
239
+ ['id', 'class', 'name', 'type', 'href', 'role'],
240
+ 120,
241
+ true
242
+ ).node
234
243
  : null,
235
244
  selection: truncateText(selection.trim(), 200),
236
245
  hints: detectPageHints(),
@@ -258,7 +267,8 @@
258
267
  if (!tailwind) {
259
268
  // Check for Tailwind's characteristic class patterns on a sample of elements
260
269
  const sample = document.querySelectorAll('[class]');
261
- const twPattern = /\b(?:flex|grid|bg-|text-|p[xytblr]?-|m[xytblr]?-|w-|h-|rounded|shadow|border)-/;
270
+ const twPattern =
271
+ /\b(?:flex|grid|bg-|text-|p[xytblr]?-|m[xytblr]?-|w-|h-|rounded|shadow|border)-/;
262
272
  for (let i = 0; i < Math.min(sample.length, 30); i++) {
263
273
  const cls = sample[i].className;
264
274
  if (typeof cls === 'string' && twPattern.test(cls)) {
@@ -291,7 +301,7 @@
291
301
  value: result.value,
292
302
  truncated: result.truncated,
293
303
  omitted: result.omitted,
294
- length: raw.length
304
+ length: raw.length,
295
305
  };
296
306
  }
297
307
 
@@ -308,7 +318,11 @@
308
318
  ? getRequiredElement(query.withinRef)
309
319
  : document.querySelector(query.selector);
310
320
  if (!root) {
311
- return { nodes: [], revision: getDocumentRevision(), registrySize: elementRegistry.size };
321
+ return {
322
+ nodes: [],
323
+ revision: getDocumentRevision(),
324
+ registrySize: elementRegistry.size,
325
+ };
312
326
  }
313
327
 
314
328
  /** @type {NodeSummary[]} */
@@ -317,11 +331,7 @@
317
331
  /** @type {Array<{ element: Element, depth: number }>} */
318
332
  const queue = [{ element: root, depth: 0 }];
319
333
 
320
- while (
321
- queue.length &&
322
- nodes.length < query.budget.maxNodes &&
323
- remaining > 0
324
- ) {
334
+ while (queue.length && nodes.length < query.budget.maxNodes && remaining > 0) {
325
335
  const next = queue.shift();
326
336
  if (!next) {
327
337
  continue;
@@ -335,7 +345,7 @@
335
345
  element,
336
346
  query.budget.attributeAllowlist,
337
347
  remaining,
338
- query.budget.includeBbox,
348
+ query.budget.includeBbox
339
349
  );
340
350
  remaining -= summary.textLength;
341
351
  nodes.push(summary.node);
@@ -369,7 +379,7 @@
369
379
  const elementRef = rememberElement(element);
370
380
  const text = truncateText(
371
381
  extractElementText(element),
372
- Math.min(Math.max(0, remainingText), 160),
382
+ Math.min(Math.max(0, remainingText), 160)
373
383
  );
374
384
  return {
375
385
  textLength: text.value.length,
@@ -377,10 +387,7 @@
377
387
  elementRef,
378
388
  tag: element.tagName.toLowerCase(),
379
389
  role: element.getAttribute('role'),
380
- name:
381
- element.getAttribute('aria-label') ||
382
- element.getAttribute('name') ||
383
- null,
390
+ name: element.getAttribute('aria-label') || element.getAttribute('name') || null,
384
391
  textExcerpt: text.value,
385
392
  attrs: summarizeAttributes(element, attributeAllowlist),
386
393
  ...(includeBbox ? { bbox: toRect(element.getBoundingClientRect()) } : {}),
@@ -432,10 +439,7 @@
432
439
  */
433
440
  function getText(elementRef, budget = 600) {
434
441
  const element = /** @type {HTMLElement} */ (getRequiredElement(elementRef));
435
- return truncateText(
436
- (element.innerText || element.textContent || '').trim(),
437
- budget,
438
- );
442
+ return truncateText((element.innerText || element.textContent || '').trim(), budget);
439
443
  }
440
444
 
441
445
  /**
@@ -707,10 +711,7 @@
707
711
 
708
712
  const result = runKeyAction(target, key, params.modifiers);
709
713
  return {
710
- elementRef:
711
- result.target instanceof Element
712
- ? rememberElement(result.target)
713
- : null,
714
+ elementRef: result.target instanceof Element ? rememberElement(result.target) : null,
714
715
  key: result.key,
715
716
  handled: result.handled,
716
717
  };
@@ -765,8 +766,8 @@
765
766
  : [];
766
767
  const indexes = Array.isArray(params.indexes)
767
768
  ? params.indexes
768
- .map((index) => Number(index))
769
- .filter((index) => Number.isInteger(index) && index >= 0)
769
+ .map((index) => Number(index))
770
+ .filter((index) => Number.isInteger(index) && index >= 0)
770
771
  : [];
771
772
 
772
773
  if (!values.length && !labels.length && !indexes.length) {
@@ -828,11 +829,7 @@
828
829
  const previous = {};
829
830
  for (const [property, value] of Object.entries(params.declarations || {})) {
830
831
  previous[property] = element.style.getPropertyValue(property);
831
- element.style.setProperty(
832
- property,
833
- value,
834
- params.important ? 'important' : '',
835
- );
832
+ element.style.setProperty(property, value, params.important ? 'important' : '');
836
833
  }
837
834
  pruneRegistry(patchRegistry, MAX_PATCH_REGISTRY_SIZE);
838
835
  const elementRef = rememberElement(element);
@@ -991,12 +988,16 @@
991
988
  function getElementRect(elementRef) {
992
989
  const el = getRequiredElement(elementRef);
993
990
  // Scroll into view so CDP can capture it in the visible viewport
994
- el.scrollIntoView({ block: 'center', inline: 'center', behavior: 'instant' });
991
+ el.scrollIntoView({
992
+ block: 'center',
993
+ inline: 'center',
994
+ behavior: 'instant',
995
+ });
995
996
  const rect = el.getBoundingClientRect();
996
997
  if (rect.width < 1 || rect.height < 1) {
997
998
  throw new Error(
998
999
  `Element has no visible area (${rect.width}\u00d7${rect.height}). ` +
999
- 'It may be hidden, collapsed, or not yet rendered.'
1000
+ 'It may be hidden, collapsed, or not yet rendered.'
1000
1001
  );
1001
1002
  }
1002
1003
  const x = Math.max(0, rect.x);
@@ -1006,8 +1007,8 @@
1006
1007
  if (width < 1 || height < 1) {
1007
1008
  throw new Error(
1008
1009
  'Element is outside the visible viewport after scroll ' +
1009
- `(${Math.round(rect.x)},${Math.round(rect.y)} ${Math.round(rect.width)}\u00d7${Math.round(rect.height)}). ` +
1010
- 'It may be in a fixed/sticky container or an iframe.'
1010
+ `(${Math.round(rect.x)},${Math.round(rect.y)} ${Math.round(rect.width)}\u00d7${Math.round(rect.height)}). ` +
1011
+ 'It may be in a fixed/sticky container or an iframe.'
1011
1012
  );
1012
1013
  }
1013
1014
  return { x, y, width, height, scale: window.devicePixelRatio || 1 };
@@ -1157,7 +1158,9 @@
1157
1158
  ? visibleText === searchText
1158
1159
  : visibleText.toLowerCase().includes(searchText.toLowerCase());
1159
1160
  if (matches) {
1160
- results.push(summarizeNode(el, ['id', 'class', 'role', 'href', 'data-testid'], 120, true).node);
1161
+ results.push(
1162
+ summarizeNode(el, ['id', 'class', 'role', 'href', 'data-testid'], 120, true).node
1163
+ );
1161
1164
  }
1162
1165
  }
1163
1166
 
@@ -1181,9 +1184,12 @@
1181
1184
 
1182
1185
  const implicitSelector = getImplicitRoleSelector(role);
1183
1186
  const attrSelector = `[role="${CSS.escape(role)}"]`;
1184
- const combinedSelector = scope === '*'
1185
- ? (implicitSelector ? `${attrSelector}, ${implicitSelector}` : attrSelector)
1186
- : scope;
1187
+ const combinedSelector =
1188
+ scope === '*'
1189
+ ? implicitSelector
1190
+ ? `${attrSelector}, ${implicitSelector}`
1191
+ : attrSelector
1192
+ : scope;
1187
1193
  const candidates = document.querySelectorAll(combinedSelector);
1188
1194
  const results = [];
1189
1195
 
@@ -1201,7 +1207,9 @@
1201
1207
  continue;
1202
1208
  }
1203
1209
  }
1204
- results.push(summarizeNode(el, ['id', 'class', 'role', 'aria-label', 'href'], 120, true).node);
1210
+ results.push(
1211
+ summarizeNode(el, ['id', 'class', 'role', 'aria-label', 'href'], 120, true).node
1212
+ );
1205
1213
  }
1206
1214
 
1207
1215
  return { nodes: results, count: results.length };
@@ -1264,47 +1272,100 @@
1264
1272
  const offsetX = Number(params.offsetX) || 0;
1265
1273
  const offsetY = Number(params.offsetY) || 0;
1266
1274
  const endPoint = { x: destPoint.x + offsetX, y: destPoint.y + offsetY };
1267
- const emptyMods = { altKey: false, ctrlKey: false, metaKey: false, shiftKey: false };
1275
+ const emptyMods = {
1276
+ altKey: false,
1277
+ ctrlKey: false,
1278
+ metaKey: false,
1279
+ shiftKey: false,
1280
+ };
1268
1281
 
1269
1282
  scrollTargetIntoView(source);
1270
1283
 
1271
1284
  const dataTransfer = new DataTransfer();
1272
1285
 
1273
- source.dispatchEvent(new MouseEvent('mousedown', {
1274
- bubbles: true, cancelable: true, composed: true,
1275
- clientX: sourcePoint.x, clientY: sourcePoint.y, ...emptyMods,
1276
- }));
1277
- source.dispatchEvent(new DragEvent('dragstart', {
1278
- bubbles: true, cancelable: true, composed: true,
1279
- clientX: sourcePoint.x, clientY: sourcePoint.y, dataTransfer,
1280
- }));
1281
- source.dispatchEvent(new DragEvent('drag', {
1282
- bubbles: true, cancelable: true, composed: true,
1283
- clientX: sourcePoint.x, clientY: sourcePoint.y, dataTransfer,
1284
- }));
1286
+ source.dispatchEvent(
1287
+ new MouseEvent('mousedown', {
1288
+ bubbles: true,
1289
+ cancelable: true,
1290
+ composed: true,
1291
+ clientX: sourcePoint.x,
1292
+ clientY: sourcePoint.y,
1293
+ ...emptyMods,
1294
+ })
1295
+ );
1296
+ source.dispatchEvent(
1297
+ new DragEvent('dragstart', {
1298
+ bubbles: true,
1299
+ cancelable: true,
1300
+ composed: true,
1301
+ clientX: sourcePoint.x,
1302
+ clientY: sourcePoint.y,
1303
+ dataTransfer,
1304
+ })
1305
+ );
1306
+ source.dispatchEvent(
1307
+ new DragEvent('drag', {
1308
+ bubbles: true,
1309
+ cancelable: true,
1310
+ composed: true,
1311
+ clientX: sourcePoint.x,
1312
+ clientY: sourcePoint.y,
1313
+ dataTransfer,
1314
+ })
1315
+ );
1285
1316
 
1286
1317
  scrollTargetIntoView(destination);
1287
1318
 
1288
- destination.dispatchEvent(new DragEvent('dragenter', {
1289
- bubbles: true, cancelable: true, composed: true,
1290
- clientX: endPoint.x, clientY: endPoint.y, dataTransfer,
1291
- }));
1292
- destination.dispatchEvent(new DragEvent('dragover', {
1293
- bubbles: true, cancelable: true, composed: true,
1294
- clientX: endPoint.x, clientY: endPoint.y, dataTransfer,
1295
- }));
1296
- destination.dispatchEvent(new DragEvent('drop', {
1297
- bubbles: true, cancelable: true, composed: true,
1298
- clientX: endPoint.x, clientY: endPoint.y, dataTransfer,
1299
- }));
1300
- source.dispatchEvent(new DragEvent('dragend', {
1301
- bubbles: true, cancelable: true, composed: true,
1302
- clientX: endPoint.x, clientY: endPoint.y, dataTransfer,
1303
- }));
1304
- source.dispatchEvent(new MouseEvent('mouseup', {
1305
- bubbles: true, cancelable: true, composed: true,
1306
- clientX: endPoint.x, clientY: endPoint.y, ...emptyMods,
1307
- }));
1319
+ destination.dispatchEvent(
1320
+ new DragEvent('dragenter', {
1321
+ bubbles: true,
1322
+ cancelable: true,
1323
+ composed: true,
1324
+ clientX: endPoint.x,
1325
+ clientY: endPoint.y,
1326
+ dataTransfer,
1327
+ })
1328
+ );
1329
+ destination.dispatchEvent(
1330
+ new DragEvent('dragover', {
1331
+ bubbles: true,
1332
+ cancelable: true,
1333
+ composed: true,
1334
+ clientX: endPoint.x,
1335
+ clientY: endPoint.y,
1336
+ dataTransfer,
1337
+ })
1338
+ );
1339
+ destination.dispatchEvent(
1340
+ new DragEvent('drop', {
1341
+ bubbles: true,
1342
+ cancelable: true,
1343
+ composed: true,
1344
+ clientX: endPoint.x,
1345
+ clientY: endPoint.y,
1346
+ dataTransfer,
1347
+ })
1348
+ );
1349
+ source.dispatchEvent(
1350
+ new DragEvent('dragend', {
1351
+ bubbles: true,
1352
+ cancelable: true,
1353
+ composed: true,
1354
+ clientX: endPoint.x,
1355
+ clientY: endPoint.y,
1356
+ dataTransfer,
1357
+ })
1358
+ );
1359
+ source.dispatchEvent(
1360
+ new MouseEvent('mouseup', {
1361
+ bubbles: true,
1362
+ cancelable: true,
1363
+ composed: true,
1364
+ clientX: endPoint.x,
1365
+ clientY: endPoint.y,
1366
+ ...emptyMods,
1367
+ })
1368
+ );
1308
1369
 
1309
1370
  return {
1310
1371
  sourceRef: rememberElement(source),
@@ -1334,7 +1395,9 @@
1334
1395
  function getStorageData(params) {
1335
1396
  const type = params.type === 'session' ? 'session' : 'local';
1336
1397
  const storage = type === 'session' ? sessionStorage : localStorage;
1337
- const keys = Array.isArray(params.keys) ? params.keys.filter((k) => typeof k === 'string') : null;
1398
+ const keys = Array.isArray(params.keys)
1399
+ ? params.keys.filter((k) => typeof k === 'string')
1400
+ : null;
1338
1401
  /** @type {Record<string, string | null>} */
1339
1402
  const result = {};
1340
1403
  if (keys) {
@@ -1452,7 +1515,10 @@
1452
1515
  return element;
1453
1516
  }
1454
1517
 
1455
- if (element instanceof HTMLOptionElement && element.parentElement instanceof HTMLSelectElement) {
1518
+ if (
1519
+ element instanceof HTMLOptionElement &&
1520
+ element.parentElement instanceof HTMLSelectElement
1521
+ ) {
1456
1522
  return element.parentElement;
1457
1523
  }
1458
1524
 
@@ -1578,9 +1644,7 @@
1578
1644
  });
1579
1645
  }
1580
1646
 
1581
- return document.activeElement instanceof Element
1582
- ? document.activeElement
1583
- : element;
1647
+ return document.activeElement instanceof Element ? document.activeElement : element;
1584
1648
  }
1585
1649
 
1586
1650
  /**
@@ -1664,7 +1728,7 @@
1664
1728
  button: buttonState.button,
1665
1729
  buttons: buttonState.buttons,
1666
1730
  ...modifiers,
1667
- }),
1731
+ })
1668
1732
  );
1669
1733
  }
1670
1734
 
@@ -1681,7 +1745,9 @@
1681
1745
  return null;
1682
1746
  }
1683
1747
 
1684
- const editable = element.querySelector("input, textarea, [contenteditable=''], [contenteditable='true']");
1748
+ const editable = element.querySelector(
1749
+ "input, textarea, [contenteditable=''], [contenteditable='true']"
1750
+ );
1685
1751
  return editable && isEditableElement(editable)
1686
1752
  ? /** @type {HTMLInputElement | HTMLTextAreaElement | HTMLElement} */ (editable)
1687
1753
  : null;
@@ -1810,7 +1876,7 @@
1810
1876
  cancelable: true,
1811
1877
  composed: true,
1812
1878
  ...modifiers,
1813
- }),
1879
+ })
1814
1880
  );
1815
1881
  }
1816
1882
 
@@ -1828,7 +1894,7 @@
1828
1894
  bubbles: true,
1829
1895
  cancelable: true,
1830
1896
  composed: true,
1831
- }),
1897
+ })
1832
1898
  );
1833
1899
  }
1834
1900
 
@@ -1845,7 +1911,7 @@
1845
1911
  inputType,
1846
1912
  bubbles: true,
1847
1913
  composed: true,
1848
- }),
1914
+ })
1849
1915
  );
1850
1916
  }
1851
1917
 
@@ -1877,8 +1943,7 @@
1877
1943
  * @returns {boolean}
1878
1944
  */
1879
1945
  function deleteTextFromEditable(element, direction) {
1880
- const inputType =
1881
- direction === 'backward' ? 'deleteContentBackward' : 'deleteContentForward';
1946
+ const inputType = direction === 'backward' ? 'deleteContentBackward' : 'deleteContentForward';
1882
1947
  if (!dispatchBeforeInputEvent(element, '', inputType)) {
1883
1948
  return false;
1884
1949
  }
@@ -1896,9 +1961,7 @@
1896
1961
  } else {
1897
1962
  const text = element.textContent || '';
1898
1963
  element.textContent =
1899
- direction === 'backward'
1900
- ? text.slice(0, Math.max(0, text.length - 1))
1901
- : text.slice(1);
1964
+ direction === 'backward' ? text.slice(0, Math.max(0, text.length - 1)) : text.slice(1);
1902
1965
  }
1903
1966
 
1904
1967
  dispatchInputEvent(element, '', inputType);
@@ -1911,7 +1974,10 @@
1911
1974
  */
1912
1975
  function handleEnterKey(element) {
1913
1976
  const editable = getEditableTarget(element);
1914
- if (editable instanceof HTMLTextAreaElement || (editable instanceof HTMLElement && editable.isContentEditable)) {
1977
+ if (
1978
+ editable instanceof HTMLTextAreaElement ||
1979
+ (editable instanceof HTMLElement && editable.isContentEditable)
1980
+ ) {
1915
1981
  return insertTextIntoEditable(editable, '\n');
1916
1982
  }
1917
1983
 
@@ -1920,7 +1986,10 @@
1920
1986
  return true;
1921
1987
  }
1922
1988
 
1923
- if (element instanceof HTMLButtonElement || (element instanceof HTMLInputElement && ['button', 'submit'].includes(element.type))) {
1989
+ if (
1990
+ element instanceof HTMLButtonElement ||
1991
+ (element instanceof HTMLInputElement && ['button', 'submit'].includes(element.type))
1992
+ ) {
1924
1993
  element.click();
1925
1994
  return true;
1926
1995
  }
@@ -1964,14 +2033,11 @@
1964
2033
  */
1965
2034
  function normalizeDomQuery(params = {}) {
1966
2035
  const rawSelector =
1967
- typeof params.selector === 'string' && params.selector.trim()
1968
- ? params.selector
1969
- : 'body';
2036
+ typeof params.selector === 'string' && params.selector.trim() ? params.selector : 'body';
1970
2037
  return {
1971
2038
  selector: escapeTailwindSelector(rawSelector),
1972
2039
  withinRef: typeof params.withinRef === 'string' ? params.withinRef : null,
1973
2040
  budget: applyBudget(params),
1974
2041
  };
1975
2042
  }
1976
-
1977
2043
  })();
@@ -31,7 +31,9 @@
31
31
  */
32
32
  function getSetupInstallState(setupStatus) {
33
33
  const hasConfiguredMcp = setupStatus.mcpClients.some((client) => client.configured);
34
- const hasInstalledCliSkill = setupStatus.skillTargets.some((target) => target.skills.some((skill) => skill.exists));
34
+ const hasInstalledCliSkill = setupStatus.skillTargets.some((target) =>
35
+ target.skills.some((skill) => skill.exists)
36
+ );
35
37
  return { hasConfiguredMcp, hasInstalledCliSkill };
36
38
  }
37
39