@reticlehq/browser 3.5.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.
Files changed (116) hide show
  1. package/dist/actions/action-report.d.ts +54 -0
  2. package/dist/actions/action-report.js +60 -0
  3. package/dist/actions/actions-dom.d.ts +1 -0
  4. package/dist/actions/actions-dom.js +56 -43
  5. package/dist/actions/actions-press.d.ts +11 -4
  6. package/dist/actions/actions-press.js +132 -17
  7. package/dist/actions/actions.d.ts +1 -35
  8. package/dist/actions/actions.js +32 -65
  9. package/dist/actions/appeared-text.js +3 -0
  10. package/dist/actions/danger-context.d.ts +2 -0
  11. package/dist/actions/danger-context.js +29 -3
  12. package/dist/actions/value-input.d.ts +1 -0
  13. package/dist/actions/value-input.js +11 -0
  14. package/dist/commands/commands.js +4 -0
  15. package/dist/connect-options.d.ts +1 -0
  16. package/dist/dom/a11y.js +264 -14
  17. package/dist/dom/addressing/coverage-markers.d.ts +6 -0
  18. package/dist/dom/addressing/coverage-markers.js +13 -0
  19. package/dist/dom/addressing/refs.js +2 -1
  20. package/dist/dom/addressing/testid-attr.d.ts +4 -0
  21. package/dist/dom/addressing/testid-attr.js +14 -0
  22. package/dist/dom/dom-ignore.d.ts +2 -0
  23. package/dist/dom/dom-ignore.js +15 -2
  24. package/dist/dom/query.d.ts +2 -1
  25. package/dist/dom/query.js +116 -27
  26. package/dist/dom/render-page.d.ts +12 -0
  27. package/dist/dom/render-page.js +135 -0
  28. package/dist/dom/snapshot.js +56 -7
  29. package/dist/observers/departure.js +51 -9
  30. package/dist/observers/dom.js +35 -18
  31. package/dist/observers/field.js +5 -21
  32. package/dist/observers/health.js +63 -2
  33. package/dist/observers/network-stream.d.ts +1 -1
  34. package/dist/observers/network-stream.js +3 -3
  35. package/dist/observers/network.js +57 -8
  36. package/dist/observers/perf.js +53 -1
  37. package/dist/observers/route.js +6 -5
  38. package/dist/observers/storage.js +6 -1
  39. package/dist/presenter/carousel/carousel.d.ts +5 -2
  40. package/dist/presenter/carousel/carousel.js +30 -8
  41. package/dist/presenter/carousel/panel-slides.d.ts +3 -4
  42. package/dist/presenter/carousel/panel-slides.js +48 -9
  43. package/dist/presenter/carousel/talk-card.d.ts +1 -1
  44. package/dist/presenter/chrome/log-kinds.d.ts +5 -0
  45. package/dist/presenter/chrome/log-kinds.js +1 -0
  46. package/dist/presenter/chrome/log-memory.d.ts +13 -0
  47. package/dist/presenter/chrome/log-memory.js +39 -0
  48. package/dist/presenter/chrome/presenter-hud-chrome.d.ts +1 -0
  49. package/dist/presenter/chrome/presenter-hud-chrome.js +7 -1
  50. package/dist/presenter/chrome/presenter-log.d.ts +12 -4
  51. package/dist/presenter/chrome/presenter-log.js +100 -10
  52. package/dist/presenter/chrome/presenter-morph.d.ts +1 -0
  53. package/dist/presenter/chrome/presenter-morph.js +34 -0
  54. package/dist/presenter/chrome/presenter-report-copy.d.ts +28 -5
  55. package/dist/presenter/chrome/presenter-report-copy.js +40 -5
  56. package/dist/presenter/hud-telemetry.js +83 -3
  57. package/dist/presenter/icons/presenter-heroicons-data.d.ts +1 -0
  58. package/dist/presenter/icons/presenter-heroicons-data.js +4 -3
  59. package/dist/presenter/icons/presenter-icons.d.ts +2 -0
  60. package/dist/presenter/icons/presenter-icons.js +2 -0
  61. package/dist/presenter/presenter-account-styles.js +3 -3
  62. package/dist/presenter/presenter-account.d.ts +31 -8
  63. package/dist/presenter/presenter-account.js +105 -15
  64. package/dist/presenter/presenter-agent-link.d.ts +9 -0
  65. package/dist/presenter/presenter-agent-link.js +62 -0
  66. package/dist/presenter/presenter-chat-views.d.ts +43 -0
  67. package/dist/presenter/presenter-chat-views.js +622 -0
  68. package/dist/presenter/presenter-config.d.ts +5 -2
  69. package/dist/presenter/presenter-config.js +5 -3
  70. package/dist/presenter/presenter-controls.d.ts +3 -2
  71. package/dist/presenter/presenter-controls.js +163 -61
  72. package/dist/presenter/presenter-design-tokens.d.ts +1 -0
  73. package/dist/presenter/presenter-design-tokens.js +34 -0
  74. package/dist/presenter/presenter-dock-layout.js +1 -1
  75. package/dist/presenter/presenter-frame.d.ts +12 -0
  76. package/dist/presenter/presenter-frame.js +187 -0
  77. package/dist/presenter/presenter-harness-row.d.ts +67 -0
  78. package/dist/presenter/presenter-harness-row.js +209 -0
  79. package/dist/presenter/presenter-personas.d.ts +35 -0
  80. package/dist/presenter/presenter-personas.js +95 -0
  81. package/dist/presenter/presenter-plan.d.ts +10 -0
  82. package/dist/presenter/presenter-plan.js +186 -0
  83. package/dist/presenter/presenter-report-styles.js +46 -21
  84. package/dist/presenter/presenter-report.d.ts +2 -1
  85. package/dist/presenter/presenter-report.js +123 -20
  86. package/dist/presenter/presenter-settings-styles.js +18 -4
  87. package/dist/presenter/presenter-settings.d.ts +8 -2
  88. package/dist/presenter/presenter-settings.js +134 -33
  89. package/dist/presenter/presenter-shell-styles.js +48 -42
  90. package/dist/presenter/presenter-shell.d.ts +17 -4
  91. package/dist/presenter/presenter-shell.js +139 -79
  92. package/dist/presenter/presenter-styles.js +9 -3
  93. package/dist/presenter/presenter-workspace.js +10 -3
  94. package/dist/presenter/presenter.d.ts +6 -4
  95. package/dist/presenter/presenter.js +171 -22
  96. package/dist/presenter/tour/tour.d.ts +4 -1
  97. package/dist/presenter/tour/tour.js +6 -4
  98. package/dist/recorder/recorder.js +2 -2
  99. package/dist/registry/auto-testids.js +3 -4
  100. package/dist/registry/declared-platform.js +4 -0
  101. package/dist/reticle-inject.js +938 -494
  102. package/dist/reticle.d.ts +10 -1
  103. package/dist/reticle.js +109 -11
  104. package/dist/review/annotator-styles.d.ts +1 -1
  105. package/dist/review/annotator.d.ts +7 -0
  106. package/dist/review/annotator.js +20 -2
  107. package/dist/review/mark-anchor.js +2 -2
  108. package/dist/session-continuity.js +2 -1
  109. package/dist/storage-keys.d.ts +15 -0
  110. package/dist/storage-keys.js +21 -0
  111. package/dist/transport/transport.js +5 -3
  112. package/dist/transport/unreachable-message.d.ts +1 -1
  113. package/dist/transport/unreachable-message.js +3 -5
  114. package/package.json +3 -2
  115. package/dist/presenter/carousel/offer-card.d.ts +0 -21
  116. package/dist/presenter/carousel/offer-card.js +0 -55
