@reticlehq/browser 2.14.0 → 3.2.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.
Files changed (133) hide show
  1. package/dist/actions/actions-dom.d.ts +9 -0
  2. package/dist/actions/actions-dom.js +81 -28
  3. package/dist/actions/actions-press.d.ts +4 -0
  4. package/dist/actions/actions-press.js +46 -0
  5. package/dist/actions/actions.d.ts +1 -0
  6. package/dist/actions/actions.js +50 -13
  7. package/dist/actions/appeared-text.js +6 -0
  8. package/dist/actions/scroll.d.ts +8 -1
  9. package/dist/actions/scroll.js +28 -10
  10. package/dist/commands/commands.js +10 -3
  11. package/dist/dom/a11y.js +2 -2
  12. package/dist/dom/desktop-capture.js +1 -1
  13. package/dist/dom/dom-ignore.js +1 -1
  14. package/dist/dom/occlusion.js +31 -5
  15. package/dist/dom/paint-context.d.ts +6 -0
  16. package/dist/dom/paint-context.js +38 -0
  17. package/dist/dom/query.js +24 -7
  18. package/dist/dom/snapshot.js +1 -1
  19. package/dist/edit-epoch.d.ts +1 -1
  20. package/dist/edit-epoch.js +1 -1
  21. package/dist/index.d.ts +4 -5
  22. package/dist/index.js +3 -5
  23. package/dist/observers/animation.js +1 -1
  24. package/dist/observers/blind-spots.js +1 -1
  25. package/dist/observers/departure.js +16 -8
  26. package/dist/observers/dialogs.js +1 -1
  27. package/dist/observers/dom.js +28 -8
  28. package/dist/observers/download.js +1 -1
  29. package/dist/observers/health.js +1 -1
  30. package/dist/observers/{network-body.js → net-detail/network-body.js} +2 -2
  31. package/dist/observers/network-stream.js +2 -2
  32. package/dist/observers/network.d.ts +1 -1
  33. package/dist/observers/network.js +51 -10
  34. package/dist/observers/scroll.js +2 -2
  35. package/dist/presenter/chrome/layers.d.ts +2 -0
  36. package/dist/presenter/chrome/layers.js +2 -0
  37. package/dist/presenter/chrome/log-kinds.d.ts +12 -0
  38. package/dist/presenter/chrome/log-kinds.js +7 -0
  39. package/dist/presenter/{presenter-effects.js → chrome/presenter-effects.js} +1 -1
  40. package/dist/presenter/{presenter-log.d.ts → chrome/presenter-log.d.ts} +3 -12
  41. package/dist/presenter/{presenter-log.js → chrome/presenter-log.js} +4 -9
  42. package/dist/presenter/chrome/presenter-safe-html.d.ts +2 -0
  43. package/dist/presenter/chrome/presenter-safe-html.js +16 -0
  44. package/dist/presenter/{presenter-tally.js → chrome/presenter-tally.js} +1 -1
  45. package/dist/presenter/{presenter-heroicons-data.d.ts → icons/presenter-heroicons-data.d.ts} +1 -0
  46. package/dist/presenter/{presenter-heroicons-data.js → icons/presenter-heroicons-data.js} +1 -0
  47. package/dist/presenter/{presenter-icons.d.ts → icons/presenter-icons.d.ts} +1 -0
  48. package/dist/presenter/{presenter-icons.js → icons/presenter-icons.js} +1 -0
  49. package/dist/presenter/presenter-account-styles.d.ts +1 -0
  50. package/dist/presenter/presenter-account-styles.js +99 -0
  51. package/dist/presenter/presenter-account.d.ts +40 -0
  52. package/dist/presenter/presenter-account.js +178 -0
  53. package/dist/presenter/presenter-config.d.ts +9 -2
  54. package/dist/presenter/presenter-config.js +3 -1
  55. package/dist/presenter/presenter-controls.d.ts +1 -6
  56. package/dist/presenter/presenter-controls.js +7 -2
  57. package/dist/presenter/presenter-dock-layout.js +1 -1
  58. package/dist/presenter/presenter-drag.d.ts +1 -19
  59. package/dist/presenter/presenter-drag.js +3 -50
  60. package/dist/presenter/presenter-glow.js +1 -1
  61. package/dist/presenter/presenter-hud-position.d.ts +18 -0
  62. package/dist/presenter/presenter-hud-position.js +50 -0
  63. package/dist/presenter/presenter-offer.d.ts +25 -0
  64. package/dist/presenter/presenter-offer.js +87 -0
  65. package/dist/presenter/presenter-report-styles.js +20 -1
  66. package/dist/presenter/presenter-report.d.ts +2 -1
  67. package/dist/presenter/presenter-report.js +31 -23
  68. package/dist/presenter/presenter-run-state.d.ts +1 -1
  69. package/dist/presenter/presenter-run-state.js +1 -1
  70. package/dist/presenter/presenter-settings-styles.js +2 -1
  71. package/dist/presenter/presenter-settings.d.ts +8 -0
  72. package/dist/presenter/presenter-settings.js +58 -3
  73. package/dist/presenter/presenter-shell-styles.js +33 -15
  74. package/dist/presenter/presenter-shell.d.ts +8 -0
  75. package/dist/presenter/presenter-shell.js +47 -4
  76. package/dist/presenter/presenter-styles.d.ts +2 -1
  77. package/dist/presenter/presenter-styles.js +8 -3
  78. package/dist/presenter/presenter-workspace.d.ts +4 -0
  79. package/dist/presenter/presenter-workspace.js +29 -2
  80. package/dist/presenter/presenter.d.ts +3 -3
  81. package/dist/presenter/presenter.js +28 -14
  82. package/dist/presenter/tour/tour-view.d.ts +33 -0
  83. package/dist/presenter/tour/tour-view.js +192 -0
  84. package/dist/presenter/tour/tour.d.ts +25 -0
  85. package/dist/presenter/tour/tour.js +264 -0
  86. package/dist/recorder/recorder.js +6 -0
  87. package/dist/registry/declared-channels.d.ts +2 -0
  88. package/dist/registry/declared-channels.js +14 -0
  89. package/dist/registry/declared-platform.d.ts +4 -0
  90. package/dist/registry/declared-platform.js +34 -0
  91. package/dist/registry/emitter.js +1 -1
  92. package/dist/registry/store-adapters.js +1 -1
  93. package/dist/registry/stores.js +1 -1
  94. package/dist/reticle-presenter-helpers.js +10 -6
  95. package/dist/reticle-singleton.d.ts +2 -0
  96. package/dist/reticle-singleton.js +3 -0
  97. package/dist/reticle.js +99 -35
  98. package/dist/review/annotator-styles.js +1 -1
  99. package/dist/review/annotator.js +2 -2
  100. package/dist/review/mark-anchor.js +3 -3
  101. package/dist/transport/transport.js +2 -2
  102. package/package.json +5 -4
  103. package/dist/presenter/presenter-test-helpers.d.ts +0 -14
  104. package/dist/presenter/presenter-test-helpers.js +0 -39
  105. /package/dist/actions/{synthetic-input.d.ts → synthetic/synthetic-input.d.ts} +0 -0
  106. /package/dist/actions/{synthetic-input.js → synthetic/synthetic-input.js} +0 -0
  107. /package/dist/dom/{auto-anchor.d.ts → addressing/auto-anchor.d.ts} +0 -0
  108. /package/dist/dom/{auto-anchor.js → addressing/auto-anchor.js} +0 -0
  109. /package/dist/dom/{refs.d.ts → addressing/refs.d.ts} +0 -0
  110. /package/dist/dom/{refs.js → addressing/refs.js} +0 -0
  111. /package/dist/dom/{source.d.ts → addressing/source.d.ts} +0 -0
  112. /package/dist/dom/{source.js → addressing/source.js} +0 -0
  113. /package/dist/observers/{network-body.d.ts → net-detail/network-body.d.ts} +0 -0
  114. /package/dist/observers/{network-redact.d.ts → net-detail/network-redact.d.ts} +0 -0
  115. /package/dist/observers/{network-redact.js → net-detail/network-redact.js} +0 -0
  116. /package/dist/presenter/{overlay.d.ts → chrome/overlay.d.ts} +0 -0
  117. /package/dist/presenter/{overlay.js → chrome/overlay.js} +0 -0
  118. /package/dist/presenter/{presenter-brand.d.ts → chrome/presenter-brand.d.ts} +0 -0
  119. /package/dist/presenter/{presenter-brand.js → chrome/presenter-brand.js} +0 -0
  120. /package/dist/presenter/{presenter-effects.d.ts → chrome/presenter-effects.d.ts} +0 -0
  121. /package/dist/presenter/{presenter-hud-chrome.d.ts → chrome/presenter-hud-chrome.d.ts} +0 -0
  122. /package/dist/presenter/{presenter-hud-chrome.js → chrome/presenter-hud-chrome.js} +0 -0
  123. /package/dist/presenter/{presenter-report-copy.d.ts → chrome/presenter-report-copy.d.ts} +0 -0
  124. /package/dist/presenter/{presenter-report-copy.js → chrome/presenter-report-copy.js} +0 -0
  125. /package/dist/presenter/{presenter-tally.d.ts → chrome/presenter-tally.d.ts} +0 -0
  126. /package/dist/presenter/{presenter-verbs.d.ts → chrome/presenter-verbs.d.ts} +0 -0
  127. /package/dist/presenter/{presenter-verbs.js → chrome/presenter-verbs.js} +0 -0
  128. /package/dist/registry/{adapters.d.ts → stores/adapters.d.ts} +0 -0
  129. /package/dist/registry/{adapters.js → stores/adapters.js} +0 -0
  130. /package/dist/timers/{native-console.d.ts → console/native-console.d.ts} +0 -0
  131. /package/dist/timers/{native-console.js → console/native-console.js} +0 -0
  132. /package/dist/timers/{native-timers.d.ts → native/native-timers.d.ts} +0 -0
  133. /package/dist/timers/{native-timers.js → native/native-timers.js} +0 -0
