@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,5 +1,7 @@
1
1
  import type { HarnessConfig } from '@reticlehq/core';
2
+ import type { HarnessGateView } from './presenter-harness-row.js';
2
3
  import { SETTINGS_CSS } from './presenter-settings-styles.js';
4
+ import { type Credits } from '@reticlehq/core/hud';
3
5
  import type { AccountState } from '@reticlehq/core';
4
6
  import { type AccountDetails } from './presenter-account.js';
5
7
  export { SETTINGS_CSS };
@@ -41,11 +43,15 @@ export interface PresenterSettings {
41
43
  export declare function getPresenterSettings(): PresenterSettings;
42
44
  export declare function loadPresenterSettings(): PresenterSettings;
43
45
  export declare const SETTINGS_ACCOUNT_ATTR = "data-reticle-settings-account";
44
- export declare function paintHarnessRow(root: ParentNode, config: HarnessConfig | undefined): void;
46
+ export declare const HARNESS_LOCKED_HELP = "The Harness is not available to this workspace right now: see Plan in your Reticle dashboard.";
47
+ export declare function creditsLeft(credits: Credits | undefined): string;
48
+ export declare function paintHarnessRow(root: ParentNode, config: HarnessConfig | undefined, gate?: HarnessGateView): void;
45
49
  export declare function paintSettingsAccount(root: HTMLElement, account: AccountState | undefined, dashboardUrl: string | undefined, details?: AccountDetails): void;
50
+ export declare function paintSdkVersion(root: ParentNode, version: string | undefined): void;
46
51
  export declare function settingsPanelHtml(): string;
47
52
  export interface SettingsHost {
48
53
  onHideUntilRestart?: () => void;
54
+ onKill?: () => void;
49
55
  onHarness?: (enabled: boolean) => void;
50
56
  onSettingsChange?: (settings: PresenterSettings) => void;
51
57
  onBeforeOpen?: () => void;
@@ -57,7 +63,7 @@ export declare class PresenterSettingsPanel {
57
63
  constructor(host?: SettingsHost);
58
64
  contains(node: Node): boolean;
59
65
  mount(root: HTMLElement): void;
60
- paintHarness(config: HarnessConfig | undefined): void;
66
+ paintHarness(config: HarnessConfig | undefined, gate?: HarnessGateView): void;
61
67
  teardown(): void;
62
68
  isOpen(): boolean;
63
69
  open(): void;
@@ -4,8 +4,9 @@ import { HUD_SURFACE_CLASS } from './chrome/presenter-hud-chrome.js';
4
4
  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
+ import { creditsLeftText, creditsSpent, creditsUsedText } from '@reticlehq/core/hud';
7
8
  import { DISCOVERY_CALL_URL, FOUNDER_EMAIL, FOUNDER_MAILTO } from '@reticlehq/core';
8
- import { accountControlHtml } from './presenter-account.js';
9
+ import { ACCOUNT_TEXT, accountControlHtml, paintAccountSlot, } from './presenter-account.js';
9
10
  export { SETTINGS_CSS };
10
11
  export const OutputDetail = {
11
12
  MINIMAL: 'minimal',
@@ -131,30 +132,59 @@ function patchSettings(patch) {
131
132
  function settingsHelpIcon() {
132
133
  return hiIconHtml(PresenterIcon.HELP, PRESENTER_ICON_SIZE.HELP);
133
134
  }
134
- function settingsLabel(text, helpTitle) {
135
+ const SETTING_TEXT = {
136
+ outputDetail: ['Output detail', 'How much a copied run includes.'],
137
+ autoOpenChat: ['Open the log automatically', 'Opens this panel when a session starts.'],
138
+ showTimestamps: ['Show timestamps', 'Shows how long ago each log row happened.'],
139
+ showTally: ['Show pass/fail count', 'A score pill for this session in the toolbar.'],
140
+ reactComponents: ['React component names', 'Adds component names to copied runs.'],
141
+ blockPageInteractions: [
142
+ 'Block page while adding notes',
143
+ 'Clicks pin a note instead of using the app.',
144
+ ],
145
+ clearOnCopy: ['Clear notes after copying', 'Starts fresh once your notes are copied.'],
146
+ hideUntilRestart: ['Hide until reload', 'Hides the HUD until this page reloads.'],
147
+ harnessEnabled: [
148
+ 'Allow Reticle to drive',
149
+ 'Off by default. Unlocks at 80% instrumentation; lets Run Harness test this project.',
150
+ ],
151
+ reduceMotion: ['Reduce motion', 'Fewer HUD animations.'],
152
+ ambientGlow: ['Page glow', 'Tints the page edges while a session runs.'],
153
+ workspace: ['Workspace', 'The Reticle account this machine is signed in to.'],
154
+ };
155
+ function settingsLabel(text, helpTitle, desc = '') {
135
156
  const help = settingsHelpIcon();
136
- return `<span class="reticle-settings-label">${text}<button type="button" class="reticle-settings-help" data-reticle-settings-help title="${helpTitle}" aria-label="${helpTitle}">${help}</button></span>`;
157
+ const line = '' === desc ? '' : `<span class="reticle-settings-desc">${desc}</span>`;
158
+ return `<span class="reticle-settings-labelcol"><span class="reticle-settings-label">${text}<button type="button" class="reticle-settings-help" data-reticle-settings-help title="${helpTitle}" aria-label="${helpTitle}">${help}</button></span>${line}</span>`;
137
159
  }
138
- function settingsToggleRow(key, label, helpTitle, extra = '') {
160
+ function settingsToggleRow(key, helpTitle, extra = '') {
161
+ const [label, desc] = SETTING_TEXT[key];
139
162
  return `<div class="reticle-settings-row" ${extra}>
140
- ${settingsLabel(label, helpTitle)}
163
+ ${settingsLabel(label, helpTitle, desc)}
141
164
  <button type="button" class="reticle-settings-toggle" ${SETTING_KEY_ATTR}="${key}" role="switch" aria-checked="false"></button>
142
165
  </div>`;
143
166
  }
144
- function settingsCheckRow(key, label, checked) {
167
+ function settingsCheckRow(key, checked) {
168
+ const [label, desc] = SETTING_TEXT[key];
145
169
  const on = checked ? 'true' : 'false';
146
170
  return `<label class="reticle-settings-checkrow" data-reticle-check-row="${key}">
147
171
  <span class="reticle-settings-check" data-reticle-check="${key}" role="checkbox" aria-checked="${on}" tabindex="0"></span>
148
- <span class="reticle-settings-check-label">${label}</span>
172
+ <span class="reticle-settings-labelcol"><span class="reticle-settings-check-label">${label}</span><span class="reticle-settings-desc">${desc}</span></span>
149
173
  </label>`;
150
174
  }
151
175
  export const SETTINGS_ACCOUNT_ATTR = 'data-reticle-settings-account';
152
176
  const SETTINGS_ACCOUNT_ROW_ATTR = 'data-reticle-settings-account-row';
177
+ const HELP_TEXT_ATTR = 'data-reticle-settings-helptext';
153
178
  const SETTINGS_HARNESS_ROW_ATTR = 'data-reticle-settings-harness-row';
154
- 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.';
157
- export function paintHarnessRow(root, config) {
179
+ const HARNESS_HELP = 'Let Reticle test this app by itself to find defects, from Run Harness or from your coding agent. Off blocks every run. Set here or in your dashboard — both write to the same place.';
180
+ export const HARNESS_LOCKED_HELP = 'The Harness is not available to this workspace right now: see Plan in your Reticle dashboard.';
181
+ export function creditsLeft(credits) {
182
+ if (credits === undefined)
183
+ return '';
184
+ return creditsSpent(credits) ? creditsUsedText(credits).said : creditsLeftText(credits);
185
+ }
186
+ const ACCOUNT_HELP = `Whether this machine is signed in to a Reticle workspace. Press ${ACCOUNT_TEXT.SIGNED_OUT} to sign in through your browser.`;
187
+ export function paintHarnessRow(root, config, gate) {
158
188
  const row = root.querySelector(`[${SETTINGS_HARNESS_ROW_ATTR}]`);
159
189
  if (!(row instanceof HTMLElement))
160
190
  return;
@@ -166,12 +196,20 @@ export function paintHarnessRow(root, config) {
166
196
  const toggle = row.querySelector(`[${SETTING_KEY_ATTR}="harnessEnabled"]`);
167
197
  if (!(toggle instanceof HTMLElement))
168
198
  return;
169
- const usable = config.harnessEntitled;
199
+ const locked = gate !== undefined && !gate.unlocked ? gate.reason : undefined;
200
+ const usable = config.harnessEntitled && locked === undefined;
170
201
  toggle.setAttribute('aria-checked', config.harnessEnabled && usable ? 'true' : 'false');
171
202
  toggle.setAttribute('aria-disabled', usable ? 'false' : 'true');
172
- const help = row.querySelector('[data-reticle-help]');
203
+ const help = row.querySelector('[data-reticle-settings-help]');
204
+ const credits = creditsLeft(config.credits);
173
205
  if (help instanceof HTMLElement)
174
- help.title = usable ? HARNESS_HELP : HARNESS_LOCKED_HELP;
206
+ help.title =
207
+ locked ??
208
+ (usable
209
+ ? [HARNESS_HELP, credits].filter((t) => 0 < t.length).join(' ')
210
+ : creditsSpent(config.credits)
211
+ ? credits
212
+ : HARNESS_LOCKED_HELP);
175
213
  }
176
214
  export function paintSettingsAccount(root, account, dashboardUrl, details = {}) {
177
215
  const row = root.querySelector(`[${SETTINGS_ACCOUNT_ROW_ATTR}]`);
@@ -180,7 +218,7 @@ export function paintSettingsAccount(root, account, dashboardUrl, details = {})
180
218
  return;
181
219
  const html = accountControlHtml(account, { ...details, dashboardUrl }, true);
182
220
  row.hidden = 0 === html.length;
183
- slot.innerHTML = html;
221
+ paintAccountSlot(slot, html);
184
222
  }
185
223
  const FEEDBACK_TEXT = {
186
224
  EMAIL: 'Email the founder',
@@ -188,6 +226,23 @@ const FEEDBACK_TEXT = {
188
226
  CALL: 'Book a call',
189
227
  CALL_TITLE: 'Pick a time to talk it through',
190
228
  };
229
+ const KILL_ATTR = 'data-reticle-settings-kill';
230
+ const KILL_TEXT = {
231
+ LABEL: 'Kill Reticle',
232
+ ARMED: 'Click again to kill Reticle',
233
+ WARNING: 'Disconnects Reticle from this page: no HUD, no agent, nothing recorded. To bring it back, restart your dev server or reload the page.',
234
+ };
235
+ const SDK_VERSION_ATTR = 'data-reticle-sdk-version';
236
+ const SDK_VERSION_TEXT = {
237
+ UNKNOWN: 'Reticle SDK version unknown',
238
+ KNOWN: (version) => `Reticle SDK ${version}`,
239
+ };
240
+ export function paintSdkVersion(root, version) {
241
+ const line = root.querySelector(`[${SDK_VERSION_ATTR}]`);
242
+ if (line !== null)
243
+ line.textContent =
244
+ version === undefined ? SDK_VERSION_TEXT.UNKNOWN : SDK_VERSION_TEXT.KNOWN(version);
245
+ }
191
246
  export function settingsPanelHtml() {
192
247
  const close = hiIconHtml(PresenterIcon.REMOVE, PRESENTER_ICON_SIZE.MIN);
193
248
  const caret = hiIconHtml(PresenterIcon.CARET_RIGHT, PRESENTER_ICON_SIZE.HELP);
@@ -209,34 +264,40 @@ export function settingsPanelHtml() {
209
264
  <div class="reticle-settings-body">
210
265
  <div class="reticle-settings-section">Session</div>
211
266
  <div class="reticle-settings-row">
212
- ${settingsLabel('Output Detail', outputHelp)}
267
+ ${settingsLabel(SETTING_TEXT.outputDetail[0], outputHelp, SETTING_TEXT.outputDetail[1])}
213
268
  <button type="button" class="reticle-settings-cycle" data-reticle-settings-cycle="outputDetail"><span data-reticle-cycle-label></span><span class="reticle-settings-dots" data-reticle-cycle-dots></span></button>
214
269
  </div>
215
- ${settingsToggleRow('autoOpenChat', 'Auto-open chat', autoChatHelp)}
216
- ${settingsToggleRow('showTimestamps', 'Show timestamps', timestampsHelp)}
217
- ${settingsToggleRow('showTally', 'Show verdict tally', tallyHelp)}
270
+ ${settingsToggleRow('autoOpenChat', autoChatHelp)}
271
+ ${settingsToggleRow('showTimestamps', timestampsHelp)}
272
+ ${settingsToggleRow('showTally', tallyHelp)}
218
273
  <div class="reticle-settings-section">Inspector</div>
219
- ${settingsToggleRow('reactComponents', 'React Components', reactHelp, 'data-reticle-settings-react-row')}
274
+ ${settingsToggleRow('reactComponents', reactHelp, 'data-reticle-settings-react-row')}
220
275
  <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)}
224
- ${settingsToggleRow('harnessEnabled', 'Autonomous driving', harnessHelp, `${SETTINGS_HARNESS_ROW_ATTR} hidden`)}
225
- ${settingsToggleRow('reduceMotion', 'Reduce motion', motionHelp)}
276
+ ${settingsCheckRow('blockPageInteractions', true)}
277
+ ${settingsCheckRow('clearOnCopy', false)}
278
+ ${settingsToggleRow('hideUntilRestart', hideHelp)}
279
+ ${settingsToggleRow('harnessEnabled', harnessHelp, `${SETTINGS_HARNESS_ROW_ATTR} hidden`)}
280
+ ${settingsToggleRow('reduceMotion', motionHelp)}
226
281
  <div class="reticle-settings-section">Account</div>
227
282
  <div class="reticle-settings-row" ${SETTINGS_ACCOUNT_ROW_ATTR} hidden>
228
- ${settingsLabel('Workspace', ACCOUNT_HELP)}
283
+ ${settingsLabel(SETTING_TEXT.workspace[0], ACCOUNT_HELP, SETTING_TEXT.workspace[1])}
229
284
  <span ${SETTINGS_ACCOUNT_ATTR}></span>
230
285
  </div>
231
286
  <div class="reticle-settings-section">Status theme</div>
232
- ${settingsToggleRow('ambientGlow', 'Page glow', glowHelp)}
287
+ ${settingsToggleRow('ambientGlow', glowHelp)}
233
288
  <div class="reticle-settings-themes" data-reticle-settings-themes></div>
234
- </div>
235
- <div class="reticle-settings-foot">
289
+ <div class="reticle-settings-section">Help</div>
290
+ <div class="reticle-settings-foot">
236
291
  <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>
292
+ <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
293
  <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
294
  <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>
295
+ <p class="reticle-settings-kill-sub" ${SDK_VERSION_ATTR}>${SDK_VERSION_TEXT.UNKNOWN}</p>
296
+ <div class="reticle-settings-kill-row">
297
+ <button type="button" class="reticle-settings-kill" ${KILL_ATTR}><span data-reticle-kill-label>${KILL_TEXT.LABEL}</span></button>
298
+ <p class="reticle-settings-kill-sub">${KILL_TEXT.WARNING}</p>
299
+ </div>
300
+ </div>
240
301
  </div>
241
302
  </div>
242
303
  </div>`;
@@ -294,10 +355,13 @@ export class PresenterSettingsPanel {
294
355
  setHiIcon(closeBtn, PresenterIcon.REMOVE, PRESENTER_ICON_SIZE.MIN);
295
356
  }
296
357
  if (this.#btn !== undefined) {
358
+ const label = this.#btn.querySelector('.reticle-nav-label');
297
359
  setHiIcon(this.#btn, PresenterIcon.GEAR, PRESENTER_ICON_SIZE.TOOLBAR);
360
+ if (null !== label)
361
+ this.#btn.append(label);
298
362
  this.#btn.addEventListener('click', (e) => {
299
363
  e.stopPropagation();
300
- this.toggle();
364
+ this.open();
301
365
  });
302
366
  }
303
367
  root
@@ -310,8 +374,24 @@ export class PresenterSettingsPanel {
310
374
  this.#update({ outputDetail: next.value });
311
375
  });
312
376
  for (const help of root.querySelectorAll('.reticle-settings-help')) {
377
+ help.setAttribute('aria-expanded', 'false');
313
378
  help.addEventListener('click', (e) => {
314
379
  e.stopPropagation();
380
+ const row = help.closest('.reticle-settings-row');
381
+ if (null === row)
382
+ return;
383
+ const shown = row.querySelector(`[${HELP_TEXT_ATTR}]`);
384
+ if (null !== shown) {
385
+ shown.remove();
386
+ help.setAttribute('aria-expanded', 'false');
387
+ return;
388
+ }
389
+ const text = document.createElement('p');
390
+ text.className = 'reticle-settings-helptext';
391
+ text.setAttribute(HELP_TEXT_ATTR, '');
392
+ text.textContent = help.getAttribute('title') ?? '';
393
+ row.appendChild(text);
394
+ help.setAttribute('aria-expanded', 'true');
315
395
  });
316
396
  }
317
397
  for (const toggle of root.querySelectorAll(`[${SETTING_KEY_ATTR}]`)) {
@@ -399,6 +479,20 @@ export class PresenterSettingsPanel {
399
479
  if (dock instanceof HTMLElement)
400
480
  resetHudDockPosition(dock);
401
481
  });
482
+ root.querySelector(`[${KILL_ATTR}]`)?.addEventListener('click', (e) => {
483
+ e.stopPropagation();
484
+ const kill = e.currentTarget;
485
+ if (!(kill instanceof HTMLElement))
486
+ return;
487
+ if ('1' === kill.getAttribute('data-armed')) {
488
+ this.#host.onKill?.();
489
+ return;
490
+ }
491
+ kill.setAttribute('data-armed', '1');
492
+ const label = kill.querySelector('[data-reticle-kill-label]');
493
+ if (label !== null)
494
+ label.textContent = KILL_TEXT.ARMED;
495
+ });
402
496
  closeBtn?.addEventListener('click', (e) => {
403
497
  e.stopPropagation();
404
498
  this.close();
@@ -408,11 +502,11 @@ export class PresenterSettingsPanel {
408
502
  applyPresenterSettings(root, activeSettings);
409
503
  this.#host.onSettingsChange?.(activeSettings);
410
504
  }
411
- paintHarness(config) {
505
+ paintHarness(config, gate) {
412
506
  const root = this.#panel;
413
507
  if (root === undefined)
414
508
  return;
415
- paintHarnessRow(root, config);
509
+ paintHarnessRow(root, config, gate);
416
510
  }
417
511
  teardown() {
418
512
  this.#root = undefined;
@@ -434,6 +528,13 @@ export class PresenterSettingsPanel {
434
528
  close() {
435
529
  if (this.#root === undefined)
436
530
  return;
531
+ const kill = this.#panel?.querySelector(`[${KILL_ATTR}]`);
532
+ if (kill instanceof HTMLElement) {
533
+ kill.removeAttribute('data-armed');
534
+ const label = kill.querySelector('[data-reticle-kill-label]');
535
+ if (label !== null)
536
+ label.textContent = KILL_TEXT.LABEL;
537
+ }
437
538
  this.#root.setAttribute(SETTINGS_ATTR, '0');
438
539
  this.#panel?.setAttribute('aria-hidden', 'true');
439
540
  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,9 @@ 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
+ pointer-events:auto;}
96
93
  [${OVERLAY}][data-reticle-mode="idle"][data-reticle-block="0"] [${CHAT_PANEL}] :is(button,a,input,select,textarea){
97
94
  pointer-events:auto;}
98
95
  /**
@@ -134,8 +131,18 @@ export const SHELL_CSS = `
134
131
  so the two never overlap. The :empty rule keeps the gap closed when no account state was sent. */
135
132
  [${CHAT_PANEL}] .reticle-head-account{margin-left:auto;display:inline-flex;align-items:center;}
136
133
  [${CHAT_PANEL}] .reticle-head-account:empty{display:none;}
134
+ /* The quiet way to the platform: one line of small text before the avatar, cut short before it pushes. */
135
+ [${CHAT_PANEL}] .reticle-head-team{margin-left:auto;min-width:0;display:inline-flex;}
136
+ [${CHAT_PANEL}] .reticle-head-team:empty{display:none;}
137
+ [${CHAT_PANEL}] .reticle-head-team:not(:empty) + .reticle-head-account{margin-left:0;}
138
+ [${CHAT_PANEL}] .reticle-team-link{min-width:0;max-width:100%;overflow:hidden;padding:0;border:0;background:none;cursor:pointer;font:inherit;font-size:var(--reticle-hud-size-xs);font-weight:500;color:var(--reticle-hud-text-muted);text-decoration:none;white-space:nowrap;text-overflow:ellipsis;}
139
+ [${CHAT_PANEL}] .reticle-team-link span{display:block;overflow:hidden;text-overflow:ellipsis;}
140
+ [${CHAT_PANEL}] .reticle-team-link:hover{color:var(--reticle-hud-brand);}
141
+ [${CHAT_PANEL}] .reticle-team-link:focus-visible{outline:2px solid var(--reticle-hud-brand);outline-offset:2px;border-radius:var(--reticle-hud-radius-sm);}
137
142
  [${CHAT_PANEL}] .reticle-chat-brand .reticle-mark{height:14px;width:auto;}
138
143
  [${CHAT_PANEL}] .reticle-chat-brandname{font-size:12.5px;font-weight:600;letter-spacing:.01em;}
144
+ [${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);}
145
+ [${CHAT_PANEL}] .reticle-view-heading strong{font-size:11px;font-weight:600;}
139
146
  /* No state word here: the activity strip immediately below already says "idle · 21s", and the
140
147
  dot in front of it carries the same colour. One fact, one place. */
141
148
  [${CHAT_PILL_ATTR}] .reticle-chat-pill-text{
@@ -255,13 +262,13 @@ export const SHELL_CSS = `
255
262
  [${HUD}] .reticle-toolbar-actions{
256
263
  display:inline-flex;align-items:center;gap:2px;flex:none;
257
264
  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;}
265
+ [${HUD}] .reticle-toolbar-chrome{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(0,1fr);align-items:center;gap:var(--reticle-hud-space-1);flex:1;min-width:0;}
259
266
  [${HUD}] .reticle-tb-sep{width:1px;height:14px;background:rgba(255,255,255,.12);margin:0 4px;flex:none;align-self:center;}
260
267
 
261
268
  [${HUD}] .reticle-tb-wrap{position:relative;display:flex;align-items:center;justify-content:center;overflow:visible;}
262
269
  [${HUD}] .reticle-tb-btn{
263
270
  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;
271
+ 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
272
  background:transparent;color:rgba(255,255,255,.85);line-height:0;
266
273
  transition:background-color .15s ease,color .15s ease,transform .1s ease,opacity .2s ease;}
267
274
  [${HUD}] .reticle-tb-btn:hover{background:rgba(255,255,255,.12);color:#fff;}
@@ -294,18 +301,6 @@ export const SHELL_CSS = `
294
301
  color:#fff;background:rgba(255,255,255,.14);}
295
302
  [${HUD}] .reticle-tb-btn[data-danger]:hover:not(:disabled){color:#ff383c;
296
303
  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
304
  [${HUD}] .reticle-tb-tip{
310
305
  position:absolute;bottom:calc(100% + 14px);left:50%;transform:translateX(-50%) scale(.95);
311
306
  padding:6px 10px;background:#1a1a1a;color:rgba(255,255,255,.9);font-size:12px;font-weight:500;
@@ -359,7 +354,7 @@ export const SHELL_CSS = `
359
354
  [${HUD}] .reticle-hi-icon{display:inline-flex;align-items:center;justify-content:center;line-height:1;flex-shrink:0;color:inherit;}
360
355
  [${HUD}] .reticle-hi-icon svg{display:block;fill:none;stroke:currentColor;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;}
361
356
  [${HUD}] .reticle-live{display:none;}
362
- [${CHAT_PANEL}] .reticle-act-strip{flex:none;display:flex;align-items:center;gap:8px;padding:10px 14px;
357
+ [${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
358
  border-bottom:1px solid rgba(255,255,255,.07);background:rgba(0,0,0,.22);}
364
359
  [${CHAT_PANEL}] .reticle-act-dot{flex:none;width:7px;height:7px;border-radius:50%;background:var(--reticle-faint);
365
360
  box-shadow:0 0 0 2px rgba(255,255,255,.04);transition:background .2s,box-shadow .2s;}
@@ -382,11 +377,22 @@ export const SHELL_CSS = `
382
377
  */
383
378
  [${OVERLAY}][${STATE}="unreachable"]{--reticle-state:var(--reticle-faint);}
384
379
  [${OVERLAY}][${STATE}="unreachable"] [${CHAT_PANEL}] .reticle-act-dot{animation:none;}
380
+ /* The one status that is a sentence to act on: two lines, full contrast, the detail on hover. */
381
+ [${OVERLAY}][${STATE}="unreachable"] [${CHAT_PANEL}] .reticle-act{white-space:normal;line-height:1.35;color:var(--reticle-fg);cursor:help;}
385
382
  [${OVERLAY}][${STATE}="paused"] [${CHAT_PANEL}] .reticle-act-dot,
386
383
  [${OVERLAY}][${STATE}="ended"] [${CHAT_PANEL}] .reticle-act-dot{animation:none;}
387
384
  @keyframes reticle-idle-pulse{0%,100%{opacity:.45;transform:scale(.92)}50%{opacity:1;transform:scale(1)}}
388
385
  [${CHAT_PANEL}] .reticle-act{display:block;flex:1;min-width:0;color:var(--reticle-muted);font-size:11px;
389
386
  font-variant-numeric:tabular-nums;letter-spacing:.01em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
387
+ [${CHAT_PANEL}] .reticle-act-actions{display:flex;align-items:center;gap:4px;flex:none;margin-left:auto;}
388
+ [${CHAT_PANEL}] .reticle-status-btn{position:relative;width:26px;height:26px;display:inline-flex;align-items:center;justify-content:center;
389
+ border:1px solid var(--reticle-line);border-radius:var(--reticle-hud-radius-sm);background:transparent;color:var(--reticle-muted);cursor:pointer;}
390
+ [${CHAT_PANEL}] .reticle-status-btn[hidden]{display:none;}
391
+ [${CHAT_PANEL}] .reticle-status-btn:hover:not(:disabled){background:var(--reticle-hud-hover);color:var(--reticle-fg);}
392
+ [${CHAT_PANEL}] .reticle-status-btn:focus-visible{outline:2px solid var(--reticle-accent);outline-offset:2px;}
393
+ [${CHAT_PANEL}] .reticle-status-btn:disabled{opacity:.35;cursor:not-allowed;}
394
+ [${CHAT_PANEL}] .reticle-status-stop:hover:not(:disabled){color:var(--reticle-state);border-color:var(--reticle-state);}
395
+ [${CHAT_PANEL}] .reticle-act-actions .reticle-pause-badge{display:none;position:static;transform:none;padding:2px 5px;font-size:8px;}
390
396
  [${CHAT_PANEL}] .reticle-act-strip[data-liveness="active"] .reticle-act{color:var(--reticle-fg);}
391
397
  [${HUD}] .reticle-pass{color:var(--reticle-ok);}[${HUD}] .reticle-fail{color:var(--reticle-bad);}
392
398
  /**
@@ -408,6 +414,6 @@ export const SHELL_CSS = `
408
414
  --reticle-accent:var(--reticle-state);
409
415
  --reticle-accent-soft:color-mix(in srgb,var(--reticle-state) 18%,transparent);}
410
416
  @media (max-width:480px){
411
- [${CHAT_PANEL}]{width:min(100vw - 24px,320px);max-height:min(360px,calc(100vh - 100px));}
417
+ [${CHAT_PANEL}]{width:min(100vw - 24px,320px);max-height:min(440px,calc(100vh - 100px));}
412
418
  [${OVERLAY}][${MIN_ATTR}="0"] [${HUD}]{max-width:calc(100vw - 24px);}
413
419
  }`;
@@ -1,8 +1,10 @@
1
- import type { HarnessConfig } from '@reticlehq/core';
1
+ import type { AgentLink, HarnessConfig, HarnessDrive } from '@reticlehq/core';
2
+ import type { HarnessGateView } from './presenter-harness-row.js';
2
3
  import { PresenterReport } from './presenter-report.js';
3
4
  import type { AccountState } from '@reticlehq/core';
4
5
  import { type AccountDetails } from './presenter-account.js';
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,24 @@ interface HudShellCallbacks {
11
13
  onAnnotateToggle?: (on: boolean) => void;
12
14
  onCollapse?: () => void;
13
15
  onSyncNow?: () => void;
16
+ onHarness?: (enabled: boolean) => void;
17
+ onHarnessRun?: (persona?: string) => void;
18
+ onHarnessStop?: () => void;
19
+ onHarnessLink?: () => void;
20
+ onSignIn?: () => void;
14
21
  settings?: SettingsHost;
15
22
  }
16
23
  export declare class HudShell {
17
24
  #private;
18
- paintHarness(config: HarnessConfig | undefined): void;
25
+ setProjectId(projectId: string | undefined): void;
26
+ paintHarnessDrive(drive: HarnessDrive | undefined): void;
27
+ paintAgent(link: AgentLink | undefined): void;
28
+ paintHarness(config: HarnessConfig | undefined, gate?: HarnessGateView): void;
29
+ paintImpact(verdicts: number): void;
30
+ paintAnnotations(items: readonly AnnotationItem[]): void;
31
+ openView(view: ChatView): void;
19
32
  paintAccount(account: AccountState | undefined, dashboardUrl: string | undefined, details?: AccountDetails): void;
20
- paintOffer(offer: OfferState | undefined): void;
33
+ paintNotices(notices?: readonly unknown[]): void;
21
34
  constructor(callbacks?: HudShellCallbacks);
22
35
  static dockHtml(actStripHtml: string, bannerHtml: string, logAttr: string, flowsHtml: string, footHtml: string): string;
23
36
  get report(): PresenterReport;