@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
@@ -5,7 +5,7 @@ import { resetHudDockPosition } from './presenter-drag.js';
5
5
  import { findDock, scheduleSyncDockLayout } from './presenter-dock-layout.js';
6
6
  import { SETTINGS_CSS } from './presenter-settings-styles.js';
7
7
  import { DISCOVERY_CALL_URL, FOUNDER_EMAIL, FOUNDER_MAILTO } from '@reticlehq/core';
8
- import { accountControlHtml } from './presenter-account.js';
8
+ import { ACCOUNT_TEXT, accountControlHtml, } from './presenter-account.js';
9
9
  export { SETTINGS_CSS };
10
10
  export const OutputDetail = {
11
11
  MINIMAL: 'minimal',
@@ -150,10 +150,19 @@ function settingsCheckRow(key, label, checked) {
150
150
  }
151
151
  export const SETTINGS_ACCOUNT_ATTR = 'data-reticle-settings-account';
152
152
  const SETTINGS_ACCOUNT_ROW_ATTR = 'data-reticle-settings-account-row';
153
+ const HELP_TEXT_ATTR = 'data-reticle-settings-helptext';
153
154
  const SETTINGS_HARNESS_ROW_ATTR = 'data-reticle-settings-harness-row';
154
155
  const HARNESS_HELP = 'Let Reticle drive this app by itself to find defects. Set here or in your dashboard — both write to the same place.';
155
- const HARNESS_LOCKED_HELP = 'Autonomous driving runs on Reticle’s model spend, so it needs a plan or the free period. Claim it from the chat panel.';
156
- const ACCOUNT_HELP = 'Whether this machine is signed in to a Reticle workspace. Signing in happens in your terminal.';
156
+ const HARNESS_LOCKED_HELP = 'The Harness is not available to this workspace right now. Every workspace gets free Harness credits each month: see Plan in your Reticle dashboard.';
157
+ export function creditsLeft(credits) {
158
+ if (credits === undefined)
159
+ return '';
160
+ const left = Math.max(0, credits.limit - credits.used);
161
+ return 0 === left
162
+ ? `All ${String(credits.limit)} Harness credits used this month`
163
+ : `${String(left)} of ${String(credits.limit)} Harness credits left this month`;
164
+ }
165
+ const ACCOUNT_HELP = `Whether this machine is signed in to a Reticle workspace. Press ${ACCOUNT_TEXT.SIGNED_OUT} to sign in through your browser.`;
157
166
  export function paintHarnessRow(root, config) {
158
167
  const row = root.querySelector(`[${SETTINGS_HARNESS_ROW_ATTR}]`);
159
168
  if (!(row instanceof HTMLElement))
@@ -170,8 +179,11 @@ export function paintHarnessRow(root, config) {
170
179
  toggle.setAttribute('aria-checked', config.harnessEnabled && usable ? 'true' : 'false');
171
180
  toggle.setAttribute('aria-disabled', usable ? 'false' : 'true');
172
181
  const help = row.querySelector('[data-reticle-help]');
182
+ const credits = creditsLeft(config.credits);
173
183
  if (help instanceof HTMLElement)
174
- help.title = usable ? HARNESS_HELP : HARNESS_LOCKED_HELP;
184
+ help.title = usable
185
+ ? [HARNESS_HELP, credits].filter((t) => 0 < t.length).join(' ')
186
+ : HARNESS_LOCKED_HELP;
175
187
  }
176
188
  export function paintSettingsAccount(root, account, dashboardUrl, details = {}) {
177
189
  const row = root.querySelector(`[${SETTINGS_ACCOUNT_ROW_ATTR}]`);
@@ -188,6 +200,12 @@ const FEEDBACK_TEXT = {
188
200
  CALL: 'Book a call',
189
201
  CALL_TITLE: 'Pick a time to talk it through',
190
202
  };
203
+ const KILL_ATTR = 'data-reticle-settings-kill';
204
+ const KILL_TEXT = {
205
+ LABEL: 'Kill Reticle',
206
+ ARMED: 'Click again to kill Reticle',
207
+ WARNING: 'Disconnects Reticle from this page: no HUD, no agent, nothing recorded. To bring it back, restart your dev server or reload the page.',
208
+ };
191
209
  export function settingsPanelHtml() {
192
210
  const close = hiIconHtml(PresenterIcon.REMOVE, PRESENTER_ICON_SIZE.MIN);
193
211
  const caret = hiIconHtml(PresenterIcon.CARET_RIGHT, PRESENTER_ICON_SIZE.HELP);
@@ -218,9 +236,9 @@ export function settingsPanelHtml() {
218
236
  <div class="reticle-settings-section">Inspector</div>
219
237
  ${settingsToggleRow('reactComponents', 'React Components', reactHelp, 'data-reticle-settings-react-row')}
220
238
  <div class="reticle-settings-section">Interaction</div>
221
- ${settingsCheckRow('blockPageInteractions', 'Block page interactions', true)}
222
- ${settingsCheckRow('clearOnCopy', 'Clear on copy/send', false)}
223
- ${settingsToggleRow('hideUntilRestart', 'Hide Until Restart', hideHelp)}
239
+ ${settingsCheckRow('blockPageInteractions', 'Block page while adding notes', true)}
240
+ ${settingsCheckRow('clearOnCopy', 'Clear notes after copying', false)}
241
+ ${settingsToggleRow('hideUntilRestart', 'Hide until reload', hideHelp)}
224
242
  ${settingsToggleRow('harnessEnabled', 'Autonomous driving', harnessHelp, `${SETTINGS_HARNESS_ROW_ATTR} hidden`)}
225
243
  ${settingsToggleRow('reduceMotion', 'Reduce motion', motionHelp)}
226
244
  <div class="reticle-settings-section">Account</div>
@@ -231,12 +249,17 @@ export function settingsPanelHtml() {
231
249
  <div class="reticle-settings-section">Status theme</div>
232
250
  ${settingsToggleRow('ambientGlow', 'Page glow', glowHelp)}
233
251
  <div class="reticle-settings-themes" data-reticle-settings-themes></div>
234
- </div>
235
- <div class="reticle-settings-foot">
252
+ <div class="reticle-settings-section">Help</div>
253
+ <div class="reticle-settings-foot">
236
254
  <button type="button" class="reticle-settings-reset" data-reticle-settings-reset>Reset HUD position</button>
237
- <button type="button" class="reticle-settings-link" data-reticle-settings-mcp>Manage MCP &amp; Webhooks<span class="reticle-settings-link-caret" aria-hidden="true">${caret}</span></button>
255
+ <button type="button" class="reticle-settings-link" data-reticle-settings-mcp>MCP setup guide<span class="reticle-settings-link-caret" aria-hidden="true">${caret}</span></button>
238
256
  <a class="reticle-settings-link" data-reticle-feedback-email href="${FOUNDER_MAILTO}" target="_blank" rel="noopener noreferrer" title="${FEEDBACK_TEXT.EMAIL_TITLE}">${FEEDBACK_TEXT.EMAIL}<span class="reticle-settings-link-caret" aria-hidden="true">${caret}</span></a>
239
257
  <a class="reticle-settings-link" data-reticle-feedback-call href="${DISCOVERY_CALL_URL}" target="_blank" rel="noopener noreferrer" title="${FEEDBACK_TEXT.CALL_TITLE}">${FEEDBACK_TEXT.CALL}<span class="reticle-settings-link-caret" aria-hidden="true">${caret}</span></a>
258
+ <div class="reticle-settings-kill-row">
259
+ <button type="button" class="reticle-settings-kill" ${KILL_ATTR}><span data-reticle-kill-label>${KILL_TEXT.LABEL}</span></button>
260
+ <p class="reticle-settings-kill-sub">${KILL_TEXT.WARNING}</p>
261
+ </div>
262
+ </div>
240
263
  </div>
241
264
  </div>
242
265
  </div>`;
@@ -297,7 +320,7 @@ export class PresenterSettingsPanel {
297
320
  setHiIcon(this.#btn, PresenterIcon.GEAR, PRESENTER_ICON_SIZE.TOOLBAR);
298
321
  this.#btn.addEventListener('click', (e) => {
299
322
  e.stopPropagation();
300
- this.toggle();
323
+ this.open();
301
324
  });
302
325
  }
303
326
  root
@@ -310,8 +333,24 @@ export class PresenterSettingsPanel {
310
333
  this.#update({ outputDetail: next.value });
311
334
  });
312
335
  for (const help of root.querySelectorAll('.reticle-settings-help')) {
336
+ help.setAttribute('aria-expanded', 'false');
313
337
  help.addEventListener('click', (e) => {
314
338
  e.stopPropagation();
339
+ const row = help.closest('.reticle-settings-row');
340
+ if (null === row)
341
+ return;
342
+ const shown = row.querySelector(`[${HELP_TEXT_ATTR}]`);
343
+ if (null !== shown) {
344
+ shown.remove();
345
+ help.setAttribute('aria-expanded', 'false');
346
+ return;
347
+ }
348
+ const text = document.createElement('p');
349
+ text.className = 'reticle-settings-helptext';
350
+ text.setAttribute(HELP_TEXT_ATTR, '');
351
+ text.textContent = help.getAttribute('title') ?? '';
352
+ row.appendChild(text);
353
+ help.setAttribute('aria-expanded', 'true');
315
354
  });
316
355
  }
317
356
  for (const toggle of root.querySelectorAll(`[${SETTING_KEY_ATTR}]`)) {
@@ -399,6 +438,20 @@ export class PresenterSettingsPanel {
399
438
  if (dock instanceof HTMLElement)
400
439
  resetHudDockPosition(dock);
401
440
  });
441
+ root.querySelector(`[${KILL_ATTR}]`)?.addEventListener('click', (e) => {
442
+ e.stopPropagation();
443
+ const kill = e.currentTarget;
444
+ if (!(kill instanceof HTMLElement))
445
+ return;
446
+ if ('1' === kill.getAttribute('data-armed')) {
447
+ this.#host.onKill?.();
448
+ return;
449
+ }
450
+ kill.setAttribute('data-armed', '1');
451
+ const label = kill.querySelector('[data-reticle-kill-label]');
452
+ if (label !== null)
453
+ label.textContent = KILL_TEXT.ARMED;
454
+ });
402
455
  closeBtn?.addEventListener('click', (e) => {
403
456
  e.stopPropagation();
404
457
  this.close();
@@ -434,6 +487,13 @@ export class PresenterSettingsPanel {
434
487
  close() {
435
488
  if (this.#root === undefined)
436
489
  return;
490
+ const kill = this.#panel?.querySelector(`[${KILL_ATTR}]`);
491
+ if (kill instanceof HTMLElement) {
492
+ kill.removeAttribute('data-armed');
493
+ const label = kill.querySelector('[data-reticle-kill-label]');
494
+ if (label !== null)
495
+ label.textContent = KILL_TEXT.LABEL;
496
+ }
437
497
  this.#root.setAttribute(SETTINGS_ATTR, '0');
438
498
  this.#panel?.setAttribute('aria-hidden', 'true');
439
499
  this.#btn?.setAttribute('data-active', '0');
@@ -1,7 +1,7 @@
1
1
  import { LOG_TIME_ATTR } from './chrome/presenter-log.js';
2
2
  import { Z_HUD_TOOLTIP } from './presenter-config.js';
3
3
  import { CHAT_ATTR, CHAT_PILL_ATTR, CHAT_PLACEMENT_ATTR, DOCK_ALIGN_ATTR, DOCK_ATTR, LOG_TIMESTAMPS_ATTR, MIN_ATTR, REDUCE_MOTION_ATTR, LIVENESS_ATTR, } from './presenter-config.js';
4
- import { HUD_DROP_SHADOW, HUD_SURFACE_FILL } from './chrome/presenter-hud-chrome.js';
4
+ import { HUD_DROP_SHADOW, HUD_GLASS_PAINT, HUD_SURFACE_FILL, } from './chrome/presenter-hud-chrome.js';
5
5
  const OVERLAY = 'data-reticle-overlay';
6
6
  const HUD = 'data-reticle-hud';
7
7
  const CHAT_PANEL = 'data-reticle-chat-panel';
@@ -23,57 +23,48 @@ export const SHELL_CSS = `
23
23
  [${OVERLAY}][${STATE}="paused"]{--reticle-state:var(--reticle-c-idle);}
24
24
  [${OVERLAY}][${STATE}="ended"]{--reticle-state:var(--reticle-c-ended);}
25
25
  [${DOCK_ATTR}]{
26
- --reticle-surface:rgba(255,255,255,.06);
26
+ --reticle-surface:color-mix(in srgb,var(--reticle-hud-surface) 78%,transparent);
27
27
  /* No literal here: --reticle-accent is published on the overlay from the chosen status theme.
28
28
  Redefining it on the dock made every accented control - toolbar toggles, focus rings, the send
29
29
  button - stay blue no matter which theme was picked, because the dock's value won for its own
30
30
  subtree. The fallback only matters if the presenter is mounted without settings. */
31
31
  --reticle-accent:var(--reticle-c-active,#3b82f6);
32
32
  --reticle-accent-soft:color-mix(in srgb,var(--reticle-accent) 18%,transparent);
33
- --reticle-bg:#050506;--reticle-bg2:#0c0c10;
34
- --reticle-fg:#fff;--reticle-muted:rgba(255,255,255,.85);--reticle-faint:rgba(255,255,255,.5);
35
- --reticle-line:rgba(255,255,255,.12);--reticle-line2:rgba(255,255,255,.08);
33
+ --reticle-bg:var(--reticle-hud-ground);--reticle-bg2:var(--reticle-hud-surface);
34
+ --reticle-fg:var(--reticle-hud-text);--reticle-muted:var(--reticle-hud-text-muted);--reticle-faint:var(--reticle-hud-text-faint);
35
+ --reticle-line:var(--reticle-hud-border-strong);--reticle-line2:color-mix(in srgb,var(--reticle-hud-border) 70%,transparent);
36
36
  --reticle-read:#d4d4d4;--reticle-ok:#fafafa;--reticle-bad:#f5f5f5;
37
- --reticle-font:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
38
- --reticle-shell-ease:cubic-bezier(.22,1,.36,1);
37
+ --reticle-font:ui-sans-serif,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
38
+ --reticle-shell-ease:var(--reticle-hud-ease);
39
39
  --reticle-shell-fast:.22s var(--reticle-shell-ease);
40
40
  --reticle-mark-accent:var(--reticle-accent);
41
41
  position:fixed;right:20px;bottom:20px;left:auto;
42
42
  z-index:2147483647;pointer-events:none;display:flex;flex-direction:column;align-items:flex-end;gap:8px;
43
43
  overflow:visible;max-width:calc(100vw - 24px);font-family:var(--reticle-font);-webkit-font-smoothing:antialiased;
44
- /**
45
- * ONE width for the dock and the chat above it. They were 420px and 320px, so the toolbar
46
- * overhung the panel it belongs to and the pair read as two unrelated widgets.
47
- */
48
- /* ONE width for the chat, the capsule and the toolbar. It used to be 440 with a "compact" 340
49
- toggle; 340 is the size that actually reads well beside an app, so it is simply the size. */
50
- --reticle-dock-w:340px;
44
+ /* ONE width for the chat, the capsule and the toolbar: the dock-width token. Two widths made the
45
+ toolbar overhang the panel it belongs to, and the pair read as two unrelated widgets. */
46
+ --reticle-dock-w:var(--reticle-hud-dock-width,320px);
51
47
  /* The log is the reason the panel exists, so it gets the height rather than the chrome. */
52
- --reticle-chat-h:660px;
48
+ --reticle-chat-h:var(--reticle-hud-panel-height,440px);
53
49
  opacity:0;transform:translate3d(0,8px,0);transition:opacity var(--reticle-shell-fast),transform var(--reticle-shell-fast);}
54
50
  [${DOCK_ATTR}][data-dragged="1"]{left:var(--reticle-hud-x);top:var(--reticle-hud-y);bottom:auto;right:auto;transform:none;}
55
51
  [${DOCK_ATTR}][data-dragged="1"][data-on="1"]{transform:none;}
56
52
  [${DOCK_ATTR}][data-on="1"]{opacity:1;transform:translate3d(0,0,0);pointer-events:none;}
57
53
  [${DOCK_ATTR}][data-on="0"]{opacity:0;pointer-events:none;}
58
54
  /**
59
- * The panel is GLASS, tinted by the state colour, with the glow sitting behind it - restored from
60
- * the version before this one, where a flat near-black card had replaced it. Kept at ~92% opacity
61
- * rather than a real backdrop-filter: blur(24px) here was measured as the single most expensive
62
- * thing in the whole SDK (+4pp of main thread on the hostile fixture), and the tint buys the look
63
- * without the bill.
55
+ * The panel is painted by HUD_GLASS_PAINT: an opaque navy fill, lit by the state colour, with that
56
+ * colour glowing behind it. No backdrop-filter: blur(24px) here was measured as the single most
57
+ * expensive thing in the whole SDK.
64
58
  */
65
59
  [${CHAT_PANEL}]{
66
- background:
67
- radial-gradient(130% 90% at 50% 0%,color-mix(in srgb,var(--reticle-c-active) 18%,transparent),transparent 62%),
68
- linear-gradient(180deg,rgba(13,15,22,.96),rgba(19,22,32,.94));
69
- border:1px solid color-mix(in srgb,var(--reticle-c-active) 26%,rgba(255,255,255,.1));
60
+ ${HUD_GLASS_PAINT}
70
61
  display:none;position:absolute;right:0;left:auto;bottom:calc(100% + 8px);top:auto;z-index:5;
71
62
  box-sizing:border-box;width:var(--reticle-dock-w);max-width:min(var(--reticle-dock-w),calc(100vw - 16px));
63
+ height:min(var(--reticle-chat-max-h,var(--reticle-chat-h)),calc(100vh - 120px));
72
64
  max-height:min(var(--reticle-chat-max-h,var(--reticle-chat-h)),calc(100vh - 120px));
73
65
  flex-direction:column;overflow:hidden;text-align:left;
74
66
  color:var(--reticle-fg);font-size:13px;line-height:1.5;
75
- border-radius:16px;
76
- box-shadow:${HUD_DROP_SHADOW},0 0 54px -18px var(--reticle-c-active);
67
+ border-radius:var(--reticle-hud-radius-lg);
77
68
  contain:layout style paint;
78
69
  transform:translateZ(0);
79
70
  pointer-events:none;}
@@ -83,6 +74,9 @@ export const SHELL_CSS = `
83
74
  right:auto;left:0;}
84
75
  [${OVERLAY}][${CHAT_ATTR}="1"] [${CHAT_PANEL}]{
85
76
  display:flex;pointer-events:auto;}
77
+ [${CHAT_PANEL}][hidden]{display:none!important;}
78
+ [${DOCK_ATTR}][${CHAT_PLACEMENT_ATTR}="below"] [data-reticle-page-panel]{bottom:auto;top:calc(100% + 8px);}
79
+ [${DOCK_ATTR}][${DOCK_ALIGN_ATTR}="start"] [data-reticle-page-panel]{right:auto;left:0;}
86
80
  /**
87
81
  * Idle, and not blocking the page: the card is not in use.
88
82
  *
@@ -93,6 +87,10 @@ export const SHELL_CSS = `
93
87
  [${OVERLAY}][data-reticle-mode="idle"][data-reticle-block="0"] [${CHAT_PANEL}],
94
88
  [${OVERLAY}][data-reticle-mode="idle"][data-reticle-block="0"] [${CHAT_PANEL}] [data-reticle-log]{
95
89
  pointer-events:none;}
90
+ /* ...except a log with more than it shows: a feed nobody can scroll back through is not a log. */
91
+ [${OVERLAY}][data-reticle-mode="idle"][data-reticle-block="0"] [${CHAT_PANEL}] [data-reticle-log][data-reticle-log-scrollable],
92
+ [${OVERLAY}][data-reticle-mode="idle"][data-reticle-block="0"] [${CHAT_PANEL}] .reticle-flow-strip{
93
+ pointer-events:auto;}
96
94
  [${OVERLAY}][data-reticle-mode="idle"][data-reticle-block="0"] [${CHAT_PANEL}] :is(button,a,input,select,textarea){
97
95
  pointer-events:auto;}
98
96
  /**
@@ -136,6 +134,8 @@ export const SHELL_CSS = `
136
134
  [${CHAT_PANEL}] .reticle-head-account:empty{display:none;}
137
135
  [${CHAT_PANEL}] .reticle-chat-brand .reticle-mark{height:14px;width:auto;}
138
136
  [${CHAT_PANEL}] .reticle-chat-brandname{font-size:12.5px;font-weight:600;letter-spacing:.01em;}
137
+ [${CHAT_PANEL}] .reticle-view-heading{display:flex;justify-content:space-between;align-items:baseline;gap:8px;min-height:28px;box-sizing:border-box;padding:6px 12px 4px;border-bottom:1px solid var(--reticle-hud-border);}
138
+ [${CHAT_PANEL}] .reticle-view-heading strong{font-size:11px;font-weight:600;}
139
139
  /* No state word here: the activity strip immediately below already says "idle · 21s", and the
140
140
  dot in front of it carries the same colour. One fact, one place. */
141
141
  [${CHAT_PILL_ATTR}] .reticle-chat-pill-text{
@@ -255,13 +255,13 @@ export const SHELL_CSS = `
255
255
  [${HUD}] .reticle-toolbar-actions{
256
256
  display:inline-flex;align-items:center;gap:2px;flex:none;
257
257
  padding:2px;border-radius:999px;background:rgba(0,0,0,.2);}
258
- [${HUD}] .reticle-toolbar-chrome{display:inline-flex;align-items:center;gap:2px;flex:none;}
258
+ [${HUD}] .reticle-toolbar-chrome{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));align-items:center;gap:var(--reticle-hud-space-1);flex:1;min-width:0;}
259
259
  [${HUD}] .reticle-tb-sep{width:1px;height:14px;background:rgba(255,255,255,.12);margin:0 4px;flex:none;align-self:center;}
260
260
 
261
261
  [${HUD}] .reticle-tb-wrap{position:relative;display:flex;align-items:center;justify-content:center;overflow:visible;}
262
262
  [${HUD}] .reticle-tb-btn{
263
263
  flex:none;display:inline-flex;align-items:center;justify-content:center;
264
- width:32px;height:32px;padding:0;border:none;border-radius:50%;cursor:pointer;
264
+ width:var(--reticle-hud-control-size);height:var(--reticle-hud-control-size);padding:0;border:none;border-radius:var(--reticle-hud-radius-pill);cursor:pointer;
265
265
  background:transparent;color:rgba(255,255,255,.85);line-height:0;
266
266
  transition:background-color .15s ease,color .15s ease,transform .1s ease,opacity .2s ease;}
267
267
  [${HUD}] .reticle-tb-btn:hover{background:rgba(255,255,255,.12);color:#fff;}
@@ -294,18 +294,6 @@ export const SHELL_CSS = `
294
294
  color:#fff;background:rgba(255,255,255,.14);}
295
295
  [${HUD}] .reticle-tb-btn[data-danger]:hover:not(:disabled){color:#ff383c;
296
296
  background:color-mix(in srgb, #ff383c 25%, transparent);}
297
- /**
298
- * The toolbar is a FIXED number of slots.
299
- *
300
- * Copy and Export appear when a session ends, and the bar is already full - eleven icons in a pill
301
- * sized for nine renders the last one outside the rounded box. They take the two slots Pause and End
302
- * vacate: neither can do anything to an ended session, so the bar swaps two dead controls for two
303
- * live ones and never changes width.
304
- */
305
- [${HUD}] .reticle-tb-btn--export{display:none;}
306
- [${OVERLAY}][${STATE}="ended"] [${HUD}] .reticle-tb-btn--export{display:inline-flex;}
307
- [${OVERLAY}][${STATE}="ended"] [${HUD}] [data-reticle-pause],
308
- [${OVERLAY}][${STATE}="ended"] [${HUD}] [data-reticle-end]{display:none;}
309
297
  [${HUD}] .reticle-tb-tip{
310
298
  position:absolute;bottom:calc(100% + 14px);left:50%;transform:translateX(-50%) scale(.95);
311
299
  padding:6px 10px;background:#1a1a1a;color:rgba(255,255,255,.9);font-size:12px;font-weight:500;
@@ -359,7 +347,7 @@ export const SHELL_CSS = `
359
347
  [${HUD}] .reticle-hi-icon{display:inline-flex;align-items:center;justify-content:center;line-height:1;flex-shrink:0;color:inherit;}
360
348
  [${HUD}] .reticle-hi-icon svg{display:block;fill:none;stroke:currentColor;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;}
361
349
  [${HUD}] .reticle-live{display:none;}
362
- [${CHAT_PANEL}] .reticle-act-strip{flex:none;display:flex;align-items:center;gap:8px;padding:10px 14px;
350
+ [${CHAT_PANEL}] .reticle-act-strip{flex:none;display:flex;align-items:center;gap:7px;min-height:36px;box-sizing:border-box;padding:4px 12px;
363
351
  border-bottom:1px solid rgba(255,255,255,.07);background:rgba(0,0,0,.22);}
364
352
  [${CHAT_PANEL}] .reticle-act-dot{flex:none;width:7px;height:7px;border-radius:50%;background:var(--reticle-faint);
365
353
  box-shadow:0 0 0 2px rgba(255,255,255,.04);transition:background .2s,box-shadow .2s;}
@@ -387,6 +375,15 @@ export const SHELL_CSS = `
387
375
  @keyframes reticle-idle-pulse{0%,100%{opacity:.45;transform:scale(.92)}50%{opacity:1;transform:scale(1)}}
388
376
  [${CHAT_PANEL}] .reticle-act{display:block;flex:1;min-width:0;color:var(--reticle-muted);font-size:11px;
389
377
  font-variant-numeric:tabular-nums;letter-spacing:.01em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
378
+ [${CHAT_PANEL}] .reticle-act-actions{display:flex;align-items:center;gap:4px;flex:none;margin-left:auto;}
379
+ [${CHAT_PANEL}] .reticle-status-btn{position:relative;width:26px;height:26px;display:inline-flex;align-items:center;justify-content:center;
380
+ border:1px solid var(--reticle-line);border-radius:var(--reticle-hud-radius-sm);background:transparent;color:var(--reticle-muted);cursor:pointer;}
381
+ [${CHAT_PANEL}] .reticle-status-btn[hidden]{display:none;}
382
+ [${CHAT_PANEL}] .reticle-status-btn:hover:not(:disabled){background:var(--reticle-hud-hover);color:var(--reticle-fg);}
383
+ [${CHAT_PANEL}] .reticle-status-btn:focus-visible{outline:2px solid var(--reticle-accent);outline-offset:2px;}
384
+ [${CHAT_PANEL}] .reticle-status-btn:disabled{opacity:.35;cursor:not-allowed;}
385
+ [${CHAT_PANEL}] .reticle-status-stop:hover:not(:disabled){color:var(--reticle-state);border-color:var(--reticle-state);}
386
+ [${CHAT_PANEL}] .reticle-act-actions .reticle-pause-badge{display:none;position:static;transform:none;padding:2px 5px;font-size:8px;}
390
387
  [${CHAT_PANEL}] .reticle-act-strip[data-liveness="active"] .reticle-act{color:var(--reticle-fg);}
391
388
  [${HUD}] .reticle-pass{color:var(--reticle-ok);}[${HUD}] .reticle-fail{color:var(--reticle-bad);}
392
389
  /**
@@ -408,6 +405,6 @@ export const SHELL_CSS = `
408
405
  --reticle-accent:var(--reticle-state);
409
406
  --reticle-accent-soft:color-mix(in srgb,var(--reticle-state) 18%,transparent);}
410
407
  @media (max-width:480px){
411
- [${CHAT_PANEL}]{width:min(100vw - 24px,320px);max-height:min(360px,calc(100vh - 100px));}
408
+ [${CHAT_PANEL}]{width:min(100vw - 24px,320px);max-height:min(440px,calc(100vh - 100px));}
412
409
  [${OVERLAY}][${MIN_ATTR}="0"] [${HUD}]{max-width:calc(100vw - 24px);}
413
410
  }`;
@@ -3,6 +3,8 @@ import { PresenterReport } from './presenter-report.js';
3
3
  import type { AccountState } from '@reticlehq/core';
4
4
  import { type AccountDetails } from './presenter-account.js';
5
5
  import { type OfferState } from './carousel/offer-card.js';
6
+ import { type ChatView } from './presenter-chat-views.js';
7
+ import type { AnnotationItem } from '../review/annotator.js';
6
8
  import { type SettingsHost } from './presenter-settings.js';
7
9
  interface HudShellCallbacks {
8
10
  onChatOpen?: () => void;
@@ -11,13 +13,18 @@ interface HudShellCallbacks {
11
13
  onAnnotateToggle?: (on: boolean) => void;
12
14
  onCollapse?: () => void;
13
15
  onSyncNow?: () => void;
16
+ onHarness?: (enabled: boolean) => void;
17
+ onSignIn?: () => void;
14
18
  settings?: SettingsHost;
15
19
  }
16
20
  export declare class HudShell {
17
21
  #private;
18
22
  paintHarness(config: HarnessConfig | undefined): void;
23
+ paintImpact(verdicts: number): void;
24
+ paintAnnotations(items: readonly AnnotationItem[]): void;
25
+ openView(view: ChatView): void;
19
26
  paintAccount(account: AccountState | undefined, dashboardUrl: string | undefined, details?: AccountDetails): void;
20
- paintOffer(offer: OfferState | undefined): void;
27
+ paintOffer(offer: OfferState | undefined, notices?: readonly unknown[]): void;
21
28
  constructor(callbacks?: HudShellCallbacks);
22
29
  static dockHtml(actStripHtml: string, bannerHtml: string, logAttr: string, flowsHtml: string, footHtml: string): string;
23
30
  get report(): PresenterReport;