@reticlehq/browser 3.5.0 → 3.6.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 (87) hide show
  1. package/dist/actions/actions-dom.js +44 -43
  2. package/dist/actions/actions-press.d.ts +10 -4
  3. package/dist/actions/actions-press.js +108 -17
  4. package/dist/actions/actions.d.ts +2 -0
  5. package/dist/actions/actions.js +25 -16
  6. package/dist/actions/danger-context.js +1 -1
  7. package/dist/connect-options.d.ts +1 -0
  8. package/dist/dom/a11y.js +13 -4
  9. package/dist/dom/addressing/refs.js +2 -1
  10. package/dist/dom/addressing/testid-attr.d.ts +4 -0
  11. package/dist/dom/addressing/testid-attr.js +14 -0
  12. package/dist/dom/query.d.ts +1 -0
  13. package/dist/dom/query.js +38 -11
  14. package/dist/dom/snapshot.js +1 -0
  15. package/dist/observers/departure.js +51 -9
  16. package/dist/observers/dom.js +3 -2
  17. package/dist/observers/field.js +5 -3
  18. package/dist/observers/health.js +63 -2
  19. package/dist/observers/network-stream.d.ts +1 -1
  20. package/dist/observers/network-stream.js +3 -3
  21. package/dist/observers/network.js +1 -1
  22. package/dist/observers/storage.js +6 -1
  23. package/dist/presenter/carousel/carousel.d.ts +5 -2
  24. package/dist/presenter/carousel/carousel.js +34 -7
  25. package/dist/presenter/carousel/offer-card.d.ts +2 -2
  26. package/dist/presenter/carousel/offer-card.js +2 -1
  27. package/dist/presenter/carousel/panel-slides.d.ts +4 -3
  28. package/dist/presenter/carousel/panel-slides.js +43 -6
  29. package/dist/presenter/carousel/talk-card.d.ts +1 -1
  30. package/dist/presenter/chrome/log-kinds.d.ts +5 -0
  31. package/dist/presenter/chrome/log-kinds.js +1 -0
  32. package/dist/presenter/chrome/log-memory.d.ts +12 -0
  33. package/dist/presenter/chrome/log-memory.js +39 -0
  34. package/dist/presenter/chrome/presenter-hud-chrome.d.ts +1 -0
  35. package/dist/presenter/chrome/presenter-hud-chrome.js +7 -1
  36. package/dist/presenter/chrome/presenter-log.d.ts +5 -4
  37. package/dist/presenter/chrome/presenter-log.js +55 -5
  38. package/dist/presenter/chrome/presenter-report-copy.d.ts +11 -1
  39. package/dist/presenter/chrome/presenter-report-copy.js +14 -1
  40. package/dist/presenter/hud-telemetry.js +31 -2
  41. package/dist/presenter/icons/presenter-heroicons-data.d.ts +1 -0
  42. package/dist/presenter/icons/presenter-heroicons-data.js +4 -3
  43. package/dist/presenter/icons/presenter-icons.d.ts +2 -0
  44. package/dist/presenter/icons/presenter-icons.js +2 -0
  45. package/dist/presenter/presenter-account.d.ts +5 -4
  46. package/dist/presenter/presenter-account.js +23 -10
  47. package/dist/presenter/presenter-chat-views.d.ts +21 -0
  48. package/dist/presenter/presenter-chat-views.js +382 -0
  49. package/dist/presenter/presenter-config.d.ts +3 -2
  50. package/dist/presenter/presenter-config.js +4 -3
  51. package/dist/presenter/presenter-controls.d.ts +4 -2
  52. package/dist/presenter/presenter-controls.js +231 -51
  53. package/dist/presenter/presenter-design-tokens.d.ts +1 -0
  54. package/dist/presenter/presenter-design-tokens.js +34 -0
  55. package/dist/presenter/presenter-dock-layout.js +1 -1
  56. package/dist/presenter/presenter-frame.d.ts +12 -0
  57. package/dist/presenter/presenter-frame.js +189 -0
  58. package/dist/presenter/presenter-plan.d.ts +10 -0
  59. package/dist/presenter/presenter-plan.js +185 -0
  60. package/dist/presenter/presenter-report-styles.js +7 -0
  61. package/dist/presenter/presenter-report.d.ts +1 -1
  62. package/dist/presenter/presenter-report.js +53 -16
  63. package/dist/presenter/presenter-settings-styles.js +10 -0
  64. package/dist/presenter/presenter-settings.d.ts +5 -0
  65. package/dist/presenter/presenter-settings.js +71 -11
  66. package/dist/presenter/presenter-shell-styles.js +39 -42
  67. package/dist/presenter/presenter-shell.d.ts +8 -1
  68. package/dist/presenter/presenter-shell.js +124 -72
  69. package/dist/presenter/presenter-styles.js +9 -1
  70. package/dist/presenter/presenter.d.ts +4 -3
  71. package/dist/presenter/presenter.js +101 -13
  72. package/dist/presenter/tour/tour.d.ts +4 -1
  73. package/dist/presenter/tour/tour.js +6 -4
  74. package/dist/recorder/recorder.js +2 -2
  75. package/dist/registry/auto-testids.js +3 -4
  76. package/dist/registry/declared-platform.js +2 -0
  77. package/dist/reticle-inject.js +880 -513
  78. package/dist/reticle.d.ts +10 -1
  79. package/dist/reticle.js +73 -9
  80. package/dist/review/annotator-styles.d.ts +1 -1
  81. package/dist/review/annotator.d.ts +7 -0
  82. package/dist/review/annotator.js +20 -2
  83. package/dist/review/mark-anchor.js +2 -2
  84. package/dist/session-continuity.js +2 -1
  85. package/dist/storage-keys.d.ts +14 -0
  86. package/dist/storage-keys.js +17 -0
  87. package/package.json +3 -2