package/dist/dom/query.js CHANGED
@@ -1,12 +1,12 @@
1
1
  import { DATA_RETICLE_SOURCE_ATTR, ElementState, QueryBy, REDACTED_VALUE, TRANSPORT_LIMITS, } from '@reticlehq/core';
2
- import { isFrame, isHtmlElement, isInput, isSelect, isTextArea } from './realm.js';
2
+ import { isElement, isFrame, isHtmlElement, 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';
6
6
  import { isSensitiveKey } from '../security/serialization.js';
7
7
  import { declaredTestids } from '../registry/capabilities.js';
8
- import { identifyComponent } from '../registry/adapters.js';
9
- import { refs } from './refs.js';
8
+ import { identifyComponent } from '../registry/stores/adapters.js';
9
+ import { refs } from './addressing/refs.js';
10
10
  const TESTID_ATTR = 'data-testid';
11
11
  const SOURCE_ATTR = DATA_RETICLE_SOURCE_ATTR;
12
12
  const MAX_PRESENT_TESTIDS = 12;
@@ -27,20 +27,35 @@ function fuzzyVisibleText(actual, expected) {
27
27
  function exactVisibleText(actual, expected) {
28
28
  return normaliseVisibleText(actual) === normaliseVisibleText(expected);
29
29
  }
30
+ const NON_RENDERED_TAGS = ['script', 'style', 'template', 'noscript'];
31
+ function isNonRendered(el) {
32
+ return NON_RENDERED_TAGS.includes(el.tagName.toLowerCase());
33
+ }
30
34
  function directText(el) {
31
35
  if (isInput(el)) {
32
36
  const type = el.type.toLowerCase();
33
37
  if ('submit' === type || 'button' === type || 'reset' === type)
34
38
  return el.value;
35
39
  }
36
- const tag = el.tagName.toLowerCase();
37
- if ('script' === tag || 'style' === tag)
40
+ if (isNonRendered(el))
38
41
  return '';
39
42
  return Array.from(el.childNodes)
40
43
  .filter((node) => Node.TEXT_NODE === node.nodeType)
41
44
  .map((node) => node.textContent ?? '')
42
45
  .join('');
43
46
  }
47
+ function visibleTextOf(el, memo) {
48
+ if (isNonRendered(el) || isIgnored(el) || !isVisible(el, memo))
49
+ return '';
50
+ let text = '';
51
+ for (const node of Array.from(el.childNodes)) {
52
+ if (Node.TEXT_NODE === node.nodeType)
53
+ text += node.textContent ?? '';
54
+ else if (isElement(node))
55
+ text += visibleTextOf(node, memo);
56
+ }
57
+ return text;
58
+ }
44
59
  function semanticNameTarget(el) {
45
60
  if (isInput(el) || isTextArea(el) || isSelect(el))
46
61
  return true;
@@ -205,7 +220,8 @@ function findCandidates(query) {
205
220
  if (true === query.self) {
206
221
  if (query.scope === undefined)
207
222
  return { candidates: [], scopeMissing };
208
- if (query.text !== undefined && !fuzzyVisibleText(container.textContent ?? '', query.text)) {
223
+ if (query.text !== undefined &&
224
+ !fuzzyVisibleText(visibleTextOf(container, new Map()), query.text)) {
209
225
  return { candidates: [], scopeMissing };
210
226
  }
211
227
  return { candidates: isIgnored(container) ? [] : [container], scopeMissing };
@@ -342,10 +358,11 @@ function buildPresentRegions(query) {
342
358
  function splitTextOwner(container, wanted) {
343
359
  let best;
344
360
  let bestDepth = -1;
361
+ const memo = new Map();
345
362
  for (const el of elementsUnder(container)) {
346
363
  if (isIgnored(el))
347
364
  continue;
348
- if (!fuzzyVisibleText(el.textContent ?? '', wanted))
365
+ if (!fuzzyVisibleText(visibleTextOf(el, memo), wanted))
349
366
  continue;
350
367
  let depth = 0;
351
368
  for (let parent = el.parentElement; parent !== null; parent = parent.parentElement)
@@ -2,7 +2,7 @@ import { ElementState, SnapshotMode } from '@reticlehq/core';
2
2
  import { capturedRootOf } from './shadow-registry.js';
3
3
  import { isFrame } from './realm.js';
4
4
  import { getAccessibleName, getRole, getStates, getValue, isVisible } from './a11y.js';
5
- import { refs } from './refs.js';
5
+ import { refs } from './addressing/refs.js';
6
6
  import { isIgnored, isReticleOverlay } from './dom-ignore.js';
7
7
  const INTERACTIVE = new Set([
8
8
  'button',
@@ -1,4 +1,4 @@
1
- import { type RefRegistry } from './dom/refs.js';
1
+ import { type RefRegistry } from './dom/addressing/refs.js';
2
2
  export declare class EditEpoch {
3
3
  #private;
4
4
  constructor(registry: RefRegistry);
@@ -1,5 +1,5 @@
1
1
  import { NO_EDITS_OBSERVED } from '@reticlehq/core';
2
- import { echoRef, refs } from './dom/refs.js';
2
+ import { echoRef, refs } from './dom/addressing/refs.js';
3
3
  const HOT_UPDATE_APPLIED = 'vite:afterUpdate';
4
4
  const MAX_NAMED_FILES = 3;
5
5
  const STALE_REF_REFUSAL = 'no longer resolves to an element';
package/dist/index.d.ts CHANGED
@@ -1,11 +1,10 @@
1
- import { Reticle } from './reticle.js';
2
- export declare const reticle: Reticle;
1
+ export { reticle } from './reticle-singleton.js';
3
2
  export { Reticle } from './reticle.js';
4
3
  export type { ReticleConnectOptions } from './reticle.js';
5
4
  export { SESSION_AUTO } from '@reticlehq/core';
6
5
  export { setIgnoreSelectors } from './dom/dom-ignore.js';
7
- export { registerAdapter, identifyComponent, readComponentState, elementHasHoverHandlers, adapterNames, } from './registry/adapters.js';
8
- export type { ReticleAdapter, ComponentInfo, ComponentSource } from './registry/adapters.js';
6
+ export { registerAdapter, identifyComponent, readComponentState, elementHasHoverHandlers, adapterNames, } from './registry/stores/adapters.js';
7
+ export type { ReticleAdapter, ComponentInfo, ComponentSource } from './registry/stores/adapters.js';
9
8
  export { registerStore, unregisterStore, storeNames, readStores, readStoresWithTruncation, sourceOwner, markAdapterSource, } from './registry/stores.js';
10
9
  export type { StoreGetter, StoreLike, StoreSubscribe } from './registry/stores.js';
11
10
  export { tanstackQueryStore, jotaiStore, xstateStore, valtioStore, mobxStore, recoilStore, svelteStore, piniaStore, pushStore, } from './registry/store-adapters.js';
@@ -23,6 +22,6 @@ export type { ChartReport, ChartFinding, CanvasChartData } from './dom/chart.js'
23
22
  export { matchQuery, runQuery } from './dom/query.js';
24
23
  export { executeAction, executeSequence } from './actions/actions.js';
25
24
  export { describe, getRole, getAccessibleName, getStates, isVisible } from './dom/a11y.js';
26
- export { refs, RefRegistry } from './dom/refs.js';
25
+ export { refs, RefRegistry } from './dom/addressing/refs.js';
27
26
  export { Annotator, installAnnotator, type AnnotatorDeps } from './review/annotator.js';
28
27
  export { resolveMarkAnchor, type MarkAnchor } from './review/mark-anchor.js';
package/dist/index.js CHANGED
@@ -1,10 +1,8 @@
1
- import { Reticle } from './reticle.js';
2
- const globalStore = globalThis;
3
- export const reticle = (globalStore.__reticleInstance ??= new Reticle());
1
+ export { reticle } from './reticle-singleton.js';
4
2
  export { Reticle } from './reticle.js';
5
3
  export { SESSION_AUTO } from '@reticlehq/core';
6
4
  export { setIgnoreSelectors } from './dom/dom-ignore.js';
7
- export { registerAdapter, identifyComponent, readComponentState, elementHasHoverHandlers, adapterNames, } from './registry/adapters.js';
5
+ export { registerAdapter, identifyComponent, readComponentState, elementHasHoverHandlers, adapterNames, } from './registry/stores/adapters.js';
8
6
  export { registerStore, unregisterStore, storeNames, readStores, readStoresWithTruncation, sourceOwner, markAdapterSource, } from './registry/stores.js';
9
7
  export { tanstackQueryStore, jotaiStore, xstateStore, valtioStore, mobxStore, recoilStore, svelteStore, piniaStore, pushStore, } from './registry/store-adapters.js';
10
8
  export { registerCapabilities, getCapabilities, hasCapabilities, setCapabilitiesListener, } from './registry/capabilities.js';
@@ -16,6 +14,6 @@ export { inspectChart, canvasChartData } from './dom/chart.js';
16
14
  export { matchQuery, runQuery } from './dom/query.js';
17
15
  export { executeAction, executeSequence } from './actions/actions.js';
18
16
  export { describe, getRole, getAccessibleName, getStates, isVisible } from './dom/a11y.js';
19
- export { refs, RefRegistry } from './dom/refs.js';
17
+ export { refs, RefRegistry } from './dom/addressing/refs.js';
20
18
  export { Annotator, installAnnotator } from './review/annotator.js';
21
19
  export { resolveMarkAnchor } from './review/mark-anchor.js';
@@ -1,5 +1,5 @@
1
1
  import { EventType } from '@reticlehq/core';
2
- import { refs } from '../dom/refs.js';
2
+ import { refs } from '../dom/addressing/refs.js';
3
3
  import { isReticleOverlay } from '../dom/dom-ignore.js';
4
4
  export function installAnimation(emit) {
5
5
  const ac = new AbortController();
@@ -2,7 +2,7 @@ import { EventType, BlindSpotKind } from '@reticlehq/core';
2
2
  import { countUnmountedRows } from '../dom/virtualized.js';
3
3
  import { isCaptured } from '../dom/shadow-registry.js';
4
4
  import { sameOriginFrameBodies } from './frames.js';
5
- import { nativeClearTimeout, nativeSetTimeout } from '../timers/native-timers.js';
5
+ import { nativeClearTimeout, nativeSetTimeout } from '../timers/native/native-timers.js';
6
6
  export function countClosedShadowRoots() {
7
7
  let count = 0;
8
8
  for (const el of document.querySelectorAll('*')) {
@@ -35,15 +35,23 @@ export function installDeparture(emit) {
35
35
  return;
36
36
  const href = anchor.getAttribute('href') ?? '';
37
37
  const here = document.location.href;
38
- if (!isDeparture(href, anchor.hasAttribute('download'), here))
38
+ const download = anchor.hasAttribute('download');
39
+ if (!isDeparture(href, download, here))
39
40
  return;
40
- seq += 1;
41
- emit(EventType.NET_PENDING, {
42
- id: `nav-${String(seq)}`,
43
- method: 'GET',
44
- url: new URL(href, here).toString(),
45
- initiator: NetInitiator.NAVIGATION,
46
- ...(anchor.hasAttribute('download') ? { download: true } : {}),
41
+ const url = new URL(href, here).toString();
42
+ queueMicrotask(() => {
43
+ observeSafely(() => {
44
+ if (event.defaultPrevented)
45
+ return;
46
+ seq += 1;
47
+ emit(EventType.NET_PENDING, {
48
+ id: `nav-${String(seq)}`,
49
+ method: 'GET',
50
+ url,
51
+ initiator: NetInitiator.NAVIGATION,
52
+ ...(download ? { download: true } : {}),
53
+ });
54
+ });
47
55
  });
48
56
  });
49
57
  };
@@ -1,6 +1,6 @@
1
1
  import { EventType } from '@reticlehq/core';
2
2
  import { observeSafely } from './types.js';
3
- import { isSyntheticInput } from '../actions/synthetic-input.js';
3
+ import { isSyntheticInput } from '../actions/synthetic/synthetic-input.js';
4
4
  const DECLINED = { alert: undefined, confirm: false, prompt: null };
5
5
  const KINDS = ['alert', 'confirm', 'prompt'];
6
6
  export function installDialogs(emit) {
@@ -1,6 +1,6 @@
1
1
  import { EventType, TruncationChannel } from '@reticlehq/core';
2
2
  import { getAccessibleName, getRole, isVisible } from '../dom/a11y.js';
3
- import { refs } from '../dom/refs.js';
3
+ import { refs } from '../dom/addressing/refs.js';
4
4
  function regionKeyOf(target) {
5
5
  const el = isElement(target) ? target : null;
6
6
  if (null === el)
@@ -39,6 +39,26 @@ const MAX_PER_BATCH = 40;
39
39
  function isMeaningful(role, name) {
40
40
  return role !== 'generic' || name.length > 0;
41
41
  }
42
+ const MOUNT_SCAN_LIMIT = 40;
43
+ function describeMount(node) {
44
+ const role = getRole(node);
45
+ const name = getAccessibleName(node);
46
+ if (isMeaningful(role, name))
47
+ return { role, name, el: node };
48
+ let scanned = 0;
49
+ for (const child of node.querySelectorAll('*')) {
50
+ if (scanned >= MOUNT_SCAN_LIMIT)
51
+ return undefined;
52
+ scanned += 1;
53
+ if (isReticleOverlay(child))
54
+ continue;
55
+ const childRole = getRole(child);
56
+ const childName = getAccessibleName(child);
57
+ if (isMeaningful(childRole, childName))
58
+ return { role: childRole, name: childName, el: child };
59
+ }
60
+ return undefined;
61
+ }
42
62
  const TEXT_NODE = 3;
43
63
  function textOf(nodes) {
44
64
  let out = '';
@@ -112,12 +132,12 @@ export function installDom(emit) {
112
132
  }
113
133
  if (isReticleOverlay(node))
114
134
  continue;
115
- const role = getRole(node);
116
- const name = getAccessibleName(node);
117
- if (!isMeaningful(role, name))
135
+ const mounted = describeMount(node);
136
+ if (mounted === undefined)
118
137
  continue;
138
+ const { role, name } = mounted;
119
139
  added += 1;
120
- const ref = refs.refFor(node);
140
+ const ref = refs.refFor(mounted.el);
121
141
  emit(EventType.DOM_ADDED, { role, name, region: regionKeyOf(record.target) }, ref);
122
142
  if (DIALOG_ROLES.has(role) ||
123
143
  LIVE_ROLES.has(role) ||
@@ -135,10 +155,10 @@ export function installDom(emit) {
135
155
  }
136
156
  if (isReticleOverlay(node))
137
157
  continue;
138
- const role = getRole(node);
139
- const name = getAccessibleName(node);
140
- if (!isMeaningful(role, name))
158
+ const gone = describeMount(node);
159
+ if (gone === undefined)
141
160
  continue;
161
+ const { role, name } = gone;
142
162
  removed += 1;
143
163
  emit(EventType.DOM_REMOVED, { role, name, region: regionKeyOf(record.target) });
144
164
  }
@@ -1,6 +1,6 @@
1
1
  import { EventType } from '@reticlehq/core';
2
2
  import { captureMethod } from '../patching/capture-method.js';
3
- import { projectBody } from './network-body.js';
3
+ import { projectBody } from './net-detail/network-body.js';
4
4
  import { observeSafely, observeValue } from './types.js';
5
5
  const TEXTUAL = /text\/|json|csv|xml|ndjson|javascript/i;
6
6
  const PREVIEW_CHARS = 2000;
@@ -1,5 +1,5 @@
1
1
  import { BrowserBrand, EventType, HealthReason, RETICLE_IPC_GLOBAL, SESSION_HEALTH, } from '@reticlehq/core';
2
- import { nativeSetInterval } from '../timers/native-timers.js';
2
+ import { nativeSetInterval } from '../timers/native/native-timers.js';
3
3
  function detectRuntime() {
4
4
  const w = window;
5
5
  if (w['__TAURI_INTERNALS__'] !== undefined || w['__TAURI__'] !== undefined)
@@ -1,6 +1,6 @@
1
1
  import { REDACTED_VALUE } from '@reticlehq/core';
2
- import { isSensitiveKey, safeStringify, scrubKnownSecrets } from '../security/serialization.js';
3
- import { nativeClearTimeout, nativeSetTimeout } from '../timers/native-timers.js';
2
+ import { isSensitiveKey, safeStringify, scrubKnownSecrets } from '../../security/serialization.js';
3
+ import { nativeClearTimeout, nativeSetTimeout } from '../../timers/native/native-timers.js';
4
4
  const CAPTURABLE_CONTENT = /application\/json|text\/|application\/xml|x-www-form-urlencoded|graphql/i;
5
5
  const STREAMING_CONTENT = /event-stream|x-ndjson|application\/stream/i;
6
6
  const ENDLESS_BY_DESIGN = /event-stream/i;
@@ -1,6 +1,6 @@
1
1
  import { EventType, StreamDirection, StreamTransport } from '@reticlehq/core';
2
- import { isStreamingBody } from './network-body.js';
3
- import { nativeClearTimeout, nativeSetTimeout } from '../timers/native-timers.js';
2
+ import { isStreamingBody } from './net-detail/network-body.js';
3
+ import { nativeClearTimeout, nativeSetTimeout } from '../timers/native/native-timers.js';
4
4
  const STREAM_WATCH_MS = 10_000;
5
5
  export function watchStreamedBody(emit, res, id, url, contentType, contentLength) {
6
6
  if (!isStreamingBody(contentType, contentLength))
@@ -1,5 +1,5 @@
1
1
  import { type Emit, type Teardown } from './types.js';
2
- import { redactUrl, netUrlFields } from './network-redact.js';
2
+ import { redactUrl, netUrlFields } from './net-detail/network-redact.js';
3
3
  export { redactUrl, netUrlFields };
4
4
  type NetResponseReinterpreter = (url: string, header: (name: string) => string | null) => Record<string, unknown> | undefined;
5
5
  interface NetworkOptions {
@@ -1,8 +1,8 @@
1
- import { BlindSpotKind, EventType, RETICLE_WS_PATH, StreamTransport, StreamDirection, } from '@reticlehq/core';
1
+ import { BlindSpotKind, EventType, REQUEST_SHAPE_FIELD, REQUEST_SHAPE_NONE, RETICLE_WS_PATH, StreamTransport, StreamDirection, fnv1a, } from '@reticlehq/core';
2
2
  import { captureMethod } from '../patching/capture-method.js';
3
3
  import { observeSafely, observeValue } from './types.js';
4
- import { isCapturableType, projectBody, projectErrorBody, shouldCaptureErrorBody, withBodyDeadline, } from './network-body.js';
5
- import { redactUrl, netUrlFields } from './network-redact.js';
4
+ import { isCapturableType, projectBody, projectErrorBody, shouldCaptureErrorBody, withBodyDeadline, } from './net-detail/network-body.js';
5
+ import { redactUrl, netUrlFields } from './net-detail/network-redact.js';
6
6
  import { watchStreamedBody } from './network-stream.js';
7
7
  import { requireCapturedMethod } from '../util/captured-method.js';
8
8
  export { redactUrl, netUrlFields };
@@ -31,26 +31,66 @@ function frameFields(data, captureBodies) {
31
31
  }
32
32
  return out;
33
33
  }
34
+ const ShapeToken = { NULL: '0', STRING: 's', NUMBER: 'n', BOOLEAN: 'b', OTHER: '?' };
35
+ function shapeOf(value) {
36
+ if (null === value)
37
+ return ShapeToken.NULL;
38
+ if (Array.isArray(value))
39
+ return `[${value.map(shapeOf).join(',')}]`;
40
+ if ('object' === typeof value) {
41
+ const fields = value;
42
+ return `{${Object.keys(fields)
43
+ .sort()
44
+ .map((key) => `${key}:${shapeOf(fields[key])}`)
45
+ .join(',')}}`;
46
+ }
47
+ if ('string' === typeof value)
48
+ return ShapeToken.STRING;
49
+ if ('number' === typeof value)
50
+ return ShapeToken.NUMBER;
51
+ if ('boolean' === typeof value)
52
+ return ShapeToken.BOOLEAN;
53
+ return ShapeToken.OTHER;
54
+ }
55
+ function fingerprintBody(shape, length) {
56
+ return fnv1a(`${shape}#${String(length)}`);
57
+ }
58
+ function jsonShape(text) {
59
+ try {
60
+ return shapeOf(JSON.parse(text));
61
+ }
62
+ catch {
63
+ return ShapeToken.OTHER;
64
+ }
65
+ }
34
66
  function projectRequestBody(body, captureBodies) {
35
- if (!captureBodies)
36
- return {};
37
67
  let text;
38
68
  let contentType = 'application/json';
69
+ let shape = REQUEST_SHAPE_NONE;
39
70
  if ('string' === typeof body) {
40
71
  text = body;
72
+ if (text.length > 0)
73
+ shape = fingerprintBody(jsonShape(text), text.length);
41
74
  }
42
75
  else if (typeof URLSearchParams !== 'undefined' && body instanceof URLSearchParams) {
43
76
  text = body.toString();
44
77
  contentType = 'application/x-www-form-urlencoded';
78
+ if (text.length > 0) {
79
+ const fields = Object.fromEntries(new URLSearchParams(text));
80
+ shape = fingerprintBody(shapeOf(fields), text.length);
81
+ }
45
82
  }
46
83
  else if (body !== undefined && body !== null) {
47
- const shape = body.constructor?.name ?? typeof body;
48
- return { requestBodyType: shape };
84
+ const kind = body.constructor?.name ?? typeof body;
85
+ return captureBodies ? { requestBodyType: kind } : {};
49
86
  }
50
- if (text === undefined || 0 === text.length)
51
- return {};
87
+ const fields = { [REQUEST_SHAPE_FIELD]: shape };
88
+ if (!captureBodies || text === undefined || 0 === text.length)
89
+ return fields;
52
90
  const { body: out, truncated } = projectBody(text, contentType);
53
- return truncated ? { requestBody: out, requestBodyTruncated: true } : { requestBody: out };
91
+ return truncated
92
+ ? { ...fields, requestBody: out, requestBodyTruncated: true }
93
+ : { ...fields, requestBody: out };
54
94
  }
55
95
  function statusIsOk(status) {
56
96
  return status >= 200 && status < 400;
@@ -428,6 +468,7 @@ export function installNetwork(emit, opts = {}) {
428
468
  durationMs: 0,
429
469
  initiator: 'beacon',
430
470
  ...(initiatorStack === undefined ? {} : { initiatorStack }),
471
+ ...projectRequestBody(data, false),
431
472
  });
432
473
  });
433
474
  return sent;
@@ -1,6 +1,6 @@
1
1
  import { EventType, ScrollDirection } from '@reticlehq/core';
2
- import { refs } from '../dom/refs.js';
3
- import { nativeSetTimeout, nativeClearTimeout } from '../timers/native-timers.js';
2
+ import { refs } from '../dom/addressing/refs.js';
3
+ import { nativeSetTimeout, nativeClearTimeout } from '../timers/native/native-timers.js';
4
4
  const THROTTLE_MS = 100;
5
5
  const REVEAL_SELECTOR = '[data-reticle-reveal], [data-reveal], section';
6
6
  export function installScroll(emit) {
@@ -0,0 +1,2 @@
1
+ export declare const Z_OVERLAY = 2147483600;
2
+ export declare const Z_TOUR: number;
@@ -0,0 +1,2 @@
1
+ export const Z_OVERLAY = 2147483600;
2
+ export const Z_TOUR = Z_OVERLAY + 30;
@@ -0,0 +1,12 @@
1
+ export declare const LOG_KIND: {
2
+ readonly READ: "read";
3
+ readonly ACT: "act";
4
+ readonly NARRATION: "narration";
5
+ readonly HUMAN: "human";
6
+ };
7
+ export type LogKind = (typeof LOG_KIND)[keyof typeof LOG_KIND];
8
+ export declare const LOG_RESULT: {
9
+ readonly PASS: "pass";
10
+ readonly FAIL: "fail";
11
+ };
12
+ export type LogResult = (typeof LOG_RESULT)[keyof typeof LOG_RESULT];
@@ -0,0 +1,7 @@
1
+ export const LOG_KIND = {
2
+ READ: 'read',
3
+ ACT: 'act',
4
+ NARRATION: 'narration',
5
+ HUMAN: 'human',
6
+ };
7
+ export const LOG_RESULT = { PASS: 'pass', FAIL: 'fail' };
@@ -1,4 +1,4 @@
1
- import { nativeSetTimeout } from '../timers/native-timers.js';
1
+ import { nativeSetTimeout } from '../../timers/native/native-timers.js';
2
2
  export function moveCursor(cursor, x, y) {
3
3
  if (cursor === undefined)
4
4
  return;
@@ -1,16 +1,7 @@
1
1
  import { PresenterMode } from '@reticlehq/core';
2
- export declare const LOG_KIND: {
3
- readonly READ: "read";
4
- readonly ACT: "act";
5
- readonly NARRATION: "narration";
6
- readonly HUMAN: "human";
7
- };
8
- export type LogKind = (typeof LOG_KIND)[keyof typeof LOG_KIND];
9
- export declare const LOG_RESULT: {
10
- readonly PASS: "pass";
11
- readonly FAIL: "fail";
12
- };
13
- export type LogResult = (typeof LOG_RESULT)[keyof typeof LOG_RESULT];
2
+ import { LOG_KIND, LOG_RESULT, type LogKind, type LogResult } from './log-kinds.js';
3
+ export { LOG_KIND, LOG_RESULT };
4
+ export type { LogKind, LogResult };
14
5
  export declare const CHIP_LABEL: Record<PresenterMode, string>;
15
6
  export declare const DATA_RETICLE_LOG = "data-reticle-log";
16
7
  export declare const LOG_TIME_ATTR = "data-reticle-log-time";
@@ -1,14 +1,9 @@
1
1
  import { PresenterMode } from '@reticlehq/core';
2
- import { nativeSetTimeout } from '../timers/native-timers.js';
3
- import { PresenterIcon, PRESENTER_ICON_SIZE, hiIcon, } from './presenter-icons.js';
2
+ import { LOG_KIND, LOG_RESULT } from './log-kinds.js';
3
+ import { nativeSetTimeout } from '../../timers/native/native-timers.js';
4
+ import { PresenterIcon, PRESENTER_ICON_SIZE, hiIcon, } from '../icons/presenter-icons.js';
4
5
  const DEFAULT_LOG_MAX = 50;
5
- export const LOG_KIND = {
6
- READ: 'read',
7
- ACT: 'act',
8
- NARRATION: 'narration',
9
- HUMAN: 'human',
10
- };
11
- export const LOG_RESULT = { PASS: 'pass', FAIL: 'fail' };
6
+ export { LOG_KIND, LOG_RESULT };
12
7
  const LOG_CHIP = { read: 'READ', act: 'ACT', narration: '', human: '' };
13
8
  const LOG_CHIP_ICON = {
14
9
  read: PresenterIcon.VIEW,
@@ -0,0 +1,2 @@
1
+ export declare function esc(value: string): string;
2
+ export declare function isSafeDashboardUrl(raw: string): boolean;
@@ -0,0 +1,16 @@
1
+ export function esc(value) {
2
+ return value
3
+ .replace(/&/g, '&amp;')
4
+ .replace(/</g, '&lt;')
5
+ .replace(/>/g, '&gt;')
6
+ .replace(/"/g, '&quot;');
7
+ }
8
+ export function isSafeDashboardUrl(raw) {
9
+ try {
10
+ const scheme = new URL(raw).protocol;
11
+ return 'https:' === scheme || 'http:' === scheme;
12
+ }
13
+ catch {
14
+ return false;
15
+ }
16
+ }
@@ -1,5 +1,5 @@
1
1
  import { LOG_RESULT } from './presenter-log.js';
2
- import { PresenterIcon, PRESENTER_ICON_SIZE, hiIcon } from './presenter-icons.js';
2
+ import { PresenterIcon, PRESENTER_ICON_SIZE, hiIcon } from '../icons/presenter-icons.js';
3
3
  export function renderTally(el, runLog, prev) {
4
4
  const next = countVerdicts(runLog);
5
5
  if (el === undefined)
@@ -18,6 +18,7 @@ declare const BODIES: {
18
18
  help: string;
19
19
  'caret-right': string;
20
20
  annotate: string;
21
+ sync: string;
21
22
  };
22
23
  export declare const HERO_ICON_BODIES: Record<keyof typeof BODIES, string>;
23
24
  export type HeroIconBodyKey = keyof typeof BODIES;
@@ -18,6 +18,7 @@ const BODIES = {
18
18
  help: '<path d="M8.228 9c.549-1.165 2.03-2 3.772-2c2.21 0 4 1.343 4 3c0 1.4-1.278 2.575-3.006 2.907c-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 1 1-18 0a9 9 0 0 1 18 0"/>',
19
19
  'caret-right': '<path d="m9 5l7 7l-7 7"/>',
20
20
  annotate: '<path d="m16.862 4.487l1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10"/>',
21
+ sync: '<path d="M4.5 12c0-1.232.046-2.453.138-3.662a4.006 4.006 0 0 1 3.7-3.7a48.678 48.678 0 0 1 7.324 0a4.006 4.006 0 0 1 3.7 3.7c.017.22.032.441.046.662M4.5 12l-3-3m3 3l3-3m12 3c0 1.232-.046 2.453-.138 3.662a4.006 4.006 0 0 1-3.7 3.7a48.657 48.657 0 0 1-7.324 0a4.006 4.006 0 0 1-3.7-3.7c-.017-.22-.032-.441-.046-.662M19.5 12l-3 3m3-3l3 3"/>',
21
22
  };
22
23
  export const HERO_ICON_BODIES = BODIES;
23
24
  const SOLID_BODIES = {
@@ -19,6 +19,7 @@ export declare const PresenterIcon: {
19
19
  readonly HELP: "help";
20
20
  readonly CARET_RIGHT: "caret-right";
21
21
  readonly ANNOTATE: "annotate";
22
+ readonly SYNC: "sync";
22
23
  };
23
24
  export type PresenterIconName = (typeof PresenterIcon)[keyof typeof PresenterIcon];
24
25
  export declare const PRESENTER_ICON_SIZE: {
@@ -19,6 +19,7 @@ export const PresenterIcon = {
19
19
  HELP: 'help',
20
20
  CARET_RIGHT: 'caret-right',
21
21
  ANNOTATE: 'annotate',
22
+ SYNC: 'sync',
22
23
  };
23
24
  export const PRESENTER_ICON_SIZE = {
24
25
  CHIP: 11,
@@ -0,0 +1 @@
1
+ export declare const ACCOUNT_CSS: string;