@reticlehq/browser 3.1.0 → 3.3.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 (71) hide show
  1. package/dist/actions/actions-dom.d.ts +2 -1
  2. package/dist/actions/actions-dom.js +28 -41
  3. package/dist/actions/actions.d.ts +0 -1
  4. package/dist/actions/actions.js +13 -7
  5. package/dist/actions/value-input.js +14 -5
  6. package/dist/connect-options.d.ts +1 -0
  7. package/dist/dom/addressing/source.d.ts +2 -1
  8. package/dist/dom/dom-ignore.d.ts +1 -0
  9. package/dist/dom/dom-ignore.js +13 -0
  10. package/dist/dom/query.js +28 -9
  11. package/dist/dom/snapshot.d.ts +1 -1
  12. package/dist/observers/console.js +38 -13
  13. package/dist/observers/departure.js +16 -8
  14. package/dist/observers/dom.js +3 -1
  15. package/dist/observers/download.js +3 -2
  16. package/dist/observers/error-repeats.d.ts +18 -0
  17. package/dist/observers/error-repeats.js +41 -0
  18. package/dist/observers/field.d.ts +2 -0
  19. package/dist/observers/field.js +86 -0
  20. package/dist/observers/frames.js +3 -4
  21. package/dist/observers/install-all.js +2 -0
  22. package/dist/observers/net-detail/network-body.d.ts +2 -0
  23. package/dist/observers/net-detail/network-body.js +24 -7
  24. package/dist/observers/network.js +49 -8
  25. package/dist/observers/route.js +4 -4
  26. package/dist/observers/state.js +18 -8
  27. package/dist/presenter/carousel/carousel.d.ts +13 -0
  28. package/dist/presenter/carousel/carousel.js +126 -0
  29. package/dist/presenter/carousel/offer-card.d.ts +21 -0
  30. package/dist/presenter/carousel/offer-card.js +55 -0
  31. package/dist/presenter/carousel/panel-slides.d.ts +7 -0
  32. package/dist/presenter/carousel/panel-slides.js +10 -0
  33. package/dist/presenter/carousel/talk-card.d.ts +8 -0
  34. package/dist/presenter/carousel/talk-card.js +20 -0
  35. package/dist/presenter/chrome/presenter-log.d.ts +2 -1
  36. package/dist/presenter/chrome/presenter-log.js +14 -3
  37. package/dist/presenter/chrome/presenter-report-copy.d.ts +0 -1
  38. package/dist/presenter/chrome/presenter-report-copy.js +1 -1
  39. package/dist/presenter/hud-telemetry.d.ts +3 -0
  40. package/dist/presenter/hud-telemetry.js +87 -0
  41. package/dist/presenter/presenter-account.js +1 -1
  42. package/dist/presenter/presenter-config.d.ts +5 -1
  43. package/dist/presenter/presenter-config.js +4 -1
  44. package/dist/presenter/presenter-controls.d.ts +1 -0
  45. package/dist/presenter/presenter-controls.js +12 -1
  46. package/dist/presenter/presenter-report-styles.js +4 -0
  47. package/dist/presenter/presenter-report.js +8 -7
  48. package/dist/presenter/presenter-settings-styles.js +2 -1
  49. package/dist/presenter/presenter-settings.d.ts +6 -4
  50. package/dist/presenter/presenter-settings.js +51 -5
  51. package/dist/presenter/presenter-shell-styles.js +16 -0
  52. package/dist/presenter/presenter-shell.d.ts +7 -0
  53. package/dist/presenter/presenter-shell.js +65 -5
  54. package/dist/presenter/presenter-styles.js +6 -0
  55. package/dist/presenter/presenter-workspace.d.ts +0 -7
  56. package/dist/presenter/presenter-workspace.js +7 -7
  57. package/dist/presenter/presenter.js +46 -10
  58. package/dist/presenter/tour/tour-view.js +7 -3
  59. package/dist/presenter/tour/tour.d.ts +3 -0
  60. package/dist/presenter/tour/tour.js +26 -2
  61. package/dist/recorder/recorder.js +25 -22
  62. package/dist/reticle-inject.d.ts +2 -0
  63. package/dist/reticle-inject.js +1337 -0
  64. package/dist/reticle.js +11 -1
  65. package/dist/review/annotator.d.ts +3 -0
  66. package/dist/review/annotator.js +37 -37
  67. package/dist/review/marks-for-agent.d.ts +9 -0
  68. package/dist/review/marks-for-agent.js +12 -0
  69. package/dist/util/captured-method.d.ts +0 -1
  70. package/dist/util/captured-method.js +1 -1
  71. package/package.json +8 -4
