@reticlehq/browser 3.4.0 → 3.5.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.
- package/dist/actions/actions.d.ts +0 -1
- package/dist/actions/actions.js +0 -10
- package/dist/commands/commands.js +2 -6
- package/dist/dom/a11y.js +30 -7
- package/dist/dom/occlusion.js +5 -1
- package/dist/dom/query.js +4 -4
- package/dist/reticle-inject.js +153 -153
- package/package.json +2 -2
|
@@ -36,7 +36,6 @@ interface ActionResult {
|
|
|
36
36
|
}
|
|
37
37
|
export declare const ZOOM_NEEDS_REAL_BROWSER_MSG: string;
|
|
38
38
|
export declare function executeAction(ref: string, action: string, args?: Record<string, unknown>): Promise<ActionResult>;
|
|
39
|
-
export declare function dispatchWebMcp(tool: string, params: Record<string, unknown>, confirmDangerous?: boolean): Promise<unknown>;
|
|
40
39
|
export interface ActionStep {
|
|
41
40
|
ref: string;
|
|
42
41
|
action: string;
|
package/dist/actions/actions.js
CHANGED
|
@@ -410,16 +410,6 @@ export async function executeAction(ref, action, args = {}) {
|
|
|
410
410
|
return result(ref, action, effect, settled, settleReason, anchor, warning);
|
|
411
411
|
}
|
|
412
412
|
const sleep = (ms) => new Promise((r) => nativeSetTimeout(r, ms));
|
|
413
|
-
export async function dispatchWebMcp(tool, params, confirmDangerous = false) {
|
|
414
|
-
if (requiresDangerousConfirmation(tool) && !confirmDangerous) {
|
|
415
|
-
throw new Error(`potentially destructive WebMCP tool blocked; retry with ${DANGEROUS_ACTION_CONFIRM_ARG}=true`);
|
|
416
|
-
}
|
|
417
|
-
const mc = navigator.modelContext;
|
|
418
|
-
if (mc === undefined || typeof mc.callTool !== 'function') {
|
|
419
|
-
throw new Error('WebMCP (navigator.modelContext) not available on this page');
|
|
420
|
-
}
|
|
421
|
-
return await mc.callTool(tool, params);
|
|
422
|
-
}
|
|
423
413
|
export async function executeSequence(steps) {
|
|
424
414
|
const effects = [];
|
|
425
415
|
const stepResults = [];
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ComponentStateReason, ElementQuerySchema, ReticleCommand, SnapshotMode, TRANSPORT_LIMITS, selectPath, capDepth, } from '@reticlehq/core';
|
|
2
2
|
import { buildSnapshot } from '../dom/snapshot.js';
|
|
3
3
|
import { paintContextOf } from '../dom/paint-context.js';
|
|
4
4
|
import { matchQuery, runQuery } from '../dom/query.js';
|
|
5
|
-
import { executeAction, executeSequence
|
|
5
|
+
import { executeAction, executeSequence } from '../actions/actions.js';
|
|
6
6
|
import { describe } from '../dom/a11y.js';
|
|
7
7
|
import { documentHasSourceStamps, sourceFor, formatSource } from '../dom/addressing/source.js';
|
|
8
8
|
import { themeReport } from '../dom/theme.js';
|
|
@@ -198,10 +198,6 @@ export function createCommandRegistry() {
|
|
|
198
198
|
reg.set(ReticleCommand.MATCH, (args) => matchQuery(ElementQuerySchema.parse(record(args['query'])), str(args['state'])));
|
|
199
199
|
reg.set(ReticleCommand.ACT, (args) => {
|
|
200
200
|
const action = str(args['action']) ?? '';
|
|
201
|
-
if (action === ActionType.WEBMCP) {
|
|
202
|
-
const inner = record(args['args']);
|
|
203
|
-
return dispatchWebMcp(str(inner['tool']) ?? '', record(inner['params']), true === inner[DANGEROUS_ACTION_CONFIRM_ARG]);
|
|
204
|
-
}
|
|
205
201
|
return executeAction(str(args['ref']) ?? '', action, record(args['args']));
|
|
206
202
|
});
|
|
207
203
|
reg.set(ReticleCommand.ACT_SEQUENCE, (args) => executeSequence((Array.isArray(args['steps']) ? args['steps'] : [])));
|
package/dist/dom/a11y.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { ElementState, REDACTED_VALUE } from '@reticlehq/core';
|
|
2
2
|
import { isButton, isHtmlElement, isImage, isInput, isMeter, isOutput, isProgress, isSelect, isTextArea, } from './realm.js';
|
|
3
3
|
import { refs } from './addressing/refs.js';
|
|
4
|
+
import { capturedRootOf } from './shadow-registry.js';
|
|
4
5
|
import { inspectChart } from './chart.js';
|
|
5
6
|
import { isSensitiveKey } from '../security/serialization.js';
|
|
6
7
|
import { formatSource, sourceFromDom } from './addressing/source.js';
|
|
7
8
|
const HTML_DETAILS_TAG = 'details';
|
|
8
9
|
const HTML_DETAILS_OPEN_ATTRIBUTE = 'open';
|
|
9
|
-
const
|
|
10
|
+
const HTML_SUMMARY_TAG = 'summary';
|
|
10
11
|
const NAME_FROM_CONTENT = new Set([
|
|
11
12
|
'button',
|
|
12
13
|
'link',
|
|
@@ -173,13 +174,19 @@ function textWithoutHidden(node) {
|
|
|
173
174
|
const alt = el.getAttribute('alt');
|
|
174
175
|
return null === alt ? '' : alt;
|
|
175
176
|
}
|
|
176
|
-
|
|
177
|
+
let result = '';
|
|
178
|
+
let lastWasElement = false;
|
|
177
179
|
for (const child of el.childNodes) {
|
|
178
180
|
const piece = textWithoutHidden(child);
|
|
179
|
-
if (piece.length
|
|
180
|
-
|
|
181
|
+
if (0 === piece.length)
|
|
182
|
+
continue;
|
|
183
|
+
const isEl = Node.ELEMENT_NODE === child.nodeType;
|
|
184
|
+
if (result.length > 0 && (isEl || lastWasElement))
|
|
185
|
+
result += ' ';
|
|
186
|
+
result += piece;
|
|
187
|
+
lastWasElement = isEl;
|
|
181
188
|
}
|
|
182
|
-
return
|
|
189
|
+
return result;
|
|
183
190
|
}
|
|
184
191
|
export function getAccessibleName(el) {
|
|
185
192
|
const labelled = labelledByText(el);
|
|
@@ -299,8 +306,8 @@ function hiddenInsideClosedDetails(el) {
|
|
|
299
306
|
const details = parent.closest(HTML_DETAILS_TAG);
|
|
300
307
|
if (null === details || details.hasAttribute(HTML_DETAILS_OPEN_ATTRIBUTE))
|
|
301
308
|
return false;
|
|
302
|
-
const summary = details.
|
|
303
|
-
return
|
|
309
|
+
const summary = Array.from(details.children).find((child) => HTML_SUMMARY_TAG === child.localName);
|
|
310
|
+
return summary === undefined || !summary.contains(el);
|
|
304
311
|
}
|
|
305
312
|
function selfHidden(el) {
|
|
306
313
|
if ('true' === el.getAttribute('aria-hidden'))
|
|
@@ -322,7 +329,23 @@ function selfHidden(el) {
|
|
|
322
329
|
}
|
|
323
330
|
return false;
|
|
324
331
|
}
|
|
332
|
+
function slotInCapturedClosedRoot(el) {
|
|
333
|
+
const host = el.parentElement;
|
|
334
|
+
if (null === host || null !== host.shadowRoot)
|
|
335
|
+
return null;
|
|
336
|
+
const root = capturedRootOf(host);
|
|
337
|
+
if (null === root)
|
|
338
|
+
return null;
|
|
339
|
+
for (const slot of Array.from(root.querySelectorAll('slot'))) {
|
|
340
|
+
if (slot.assignedElements().includes(el))
|
|
341
|
+
return slot;
|
|
342
|
+
}
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
325
345
|
function parentAcrossShadowBoundary(el) {
|
|
346
|
+
const slot = el.assignedSlot ?? slotInCapturedClosedRoot(el);
|
|
347
|
+
if (null !== slot)
|
|
348
|
+
return slot;
|
|
326
349
|
if (null !== el.parentElement)
|
|
327
350
|
return el.parentElement;
|
|
328
351
|
const host = el.getRootNode().host;
|
package/dist/dom/occlusion.js
CHANGED
|
@@ -14,9 +14,13 @@ export function hitTestOccluder(el, rect) {
|
|
|
14
14
|
if (typeof doc.elementFromPoint !== 'function')
|
|
15
15
|
return null;
|
|
16
16
|
const withStack = doc;
|
|
17
|
+
const isOursButNotTheTarget = (candidate) => isReticleUi(candidate) &&
|
|
18
|
+
candidate !== el &&
|
|
19
|
+
!el.contains(candidate) &&
|
|
20
|
+
!candidate.contains(el);
|
|
17
21
|
const topAt = (x, y) => {
|
|
18
22
|
const found = withStack.elementsFromPoint?.(x, y) ?? [doc.elementFromPoint(x, y)];
|
|
19
|
-
return found.find((c) => null !== c && !
|
|
23
|
+
return found.find((c) => null !== c && !isOursButNotTheTarget(c)) ?? null;
|
|
20
24
|
};
|
|
21
25
|
const blockers = new Map();
|
|
22
26
|
for (const [fx, fy] of SAMPLE_POINTS) {
|
package/dist/dom/query.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DATA_RETICLE_SOURCE_ATTR, ElementState, QueryBy, REDACTED_VALUE, TRANSPORT_LIMITS, } from '@reticlehq/core';
|
|
2
|
-
import { isElement, isFrame,
|
|
2
|
+
import { isActionTarget, isElement, isFrame, isInput, isSelect, isTextArea } from './realm.js';
|
|
3
3
|
import { capturedRootOf } from './shadow-registry.js';
|
|
4
4
|
import { getAccessibleName, getRole, describe, getStates, isInViewport, isVisible, } from './a11y.js';
|
|
5
5
|
import { isIgnored, isReticleOverlay } from './dom-ignore.js';
|
|
@@ -71,11 +71,11 @@ function resolveContainer(scope) {
|
|
|
71
71
|
if (scope === undefined)
|
|
72
72
|
return { container: document.body, scopeMissing: false };
|
|
73
73
|
const byRef = refs.resolve(scope);
|
|
74
|
-
if (
|
|
74
|
+
if (isActionTarget(byRef))
|
|
75
75
|
return { container: byRef, scopeMissing: false };
|
|
76
76
|
try {
|
|
77
77
|
const found = document.querySelector(scope);
|
|
78
|
-
if (
|
|
78
|
+
if (isActionTarget(found))
|
|
79
79
|
return { container: found, scopeMissing: false };
|
|
80
80
|
}
|
|
81
81
|
catch {
|
|
@@ -269,7 +269,7 @@ export function matchQuery(query, state, limit = MAX_DESCRIBED) {
|
|
|
269
269
|
const filtered = state === undefined ? elements : elements.filter((el) => inState(el, state, visMemo));
|
|
270
270
|
const attrs = query.attrs;
|
|
271
271
|
const described = filtered.slice(0, Math.max(0, Math.min(limit, MAX_DESCRIBED)));
|
|
272
|
-
const stampViewport = described.length > 1;
|
|
272
|
+
const stampViewport = described.length > 1 || ElementState.IN_VIEWPORT === state;
|
|
273
273
|
const descriptors = described.map((el) => {
|
|
274
274
|
let base = describe(el, visMemo);
|
|
275
275
|
if (stampViewport &&
|