@@ -1,23 +1,29 @@
1
- import { ActionType, HumanControlKind, ReticleCommand, PresenterMode, SessionState, isPresenterTone, isSessionState, } from '@reticlehq/core';
1
+ import { ActionType, HudCorner, HudVisibility, HumanControlKind, ReticleCommand, PresenterMode, SessionState, isPresenterTone, isSessionState, } from '@reticlehq/core';
2
2
  import { refs } from '../dom/addressing/refs.js';
3
- import { unreachableStripText } from '../transport/unreachable-message.js';
3
+ import { UNREACHABLE_HEADLINE, unreachableMessage } from '../transport/unreachable-message.js';
4
4
  import { actionVerb } from './chrome/presenter-verbs.js';
5
5
  import { nativeSetTimeout, nativeClearTimeout, nativeNow } from '../timers/native/native-timers.js';
6
- import { LOG_KIND, CHIP_LABEL, DATA_RETICLE_LOG, clampLogMax, formatElapsed, humanDuration, appendLogRow, clearLogRows, trimLogRows, } from './chrome/presenter-log.js';
6
+ import { LOG_KIND, CHIP_LABEL, LOG_ACTOR, DATA_RETICLE_LOG, clampLogMax, formatElapsed, humanDuration, appendLogRow, clearLogRows, trimLogRows, } from './chrome/presenter-log.js';
7
7
  import { PRESENTER_CSS } from './presenter-styles.js';
8
8
  import { HudShell } from './presenter-shell.js';
9
+ import { HARNESS_LOG_ATTR, harnessGateIn } from './presenter-harness-row.js';
10
+ import { platformDetails } from './presenter-account.js';
9
11
  import { parseImpactSnapshot } from './chrome/presenter-report-copy.js';