package/dist/reticle.js CHANGED
@@ -10,6 +10,7 @@ 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 { setNetworkBodyMaxChars } from './observers/net-detail/network-body.js';
13
14
  import { installOverlay } from './presenter/chrome/overlay.js';
14
15
  import { LOG_KIND, LOG_RESULT } from './presenter/chrome/log-kinds.js';
15
16
  import { actionVerb } from './presenter/chrome/presenter-verbs.js';
@@ -187,6 +188,9 @@ export class Reticle {
187
188
  });
188
189
  setCapabilitiesListener(() => this.#transport?.reannounce());
189
190
  setPresenterVisible(true === options.exposePresenter);
191
+ if (options.networkBodyMaxChars !== undefined) {
192
+ setNetworkBodyMaxChars(options.networkBodyMaxChars);
193
+ }
190
194
  const emit = this.#emit;
191
195
  this.#captureBodies = true === options.captureNetworkBodies;
192
196
  this.#sourceMapping =
@@ -248,6 +252,7 @@ export class Reticle {
248
252
  panelOptions.onControl = (intent) => this.#emit(EventType.HUMAN_CONTROL, intent.text !== undefined
249
253
  ? { kind: intent.kind, text: intent.text }
250
254
  : { kind: intent.kind });
255
+ panelOptions.onHudUse = (use) => this.#emit(EventType.HUD_USED, { ...use });
251
256
  const panel = new Presenter(panelOptions);
252
257
  this.#presenter = panel;
253
258
  panel.mount();
@@ -255,12 +260,15 @@ export class Reticle {
255
260
  panel.handlePush(buffered);
256
261
  this.#pendingPushes.clear();
257
262
  void import('./presenter/tour/tour.js').then((tour) => {
258
- tour.mountTour({
263
+ if (!this.#connected)
264
+ return;
265
+ const shown = tour.mountTour({
259
266
  document,
260
267
  storage: tour.safeLocalStorage(),
261
268
  projectId: reticleParamsFromSearch(window.location.search).projectId ?? window.location.origin,
262
269
  isDriving: () => PresenterMode.IDLE !== panel.mode,
263
270
  search: window.location.search,
271
+ navigator,
264
272
  copy: (text) => navigator.clipboard?.writeText(text).then(() => true, () => false) ?? false,
265
273
  select: (element) => {
266
274
  const selection = window.getSelection();
@@ -272,6 +280,8 @@ export class Reticle {
272
280
  selection.addRange(range);
273
281
  },
274
282
  });
283
+ if (shown !== undefined)
284
+ this.#teardowns.push(shown.destroy);
275
285
  });
276
286
  if (this.#bridgeConnected)
277
287
  panel.sessionStart();
@@ -16,7 +16,9 @@ export interface AnnotatorDeps {
16
16
  export interface AnnotatorChrome {
17
17
  markersBtn?: HTMLElement;
18
18
  clearBtn?: HTMLElement;
19
+ copyRow?: HTMLElement;
19
20
  countEl?: HTMLElement;
21
+ onCount?: (count: number) => void;
20
22
  }
21
23
  export declare class Annotator {
22
24
  #private;
@@ -29,6 +31,7 @@ export declare class Annotator {
29
31
  attachChrome(chrome: AnnotatorChrome): void;
30
32
  destroy(): void;
31
33
  toggle(on?: boolean): void;
34
+ agentPrompt(pageUrl: string): string;
32
35
  clearAll(): void;
33
36
  syncAnchors(): void;
34
37
  }
@@ -2,6 +2,7 @@ import { isSyntheticInput } from '../actions/synthetic/synthetic-input.js';
2
2
  import { EventType } from '@reticlehq/core';
3
3
  import { isReticleUi, isReticleOverlay } from '../dom/dom-ignore.js';
4
4
  import { resolveMarkAnchor } from './mark-anchor.js';
5
+ import { marksCountText, marksForAgent } from './marks-for-agent.js';
5
6
  import { nativeSetTimeout, nativeClearTimeout } from '../timers/native/native-timers.js';
6
7
  import { ANNOTATOR_CSS, ANNOTATOR_ROOT_HTML, MARK_CANCEL, MARK_PENDING_GLYPH, MARK_PLACEHOLDER, MARK_SUBMIT, } from './annotator-styles.js';
7
8
  const HIGHLIGHT_REST_MS = 130;
@@ -33,13 +34,11 @@ export class Annotator {
33
34
  #pendingTarget;
34
35
  #markersBtn;
35
36
  #clearBtn;
37
+ #copyRow;
38
+ #onChromeCount;
36
39
  #countEl;
37
40
  #accent;
38
- #onClick;
39
- #onKeydown;
40
- #onMove;
41
- #onScroll;
42
- #onResize;
41
+ #ac;
43
42
  #mo;
44
43
  constructor(deps) {
45
44
  this.#emit = deps.emit;
@@ -69,16 +68,23 @@ export class Annotator {
69
68
  this.#hi = root.querySelector(sel('hi')) ?? undefined;
70
69
  this.#hiLabel = root.querySelector(sel('hilabel')) ?? undefined;
71
70
  this.#selBox = root.querySelector(sel('sel')) ?? undefined;
72
- this.#onClick = (ev) => this.#handleClick(ev);
73
- document.addEventListener('click', this.#onClick, { capture: true });
74
- this.#onMove = (ev) => this.#scheduleMove(ev);
75
- document.addEventListener('mousemove', this.#onMove, { passive: true, capture: true });
76
- this.#onKeydown = (ev) => this.#handleKey(ev);
77
- document.addEventListener('keydown', this.#onKeydown);
78
- this.#onScroll = () => this.#reposition();
79
- this.#onResize = () => this.#reposition();
80
- window.addEventListener('scroll', this.#onScroll, true);
81
- window.addEventListener('resize', this.#onResize);
71
+ const ac = new AbortController();
72
+ this.#ac = ac;
73
+ document.addEventListener('click', (ev) => this.#handleClick(ev), {
74
+ capture: true,
75
+ signal: ac.signal,
76
+ });
77
+ document.addEventListener('mousemove', (ev) => this.#scheduleMove(ev), {
78
+ passive: true,
79
+ capture: true,
80
+ signal: ac.signal,
81
+ });
82
+ document.addEventListener('keydown', (ev) => this.#handleKey(ev), { signal: ac.signal });
83
+ window.addEventListener('scroll', () => this.#reposition(), {
84
+ capture: true,
85
+ signal: ac.signal,
86
+ });
87
+ window.addEventListener('resize', () => this.#reposition(), { signal: ac.signal });
82
88
  this.#mo = new MutationObserver(() => this.syncAnchors());
83
89
  this.#mo.observe(document.documentElement, { childList: true, subtree: true });
84
90
  if (undefined !== this.#accent)
@@ -94,6 +100,8 @@ export class Annotator {
94
100
  attachChrome(chrome) {
95
101
  this.#markersBtn = chrome.markersBtn;
96
102
  this.#clearBtn = chrome.clearBtn;
103
+ this.#copyRow = chrome.copyRow;
104
+ this.#onChromeCount = chrome.onCount;
97
105
  this.#countEl = chrome.countEl;
98
106
  this.#markersBtn?.addEventListener('click', (e) => {
99
107
  e.stopPropagation();
@@ -106,26 +114,8 @@ export class Annotator {
106
114
  this.#syncChrome();
107
115
  }
108
116
  destroy() {
109
- if (this.#onClick !== undefined) {
110
- document.removeEventListener('click', this.#onClick, { capture: true });
111
- this.#onClick = undefined;
112
- }
113
- if (this.#onKeydown !== undefined) {
114
- document.removeEventListener('keydown', this.#onKeydown);
115
- this.#onKeydown = undefined;
116
- }
117
- if (this.#onMove !== undefined) {
118
- document.removeEventListener('mousemove', this.#onMove, { capture: true });
119
- this.#onMove = undefined;
120
- }
121
- if (this.#onScroll !== undefined) {
122
- window.removeEventListener('scroll', this.#onScroll, true);
123
- this.#onScroll = undefined;
124
- }
125
- if (this.#onResize !== undefined) {
126
- window.removeEventListener('resize', this.#onResize);
127
- this.#onResize = undefined;
128
- }
117
+ this.#ac?.abort();
118
+ this.#ac = undefined;
129
119
  this.#mo?.disconnect();
130
120
  this.#mo = undefined;
131
121
  if (this.#hiTimer !== undefined)
@@ -151,6 +141,9 @@ export class Annotator {
151
141
  }
152
142
  this.#syncChrome();
153
143
  }
144
+ agentPrompt(pageUrl) {
145
+ return marksForAgent(this.#marks, pageUrl);
146
+ }
154
147
  clearAll() {
155
148
  this.#closePopover();
156
149
  for (const mark of this.#marks)
@@ -193,6 +186,13 @@ export class Annotator {
193
186
  this.#countEl.hidden = 0 === n;
194
187
  }
195
188
  this.#clearBtn?.toggleAttribute('disabled', 0 === n);
189
+ this.#onChromeCount?.(n);
190
+ if (this.#copyRow !== undefined) {
191
+ this.#copyRow.hidden = 0 === n;
192
+ const text = this.#copyRow.querySelector('[data-reticle-marks-text]');
193
+ if (text !== null)
194
+ text.textContent = marksCountText(n);
195
+ }
196
196
  this.#markersBtn?.toggleAttribute('disabled', 0 === n);
197
197
  }
198
198
  #handleKey(ev) {
@@ -344,8 +344,8 @@ export class Annotator {
344
344
  pop.innerHTML = `<div class="reticle-mark-where"></div>
345
345
  <textarea rows="2" placeholder="${MARK_PLACEHOLDER}"></textarea>
346
346
  <div class="reticle-mark-row">
347
- <button type="button" data-cancel>${MARK_CANCEL}</button>
348
- <button type="button" data-send disabled>${MARK_SUBMIT}</button>
347
+ <button type="button" data-cancel data-reticle-mark-cancel>${MARK_CANCEL}</button>
348
+ <button type="button" data-send data-reticle-mark-send disabled>${MARK_SUBMIT}</button>
349
349
  </div>`;
350
350
  const whereEl = pop.querySelector('.reticle-mark-where');
351
351
  if (whereEl !== null)
@@ -0,0 +1,9 @@
1
+ export interface MarkForAgent {
2
+ note: string;
3
+ label: string;
4
+ anchor: string;
5
+ route: string;
6
+ source?: string | undefined;
7
+ }
8
+ export declare function marksCountText(n: number): string;
9
+ export declare function marksForAgent(marks: readonly MarkForAgent[], pageUrl: string): string;
@@ -0,0 +1,12 @@
1
+ export function marksCountText(n) {
2
+ return `${String(n)} ${1 === n ? 'note' : 'notes'} on this page`;
3
+ }
4
+ export function marksForAgent(marks, pageUrl) {
5
+ const n = marks.length;
6
+ const head = `I marked ${String(n)} ${1 === n ? 'issue' : 'issues'} on ${pageUrl} with Reticle. Fix each one, then verify it in the running app:`;
7
+ const rows = marks.map((m, i) => {
8
+ const where = m.source === undefined ? m.anchor : `${m.anchor}, ${m.source}`;
9
+ return `${String(i + 1)}. ${m.note}\n ${m.label} (${where}) on ${m.route}`;
10
+ });
11
+ return [head, '', ...rows].join('\n');
12
+ }
@@ -1,2 +1 @@
1
- export declare function capturedMethod<T>(target: object, key: PropertyKey): T | undefined;
2
1
  export declare function requireCapturedMethod<T>(target: object, key: PropertyKey): T;
@@ -1,4 +1,4 @@
1
- export function capturedMethod(target, key) {
1
+ function capturedMethod(target, key) {
2
2
  return Object.getOwnPropertyDescriptor(target, key)?.value;
3
3
  }
4
4
  export function requireCapturedMethod(target, key) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reticlehq/browser",
3
- "version": "3.1.0",
3
+ "version": "3.3.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",
@@ -10,10 +10,14 @@
10
10
  "types": "./dist/index.d.ts",
11
11
  "default": "./dist/index.js"
12
12
  },
13
- "./package.json": "./package.json"
13
+ "./package.json": "./package.json",
14
+ "./inject": {
15
+ "types": "./dist/reticle-inject.d.ts",
16
+ "default": "./dist/reticle-inject.js"
17
+ }
14
18
  },
15
19
  "dependencies": {
16
- "@reticlehq/core": "3.1.0"
20
+ "@reticlehq/core": "3.3.0"
17
21
  },
18
22
  "devDependencies": {
19
23
  "@iconify-json/heroicons-outline": "^1.2.1",
@@ -60,7 +64,7 @@
60
64
  "node": ">=20.0.0"
61
65
  },
62
66
  "scripts": {
63
- "build": "node scripts/embed-heroicons.mjs && tsc -b && node ../../../scripts/alias-dist.mjs",
67
+ "build": "node scripts/embed-heroicons.mjs && tsc -b && node ../../../scripts/alias-dist.mjs && node scripts/bundle-inject.mjs",
64
68
  "typecheck": "tsc -b",
65
69
  "lint": "eslint src",
66
70
  "test:unit": "vitest run src --passWithNoTests",