@reticlehq/browser 3.6.0 → 3.7.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/action-report.d.ts +54 -0
- package/dist/actions/action-report.js +60 -0
- package/dist/actions/actions-dom.d.ts +1 -0
- package/dist/actions/actions-dom.js +12 -0
- package/dist/actions/actions-press.d.ts +1 -0
- package/dist/actions/actions-press.js +25 -1
- package/dist/actions/actions.d.ts +1 -37
- package/dist/actions/actions.js +24 -66
- package/dist/actions/appeared-text.js +3 -0
- package/dist/actions/danger-context.d.ts +2 -0
- package/dist/actions/danger-context.js +28 -2
- package/dist/actions/value-input.d.ts +1 -0
- package/dist/actions/value-input.js +11 -0
- package/dist/commands/commands.js +4 -0
- package/dist/dom/a11y.js +251 -10
- package/dist/dom/addressing/coverage-markers.d.ts +6 -0
- package/dist/dom/addressing/coverage-markers.js +13 -0
- package/dist/dom/dom-ignore.d.ts +2 -0
- package/dist/dom/dom-ignore.js +15 -2
- package/dist/dom/query.d.ts +1 -1
- package/dist/dom/query.js +78 -16
- package/dist/dom/render-page.d.ts +12 -0
- package/dist/dom/render-page.js +135 -0
- package/dist/dom/snapshot.js +55 -7
- package/dist/observers/dom.js +32 -16
- package/dist/observers/field.js +3 -21
- package/dist/observers/network.js +56 -7
- package/dist/observers/perf.js +53 -1
- package/dist/observers/route.js +6 -5
- package/dist/presenter/carousel/carousel.js +2 -7
- package/dist/presenter/carousel/panel-slides.d.ts +2 -4
- package/dist/presenter/carousel/panel-slides.js +17 -15
- package/dist/presenter/chrome/log-memory.d.ts +1 -0
- package/dist/presenter/chrome/presenter-log.d.ts +7 -0
- package/dist/presenter/chrome/presenter-log.js +48 -8
- package/dist/presenter/chrome/presenter-morph.d.ts +1 -0
- package/dist/presenter/chrome/presenter-morph.js +34 -0
- package/dist/presenter/chrome/presenter-report-copy.d.ts +17 -4
- package/dist/presenter/chrome/presenter-report-copy.js +26 -4
- package/dist/presenter/hud-telemetry.js +55 -4
- package/dist/presenter/presenter-account-styles.js +3 -3
- package/dist/presenter/presenter-account.d.ts +27 -5
- package/dist/presenter/presenter-account.js +83 -6
- package/dist/presenter/presenter-agent-link.d.ts +9 -0
- package/dist/presenter/presenter-agent-link.js +62 -0
- package/dist/presenter/presenter-chat-views.d.ts +27 -5
- package/dist/presenter/presenter-chat-views.js +312 -72
- package/dist/presenter/presenter-config.d.ts +2 -0
- package/dist/presenter/presenter-config.js +1 -0
- package/dist/presenter/presenter-controls.d.ts +0 -1
- package/dist/presenter/presenter-controls.js +13 -91
- package/dist/presenter/presenter-design-tokens.js +1 -1
- package/dist/presenter/presenter-frame.d.ts +1 -1
- package/dist/presenter/presenter-frame.js +8 -10
- package/dist/presenter/presenter-harness-row.d.ts +67 -0
- package/dist/presenter/presenter-harness-row.js +209 -0
- package/dist/presenter/presenter-personas.d.ts +35 -0
- package/dist/presenter/presenter-personas.js +95 -0
- package/dist/presenter/presenter-plan.js +25 -24
- package/dist/presenter/presenter-report-styles.js +40 -22
- package/dist/presenter/presenter-report.d.ts +2 -1
- package/dist/presenter/presenter-report.js +83 -17
- package/dist/presenter/presenter-settings-styles.js +8 -4
- package/dist/presenter/presenter-settings.d.ts +7 -6
- package/dist/presenter/presenter-settings.js +75 -34
- package/dist/presenter/presenter-shell-styles.js +12 -3
- package/dist/presenter/presenter-shell.d.ts +10 -4
- package/dist/presenter/presenter-shell.js +31 -23
- package/dist/presenter/presenter-styles.js +0 -2
- package/dist/presenter/presenter-workspace.js +10 -3
- package/dist/presenter/presenter.d.ts +3 -2
- package/dist/presenter/presenter.js +81 -20
- package/dist/registry/declared-platform.js +2 -0
- package/dist/reticle-inject.js +777 -700
- package/dist/reticle.js +36 -2
- package/dist/storage-keys.d.ts +3 -2
- package/dist/storage-keys.js +7 -3
- package/dist/transport/transport.js +5 -3
- package/dist/transport/unreachable-message.d.ts +1 -1
- package/dist/transport/unreachable-message.js +3 -5
- package/package.json +2 -2
- package/dist/presenter/carousel/offer-card.d.ts +0 -21
- package/dist/presenter/carousel/offer-card.js +0 -56
package/dist/dom/a11y.js
CHANGED
|
@@ -277,6 +277,7 @@ export function getStates(el, visible = isVisible(el)) {
|
|
|
277
277
|
states.push(ElementState.FOCUSED);
|
|
278
278
|
return states;
|
|
279
279
|
}
|
|
280
|
+
const SENSITIVE_AUTOCOMPLETE = /\b(cc-|current-password|new-password|one-time-code)/i;
|
|
280
281
|
export function isSensitiveField(el) {
|
|
281
282
|
if (!isInput(el) && !isTextArea(el) && !isSelect(el)) {
|
|
282
283
|
return false;
|
|
@@ -289,7 +290,7 @@ export function isSensitiveField(el) {
|
|
|
289
290
|
el.getAttribute(DEFAULT_TESTID_ATTR) ?? '',
|
|
290
291
|
el.getAttribute('aria-label') ?? '',
|
|
291
292
|
];
|
|
292
|
-
const sensitiveAutocomplete =
|
|
293
|
+
const sensitiveAutocomplete = SENSITIVE_AUTOCOMPLETE.test(autocomplete);
|
|
293
294
|
return ((isInput(el) && 'password' === el.type.toLowerCase()) ||
|
|
294
295
|
sensitiveAutocomplete ||
|
|
295
296
|
identifiers.some(isSensitiveKey));
|
|
@@ -297,7 +298,7 @@ export function isSensitiveField(el) {
|
|
|
297
298
|
export function getValue(el) {
|
|
298
299
|
if (isInput(el) || isTextArea(el) || isSelect(el)) {
|
|
299
300
|
if (isSensitiveField(el))
|
|
300
|
-
return REDACTED_VALUE;
|
|
301
|
+
return '' === el.value ? '' : REDACTED_VALUE;
|
|
301
302
|
return el.value;
|
|
302
303
|
}
|
|
303
304
|
const valueNow = el.getAttribute('aria-valuenow');
|
|
@@ -318,16 +319,18 @@ function stalledFade(el) {
|
|
|
318
319
|
return false;
|
|
319
320
|
return el.getAnimations().some((a) => 'running' === a.playState || a.pending);
|
|
320
321
|
}
|
|
321
|
-
|
|
322
|
-
|
|
322
|
+
const OVERLAY_ARIA_HIDDEN = 'data-aria-hidden';
|
|
323
|
+
function hiddenByAnOverlay(el) {
|
|
324
|
+
return el.hasAttribute(OVERLAY_ARIA_HIDDEN);
|
|
325
|
+
}
|
|
326
|
+
function selfHidden(el, style) {
|
|
327
|
+
if ('true' === el.getAttribute('aria-hidden') && !hiddenByAnOverlay(el))
|
|
323
328
|
return true;
|
|
324
329
|
if (isHtmlElement(el) && el.hidden)
|
|
325
330
|
return true;
|
|
326
331
|
if (hiddenInsideClosedDetails(el))
|
|
327
332
|
return true;
|
|
328
|
-
|
|
329
|
-
if (view !== null) {
|
|
330
|
-
const style = view.getComputedStyle(el);
|
|
333
|
+
if (style !== null) {
|
|
331
334
|
if ('none' === style.display ||
|
|
332
335
|
'hidden' === style.visibility ||
|
|
333
336
|
'collapse' === style.visibility) {
|
|
@@ -360,13 +363,252 @@ function parentAcrossShadowBoundary(el) {
|
|
|
360
363
|
const host = el.getRootNode().host;
|
|
361
364
|
return host ?? null;
|
|
362
365
|
}
|
|
366
|
+
const CLIPPING_OVERFLOW = new Set(['hidden', 'clip']);
|
|
367
|
+
const SCROLLING_OVERFLOW = new Set(['auto', 'scroll']);
|
|
368
|
+
const NON_CLIPPING_DISPLAY = new Set([
|
|
369
|
+
'contents',
|
|
370
|
+
'table-row',
|
|
371
|
+
'table-row-group',
|
|
372
|
+
'table-header-group',
|
|
373
|
+
'table-footer-group',
|
|
374
|
+
'table-column',
|
|
375
|
+
'table-column-group',
|
|
376
|
+
]);
|
|
377
|
+
const TRANSFORM_CONTAINING_BLOCK_PROPERTIES = new Set([
|
|
378
|
+
'transform',
|
|
379
|
+
'translate',
|
|
380
|
+
'rotate',
|
|
381
|
+
'scale',
|
|
382
|
+
'perspective',
|
|
383
|
+
]);
|
|
384
|
+
const CONTAINING_BLOCK_PROPERTIES = new Set([
|
|
385
|
+
...TRANSFORM_CONTAINING_BLOCK_PROPERTIES,
|
|
386
|
+
'filter',
|
|
387
|
+
'backdrop-filter',
|
|
388
|
+
]);
|
|
389
|
+
const CONTAINING_BLOCK_CONTAIN = new Set(['layout', 'paint', 'strict', 'content']);
|
|
390
|
+
const REPLACED_INLINE_TAGS = new Set([
|
|
391
|
+
'img',
|
|
392
|
+
'video',
|
|
393
|
+
'audio',
|
|
394
|
+
'canvas',
|
|
395
|
+
'iframe',
|
|
396
|
+
'embed',
|
|
397
|
+
'object',
|
|
398
|
+
'input',
|
|
399
|
+
'textarea',
|
|
400
|
+
'select',
|
|
401
|
+
]);
|
|
402
|
+
const visibilityInfoMemos = new WeakMap();
|
|
403
|
+
function visibilityInfoMemo(memo) {
|
|
404
|
+
if (memo === undefined)
|
|
405
|
+
return new Map();
|
|
406
|
+
let info = visibilityInfoMemos.get(memo);
|
|
407
|
+
if (info === undefined) {
|
|
408
|
+
info = new Map();
|
|
409
|
+
visibilityInfoMemos.set(memo, info);
|
|
410
|
+
}
|
|
411
|
+
return info;
|
|
412
|
+
}
|
|
413
|
+
function ownVisibilityInfo(el, memo) {
|
|
414
|
+
let info = memo.get(el);
|
|
415
|
+
if (info === undefined) {
|
|
416
|
+
const style = el.ownerDocument.defaultView?.getComputedStyle(el) ?? null;
|
|
417
|
+
if (selfHidden(el, style)) {
|
|
418
|
+
info = {
|
|
419
|
+
hidden: true,
|
|
420
|
+
style,
|
|
421
|
+
parent: null,
|
|
422
|
+
clipX: false,
|
|
423
|
+
clipY: false,
|
|
424
|
+
scrollX: false,
|
|
425
|
+
scrollY: false,
|
|
426
|
+
hasClipping: false,
|
|
427
|
+
};
|
|
428
|
+
memo.set(el, info);
|
|
429
|
+
return info;
|
|
430
|
+
}
|
|
431
|
+
const parent = parentAcrossShadowBoundary(el);
|
|
432
|
+
const inherited = null === parent ? undefined : ownVisibilityInfo(parent, memo);
|
|
433
|
+
const [overflowX, overflowY] = overflowAxes(style);
|
|
434
|
+
const root = el.ownerDocument.documentElement;
|
|
435
|
+
const viewportOverflow = el === root ||
|
|
436
|
+
(el === el.ownerDocument.body &&
|
|
437
|
+
overflowAxes(ownVisibilityInfo(root, memo).style).every((axis) => 'visible' === axis));
|
|
438
|
+
const hasClipBox = !viewportOverflow &&
|
|
439
|
+
!NON_CLIPPING_DISPLAY.has(style?.display ?? '') &&
|
|
440
|
+
!(isHtmlElement(el) && 'inline' === style?.display);
|
|
441
|
+
const clipX = hasClipBox && CLIPPING_OVERFLOW.has(overflowX);
|
|
442
|
+
const clipY = hasClipBox && CLIPPING_OVERFLOW.has(overflowY);
|
|
443
|
+
info = {
|
|
444
|
+
hidden: true === inherited?.hidden,
|
|
445
|
+
style,
|
|
446
|
+
parent,
|
|
447
|
+
clipX,
|
|
448
|
+
clipY,
|
|
449
|
+
scrollX: hasClipBox && SCROLLING_OVERFLOW.has(overflowX),
|
|
450
|
+
scrollY: hasClipBox && SCROLLING_OVERFLOW.has(overflowY),
|
|
451
|
+
hasClipping: clipX || clipY || true === inherited?.hasClipping,
|
|
452
|
+
};
|
|
453
|
+
memo.set(el, info);
|
|
454
|
+
}
|
|
455
|
+
return info;
|
|
456
|
+
}
|
|
457
|
+
function overflowAxes(style) {
|
|
458
|
+
const shorthand = style?.overflow ?? '';
|
|
459
|
+
const [x = 'visible', y = x] = ('' === shorthand ? 'visible' : shorthand).split(/\s+/);
|
|
460
|
+
return [
|
|
461
|
+
null === style || '' === style.overflowX ? x : style.overflowX,
|
|
462
|
+
null === style || '' === style.overflowY ? y : style.overflowY,
|
|
463
|
+
];
|
|
464
|
+
}
|
|
465
|
+
function positionedContainingBlock(el, fixed, memo) {
|
|
466
|
+
for (let parent = ownVisibilityInfo(el, memo).parent; parent !== null; parent = ownVisibilityInfo(parent, memo).parent) {
|
|
467
|
+
const style = ownVisibilityInfo(parent, memo).style;
|
|
468
|
+
if (null === style || 'contents' === style.display)
|
|
469
|
+
continue;
|
|
470
|
+
if (!fixed && '' !== style.position && 'static' !== style.position)
|
|
471
|
+
return parent;
|
|
472
|
+
const transformable = !('inline' === style.display &&
|
|
473
|
+
isHtmlElement(parent) &&
|
|
474
|
+
!REPLACED_INLINE_TAGS.has(parent.localName)) &&
|
|
475
|
+
'table-column' !== style.display &&
|
|
476
|
+
'table-column-group' !== style.display;
|
|
477
|
+
const applies = (property) => transformable || !TRANSFORM_CONTAINING_BLOCK_PROPERTIES.has(property);
|
|
478
|
+
if ([...CONTAINING_BLOCK_PROPERTIES].some((property) => {
|
|
479
|
+
const value = style.getPropertyValue(property);
|
|
480
|
+
return applies(property) && '' !== value && 'none' !== value;
|
|
481
|
+
}) ||
|
|
482
|
+
style.willChange
|
|
483
|
+
.split(',')
|
|
484
|
+
.some((property) => CONTAINING_BLOCK_PROPERTIES.has(property.trim()) && applies(property.trim())) ||
|
|
485
|
+
(transformable &&
|
|
486
|
+
!NON_CLIPPING_DISPLAY.has(style.display) &&
|
|
487
|
+
(style.contain.split(/\s+/).some((value) => CONTAINING_BLOCK_CONTAIN.has(value)) ||
|
|
488
|
+
'auto' === style.contentVisibility)))
|
|
489
|
+
return parent;
|
|
490
|
+
}
|
|
491
|
+
return null;
|
|
492
|
+
}
|
|
493
|
+
function clippingBox(el, info) {
|
|
494
|
+
if (info.clipBox !== undefined)
|
|
495
|
+
return info.clipBox;
|
|
496
|
+
const rect = el.getBoundingClientRect();
|
|
497
|
+
const style = info.style;
|
|
498
|
+
const px = (value) => Number.parseFloat(value ?? '') || 0;
|
|
499
|
+
const border = (value, kind) => kind === undefined || '' === kind || 'none' === kind || 'hidden' === kind ? 0 : px(value);
|
|
500
|
+
const scaleX = isHtmlElement(el) && el.offsetWidth > 0 ? rect.width / el.offsetWidth : 1;
|
|
501
|
+
const scaleY = isHtmlElement(el) && el.offsetHeight > 0 ? rect.height / el.offsetHeight : 1;
|
|
502
|
+
const left = isHtmlElement(el) && el.offsetWidth > 0
|
|
503
|
+
? rect.left + el.clientLeft * scaleX
|
|
504
|
+
: rect.left + border(style?.borderLeftWidth, style?.borderLeftStyle);
|
|
505
|
+
const top = isHtmlElement(el) && el.offsetHeight > 0
|
|
506
|
+
? rect.top + el.clientTop * scaleY
|
|
507
|
+
: rect.top + border(style?.borderTopWidth, style?.borderTopStyle);
|
|
508
|
+
const right = isHtmlElement(el) && el.offsetWidth > 0
|
|
509
|
+
? left + el.clientWidth * scaleX
|
|
510
|
+
: rect.right - border(style?.borderRightWidth, style?.borderRightStyle);
|
|
511
|
+
const bottom = isHtmlElement(el) && el.offsetHeight > 0
|
|
512
|
+
? top + el.clientHeight * scaleY
|
|
513
|
+
: rect.bottom - border(style?.borderBottomWidth, style?.borderBottomStyle);
|
|
514
|
+
const margin = (style?.overflowClipMargin ?? '').split(/\s+/);
|
|
515
|
+
const outset = px(margin.at(-1));
|
|
516
|
+
const borderBox = margin.includes('border-box');
|
|
517
|
+
const contentBox = margin.includes('content-box');
|
|
518
|
+
const [x, y] = overflowAxes(style);
|
|
519
|
+
const clipLeft = 'clip' === x
|
|
520
|
+
? (borderBox ? rect.left : left + (contentBox ? px(style?.paddingLeft) * scaleX : 0)) -
|
|
521
|
+
outset * scaleX
|
|
522
|
+
: left;
|
|
523
|
+
const clipRight = 'clip' === x
|
|
524
|
+
? (borderBox ? rect.right : right - (contentBox ? px(style?.paddingRight) * scaleX : 0)) +
|
|
525
|
+
outset * scaleX
|
|
526
|
+
: right;
|
|
527
|
+
const clipTop = 'clip' === y
|
|
528
|
+
? (borderBox ? rect.top : top + (contentBox ? px(style?.paddingTop) * scaleY : 0)) -
|
|
529
|
+
outset * scaleY
|
|
530
|
+
: top;
|
|
531
|
+
const clipBottom = 'clip' === y
|
|
532
|
+
? (borderBox ? rect.bottom : bottom - (contentBox ? px(style?.paddingBottom) * scaleY : 0)) +
|
|
533
|
+
outset * scaleY
|
|
534
|
+
: bottom;
|
|
535
|
+
info.clipBox = {
|
|
536
|
+
left: clipLeft,
|
|
537
|
+
right: clipRight,
|
|
538
|
+
top: clipTop,
|
|
539
|
+
bottom: clipBottom,
|
|
540
|
+
width: clipRight - clipLeft,
|
|
541
|
+
height: clipBottom - clipTop,
|
|
542
|
+
};
|
|
543
|
+
return info.clipBox;
|
|
544
|
+
}
|
|
545
|
+
function scrolledInto(b, port, alongX, alongY) {
|
|
546
|
+
const axis = (lo, hi, size, from, to) => {
|
|
547
|
+
if (hi > from && lo < to)
|
|
548
|
+
return [lo, hi];
|
|
549
|
+
const span = Math.min(size, to - from);
|
|
550
|
+
return lo >= to ? [to - span, to] : [from, from + span];
|
|
551
|
+
};
|
|
552
|
+
const [left, right] = alongX
|
|
553
|
+
? axis(b.left, b.right, b.width, port.left, port.right)
|
|
554
|
+
: [b.left, b.right];
|
|
555
|
+
const [top, bottom] = alongY
|
|
556
|
+
? axis(b.top, b.bottom, b.height, port.top, port.bottom)
|
|
557
|
+
: [b.top, b.bottom];
|
|
558
|
+
return { left, right, top, bottom, width: right - left, height: bottom - top };
|
|
559
|
+
}
|
|
560
|
+
function visibleBounds(el, memo, initial, throughScrollers = false) {
|
|
561
|
+
let bounds = initial;
|
|
562
|
+
const target = ownVisibilityInfo(el, memo);
|
|
563
|
+
if (target.hidden)
|
|
564
|
+
return null;
|
|
565
|
+
if (null === target.parent || !ownVisibilityInfo(target.parent, memo).hasClipping)
|
|
566
|
+
return bounds;
|
|
567
|
+
let clipFrom;
|
|
568
|
+
for (let current = el; current !== null; current = ownVisibilityInfo(current, memo).parent) {
|
|
569
|
+
const info = ownVisibilityInfo(current, memo);
|
|
570
|
+
if (info.hidden)
|
|
571
|
+
return null;
|
|
572
|
+
if (current === clipFrom)
|
|
573
|
+
clipFrom = undefined;
|
|
574
|
+
const style = info.style;
|
|
575
|
+
if (throughScrollers &&
|
|
576
|
+
current !== el &&
|
|
577
|
+
undefined === clipFrom &&
|
|
578
|
+
(info.scrollX || info.scrollY)) {
|
|
579
|
+
bounds ??= el.getBoundingClientRect();
|
|
580
|
+
if (bounds.width > 0 && bounds.height > 0) {
|
|
581
|
+
bounds = scrolledInto(bounds, clippingBox(current, info), info.scrollX, info.scrollY);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
if (current !== el && undefined === clipFrom && (info.clipX || info.clipY)) {
|
|
585
|
+
bounds ??= el.getBoundingClientRect();
|
|
586
|
+
if (bounds.width > 0 && bounds.height > 0) {
|
|
587
|
+
const clip = clippingBox(current, info);
|
|
588
|
+
const left = info.clipX ? Math.max(bounds.left, clip.left) : bounds.left;
|
|
589
|
+
const right = info.clipX ? Math.min(bounds.right, clip.right) : bounds.right;
|
|
590
|
+
const top = info.clipY ? Math.max(bounds.top, clip.top) : bounds.top;
|
|
591
|
+
const bottom = info.clipY ? Math.min(bounds.bottom, clip.bottom) : bounds.bottom;
|
|
592
|
+
if (right <= left || bottom <= top)
|
|
593
|
+
return null;
|
|
594
|
+
bounds = { left, right, top, bottom, width: right - left, height: bottom - top };
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
if (clipFrom === undefined && ('absolute' === style?.position || 'fixed' === style?.position)) {
|
|
598
|
+
clipFrom = positionedContainingBlock(current, 'fixed' === style?.position, memo);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return bounds;
|
|
602
|
+
}
|
|
363
603
|
export function isInViewport(el, memo) {
|
|
364
604
|
if (!isVisible(el, memo))
|
|
365
605
|
return false;
|
|
366
606
|
const view = el.ownerDocument.defaultView;
|
|
367
607
|
if (null === view)
|
|
368
608
|
return false;
|
|
369
|
-
const r = el.getBoundingClientRect();
|
|
609
|
+
const r = visibleBounds(el, visibilityInfoMemo(memo), el.getBoundingClientRect());
|
|
610
|
+
if (null === r || undefined === r)
|
|
611
|
+
return false;
|
|
370
612
|
if (r.width <= 0 || r.height <= 0)
|
|
371
613
|
return false;
|
|
372
614
|
return r.bottom > 0 && r.right > 0 && r.top < view.innerHeight && r.left < view.innerWidth;
|
|
@@ -377,8 +619,7 @@ export function isVisible(el, memo) {
|
|
|
377
619
|
const cached = memo?.get(el);
|
|
378
620
|
if (cached !== undefined)
|
|
379
621
|
return cached;
|
|
380
|
-
const
|
|
381
|
-
const result = !selfHidden(el) && (null === parent || isVisible(parent, memo));
|
|
622
|
+
const result = visibleBounds(el, visibilityInfoMemo(memo), undefined, true) !== null;
|
|
382
623
|
if (memo !== undefined)
|
|
383
624
|
memo.set(el, result);
|
|
384
625
|
return result;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { CoverageMarker, SOURCE_MAPPING_CAPABILITY, DATA_RETICLE_SOURCE_ATTR, RETICLE_OVERLAY_SELECTOR, } from '@reticlehq/core';
|
|
2
|
+
import { testIdSelector } from './testid-attr.js';
|
|
3
|
+
const appCarries = (doc, selector) => Array.from(doc.querySelectorAll(selector)).some((el) => null === el.closest(RETICLE_OVERLAY_SELECTOR));
|
|
4
|
+
export function coverageMarkers(doc) {
|
|
5
|
+
return [
|
|
6
|
+
...(appCarries(doc, `[${DATA_RETICLE_SOURCE_ATTR}]`) ? [CoverageMarker.SOURCE] : []),
|
|
7
|
+
...(appCarries(doc, testIdSelector()) ? [CoverageMarker.TESTID] : []),
|
|
8
|
+
];
|
|
9
|
+
}
|
|
10
|
+
export function notApplicableFor(page) {
|
|
11
|
+
const couldStamp = page.buildPlugin || 0 < page.adapters.length || page.markers.includes(CoverageMarker.SOURCE);
|
|
12
|
+
return couldStamp ? [] : [SOURCE_MAPPING_CAPABILITY];
|
|
13
|
+
}
|
package/dist/dom/dom-ignore.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export declare const RETICLE_OVERLAY: string;
|
|
2
2
|
export declare function setPresenterVisible(visible: boolean): void;
|
|
3
|
+
export declare function setPresenterViewer(harness: boolean): void;
|
|
3
4
|
export declare function isPresenterVisible(): boolean;
|
|
4
5
|
export declare function setIgnoreSelectors(selectors: string[]): void;
|
|
5
6
|
export declare function isReticleOverlay(el: Element): boolean;
|
|
7
|
+
export declare function isUnobserved(el: Element): boolean;
|
|
6
8
|
export declare function isReticleUi(node: Element | null): boolean;
|
|
7
9
|
export declare function isIgnored(el: Element): boolean;
|
|
8
10
|
export declare function appModalOpen(doc: Document): boolean;
|
package/dist/dom/dom-ignore.js
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
import { RETICLE_OVERLAY_SELECTOR } from '@reticlehq/core';
|
|
2
|
+
export const RETICLE_OVERLAY = RETICLE_OVERLAY_SELECTOR;
|
|
2
3
|
const DEV_OVERLAYS = '[data-agentation],#__next-build-watcher,nextjs-portal,[data-nextjs-dialog],[data-nextjs-toast]';
|
|
3
4
|
let extraIgnore = '';
|
|
4
5
|
let presenterVisible = false;
|
|
5
6
|
export function setPresenterVisible(visible) {
|
|
6
7
|
presenterVisible = visible;
|
|
7
8
|
}
|
|
9
|
+
let harnessViewing = false;
|
|
10
|
+
export function setPresenterViewer(harness) {
|
|
11
|
+
harnessViewing = harness;
|
|
12
|
+
}
|
|
13
|
+
const exposed = () => presenterVisible && !harnessViewing;
|
|
8
14
|
export function isPresenterVisible() {
|
|
9
15
|
return presenterVisible;
|
|
10
16
|
}
|
|
@@ -14,11 +20,18 @@ export function setIgnoreSelectors(selectors) {
|
|
|
14
20
|
export function isReticleOverlay(el) {
|
|
15
21
|
return el.closest(RETICLE_OVERLAY) !== null;
|
|
16
22
|
}
|
|
23
|
+
const HUD_DOCK = '[data-reticle-dock]';
|
|
24
|
+
export function isUnobserved(el) {
|
|
25
|
+
if (!isReticleOverlay(el))
|
|
26
|
+
return false;
|
|
27
|
+
return !exposed() || null === el.closest(HUD_DOCK);
|
|
28
|
+
}
|
|
17
29
|
export function isReticleUi(node) {
|
|
18
30
|
return node !== null && node.closest(RETICLE_OVERLAY) !== null;
|
|
19
31
|
}
|
|
32
|
+
const HUD_NARRATION = '[data-reticle-log],[data-reticle-harness-log],[data-reticle-chat-pill-text],[data-reticle-agent-said],[data-reticle-agent-note]';
|
|
20
33
|
export function isIgnored(el) {
|
|
21
|
-
const ours =
|
|
34
|
+
const ours = exposed() ? HUD_NARRATION : RETICLE_OVERLAY;
|
|
22
35
|
const sel = [ours, DEV_OVERLAYS, extraIgnore].filter((part) => part.length > 0).join(',');
|
|
23
36
|
return el.closest(sel) !== null;
|
|
24
37
|
}
|
package/dist/dom/query.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ElementState, type ElementQuery, type MatchResult, type QueryResult } from '@reticlehq/core';
|
|
2
2
|
export declare const ATTR_VALUE_MAX = 512;
|
|
3
|
-
export declare function matchQuery(query: ElementQuery, state?: ElementState, limit?: number): MatchResult;
|
|
3
|
+
export declare function matchQuery(query: ElementQuery, state?: ElementState, limit?: number, wholePhrase?: boolean): MatchResult;
|
|
4
4
|
export declare function elementsMatching(query: ElementQuery): HTMLElement[];
|
|
5
5
|
export declare function runQuery(query: ElementQuery, limit?: number): QueryResult;
|
package/dist/dom/query.js
CHANGED
|
@@ -24,6 +24,11 @@ function fuzzyVisibleText(actual, expected) {
|
|
|
24
24
|
.toLowerCase()
|
|
25
25
|
.includes(normaliseVisibleText(expected).toLowerCase());
|
|
26
26
|
}
|
|
27
|
+
function textMatches(actual, query, expected) {
|
|
28
|
+
if (true !== query.matchCase)
|
|
29
|
+
return fuzzyVisibleText(actual, expected);
|
|
30
|
+
return normaliseVisibleText(actual).includes(normaliseVisibleText(expected));
|
|
31
|
+
}
|
|
27
32
|
function exactVisibleText(actual, expected) {
|
|
28
33
|
return normaliseVisibleText(actual) === normaliseVisibleText(expected);
|
|
29
34
|
}
|
|
@@ -45,17 +50,29 @@ function directText(el) {
|
|
|
45
50
|
.join('');
|
|
46
51
|
}
|
|
47
52
|
function visibleTextOf(el, memo) {
|
|
48
|
-
if (isNonRendered(el) || isIgnored(el)
|
|
53
|
+
if (isNonRendered(el) || isIgnored(el))
|
|
49
54
|
return '';
|
|
55
|
+
const visible = isVisible(el, memo);
|
|
56
|
+
const apart = laysOutApart(el);
|
|
50
57
|
let text = '';
|
|
51
58
|
for (const node of Array.from(el.childNodes)) {
|
|
52
|
-
if (Node.TEXT_NODE === node.nodeType)
|
|
59
|
+
if (Node.TEXT_NODE === node.nodeType && visible)
|
|
53
60
|
text += node.textContent ?? '';
|
|
54
|
-
else if (isElement(node))
|
|
55
|
-
|
|
61
|
+
else if (isElement(node)) {
|
|
62
|
+
const own = visibleTextOf(node, memo);
|
|
63
|
+
text += apart || ownBox(node) ? ` ${own} ` : own;
|
|
64
|
+
}
|
|
56
65
|
}
|
|
57
66
|
return text;
|
|
58
67
|
}
|
|
68
|
+
function laysOutApart(el) {
|
|
69
|
+
const display = el.ownerDocument.defaultView?.getComputedStyle(el).display ?? '';
|
|
70
|
+
return display.includes('flex') || display.includes('grid');
|
|
71
|
+
}
|
|
72
|
+
function ownBox(el) {
|
|
73
|
+
const display = el.ownerDocument.defaultView?.getComputedStyle(el).display ?? 'inline';
|
|
74
|
+
return !display.startsWith('inline') && 'contents' !== display;
|
|
75
|
+
}
|
|
59
76
|
function semanticNameTarget(el) {
|
|
60
77
|
if (isInput(el) || isTextArea(el) || isSelect(el))
|
|
61
78
|
return true;
|
|
@@ -126,11 +143,22 @@ function queryByRoleAndName(container, role, name) {
|
|
|
126
143
|
return elementsUnder(container).filter((el) => matchesRole(getRole(el), role) &&
|
|
127
144
|
(name === undefined || exactVisibleText(getAccessibleName(el), name)));
|
|
128
145
|
}
|
|
129
|
-
function queryByText(container, value) {
|
|
130
|
-
return elementsUnder(container).filter((el) =>
|
|
146
|
+
function queryByText(container, value, query) {
|
|
147
|
+
return elementsUnder(container).filter((el) => textMatches(directText(el), query, value));
|
|
131
148
|
}
|
|
132
149
|
function queryByLabel(container, value) {
|
|
133
|
-
|
|
150
|
+
const expected = normaliseVisibleText(value).toLowerCase();
|
|
151
|
+
const exact = [];
|
|
152
|
+
const fuzzy = [];
|
|
153
|
+
for (const el of elementsUnder(container)) {
|
|
154
|
+
if (!semanticNameTarget(el))
|
|
155
|
+
continue;
|
|
156
|
+
const name = normaliseVisibleText(getAccessibleName(el)).toLowerCase();
|
|
157
|
+
if (!name.includes(expected))
|
|
158
|
+
continue;
|
|
159
|
+
(name === expected ? exact : fuzzy).push(el);
|
|
160
|
+
}
|
|
161
|
+
return exact.length > 0 ? exact : fuzzy;
|
|
134
162
|
}
|
|
135
163
|
function queryByPlaceholder(container, value) {
|
|
136
164
|
return elementsUnder(container).filter((el) => {
|
|
@@ -155,7 +183,7 @@ function findIn(container, query) {
|
|
|
155
183
|
case QueryBy.ROLE:
|
|
156
184
|
return queryByRoleAndName(container, value, query.name);
|
|
157
185
|
case QueryBy.TEXT:
|
|
158
|
-
return queryByText(container, value);
|
|
186
|
+
return queryByText(container, value, query);
|
|
159
187
|
case QueryBy.LABEL:
|
|
160
188
|
return queryByLabel(container, value);
|
|
161
189
|
case QueryBy.PLACEHOLDER:
|
|
@@ -177,7 +205,7 @@ function findIn(container, query) {
|
|
|
177
205
|
return queryByRoleAndName(container, query.role, query.name);
|
|
178
206
|
}
|
|
179
207
|
if (query.text !== undefined)
|
|
180
|
-
return queryByText(container, query.text);
|
|
208
|
+
return queryByText(container, query.text, query);
|
|
181
209
|
if (query.label !== undefined)
|
|
182
210
|
return queryByLabel(container, query.label);
|
|
183
211
|
if (query.placeholder !== undefined)
|
|
@@ -229,7 +257,7 @@ function findCandidates(query) {
|
|
|
229
257
|
if (query.scope === undefined)
|
|
230
258
|
return { candidates: [], scopeMissing };
|
|
231
259
|
if (query.text !== undefined &&
|
|
232
|
-
!
|
|
260
|
+
!textMatches(visibleTextOf(container, new Map()), query, query.text)) {
|
|
233
261
|
return { candidates: [], scopeMissing };
|
|
234
262
|
}
|
|
235
263
|
return { candidates: isIgnored(container) ? [] : [container], scopeMissing };
|
|
@@ -269,10 +297,13 @@ function inState(el, state, memo) {
|
|
|
269
297
|
return getStates(el, isVisible(el, memo)).includes(state);
|
|
270
298
|
}
|
|
271
299
|
const MAX_DESCRIBED = TRANSPORT_LIMITS.MAX_COLLECTION_ITEMS;
|
|
272
|
-
export function matchQuery(query, state, limit = MAX_DESCRIBED) {
|
|
300
|
+
export function matchQuery(query, state, limit = MAX_DESCRIBED, wholePhrase = true) {
|
|
273
301
|
const found = findCandidates(query);
|
|
274
|
-
const elements = found.candidates;
|
|
275
302
|
const scopeMissing = found.scopeMissing;
|
|
303
|
+
const wanted = wholePhrase && textOnly(query) ? wantedTextOf(query) : undefined;
|
|
304
|
+
const scope = 0 === found.candidates.length ? resolveContainer(query.scope).container : null;
|
|
305
|
+
const owner = null === scope || wanted === undefined ? undefined : splitTextOwner(scope, wanted, query);
|
|
306
|
+
const elements = owner === undefined ? found.candidates : [owner];
|
|
276
307
|
const visMemo = new Map();
|
|
277
308
|
const filtered = state === undefined ? elements : elements.filter((el) => inState(el, state, visMemo));
|
|
278
309
|
const attrs = query.attrs;
|
|
@@ -363,14 +394,14 @@ function buildPresentRegions(query) {
|
|
|
363
394
|
}
|
|
364
395
|
return regions;
|
|
365
396
|
}
|
|
366
|
-
function splitTextOwner(container, wanted) {
|
|
397
|
+
function splitTextOwner(container, wanted, query) {
|
|
367
398
|
let best;
|
|
368
399
|
let bestDepth = -1;
|
|
369
400
|
const memo = new Map();
|
|
370
401
|
for (const el of elementsUnder(container)) {
|
|
371
402
|
if (isIgnored(el))
|
|
372
403
|
continue;
|
|
373
|
-
if (!
|
|
404
|
+
if (!textMatches(visibleTextOf(el, memo), query, wanted))
|
|
374
405
|
continue;
|
|
375
406
|
let depth = 0;
|
|
376
407
|
for (let parent = el.parentElement; parent !== null; parent = parent.parentElement)
|
|
@@ -382,6 +413,32 @@ function splitTextOwner(container, wanted) {
|
|
|
382
413
|
}
|
|
383
414
|
return best;
|
|
384
415
|
}
|
|
416
|
+
function caseOnlyMatch(container, wanted) {
|
|
417
|
+
const target = normaliseVisibleText(wanted).toLowerCase();
|
|
418
|
+
if (0 === target.length)
|
|
419
|
+
return undefined;
|
|
420
|
+
const memo = new Map();
|
|
421
|
+
for (const el of elementsUnder(container)) {
|
|
422
|
+
if (isIgnored(el))
|
|
423
|
+
continue;
|
|
424
|
+
const text = normaliseVisibleText(visibleTextOf(el, memo));
|
|
425
|
+
const at = text.toLowerCase().indexOf(target);
|
|
426
|
+
if (-1 !== at)
|
|
427
|
+
return text.slice(at, at + target.length);
|
|
428
|
+
}
|
|
429
|
+
return undefined;
|
|
430
|
+
}
|
|
431
|
+
const TEXT_ONLY_FIELDS = new Set([
|
|
432
|
+
'text',
|
|
433
|
+
'matchCase',
|
|
434
|
+
'scope',
|
|
435
|
+
'by',
|
|
436
|
+
'value',
|
|
437
|
+
]);
|
|
438
|
+
function textOnly(query) {
|
|
439
|
+
return ((query.by === undefined || QueryBy.TEXT === query.by) &&
|
|
440
|
+
Object.keys(query).every((key) => TEXT_ONLY_FIELDS.has(key)));
|
|
441
|
+
}
|
|
385
442
|
function wantedTextOf(query) {
|
|
386
443
|
if (query.text !== undefined)
|
|
387
444
|
return query.text;
|
|
@@ -453,9 +510,14 @@ function buildEmptyHint(query) {
|
|
|
453
510
|
};
|
|
454
511
|
const wanted = wantedTextOf(query);
|
|
455
512
|
if (wanted !== undefined) {
|
|
456
|
-
const owner = splitTextOwner(container, wanted);
|
|
513
|
+
const owner = splitTextOwner(container, wanted, query);
|
|
457
514
|
if (owner !== undefined)
|
|
458
515
|
hint.splitText = describe(owner);
|
|
516
|
+
else if (true === query.matchCase) {
|
|
517
|
+
const shown = caseOnlyMatch(container, wanted);
|
|
518
|
+
if (shown !== undefined)
|
|
519
|
+
hint.caseMiss = shown;
|
|
520
|
+
}
|
|
459
521
|
}
|
|
460
522
|
const near = nameNearMisses(container, query);
|
|
461
523
|
if (near.length > 0)
|
|
@@ -469,7 +531,7 @@ export function elementsMatching(query) {
|
|
|
469
531
|
return findCandidates(query).candidates;
|
|
470
532
|
}
|
|
471
533
|
export function runQuery(query, limit) {
|
|
472
|
-
const result = matchQuery(query, undefined, limit);
|
|
534
|
+
const result = matchQuery(query, undefined, limit, false);
|
|
473
535
|
const scopeFields = true === result.scopeMissing ? { scopeMissing: true } : {};
|
|
474
536
|
if (0 === result.elements.length) {
|
|
475
537
|
return {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const MASK = "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
|
|
2
|
+
export type RenderResult = {
|
|
3
|
+
ok: true;
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
chunks: string[];
|
|
7
|
+
} | {
|
|
8
|
+
ok: false;
|
|
9
|
+
reason: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function cloneForRender(doc: Document): HTMLElement;
|
|
12
|
+
export declare function renderPage(): Promise<RenderResult>;
|