10
- import { BorderMode, DEFAULT_BORDER_MODE, DATA_BUSY, BUSY_OFF, effectivePaceMs, IDLE_AFTER_MS, HEARTBEAT_MS, IDLE_NOTICE_MS, IDLE_END_MS, IDLE_END_MIN_MS, ACT_STRIP, STATE_ATTR, UNREACHABLE_STATE, GLOW_FADE_MS, GLOW_ON, GLOW_OFF, DATA_ON, THROTTLED_ATTR, LIVENESS_ATTR, MARKERS_BTN_ATTR, CLEAR_MARKS_ATTR, COPY_MARKS_ATTR, MARKS_ROW_ATTR, MARK_COUNT_ATTR, } from './presenter-config.js';
12
+ import { scheduleSyncDockLayout } from './presenter-dock-layout.js';
13
+ import { applyHudPosition, clampHudPosition, hudLayoutBox, resetHudDockPosition, } from './presenter-hud-position.js';
14
+ import { BorderMode, DEFAULT_BORDER_MODE, DOCK_ATTR, HIDDEN_UNTIL_RESTART_ATTR, DATA_BUSY, BUSY_OFF, effectivePaceMs, IDLE_AFTER_MS, HEARTBEAT_MS, IDLE_NOTICE_MS, IDLE_END_MS, IDLE_END_MIN_MS, ACT_STRIP, STATE_ATTR, UNREACHABLE_STATE, GLOW_FADE_MS, GLOW_ON, GLOW_OFF, DATA_ON, THROTTLED_ATTR, LIVENESS_ATTR, MARKERS_BTN_ATTR, CLEAR_MARKS_ATTR, COPY_MARKS_ATTR, MARKS_ROW_ATTR, MARK_COUNT_ATTR, } from './presenter-config.js';
11
15
  import { buildRunState } from './presenter-run-state.js';
12
16
  import { moveCursor, ringAround, spawnRipple, pace } from './chrome/presenter-effects.js';
13
17
  import { GlowController } from './presenter-glow.js';
14
18
  import { PresenterIcon, PRESENTER_ICON_SIZE, hiIcon } from './icons/presenter-icons.js';
15
19
  import { renderTally } from './chrome/presenter-tally.js';
16
- import { CONTROLS_BANNER_HTML, CONTROLS_FLOWS_HTML, CONTROLS_MARKS_HTML, CONTROLS_FOOT_HTML, ENDED_FADE_MS, ControlPanel, } from './presenter-controls.js';
17
- import { statusTheme, blockerHtml, getPresenterSettings, OutputDetail, syncPageBlocker, } from './presenter-settings.js';
20
+ import { CONTROLS_BANNER_HTML, CONTROLS_MARKS_HTML, CONTROLS_FOOT_HTML, CONTROLS_STATUS_HTML, ENDED_FADE_MS, ControlPanel, } from './presenter-controls.js';
21
+ import { statusTheme, blockerHtml, getPresenterSettings, OutputDetail, syncPageBlocker, paintSdkVersion, } from './presenter-settings.js';
18
22
  import { Annotator } from '../review/annotator.js';
19
23
  import { shouldAutoOpenChat } from './presenter-shell.js';
20
24
  import { installHudTelemetry } from './hud-telemetry.js';
25
+ import { PlanBoard, parsePlanView } from './presenter-plan.js';
26
+ import { claimLog, readRememberedLog, rememberLog, } from './chrome/log-memory.js';
21
27
  const COPIED_FLASH_MS = 1600;
22
28
  export { GlowPhase } from './presenter-config.js';
23
29
  export { LOG_KIND, LOG_RESULT } from './chrome/presenter-log.js';
@@ -42,6 +48,7 @@ export class Presenter {
42
48
  #borderMode;
43
49
  #glowCtl;
44
50
  #lastActionText = '';
51
+ #lastActor;
45
52
  #heartbeatTimer;
46
53
  #idleEndMs;
47
54
  #sessionId;
@@ -51,6 +58,11 @@ export class Presenter {
51
58
  #sessionActive = false;
52
59
  #logMax;
53
60
  #log;
61
+ #harnessLog;
62
+ #harnessRunId;
63
+ #harnessSteps;
64
+ #plan = new PlanBoard();
65
+ #remembered = [];
54
66
  #logBaseMs;
55
67
  #onControl;
56
68
  #onHudUse;
@@ -92,16 +104,26 @@ export class Presenter {
92
104
  onAnnotateToggle: () => this.#syncAnnotator(),
93
105
  onCollapse: () => this.#syncAnnotator(),
94
106
  onSyncNow: () => this.#onControl?.({ kind: HumanControlKind.SYNC }),
107
+ onSignIn: () => this.#onControl?.({ kind: HumanControlKind.SIGNIN }),
108
+ onHarness: (enabled) => this.#onControl?.({ kind: HumanControlKind.HARNESS, text: enabled ? 'on' : 'off' }),
109
+ onHarnessRun: (persona) => this.#onControl?.({
110
+ kind: HumanControlKind.HARNESS_RUN,
111
+ ...(persona === undefined ? {} : { text: persona }),
112
+ }),
113
+ onHarnessStop: () => this.#onControl?.({ kind: HumanControlKind.HARNESS_STOP }),
114
+ onHarnessLink: () => this.#onControl?.({ kind: HumanControlKind.HARNESS_LINK }),
95
115
  settings: {
96
116
  onBeforeOpen: () => {
97
117
  if (this.#shell.isCollapsed())
98
118
  this.#shell.expand();
99
119
  },
100
120
  onHideUntilRestart: () => this.#applyHideUntilRestart(),
121
+ onKill: () => options.onKill?.(),
101
122
  onSettingsChange: (s) => this.#onSettingsChange(s),
102
123
  onHarness: (enabled) => this.#onControl?.({ kind: HumanControlKind.HARNESS, text: enabled ? 'on' : 'off' }),
103
124
  },
104
125
  });