package/dist/reticle.d.ts CHANGED
@@ -1,8 +1,12 @@
1
- import { EventType, RETICLE_URL_PARAM, type ReticleEvent } from '@reticlehq/core';
1
+ import { EventType, RETICLE_URL_PARAM, HudVisibility, type ReticleEvent } from '@reticlehq/core';
2
2
  import { type CapabilitiesInput } from './registry/capabilities.js';
3
3
  import type { ReticleConnectOptions } from './connect-options.js';
4
4
  export type { ReticleConnectOptions };
5
5
  export declare function shouldBlockProduction(nodeEnv: string | undefined, allowInProduction: boolean): boolean;
6
+ export declare function withBridgePath(bridgeUrl: string): {
7
+ url: string;
8
+ adjusted: boolean;
9
+ };
6
10
  export declare function connectionPolicy(pageHostname: string, bridgeUrl: string, allowNonLocalhost: boolean, token: string | undefined, pageProtocol?: string): {
7
11
  allowed: boolean;
8
12
  reason?: string;
@@ -12,6 +16,11 @@ export { RETICLE_URL_PARAM };
12
16
  export declare function reticleParamsFromSearch(search: string): {
13
17
  session?: string;
14
18
  projectId?: string;
19
+ hud?: HudVisibility;
20
+ };
21
+ export declare function hudWithoutPanel(hud: HudVisibility | undefined): {
22
+ remember?: HudVisibility;
23
+ placed: HudVisibility[];
15
24
  };
16
25
  export declare function resolveConnectIdentity(options: {
17
26
  session?: string;
package/dist/reticle.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { setPresenterVisible } from './dom/dom-ignore.js';
2
- import { EventType, RETICLE_DEFAULT_PORT, RETICLE_PROTOCOL_VERSION, RETICLE_URL_PARAM, bridgeWsUrl, ReticleCommand, MessageKind, SESSION_AUTO, SessionState, TRANSPORT_LIMITS, buildRedactionPolicy, isLoopbackHostname, isLocalPage, setActiveRedactionPolicy, wireRedactionKeys, RETICLE_ROOT_GLOBAL, RETICLE_SDK_VERSION_GLOBAL, CONTRACT_FINGERPRINT, CONTRACT_PARTS, newDocumentId, NO_EDITS_OBSERVED, PresenterMode, } from '@reticlehq/core';
2
+ import { EventType, RETICLE_DEFAULT_PORT, RETICLE_PROTOCOL_VERSION, RETICLE_URL_PARAM, RETICLE_WS_PATH, bridgeWsUrl, ReticleCommand, MessageKind, SESSION_AUTO, SessionState, TRANSPORT_LIMITS, buildRedactionPolicy, isLoopbackHostname, isLocalPage, setActiveRedactionPolicy, wireRedactionKeys, RETICLE_ROOT_GLOBAL, RETICLE_SDK_VERSION_GLOBAL, CONTRACT_FINGERPRINT, CONTRACT_PARTS, newDocumentId, NO_EDITS_OBSERVED, PresenterMode, CommandActor, HudCorner, HudVisibility, } from '@reticlehq/core';
3
3
  import { rememberSessionLabel } from './session-continuity.js';
4
4
  import { editEpoch } from './edit-epoch.js';
5
5
  import { createCommandRegistry, RELOAD_CACHE_BUST_PARAM, } from './commands/commands.js';
@@ -10,9 +10,10 @@ import { declaredChannels } from './registry/declared-channels.js';
10
10
  import { declaredCommands, declaredPlatform } from './registry/declared-platform.js';
11
11
  import { registerCapabilities, setCapabilitiesListener, hasCapabilities, } from './registry/capabilities.js';
12
12
  import { installAllObservers, runTeardowns } from './observers/install-all.js';
13
+ import { setTestIdAttr } from './dom/addressing/testid-attr.js';
13
14
  import { setNetworkBodyMaxChars } from './observers/net-detail/network-body.js';
14
15
  import { installOverlay } from './presenter/chrome/overlay.js';
15
- import { LOG_KIND, LOG_RESULT } from './presenter/chrome/log-kinds.js';
16
+ import { LOG_ACTOR, LOG_KIND, LOG_RESULT } from './presenter/chrome/log-kinds.js';
16
17
  import { actionVerb } from './presenter/chrome/presenter-verbs.js';
17
18
  import { str, refLabel, modeForCommand, presentStatus } from './reticle-presenter-helpers.js';
18
19
  import { resetClock } from './timers/clock.js';
@@ -22,6 +23,21 @@ import { Annotator } from './review/annotator.js';
22
23
  export function shouldBlockProduction(nodeEnv, allowInProduction) {
23
24
  return 'production' === nodeEnv && !allowInProduction;
24
25
  }
26
+ export function withBridgePath(bridgeUrl) {
27
+ let parsed;
28
+ try {
29
+ parsed = new URL(bridgeUrl);
30
+ }
31
+ catch {
32
+ return { url: bridgeUrl, adjusted: false };
33
+ }
34
+ if ((parsed.protocol !== 'ws:' && parsed.protocol !== 'wss:') ||
35
+ (parsed.pathname !== '' && parsed.pathname !== '/')) {
36
+ return { url: bridgeUrl, adjusted: false };
37
+ }
38
+ parsed.pathname = RETICLE_WS_PATH;
39
+ return { url: parsed.toString(), adjusted: true };
40
+ }
25
41
  export function connectionPolicy(pageHostname, bridgeUrl, allowNonLocalhost, token, pageProtocol = 'http:') {
26
42
  let bridge;
27
43
  try {
@@ -57,6 +73,7 @@ export function connectionPolicy(pageHostname, bridgeUrl, allowNonLocalhost, tok
57
73
  }
58
74
  return { allowed: true };
59
75
  }
76
+ const KILLED_MESSAGE = '[Reticle] killed for this page. Restart your dev server or reload the page to bring it back.';
60
77
  const BRIDGE_LOST_SUMMARY = 'Session ended - lost connection to Reticle (the agent is no longer running).';
61
78
  export function resolveSessionLabel(option, gen) {
62
79
  return option === undefined || option === SESSION_AUTO ? gen() : option;
@@ -78,12 +95,20 @@ export function reticleParamsFromSearch(search) {
78
95
  const out = {};
79
96
  const session = params.get(RETICLE_URL_PARAM.SESSION);
80
97
  const projectId = params.get(RETICLE_URL_PARAM.PROJECT);
98
+ const hud = oneOf(HudVisibility, params.get(RETICLE_URL_PARAM.HUD));
81
99
  if (session !== null && session.length > 0)
82
100
  out.session = session;
83
101
  if (projectId !== null && projectId.length > 0)
84
102
  out.projectId = projectId;
103
+ if (hud !== undefined)
104
+ out.hud = hud;
85
105
  return out;
86
106
  }
107
+ export function hudWithoutPanel(hud) {
108
+ if (hud === undefined)
109
+ return { placed: [] };
110
+ return { remember: hud, placed: HudVisibility.SHOWN === hud ? [] : [hud] };
111
+ }
87
112
  export function resolveConnectIdentity(options, search) {
88
113
  const url = reticleParamsFromSearch(search);
89
114
  const explicitSession = options.session !== undefined && options.session !== SESSION_AUTO ? options.session : undefined;
@@ -116,6 +141,7 @@ export class Reticle {
116
141
  #overlay;
117
142
  #presenter;
118
143
  #pendingPushes = new Map();
144
+ #pendingHud;
119
145
  #readPanelSettings;
120
146
  #panelRequested = false;
121
147
  #bridgeConnected = false;
@@ -142,7 +168,11 @@ export class Reticle {
142
168
  return;
143
169
  }
144
170
  stripReloadCacheBustParam();
145
- const url = options.url ?? bridgeWsUrl(RETICLE_DEFAULT_PORT);
171
+ const bridge = withBridgePath(options.url ?? bridgeWsUrl(RETICLE_DEFAULT_PORT));
172
+ const url = bridge.url;
173
+ if (bridge.adjusted) {
174
+ globalThis.console.warn(`[Reticle] bridge URL has no path; using ${url} (the bridge only accepts WebSocket upgrades on ${RETICLE_WS_PATH}).`);
175
+ }
146
176
  const policy = connectionPolicy(window.location.hostname, url, true === options.allowNonLocalhost, options.token, window.location.protocol);
147
177
  if (!policy.allowed) {
148
178
  globalThis.console.warn(`[Reticle] ${policy.reason ?? 'connection blocked'}`);
@@ -188,6 +218,7 @@ export class Reticle {
188
218
  });
189
219
  setCapabilitiesListener(() => this.#transport?.reannounce());
190
220
  setPresenterVisible(true === options.exposePresenter);
221
+ setTestIdAttr(options.testIdAttribute);
191
222
  if (options.networkBodyMaxChars !== undefined) {
192
223
  setNetworkBodyMaxChars(options.networkBodyMaxChars);
193
224
  }
@@ -205,7 +236,8 @@ export class Reticle {
205
236
  this.#overlay = installOverlay();
206
237
  this.#overlay.update({ connected: true, events: 0 });
207
238
  }
208
- if (options.present !== false)
239
+ this.#pendingHud = reticleParamsFromSearch(window.location.search).hud;
240
+ if (options.present !== false && HudVisibility.REMOVED !== this.#pendingHud)
209
241
  this.#fetchAndShowPanel(options);
210
242
  if (true === options.recorder) {
211
243
  this.#recorder = installRecorder({ emit, now: () => Date.now() });
@@ -215,7 +247,7 @@ export class Reticle {
215
247
  this.#annotator = new Annotator({
216
248
  emit,
217
249
  now: () => Date.now(),
218
- onMark: (mark) => this.#presenter?.log(LOG_KIND.HUMAN, `🚩 #${String(mark.index)} ${mark.anchor}${mark.source !== undefined ? ` · ${mark.source}` : ''} — ${mark.note}`),
250
+ onMark: (mark) => this.#presenter?.log(LOG_KIND.HUMAN, `🚩 #${String(mark.index)} ${mark.label}${mark.source !== undefined ? ` · ${mark.source}` : ''} — ${mark.note}`),
219
251
  shouldBlock: () => this.#readPanelSettings?.().blockPageInteractions ?? true,
220
252
  });
221
253
  this.#annotator.mount();
@@ -253,12 +285,19 @@ export class Reticle {
253
285
  ? { kind: intent.kind, text: intent.text }
254
286
  : { kind: intent.kind });
255
287
  panelOptions.onHudUse = (use) => this.#emit(EventType.HUD_USED, { ...use });
288
+ panelOptions.onKill = () => {
289
+ this.disconnect();
290
+ nativeWarn(KILLED_MESSAGE);
291
+ };
256
292
  const panel = new Presenter(panelOptions);
257
293
  this.#presenter = panel;
258
294
  panel.mount();
259
295
  for (const buffered of this.#pendingPushes.values())
260
296
  panel.handlePush(buffered);
261
297
  this.#pendingPushes.clear();
298
+ if (this.#pendingHud !== undefined)
299
+ panel.placeHud(this.#pendingHud, undefined);
300
+ this.#pendingHud = undefined;
262
301
  void import('./presenter/tour/tour.js').then((tour) => {
263
302
  if (!this.#connected)
264
303
  return;
@@ -393,10 +432,31 @@ export class Reticle {
393
432
  const idleEndMs = command.args['idleEndMs'];
394
433
  if ('number' === typeof idleEndMs)
395
434
  this.#presenter?.setIdleEndMs(idleEndMs);
396
- return { ok: true, result: { applied: this.#presenter !== undefined, idleEndMs } };
435
+ const hud = oneOf(HudVisibility, command.args['hud']);
436
+ const corner = oneOf(HudCorner, command.args['corner']);
437
+ const presenter = this.#presenter;
438
+ let placed;
439
+ if (presenter !== undefined)
440
+ placed = presenter.placeHud(hud, corner);
441
+ else {
442
+ const early = hudWithoutPanel(hud);
443
+ if (early.remember !== undefined)
444
+ this.#pendingHud = early.remember;
445
+ placed = early.placed;
446
+ }
447
+ return {
448
+ ok: true,
449
+ result: {
450
+ applied: this.#presenter !== undefined,
451
+ idleEndMs,
452
+ ...(0 < placed.length ? { hud: placed } : {}),
453
+ },
454
+ };
397
455
  }
398
456
  if (command.name === ReticleCommand.PRESENTER ||
399
457
  command.name === ReticleCommand.FLOWS ||
458
+ command.name === ReticleCommand.FLOW_PROGRESS ||
459
+ command.name === ReticleCommand.PLAN ||
400
460
  command.name === ReticleCommand.IMPACT) {
401
461
  if (this.#presenter === undefined) {
402
462
  this.#pendingPushes.set(command.name, command);
@@ -431,10 +491,11 @@ export class Reticle {
431
491
  return;
432
492
  p.setMode(modeForCommand(command.name));
433
493
  this.#actHandle = undefined;
494
+ const actor = CommandActor.HARNESS === command.by ? LOG_ACTOR.HARNESS : LOG_ACTOR.AGENT;
434
495
  if (command.name === ReticleCommand.ACT) {
435
496
  const ref = str(command.args['ref']);
436
497
  const label = refLabel(ref);
437
- this.#actHandle = p.log(LOG_KIND.ACT, `${actionVerb(str(command.args['action']))} ${label}`);
498
+ this.#actHandle = p.log(LOG_KIND.ACT, `${actionVerb(str(command.args['action']))} ${label}`, undefined, actor);
438
499
  await p.beforeAct(ref, str(command.args['action']), label);
439
500
  }
440
501
  else if (command.name === ReticleCommand.ACT_SEQUENCE) {
@@ -443,14 +504,17 @@ export class Reticle {
443
504
  const s = step;
444
505
  const ref = str(s.ref);
445
506
  const label = refLabel(ref);
446
- this.#actHandle = p.log(LOG_KIND.ACT, `${actionVerb(str(s.action))} ${label}`);
507
+ this.#actHandle = p.log(LOG_KIND.ACT, `${actionVerb(str(s.action))} ${label}`, undefined, actor);
447
508
  await p.beforeAct(ref, str(s.action), label);
448
509
  }
449
510
  }
450
511
  else {
451
512
  const label = presentStatus(command.name, command.args);
452
513
  p.status(label);
453
- p.log(LOG_KIND.READ, label);
514
+ p.log(LOG_KIND.READ, label, undefined, actor);
454
515
  }
455
516
  }
456
517
  }
518
+ function oneOf(values, raw) {
519
+ return Object.values(values).includes(raw) ? raw : undefined;
520
+ }
@@ -3,4 +3,4 @@ export declare const MARK_SUBMIT = "Add";
3
3
  export declare const MARK_CANCEL = "Cancel";
4
4
  export declare const MARK_PENDING_GLYPH = "+";
5
5
  export declare const ANNOTATOR_CSS: string;
6
- export declare const ANNOTATOR_ROOT_HTML = "<div data-reticle-mark=\"hi\"><span data-reticle-mark=\"hilabel\"></span></div><div data-reticle-mark=\"sel\" hidden></div>";
6
+ export declare const ANNOTATOR_ROOT_HTML: string;
@@ -1,4 +1,5 @@
1
1
  import { EventType } from '@reticlehq/core';
2
+ import type { MarkForAgent } from './marks-for-agent.js';
2
3
  interface MarkReport {
3
4
  note: string;
4
5
  anchor: string;
@@ -19,12 +20,18 @@ export interface AnnotatorChrome {
19
20
  copyRow?: HTMLElement;
20
21
  countEl?: HTMLElement;
21
22
  onCount?: (count: number) => void;
23
+ onMarksChange?: (marks: readonly AnnotationItem[]) => void;
24
+ }
25
+ export interface AnnotationItem extends MarkForAgent {
26
+ id: string;
27
+ createdAt: number;
22
28
  }
23
29
  export declare class Annotator {
24
30
  #private;
25
31
  constructor(deps: AnnotatorDeps);
26
32
  get active(): boolean;
27
33
  get markCount(): number;
34
+ annotations(): AnnotationItem[];
28
35
  mount(): void;
29
36
  setAccent(hex: string): void;
30
37
  attachChrome(chrome: AnnotatorChrome): void;
@@ -1,3 +1,4 @@
1
+ import { readTestId } from '../dom/addressing/testid-attr.js';
1
2
  import { isSyntheticInput } from '../actions/synthetic/synthetic-input.js';
2
3
  import { EventType } from '@reticlehq/core';
3
4
  import { isReticleUi, isReticleOverlay } from '../dom/dom-ignore.js';
@@ -36,6 +37,7 @@ export class Annotator {
36
37
  #clearBtn;
37
38
  #copyRow;
38
39
  #onChromeCount;
40
+ #onMarksChange;
39
41
  #countEl;
40
42
  #accent;
41
43
  #ac;
@@ -53,6 +55,17 @@ export class Annotator {
53
55
  get markCount() {
54
56
  return this.#marks.length;
55
57
  }
58
+ annotations() {
59
+ return this.#marks.map(({ id, createdAt, note, label, anchor, route, source }) => ({
60
+ id,
61
+ createdAt,
62
+ note,
63
+ label,
64
+ anchor,
65
+ route,
66
+ ...(source === undefined ? {} : { source }),
67
+ }));
68
+ }
56
69
  mount() {
57
70
  if (this.#root !== undefined || 'undefined' === typeof document)
58
71
  return;
@@ -99,6 +112,7 @@ export class Annotator {
99
112
  this.#clearBtn = chrome.clearBtn;
100
113
  this.#copyRow = chrome.copyRow;
101
114
  this.#onChromeCount = chrome.onCount;
115
+ this.#onMarksChange = chrome.onMarksChange;
102
116
  this.#countEl = chrome.countEl;
103
117
  this.#markersBtn?.addEventListener('click', (e) => {
104
118
  e.stopPropagation();
@@ -184,6 +198,7 @@ export class Annotator {
184
198
  }
185
199
  this.#clearBtn?.toggleAttribute('disabled', 0 === n);
186
200
  this.#onChromeCount?.(n);
201
+ this.#onMarksChange?.(this.annotations());
187
202
  if (this.#copyRow !== undefined) {
188
203
  this.#copyRow.hidden = 0 === n;
189
204
  const text = this.#copyRow.querySelector('[data-reticle-marks-text]');
@@ -406,6 +421,7 @@ export class Annotator {
406
421
  route: editing.route,
407
422
  });
408
423
  this.#report(note, editing.anchor, editing.label, this.#marks.indexOf(editing) + 1, editing.source);
424
+ this.#syncChrome();
409
425
  this.#closePopover();
410
426
  return;
411
427
  }
@@ -480,8 +496,10 @@ export class Annotator {
480
496
  pin.style.left = `${String((x / window.innerWidth) * 100)}%`;
481
497
  pin.style.top = `${String(isFixed ? y : yDoc - window.scrollY)}px`;
482
498
  this.#root.appendChild(pin);
499
+ const createdAt = this.#now();
483
500
  const mark = {
484
- id: `${resolved.anchor}:${String(this.#now())}:${String(n)}`,
501
+ id: `${resolved.anchor}:${String(createdAt)}:${String(n)}`,
502
+ createdAt,
485
503
  note,
486
504
  label: resolved.label,
487
505
  anchor: resolved.anchor,
@@ -593,7 +611,7 @@ function clampPop(x, y) {
593
611
  return { left, top };
594
612
  }
595
613
  function describeEl(el) {
596
- const testid = el.getAttribute('data-testid');
614
+ const testid = readTestId(el);
597
615
  if (testid !== null && testid.length > 0)
598
616
  return testid;
599
617
  const tag = el.tagName.toLowerCase();
@@ -1,9 +1,9 @@
1
+ import { readTestId } from '../dom/addressing/testid-attr.js';
1
2
  import { MarkAnchorStrategy } from '@reticlehq/core';
2
3
  import { AnchorStrategy, synthesizeAnchor, } from '../dom/addressing/auto-anchor.js';
3
4
  import { getAccessibleName, getRole } from '../dom/a11y.js';
4
5
  import { identifyComponent } from '../registry/stores/adapters.js';
5
6
  import { sourceFor } from '../dom/addressing/source.js';
6
- const TESTID_ATTR = 'data-testid';
7
7
  const STRATEGY = {
8
8
  [AnchorStrategy.TESTID]: MarkAnchorStrategy.TESTID,
9
9
  [AnchorStrategy.COMPONENT]: MarkAnchorStrategy.COMPONENT,
@@ -16,7 +16,7 @@ function labelFor(el, role, name) {
16
16
  return role.length > 0 ? role : el.tagName.toLowerCase();
17
17
  }
18
18
  export function resolveMarkAnchor(el) {
19
- const testid = el.getAttribute(TESTID_ATTR) ?? undefined;
19
+ const testid = readTestId(el) ?? undefined;
20
20
  const info = identifyComponent(el);
21
21
  const component = info?.componentStack[0];
22
22
  const source = sourceFor(el, info?.source);
@@ -1,4 +1,5 @@
1
- const STORAGE_KEY = '__reticle_session';
1
+ import { ReticleStorageKey } from './storage-keys.js';
2
+ const STORAGE_KEY = ReticleStorageKey.SESSION;
2
3
  export function rememberSessionLabel(explicit, store, generate) {
3
4
  const read = () => {
4
5
  try {
@@ -0,0 +1,14 @@
1
+ export declare const ReticleStorageKey: {
2
+ readonly SESSION: "__reticle_session";
3
+ readonly REF_BASE: "__reticle_ref_base";
4
+ readonly PRESENTER_LOG: "reticle-presenter-log";
5
+ readonly PRESENTER_LOG_OWNER: "reticle-presenter-log-owner";
6
+ readonly PRESENTER_SETTINGS: "reticle-presenter-settings";
7
+ readonly PRESENTER_MINIMISED: "reticle-presenter-minimised";
8
+ readonly CAROUSEL_DISMISSED: "reticle.carousel.dismissed";
9
+ readonly HARNESS_OFFER_DISMISSED: "reticle.harnessOffer.dismissed";
10
+ readonly ANNOTATION_HISTORY: "reticle.annotations.history.v1";
11
+ readonly RECENT_PLAYS: "reticle.flow.recent.v1";
12
+ };
13
+ export declare const TOUR_SEEN_KEY_PREFIX = "reticle.tour.seen.";
14
+ export declare function isReticleStorageKey(key: string): boolean;
@@ -0,0 +1,17 @@
1
+ export const ReticleStorageKey = {
2
+ SESSION: '__reticle_session',
3
+ REF_BASE: '__reticle_ref_base',
4
+ PRESENTER_LOG: 'reticle-presenter-log',
5
+ PRESENTER_LOG_OWNER: 'reticle-presenter-log-owner',
6
+ PRESENTER_SETTINGS: 'reticle-presenter-settings',
7
+ PRESENTER_MINIMISED: 'reticle-presenter-minimised',
8
+ CAROUSEL_DISMISSED: 'reticle.carousel.dismissed',
9
+ HARNESS_OFFER_DISMISSED: 'reticle.harnessOffer.dismissed',
10
+ ANNOTATION_HISTORY: 'reticle.annotations.history.v1',
11
+ RECENT_PLAYS: 'reticle.flow.recent.v1',
12
+ };
13
+ export const TOUR_SEEN_KEY_PREFIX = 'reticle.tour.seen.';
14
+ const OWN_KEYS = new Set(Object.values(ReticleStorageKey));
15
+ export function isReticleStorageKey(key) {
16
+ return OWN_KEYS.has(key) || key.startsWith(TOUR_SEEN_KEY_PREFIX);
17
+ }
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@reticlehq/browser",
3
- "version": "3.5.0",
3
+ "version": "3.6.0",
4
4
  "description": "Reticle browser SDK — installs observers, builds semantic snapshots, executes actions, talks to the bridge.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
+ "module": "./dist/reticle-inject.js",
7
8
  "types": "./dist/index.d.ts",
8
9
  "exports": {
9
10
  ".": {
@@ -17,7 +18,7 @@
17
18
  }
18
19
  },
19
20
  "dependencies": {
20
- "@reticlehq/core": "3.5.0"
21
+ "@reticlehq/core": "3.6.0"
21
22
  },
22
23
  "devDependencies": {
23
24
  "@iconify-json/heroicons-outline": "^1.2.1",