126
+ this.#shell.setProjectId(options.projectId);
105
127
  }
106
128
  setControlHandler(handler) {
107
129
  this.#onControl = handler;
@@ -118,21 +140,41 @@ export class Presenter {
118
140
  refilterFlows() {
119
141
  this.#panel.refilterFlows();
120
142
  }
143
+ showSdkVersion(version) {
144
+ if (this.#root !== undefined)
145
+ paintSdkVersion(this.#root, version);
146
+ }
121
147
  handlePush(command) {
122
148
  const a = command.args;
123
149
  if (command.name === ReticleCommand.FLOWS)
124
150
  return void this.#panel.setFlows(a['flows']);
151
+ if (command.name === ReticleCommand.FLOW_PROGRESS)
152
+ return void this.#panel.setFlowProgress(a);
153
+ if (command.name === ReticleCommand.PLAN) {
154
+ const view = parsePlanView(a);
155
+ if (view !== undefined)
156
+ this.#plan.paint(view);
157
+ return;
158
+ }
125
159
  if (command.name === ReticleCommand.IMPACT) {
126
160
  const snapshot = parseImpactSnapshot(a['snapshot']);
127
161
  if (snapshot !== undefined) {
128
162
  this.#shell.report.setSnapshot(snapshot);
129
- this.#shell.paintOffer(snapshot.harnessOffer);
130
- this.#shell.paintHarness(snapshot.harnessConfig);
163
+ this.#shell.paintNotices(snapshot.notices);
164
+ this.#shell.paintHarness(snapshot.harnessConfig, harnessGateIn(snapshot.instrumentation, snapshot.harnessConfig));
165
+ this.#shell.paintHarnessDrive(snapshot.harnessDrive);
166
+ this.#paintHarnessRun(snapshot.harnessDrive);
167
+ this.#shell.paintAgent(snapshot.agent);
168
+ this.#shell.paintImpact(snapshot.project.counts.verdicts);
169
+ const owner = true === snapshot.account?.signedIn ? snapshot.account.email : undefined;
170
+ if (claimLog(owner))
171
+ this.#clearRunLog();
131
172
  this.#shell.paintAccount(snapshot.account, snapshot.dashboardUrl, {
132
173
  projectName: snapshot.projectName,
133
174
  dashboardUrl: snapshot.dashboardUrl,
134
175
  verdicts: snapshot.project.counts.verdicts,
135
176
  defects: snapshot.project.counts.failed,
177
+ ...platformDetails(snapshot.harnessConfig, snapshot.sync),
136
178
  });
137
179
  }
138
180
  return;
@@ -159,13 +201,13 @@ export class Presenter {
159
201
  document.head.appendChild(style);
160
202
  const root = document.createElement('div');
161
203
  root.setAttribute('data-reticle-overlay', '');
162
- const actStrip = `<div class="reticle-act-strip" data-liveness="idle"><span class="reticle-act-dot" aria-hidden="true"></span><span class="reticle-act">${ACT_STRIP.READY}</span><span class="reticle-chip" data-reticle-chip></span></div>`;
204
+ const actStrip = `<div class="reticle-act-strip" data-liveness="idle"><span class="reticle-act-dot" aria-hidden="true"></span><span class="reticle-act">${ACT_STRIP.READY}</span>${CONTROLS_BANNER_HTML}<span class="reticle-chip" data-reticle-chip></span><div class="reticle-act-actions">${CONTROLS_STATUS_HTML}</div></div>`;
163
205
  root.innerHTML = `
164
206
  ${blockerHtml()}
165
207
  <div data-reticle-glow></div>
166
208
  <div data-reticle-cursor></div>
167
209
  <div data-reticle-ring></div>
168
- ${HudShell.dockHtml(actStrip, CONTROLS_BANNER_HTML, DATA_RETICLE_LOG, CONTROLS_MARKS_HTML + CONTROLS_FLOWS_HTML, CONTROLS_FOOT_HTML)}`;
210
+ ${HudShell.dockHtml(actStrip, '', DATA_RETICLE_LOG, CONTROLS_MARKS_HTML, CONTROLS_FOOT_HTML)}`;
169
211
  document.body.appendChild(root);
170
212
  this.#root = root;
171
213
  this.#glow = root.querySelector('[data-reticle-glow]') ?? undefined;
@@ -179,18 +221,40 @@ export class Presenter {
179
221
  this.#chatPillTime =
180
222
  root.querySelector('[data-reticle-chat-pill-time]') ?? undefined;
181
223
  this.#log = root.querySelector(`[${DATA_RETICLE_LOG}]`) ?? undefined;
224
+ this.#harnessLog = root.querySelector(`[${HARNESS_LOG_ATTR}]`) ?? undefined;
182
225
  this.#chip = root.querySelector('[data-reticle-chip]') ?? undefined;
183
226
  this.#tally = root.querySelector('[data-reticle-tally]') ?? undefined;
184
227
  this.#shell.mount(root);
185
228
  syncPageBlocker(root, getPresenterSettings(), false);
186
229
  this.#glowCtl.setElements(this.#glow, this.#cursor);
187
230
  this.#panel.mount(root, this.#glow);
231
+ this.#plan.mount(root);
188
232
  if (this.#onHudUse !== undefined) {
189
233
  this.#hudTelemetryTeardown = installHudTelemetry(document, root, this.#onHudUse);
190
234
  }
191
235
  this.setMode(this.#mode);
236
+ this.#restoreLog();
192
237
  this.#renderTally();
193
238
  }
239
+ #restoreLog() {
240
+ if (this.#log === undefined || 0 < this.#remembered.length)
241
+ return;
242
+ for (const row of readRememberedLog().slice(-this.#logMax)) {
243
+ for (const log of this.#logsFor(row.actor, true === row.summary)) {
244
+ const handle = appendLogRow(log, row.kind, row.text, row.ts, this.#logMax, row.actor);
245
+ if (row.result !== undefined)
246
+ handle.result(row.result);
247
+ }
248
+ this.#logBaseMs = this.#now() - row.at;
249
+ this.#runLog.push({
250
+ at: row.at,
251
+ kind: row.kind,
252
+ text: row.text,
253
+ ...(row.result === undefined ? {} : { result: row.result }),
254
+ });
255
+ this.#remembered.push(row);
256
+ }
257
+ }
194
258
  bindAnnotator(annotator) {
195
259
  this.#annotator = annotator;
196
260
  const root = this.#root;
@@ -207,9 +271,10 @@ export class Presenter {
207
271
  let marks = annotator.markCount;
208
272
  chrome.onCount = (n) => {
209
273
  if (0 === marks && n > 0)
210
- this.#shell.openChat();
274
+ this.#shell.openView('annotations');
211
275
  marks = n;
212
276
  };
277
+ chrome.onMarksChange = (items) => this.#shell.paintAnnotations(items);
213
278
  if (copy instanceof HTMLElement) {
214
279
  copy.addEventListener('click', (e) => {
215
280
  e.stopPropagation();
@@ -253,6 +318,9 @@ export class Presenter {
253
318
  this.#sessionActive = false;
254
319
  this.#logBaseMs = undefined;
255
320
  this.#log = undefined;
321
+ this.#harnessLog = undefined;
322
+ this.#remembered = [];
323
+ rememberLog([]);
256
324
  this.#root?.remove();
257
325
  document.querySelectorAll('style[data-reticle-overlay]').forEach((s) => s.remove());
258
326
  this.#root = undefined;
@@ -283,8 +351,9 @@ export class Presenter {
283
351
  const dock = this.#root?.querySelector('[data-reticle-dock]');
284
352
  dock?.setAttribute(DATA_ON, GLOW_ON);
285
353
  this.#hud?.setAttribute(DATA_ON, GLOW_ON);
286
- this.#lastActionText = unreachableStripText(url, attempts);
354
+ this.#lastActionText = UNREACHABLE_HEADLINE;
287
355
  this.#paintActStrip(this.#lastActionText, true);
356
+ this.#actLine?.setAttribute('title', unreachableMessage(url, attempts));
288
357
  if (shouldAutoOpenChat(getPresenterSettings().autoOpenChat))
289
358
  this.#shell.openChat();
290
359
  }
@@ -362,9 +431,25 @@ export class Presenter {
362
431
  this.#lastActionText = text;
363
432
  this.#paintActStrip(text, false);
364
433
  }
365
- #paintActStrip(text, idle) {
366
- if (this.#actLine !== undefined)
434
+ #paintHarnessRun(drive) {
435
+ if (drive !== undefined && drive.runId !== this.#harnessRunId) {
436
+ this.#harnessRunId = drive.runId;
437
+ if (this.#harnessLog !== undefined)
438
+ clearLogRows(this.#harnessLog);
439
+ }
440
+ const was = this.#harnessSteps;
441
+ this.#harnessSteps = drive?.steps;
442
+ if (drive !== undefined)
443
+ this.#paintActStrip(this.#lastActionText, false);
444
+ else if (was !== undefined)
445
+ this.#paintActStrip(ACT_STRIP.READY, true);
446
+ }
447
+ #paintActStrip(raw, idle) {
448
+ const text = this.#harnessSteps === undefined ? raw : ACT_STRIP.harnessTesting(this.#harnessSteps);
449
+ if (this.#actLine !== undefined) {
367
450
  this.#actLine.textContent = text;
451
+ this.#actLine.removeAttribute('title');
452
+ }
368
453
  const liveness = idle ? 'idle' : 'active';
369
454
  if (this.#actStrip !== undefined)
370
455
  this.#actStrip.setAttribute('data-liveness', liveness);
@@ -400,6 +485,12 @@ export class Presenter {
400
485
  return;
401
486
  if ('' === this.#lastActionText)
402
487
  return;
488
+ if (this.#harnessSteps !== undefined)
489
+ return;
490
+ if (LOG_ACTOR.HARNESS === this.#lastActor) {
491
+ this.#paintActStrip(ACT_STRIP.READY, true);
492
+ return;
493
+ }
403
494
  const since = ACT_STRIP.SINCE_LAST;
404
495
  this.#paintActStrip(`${ACT_STRIP.PLANNING_PREFIX}${humanDuration(idleMs)}${since}`, true);
405
496
  if (this.#chatPillTime !== undefined)
@@ -414,6 +505,37 @@ export class Presenter {
414
505
  this.#heartbeatTimer = undefined;
415
506
  }
416
507
  }
508
+ placeHud(visibility, corner) {
509
+ const root = this.#root;
510
+ if (root === undefined)
511
+ return [];
512
+ const applied = [];
513
+ if (HudVisibility.REMOVED === visibility) {
514
+ this.destroy();
515
+ return [visibility];
516
+ }
517
+ if (HudVisibility.HIDDEN === visibility)
518
+ root.setAttribute(HIDDEN_UNTIL_RESTART_ATTR, '1');
519
+ if (HudVisibility.SHOWN === visibility)
520
+ root.removeAttribute(HIDDEN_UNTIL_RESTART_ATTR);
521
+ if (visibility !== undefined)
522
+ applied.push(visibility);
523
+ const dock = root.querySelector(`[${DOCK_ATTR}]`);
524
+ if (corner !== undefined && dock instanceof HTMLElement) {
525
+ if (HudCorner.BOTTOM_RIGHT === corner)
526
+ resetHudDockPosition(dock);
527
+ else {
528
+ const box = hudLayoutBox(dock);
529
+ const left = corner.endsWith('left') ? 0 : window.innerWidth;
530
+ const top = corner.startsWith('top') ? 0 : window.innerHeight;
531
+ const at = clampHudPosition(left, top, box.width, box.height, window.innerWidth, window.innerHeight);
532
+ applyHudPosition(dock, at.left, at.top);
533
+ }
534
+ applied.push(corner);
535
+ scheduleSyncDockLayout(dock, root);
536
+ }
537
+ return applied;
538
+ }
417
539
  setIdleEndMs(ms) {
418
540
  if (!Number.isFinite(ms))
419
541
  return;
@@ -446,9 +568,13 @@ export class Presenter {
446
568
  }
447
569
  #clearRunLog() {
448
570
  this.#runLog = [];
571
+ this.#remembered = [];
572
+ rememberLog([]);
449
573
  this.#tallied = { passes: 0, fails: 0 };
450
574
  if (this.#log !== undefined)
451
575
  clearLogRows(this.#log);
576
+ if (this.#harnessLog !== undefined)
577
+ clearLogRows(this.#harnessLog);
452
578
  this.#renderTally();
453
579
  }
454
580
  #applyHideUntilRestart() {
@@ -463,14 +589,19 @@ export class Presenter {
463
589
  else {
464
590
  this.#renderTally();
465
591
  }
466
- if (this.#root !== undefined) {
467
- const live = SessionState.ACTIVE === this.#panel.state && !this.#shell.isCollapsed();
468
- syncPageBlocker(this.#root, settings, live);
469
- }
592
+ this.#syncAnnotator();
593
+ }
594
+ #logsFor(actor, summary) {
595
+ const agent = this.#log;
596
+ const harness = this.#harnessLog;
597
+ if (LOG_ACTOR.HARNESS !== actor || harness === undefined)
598
+ return agent === undefined ? [] : [agent];
599
+ return summary && agent !== undefined ? [agent, harness] : [harness];
470
600
  }
471
- log(kind, text, result) {
601
+ log(kind, text, result, actor, summary = false) {
472
602
  const ms = this.#now();
473
603
  this.#glowCtl.markActivity(ms);
604
+ this.#lastActor = actor;
474
605
  if (this.#log === undefined)
475
606
  return undefined;
476
607
  const trimmed = text.trim();
@@ -484,9 +615,23 @@ export class Presenter {
484
615
  while (this.#runLog.length > this.#logMax)
485
616
  this.#runLog.shift();
486
617
  const ts = formatElapsed(ms - this.#logBaseMs);
487
- const handle = appendLogRow(this.#log, kind, trimmed, ts, this.#logMax);
618
+ const handles = this.#logsFor(actor, summary).map((log) => appendLogRow(log, kind, trimmed, ts, this.#logMax, actor));
619
+ const handle = { result: (r) => handles.forEach((h) => h.result(r)) };
488
620
  if (result !== undefined)
489
621
  handle.result(result);
622
+ const kept = {
623
+ kind,
624
+ text: trimmed,
625
+ ts,
626
+ at: ms - this.#logBaseMs,
627
+ ...(actor === undefined ? {} : { actor }),
628
+ ...(summary ? { summary: true } : {}),
629
+ ...(result === undefined ? {} : { result }),
630
+ };
631
+ this.#remembered.push(kept);
632
+ while (this.#remembered.length > this.#logMax)
633
+ this.#remembered.shift();
634
+ rememberLog(this.#remembered);
490
635
  if (this.#shell.isCollapsed())
491
636
  this.#shell.pulseFab(true);
492
637
  this.#renderTally();
@@ -494,6 +639,8 @@ export class Presenter {
494
639
  result: (r) => {
495
640
  handle.result(r);
496
641
  entry.result = r;
642
+ kept.result = r;
643
+ rememberLog(this.#remembered);
497
644
  this.#renderTally();
498
645
  },
499
646
  };
@@ -505,14 +652,16 @@ export class Presenter {
505
652
  }
506
653
  this.#tallied = renderTally(this.#tally, this.#runLog, this.#tallied);
507
654
  }
508
- narrate(text, level = 'info') {
655
+ narrate(text, level = 'info', actor = LOG_ACTOR.AGENT, summary = false) {
509
656
  const line = 'info' === level ? text : `[${level}] ${text}`;
510
- return this.log(LOG_KIND.NARRATION, line);
657
+ return this.log(LOG_KIND.NARRATION, line, undefined, actor, summary);
511
658
  }
512
659
  #pruneLog() {
513
660
  if (this.#log === undefined)
514
661
  return;
515
662
  trimLogRows(this.#log, this.#logMax);
663
+ if (this.#harnessLog !== undefined)
664
+ trimLogRows(this.#harnessLog, this.#logMax);
516
665
  }
517
666
  setThrottled(throttled) {
518
667
  this.#root?.setAttribute(THROTTLED_ATTR, throttled ? '1' : '0');
@@ -1,5 +1,8 @@
1
1
  import { isLastSlide } from './tour-view.js';
2
- export declare const TOUR_SEEN_KEY_PREFIX = "reticle.tour.seen.";
2
+ import { TourAnchor } from '@reticlehq/core/tour';
3
+ import { TOUR_SEEN_KEY_PREFIX } from '../../storage-keys.js';
4
+ export { TOUR_SEEN_KEY_PREFIX };
5
+ export declare const HUD_CONTROL_SELECTORS: Readonly<Partial<Record<TourAnchor, string>>>;
3
6
  export declare const tourSeenKey: (projectId: string) => string;
4
7
  export interface TourStorage {
5
8
  getItem: (key: string) => string | null;
@@ -2,7 +2,8 @@ import { COPIED_LABEL, COPY_FLASH_MS, COPY_LABEL, COPY_MANUAL_LABEL, TOUR_ATTR,
2
2
  import { TourAnchor } from '@reticlehq/core/tour';
3
3
  import { RETICLE_URL_PARAM } from '@reticlehq/core';
4
4
  import { appModalOpen } from '../../dom/dom-ignore.js';
5
- export const TOUR_SEEN_KEY_PREFIX = 'reticle.tour.seen.';
5
+ import { TOUR_SEEN_KEY_PREFIX } from '../../storage-keys.js';
6
+ export { TOUR_SEEN_KEY_PREFIX };
6
7
  const APP_SELECTORS = ['main', '#root', '#app'];
7
8
  const HUD_PARTS = ['[data-reticle-hud]', '[data-reticle-chat-panel]'];
8
9
  function unionOfVisible(doc, selectors) {
@@ -23,10 +24,11 @@ function unionOfVisible(doc, selectors) {
23
24
  return undefined;
24
25
  return { left, top, width: right - left, height: bottom - top };
25
26
  }
26
- const HUD_CONTROL_SELECTORS = {
27
+ export const HUD_CONTROL_SELECTORS = {
27
28
  [TourAnchor.HUD_CHAT]: '[data-reticle-chat-toggle]',
28
- [TourAnchor.HUD_ANNOTATE]: '[data-reticle-annotate-btn]',
29
- [TourAnchor.HUD_IMPACT]: '[data-reticle-report-btn]',
29
+ [TourAnchor.HUD_FLOWS]: '[data-reticle-chat-view-btn="flows"]',
30
+ [TourAnchor.HUD_ANNOTATE]: '[data-reticle-chat-view-btn="annotations"]',
31
+ [TourAnchor.HUD_IMPACT]: '[data-reticle-chat-impact]',
30
32
  [TourAnchor.HUD_SETTINGS]: '[data-reticle-settings-btn]',
31
33
  };
32
34
  function anchorBox(doc, anchor) {
@@ -1,3 +1,4 @@
1
+ import { readTestId } from '../dom/addressing/testid-attr.js';
1
2
  import { ActionType, AnchorKind, AnnotationKind, DEGRADED_ANCHOR_ROLE, EventType, FLOW_FILE_VERSION, FlowStepTool, RecorderPhase, PredicateKind, } from '@reticlehq/core';
2
3
  import { REDACTED_VALUE } from '@reticlehq/core';
3
4
  import { sourceFromDom } from '../dom/addressing/source.js';
@@ -9,7 +10,6 @@ export const RECORDER_EMPTY_MSG = 'recorded 0 steps';
9
10
  const STATUS_RECORDING = 'recording…';
10
11
  const STATUS_IDLE = 'ready';
11
12
  const STATUS_ANNOTATE = 'pick an annotation';
12
- const TESTID_ATTR = 'data-testid';
13
13
  const TOOL = FlowStepTool.ACT;
14
14
  const BUTTON_LABEL = {
15
15
  record: 'Record',
@@ -31,7 +31,7 @@ const NEEDS_SIGNAL = new Set([
31
31
  ]);
32
32
  const DEFAULT_NAME = 'recorded-flow';
33
33
  export function anchorFor(el) {
34
- const testid = el.getAttribute(TESTID_ATTR);
34
+ const testid = readTestId(el);
35
35
  if (testid !== null && testid.length > 0) {
36
36
  return { anchor: { kind: AnchorKind.TESTID, value: testid }, degraded: false };
37
37
  }
@@ -1,5 +1,4 @@
1
- const TESTID_ATTR = 'data-testid';
2
- const TESTID_SELECTOR = `[${TESTID_ATTR}]`;
1
+ import { getTestIdAttr, testIdSelector } from '../dom/addressing/testid-attr.js';
3
2
  export const MAX_AUTO_TESTIDS = 200;
4
3
  const RETICLE_OWN_PREFIX = 'reticle-';
5
4
  export function domTestids(doc) {
@@ -7,13 +6,13 @@ export function domTestids(doc) {
7
6
  const seen = new Set();
8
7
  let nodes;
9
8
  try {
10
- nodes = doc.querySelectorAll(TESTID_SELECTOR);
9
+ nodes = doc.querySelectorAll(testIdSelector());
11
10
  }
12
11
  catch {
13
12
  return out;
14
13
  }
15
14
  for (let i = 0; i < nodes.length && out.length < MAX_AUTO_TESTIDS; i += 1) {
16
- const value = nodes[i]?.getAttribute(TESTID_ATTR);
15
+ const value = nodes[i]?.getAttribute(getTestIdAttr());
17
16
  if (null === value || undefined === value)
18
17
  continue;
19
18
  const trimmed = value.trim();
@@ -25,9 +25,13 @@ export const SERVED_COMMANDS = [
25
25
  ReticleCommand.PRESENTER,
26
26
  ReticleCommand.IMPACT,
27
27
  ReticleCommand.CAPTURE,
28
+ ReticleCommand.RENDER,
28
29
  ReticleCommand.NAVIGATE,
29
30
  ReticleCommand.REFRESH,
30
31
  ReticleCommand.FLOWS,
32
+ ReticleCommand.FLOW_PROGRESS,
33
+ ReticleCommand.PLAN,
34
+ ReticleCommand.MARKERS,
31
35
  ];
32
36
  export function declaredCommands() {
33
37
  return [...SERVED_COMMANDS];