@reticlehq/browser 2.14.0 → 3.1.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 (130) hide show
  1. package/dist/actions/actions-dom.d.ts +8 -0
  2. package/dist/actions/actions-dom.js +70 -3
  3. package/dist/actions/actions-press.d.ts +4 -0
  4. package/dist/actions/actions-press.js +46 -0
  5. package/dist/actions/actions.d.ts +1 -0
  6. package/dist/actions/actions.js +41 -8
  7. package/dist/actions/appeared-text.js +6 -0
  8. package/dist/actions/scroll.d.ts +8 -1
  9. package/dist/actions/scroll.js +28 -10
  10. package/dist/commands/commands.js +10 -3
  11. package/dist/dom/a11y.js +2 -2
  12. package/dist/dom/desktop-capture.js +1 -1
  13. package/dist/dom/dom-ignore.js +1 -1
  14. package/dist/dom/occlusion.js +31 -5
  15. package/dist/dom/paint-context.d.ts +6 -0
  16. package/dist/dom/paint-context.js +38 -0
  17. package/dist/dom/query.js +2 -2
  18. package/dist/dom/snapshot.js +1 -1
  19. package/dist/edit-epoch.d.ts +1 -1
  20. package/dist/edit-epoch.js +1 -1
  21. package/dist/index.d.ts +4 -5
  22. package/dist/index.js +3 -5
  23. package/dist/observers/animation.js +1 -1
  24. package/dist/observers/blind-spots.js +1 -1
  25. package/dist/observers/dialogs.js +1 -1
  26. package/dist/observers/dom.js +28 -8
  27. package/dist/observers/download.js +1 -1
  28. package/dist/observers/health.js +1 -1
  29. package/dist/observers/{network-body.js → net-detail/network-body.js} +2 -2
  30. package/dist/observers/network-stream.js +2 -2
  31. package/dist/observers/network.d.ts +1 -1
  32. package/dist/observers/network.js +2 -2
  33. package/dist/observers/scroll.js +2 -2
  34. package/dist/presenter/chrome/layers.d.ts +2 -0
  35. package/dist/presenter/chrome/layers.js +2 -0
  36. package/dist/presenter/chrome/log-kinds.d.ts +12 -0
  37. package/dist/presenter/chrome/log-kinds.js +7 -0
  38. package/dist/presenter/{presenter-effects.js → chrome/presenter-effects.js} +1 -1
  39. package/dist/presenter/{presenter-log.d.ts → chrome/presenter-log.d.ts} +3 -12
  40. package/dist/presenter/{presenter-log.js → chrome/presenter-log.js} +4 -9
  41. package/dist/presenter/chrome/presenter-safe-html.d.ts +2 -0
  42. package/dist/presenter/chrome/presenter-safe-html.js +16 -0
  43. package/dist/presenter/{presenter-tally.js → chrome/presenter-tally.js} +1 -1
  44. package/dist/presenter/{presenter-heroicons-data.d.ts → icons/presenter-heroicons-data.d.ts} +1 -0
  45. package/dist/presenter/{presenter-heroicons-data.js → icons/presenter-heroicons-data.js} +1 -0
  46. package/dist/presenter/{presenter-icons.d.ts → icons/presenter-icons.d.ts} +1 -0
  47. package/dist/presenter/{presenter-icons.js → icons/presenter-icons.js} +1 -0
  48. package/dist/presenter/presenter-account-styles.d.ts +1 -0
  49. package/dist/presenter/presenter-account-styles.js +99 -0
  50. package/dist/presenter/presenter-account.d.ts +40 -0
  51. package/dist/presenter/presenter-account.js +178 -0
  52. package/dist/presenter/presenter-config.d.ts +9 -2
  53. package/dist/presenter/presenter-config.js +3 -1
  54. package/dist/presenter/presenter-controls.d.ts +1 -6
  55. package/dist/presenter/presenter-controls.js +7 -2
  56. package/dist/presenter/presenter-dock-layout.js +1 -1
  57. package/dist/presenter/presenter-drag.d.ts +1 -19
  58. package/dist/presenter/presenter-drag.js +3 -50
  59. package/dist/presenter/presenter-glow.js +1 -1
  60. package/dist/presenter/presenter-hud-position.d.ts +18 -0
  61. package/dist/presenter/presenter-hud-position.js +50 -0
  62. package/dist/presenter/presenter-report-styles.js +16 -1
  63. package/dist/presenter/presenter-report.d.ts +2 -1
  64. package/dist/presenter/presenter-report.js +29 -22
  65. package/dist/presenter/presenter-run-state.d.ts +1 -1
  66. package/dist/presenter/presenter-run-state.js +1 -1
  67. package/dist/presenter/presenter-settings-styles.js +2 -1
  68. package/dist/presenter/presenter-settings.d.ts +4 -0
  69. package/dist/presenter/presenter-settings.js +20 -2
  70. package/dist/presenter/presenter-shell-styles.js +21 -15
  71. package/dist/presenter/presenter-shell.d.ts +4 -0
  72. package/dist/presenter/presenter-shell.js +25 -4
  73. package/dist/presenter/presenter-styles.d.ts +2 -1
  74. package/dist/presenter/presenter-styles.js +6 -3
  75. package/dist/presenter/presenter-workspace.d.ts +4 -0
  76. package/dist/presenter/presenter-workspace.js +29 -2
  77. package/dist/presenter/presenter.d.ts +3 -3
  78. package/dist/presenter/presenter.js +25 -14
  79. package/dist/presenter/tour/tour-view.d.ts +33 -0
  80. package/dist/presenter/tour/tour-view.js +188 -0
  81. package/dist/presenter/tour/tour.d.ts +25 -0
  82. package/dist/presenter/tour/tour.js +245 -0
  83. package/dist/recorder/recorder.js +6 -0
  84. package/dist/registry/declared-channels.d.ts +2 -0
  85. package/dist/registry/declared-channels.js +14 -0
  86. package/dist/registry/declared-platform.d.ts +4 -0
  87. package/dist/registry/declared-platform.js +34 -0
  88. package/dist/registry/emitter.js +1 -1
  89. package/dist/registry/store-adapters.js +1 -1
  90. package/dist/registry/stores.js +1 -1
  91. package/dist/reticle-presenter-helpers.js +10 -6
  92. package/dist/reticle-singleton.d.ts +2 -0
  93. package/dist/reticle-singleton.js +3 -0
  94. package/dist/reticle.js +95 -35
  95. package/dist/review/annotator-styles.js +1 -1
  96. package/dist/review/annotator.js +2 -2
  97. package/dist/review/mark-anchor.js +3 -3
  98. package/dist/transport/transport.js +2 -2
  99. package/package.json +5 -4
  100. package/dist/presenter/presenter-test-helpers.d.ts +0 -14
  101. package/dist/presenter/presenter-test-helpers.js +0 -39
  102. /package/dist/actions/{synthetic-input.d.ts → synthetic/synthetic-input.d.ts} +0 -0
  103. /package/dist/actions/{synthetic-input.js → synthetic/synthetic-input.js} +0 -0
  104. /package/dist/dom/{auto-anchor.d.ts → addressing/auto-anchor.d.ts} +0 -0
  105. /package/dist/dom/{auto-anchor.js → addressing/auto-anchor.js} +0 -0
  106. /package/dist/dom/{refs.d.ts → addressing/refs.d.ts} +0 -0
  107. /package/dist/dom/{refs.js → addressing/refs.js} +0 -0
  108. /package/dist/dom/{source.d.ts → addressing/source.d.ts} +0 -0
  109. /package/dist/dom/{source.js → addressing/source.js} +0 -0
  110. /package/dist/observers/{network-body.d.ts → net-detail/network-body.d.ts} +0 -0
  111. /package/dist/observers/{network-redact.d.ts → net-detail/network-redact.d.ts} +0 -0
  112. /package/dist/observers/{network-redact.js → net-detail/network-redact.js} +0 -0
  113. /package/dist/presenter/{overlay.d.ts → chrome/overlay.d.ts} +0 -0
  114. /package/dist/presenter/{overlay.js → chrome/overlay.js} +0 -0
  115. /package/dist/presenter/{presenter-brand.d.ts → chrome/presenter-brand.d.ts} +0 -0
  116. /package/dist/presenter/{presenter-brand.js → chrome/presenter-brand.js} +0 -0
  117. /package/dist/presenter/{presenter-effects.d.ts → chrome/presenter-effects.d.ts} +0 -0
  118. /package/dist/presenter/{presenter-hud-chrome.d.ts → chrome/presenter-hud-chrome.d.ts} +0 -0
  119. /package/dist/presenter/{presenter-hud-chrome.js → chrome/presenter-hud-chrome.js} +0 -0
  120. /package/dist/presenter/{presenter-report-copy.d.ts → chrome/presenter-report-copy.d.ts} +0 -0
  121. /package/dist/presenter/{presenter-report-copy.js → chrome/presenter-report-copy.js} +0 -0
  122. /package/dist/presenter/{presenter-tally.d.ts → chrome/presenter-tally.d.ts} +0 -0
  123. /package/dist/presenter/{presenter-verbs.d.ts → chrome/presenter-verbs.d.ts} +0 -0
  124. /package/dist/presenter/{presenter-verbs.js → chrome/presenter-verbs.js} +0 -0
  125. /package/dist/registry/{adapters.d.ts → stores/adapters.d.ts} +0 -0
  126. /package/dist/registry/{adapters.js → stores/adapters.js} +0 -0
  127. /package/dist/timers/{native-console.d.ts → console/native-console.d.ts} +0 -0
  128. /package/dist/timers/{native-console.js → console/native-console.js} +0 -0
  129. /package/dist/timers/{native-timers.d.ts → native/native-timers.d.ts} +0 -0
  130. /package/dist/timers/{native-timers.js → native/native-timers.js} +0 -0
@@ -19,7 +19,9 @@ export const HEARTBEAT_MS = 1000;
19
19
  export const IDLE_NOTICE_MS = 4000;
20
20
  export const ACT_STRIP = {
21
21
  READY: 'Ready',
22
- IDLE_PREFIX: 'idle · ',
22
+ PLANNING_PREFIX: 'planning next action · ',
23
+ STOPPED: 'stopped',
24
+ STOPPED_PREFIX: 'stopped · ',
23
25
  NOW: 'now',
24
26
  SINCE_LAST: ' since last action',
25
27
  };
@@ -1,10 +1,6 @@
1
1
  import { HumanControlKind, PresenterTone, SessionState } from '@reticlehq/core';
2
+ export type { ControlIntent, ControlHandler } from './presenter-config.js';
2
3
  export declare const ENDED_FADE_MS = 4000;
3
- export interface ControlIntent {
4
- kind: HumanControlKind;
5
- text?: string;
6
- }
7
- export type ControlHandler = (intent: ControlIntent) => void;
8
4
  export declare const CONTROLS_CSS: string;
9
5
  export declare const CONTROLS_TOOLBAR_HTML: string;
10
6
  export declare const CONTROLS_BANNER_HTML = "<div data-reticle-banner class=\"reticle-banner\">Session ended</div>";
@@ -28,4 +24,3 @@ export declare class ControlPanel {
28
24
  refilterFlows(): void;
29
25
  setState(state: SessionState, text?: string, tone?: PresenterTone): void;
30
26
  }
31
- export {};
@@ -1,7 +1,7 @@
1
1
  import { HumanControlKind, PresenterTone, SessionState } from '@reticlehq/core';
2
- import { nativeSetTimeout, nativeClearTimeout } from '../timers/native-timers.js';
2
+ import { nativeSetTimeout, nativeClearTimeout } from '../timers/native/native-timers.js';
3
3
  import { CHAT_TOGGLE_ATTR, CLEAR_MARKS_ATTR, MARKERS_BTN_ATTR } from './presenter-config.js';
4
- import { PresenterIcon, PRESENTER_ICON_SIZE, hiIcon, hiIconHtml, hiToggleIconHtml, } from './presenter-icons.js';
4
+ import { PresenterIcon, PRESENTER_ICON_SIZE, hiIcon, hiIconHtml, hiToggleIconHtml, } from './icons/presenter-icons.js';
5
5
  import { getPresenterSettings } from './presenter-settings.js';
6
6
  import { mountWorkspaceSelector, workspaceRowHtml } from './presenter-workspace.js';
7
7
  const DATA_RETICLE_STATE = 'data-reticle-state';
@@ -57,6 +57,11 @@ export const CONTROLS_CSS = `
57
57
  background:rgba(255,255,255,.06);color:rgba(255,255,255,.7);cursor:pointer;line-height:0;transition:background .12s,color .12s;}
58
58
  [data-reticle-chat-panel] .reticle-workspace-copy:hover:not(:disabled){background:rgba(255,255,255,.12);color:#fff;}
59
59
  [data-reticle-chat-panel] .reticle-workspace-copy:disabled{opacity:.35;cursor:not-allowed;}
60
+ /* The account capsule, to the right of the menu title. Matches the local idiom in this block:
61
+ the workspace menu predates the design tokens used in the report panel and states its own
62
+ greys, so a token here would be the only one and would not match its neighbours. */
63
+ [data-reticle-chat-panel] .reticle-workspace-menu-actions{
64
+ display:flex;align-items:center;gap:6px;}
60
65
  [data-reticle-chat-panel] .reticle-workspace-menu-row{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;padding:4px 0;}
61
66
  [data-reticle-chat-panel] .reticle-workspace-menu-k{flex:none;color:rgba(255,255,255,.42);}
62
67
  [data-reticle-chat-panel] .reticle-workspace-menu-v{min-width:0;text-align:right;color:rgba(255,255,255,.9);font-weight:500;word-break:break-all;}
@@ -1,5 +1,5 @@
1
1
  import { CHAT_ATTR, CHAT_PANEL_ATTR, CHAT_PLACEMENT_ATTR, DOCK_ALIGN_ATTR, DOCK_ATTR, HUD_DOCK_MARGIN_PX, Placement, SETTINGS_ATTR, SETTINGS_PANEL_ATTR, SETTINGS_PLACEMENT_ATTR, } from './presenter-config.js';
2
- import { applyHudPosition, isHudDragged, readHudPosition, relayoutHudPosition, } from './presenter-drag.js';
2
+ import { applyHudPosition, isHudDragged, readHudPosition, relayoutHudPosition, } from './presenter-hud-position.js';
3
3
  const HUD_ATTR = 'data-reticle-hud';
4
4
  const DOCK_PANEL_GAP_PX = 8;
5
5
  const DOCK_SETTINGS_GAP_PX = 12;
@@ -1,26 +1,8 @@
1
+ export { applyHudPosition, isHudDragged, readHudPosition, relayoutHudPosition, clampHudPosition, hudLayoutBox, resetHudDockPosition, } from './presenter-hud-position.js';
1
2
  interface HudDragCallbacks {
2
3
  onDragMove?: () => void;
3
4
  onDragEnd?: (moved: boolean) => void;
4
5
  }
5
- export declare function isHudDragged(hud: HTMLElement): boolean;
6
- export declare function clampHudPosition(left: number, top: number, width: number, height: number, viewportWidth: number, viewportHeight: number, margin?: number): {
7
- left: number;
8
- top: number;
9
- };
10
- export declare function applyHudPosition(hud: HTMLElement, left: number, top: number): void;
11
- export declare function resetHudDockPosition(hud: HTMLElement): void;
12
- export declare function hudLayoutBox(hud: HTMLElement): {
13
- left: number;
14
- top: number;
15
- width: number;
16
- height: number;
17
- };
18
- export declare function readHudPosition(hud: HTMLElement): {
19
- left: number;
20
- top: number;
21
- };
22
- export declare function relayoutHudPosition(hud: HTMLElement): void;
23
6
  export declare function installHudPositionGuards(hud: HTMLElement, overlay: HTMLElement): () => void;
24
7
  export declare function installHudDrag(hud: HTMLElement, head: HTMLElement, callbacks?: HudDragCallbacks): () => void;
25
8
  export declare function installHudDragHandles(hud: HTMLElement, handles: HTMLElement[], callbacks?: HudDragCallbacks): () => void;
26
- export {};
@@ -1,55 +1,8 @@
1
- import { HUD_DOCK_MARGIN_PX, HUD_DRAG_IGNORE_SEL, HUD_DRAG_THRESHOLD_PX, HUD_DRAGGED_ATTR, HUD_POS_X_VAR, HUD_POS_Y_VAR, MIN_ATTR, CHAT_ATTR, SETTINGS_ATTR, } from './presenter-config.js';
1
+ import { HUD_DRAG_IGNORE_SEL, HUD_DRAG_THRESHOLD_PX, MIN_ATTR, CHAT_ATTR, SETTINGS_ATTR, } from './presenter-config.js';
2
2
  import { scheduleSyncDockLayout } from './presenter-dock-layout.js';
3
+ import { applyHudPosition, clampHudPosition, hudLayoutBox, relayoutHudPosition, } from './presenter-hud-position.js';
4
+ export { applyHudPosition, isHudDragged, readHudPosition, relayoutHudPosition, clampHudPosition, hudLayoutBox, resetHudDockPosition, } from './presenter-hud-position.js';
3
5
  const DRAG_HANDLE_DRAGGING_CLASS = 'reticle-drag-handle--dragging';
4
- export function isHudDragged(hud) {
5
- return '1' === hud.getAttribute(HUD_DRAGGED_ATTR);
6
- }
7
- export function clampHudPosition(left, top, width, height, viewportWidth, viewportHeight, margin = HUD_DOCK_MARGIN_PX) {
8
- const minLeft = margin;
9
- const minTop = margin;
10
- const maxLeft = Math.max(minLeft, viewportWidth - width - margin);
11
- const maxTop = Math.max(minTop, viewportHeight - height - margin);
12
- return {
13
- left: Math.max(minLeft, Math.min(left, maxLeft)),
14
- top: Math.max(minTop, Math.min(top, maxTop)),
15
- };
16
- }
17
- export function applyHudPosition(hud, left, top) {
18
- hud.setAttribute(HUD_DRAGGED_ATTR, '1');
19
- hud.style.setProperty(HUD_POS_X_VAR, `${String(left)}px`);
20
- hud.style.setProperty(HUD_POS_Y_VAR, `${String(top)}px`);
21
- }
22
- export function resetHudDockPosition(hud) {
23
- hud.removeAttribute(HUD_DRAGGED_ATTR);
24
- hud.style.removeProperty(HUD_POS_X_VAR);
25
- hud.style.removeProperty(HUD_POS_Y_VAR);
26
- }
27
- export function hudLayoutBox(hud) {
28
- const rect = hud.getBoundingClientRect();
29
- return {
30
- left: rect.left,
31
- top: rect.top,
32
- width: rect.width,
33
- height: rect.height,
34
- };
35
- }
36
- export function readHudPosition(hud) {
37
- const x = hud.style.getPropertyValue(HUD_POS_X_VAR);
38
- const y = hud.style.getPropertyValue(HUD_POS_Y_VAR);
39
- if (x !== '' && y !== '') {
40
- return { left: Number.parseFloat(x), top: Number.parseFloat(y) };
41
- }
42
- const rect = hud.getBoundingClientRect();
43
- return { left: rect.left, top: rect.top };
44
- }
45
- export function relayoutHudPosition(hud) {
46
- if (!isHudDragged(hud))
47
- return;
48
- const { left, top } = readHudPosition(hud);
49
- const { width, height } = hudLayoutBox(hud);
50
- const next = clampHudPosition(left, top, width, height, window.innerWidth, window.innerHeight);
51
- applyHudPosition(hud, next.left, next.top);
52
- }
53
6
  export function installHudPositionGuards(hud, overlay) {
54
7
  const scheduleRelayout = () => {
55
8
  scheduleSyncDockLayout(hud, overlay);
@@ -1,5 +1,5 @@
1
1
  import { PresenterMode } from '@reticlehq/core';
2
- import { nativeSetTimeout, nativeClearTimeout } from '../timers/native-timers.js';
2
+ import { nativeSetTimeout, nativeClearTimeout } from '../timers/native/native-timers.js';
3
3
  import { GlowPhase, BorderMode, DATA_BUSY, BUSY_ON, BUSY_OFF, GLOW_ON, GLOW_OFF, DATA_ON, } from './presenter-config.js';
4
4
  export class GlowController {
5
5
  #phase = GlowPhase.IDLE;
@@ -0,0 +1,18 @@
1
+ export declare function isHudDragged(hud: HTMLElement): boolean;
2
+ export declare function clampHudPosition(left: number, top: number, width: number, height: number, viewportWidth: number, viewportHeight: number, margin?: number): {
3
+ left: number;
4
+ top: number;
5
+ };
6
+ export declare function applyHudPosition(hud: HTMLElement, left: number, top: number): void;
7
+ export declare function resetHudDockPosition(hud: HTMLElement): void;
8
+ export declare function hudLayoutBox(hud: HTMLElement): {
9
+ left: number;
10
+ top: number;
11
+ width: number;
12
+ height: number;
13
+ };
14
+ export declare function readHudPosition(hud: HTMLElement): {
15
+ left: number;
16
+ top: number;
17
+ };
18
+ export declare function relayoutHudPosition(hud: HTMLElement): void;
@@ -0,0 +1,50 @@
1
+ import { HUD_DOCK_MARGIN_PX, HUD_DRAGGED_ATTR, HUD_POS_X_VAR, HUD_POS_Y_VAR, } from './presenter-config.js';
2
+ export function isHudDragged(hud) {
3
+ return '1' === hud.getAttribute(HUD_DRAGGED_ATTR);
4
+ }
5
+ export function clampHudPosition(left, top, width, height, viewportWidth, viewportHeight, margin = HUD_DOCK_MARGIN_PX) {
6
+ const minLeft = margin;
7
+ const minTop = margin;
8
+ const maxLeft = Math.max(minLeft, viewportWidth - width - margin);
9
+ const maxTop = Math.max(minTop, viewportHeight - height - margin);
10
+ return {
11
+ left: Math.max(minLeft, Math.min(left, maxLeft)),
12
+ top: Math.max(minTop, Math.min(top, maxTop)),
13
+ };
14
+ }
15
+ export function applyHudPosition(hud, left, top) {
16
+ hud.setAttribute(HUD_DRAGGED_ATTR, '1');
17
+ hud.style.setProperty(HUD_POS_X_VAR, `${String(left)}px`);
18
+ hud.style.setProperty(HUD_POS_Y_VAR, `${String(top)}px`);
19
+ }
20
+ export function resetHudDockPosition(hud) {
21
+ hud.removeAttribute(HUD_DRAGGED_ATTR);
22
+ hud.style.removeProperty(HUD_POS_X_VAR);
23
+ hud.style.removeProperty(HUD_POS_Y_VAR);
24
+ }
25
+ export function hudLayoutBox(hud) {
26
+ const rect = hud.getBoundingClientRect();
27
+ return {
28
+ left: rect.left,
29
+ top: rect.top,
30
+ width: rect.width,
31
+ height: rect.height,
32
+ };
33
+ }
34
+ export function readHudPosition(hud) {
35
+ const x = hud.style.getPropertyValue(HUD_POS_X_VAR);
36
+ const y = hud.style.getPropertyValue(HUD_POS_Y_VAR);
37
+ if (x !== '' && y !== '') {
38
+ return { left: Number.parseFloat(x), top: Number.parseFloat(y) };
39
+ }
40
+ const rect = hud.getBoundingClientRect();
41
+ return { left: rect.left, top: rect.top };
42
+ }
43
+ export function relayoutHudPosition(hud) {
44
+ if (!isHudDragged(hud))
45
+ return;
46
+ const { left, top } = readHudPosition(hud);
47
+ const { width, height } = hudLayoutBox(hud);
48
+ const next = clampHudPosition(left, top, width, height, window.innerWidth, window.innerHeight);
49
+ applyHudPosition(hud, next.left, next.top);
50
+ }
@@ -1,4 +1,4 @@
1
- import { HUD_DROP_SHADOW } from './presenter-hud-chrome.js';
1
+ import { HUD_DROP_SHADOW } from './chrome/presenter-hud-chrome.js';
2
2
  import { DOCK_ALIGN_ATTR, DOCK_ATTR, REPORT_ATTR, REPORT_PANEL_ATTR } from './presenter-config.js';
3
3
  const OVERLAY = 'data-reticle-overlay';
4
4
  export const REPORT_CSS = `
@@ -123,4 +123,19 @@ export const REPORT_CSS = `
123
123
  [${REPORT_PANEL_ATTR}] .reticle-report-links a{
124
124
  color:var(--reticle-faint);font-size:10.5px;text-decoration:none;}
125
125
  [${REPORT_PANEL_ATTR}] .reticle-report-links a:hover{color:var(--reticle-fg);text-decoration:underline;}
126
+
127
+ /* Who this record belongs to, and the push-now control. Tokens only — no literal colours. */
128
+ [${REPORT_PANEL_ATTR}] .reticle-report-identity{
129
+ display:flex;align-items:center;justify-content:flex-end;gap:6px;padding:10px 14px 0;min-height:0;}
130
+ [${REPORT_PANEL_ATTR}] .reticle-report-identity:empty{display:none;}
131
+ [${REPORT_PANEL_ATTR}] .reticle-sync-now{
132
+ display:inline-flex;align-items:center;justify-content:center;
133
+ border:none;background:transparent;color:var(--reticle-faint);cursor:pointer;line-height:0;padding:2px;}
134
+ [${REPORT_PANEL_ATTR}] .reticle-sync-now:hover{color:var(--reticle-fg);}
135
+ /* The one moment this control has an opinion: it says it is going, not that it has arrived. */
136
+ [${REPORT_PANEL_ATTR}] .reticle-sync-now[data-syncing="1"]{
137
+ color:var(--reticle-c-active);animation:reticle-sync-spin 900ms linear infinite;}
138
+ @keyframes reticle-sync-spin{to{transform:rotate(360deg);}}
139
+ @media (prefers-reduced-motion:reduce){
140
+ [${REPORT_PANEL_ATTR}] .reticle-sync-now[data-syncing="1"]{animation:none;}}
126
141
  `;
@@ -1,8 +1,9 @@
1
1
  import { type AccountState, type ImpactScope, type ImpactSnapshot } from '@reticlehq/core';
2
2
  export declare function reportPanelHtml(): string;
3
- export declare function reportBodyHtml(scope: ImpactScope, dashboardUrl?: string, account?: AccountState): string;
3
+ export declare function reportBodyHtml(scope: ImpactScope, dashboardUrl?: string, account?: AccountState, projectName?: string): string;
4
4
  interface ReportHost {
5
5
  onBeforeOpen?: () => void;
6
+ onSyncNow?: () => void;
6
7
  }
7
8
  export declare class PresenterReport {
8
9
  #private;
@@ -1,8 +1,10 @@
1
1
  import { IMPACT_DEFECT_LIMIT, } from '@reticlehq/core';
2
- import { PresenterIcon, PRESENTER_ICON_SIZE, hiIconHtml } from './presenter-icons.js';
3
- import { HUD_SURFACE_CLASS } from './presenter-hud-chrome.js';
2
+ import { PresenterIcon, PRESENTER_ICON_SIZE, hiIconHtml } from './icons/presenter-icons.js';
3
+ import { HUD_SURFACE_CLASS } from './chrome/presenter-hud-chrome.js';
4
+ import { esc, isSafeDashboardUrl } from './chrome/presenter-safe-html.js';
5
+ import { ACCOUNT_SIGNIN_ATTR, ACCOUNT_TEXT, SYNC_BTN_ATTR, accountControlHtml, syncButtonHtml, } from './presenter-account.js';
4
6
  import { REPORT_PANEL_ATTR, REPORT_ATTR, REPORT_CLOSE_ATTR } from './presenter-config.js';
5
- import { REPORT_LINKS, REPORT_TEXT, buildLinkedInShareUrl, buildReferralText, buildShareText, buildXShareUrl, compactDuration, compactNumber, } from './presenter-report-copy.js';
7
+ import { REPORT_LINKS, REPORT_TEXT, buildLinkedInShareUrl, buildReferralText, buildShareText, buildXShareUrl, compactDuration, compactNumber, } from './chrome/presenter-report-copy.js';
6
8
  const SCOPE_ATTR = 'data-reticle-report-scope';
7
9
  const SHARE_X_ATTR = 'data-reticle-share-x';
8
10
  const SHARE_IN_ATTR = 'data-reticle-share-in';
@@ -55,22 +57,6 @@ function chart(scope) {
55
57
  .join('');
56
58
  return `<div class="reticle-report-chart-wrap"><span class="reticle-report-section">${REPORT_TEXT.CHART}</span><div class="reticle-report-chart">${bars}</div></div>`;
57
59
  }
58
- function esc(value) {
59
- return value
60
- .replace(/&/g, '&amp;')
61
- .replace(/</g, '&lt;')
62
- .replace(/>/g, '&gt;')
63
- .replace(/"/g, '&quot;');
64
- }
65
- function isSafeDashboardUrl(raw) {
66
- try {
67
- const scheme = new URL(raw).protocol;
68
- return 'https:' === scheme || 'http:' === scheme;
69
- }
70
- catch {
71
- return false;
72
- }
73
- }
74
60
  function defects(scope, dashboardUrl) {
75
61
  const list = (scope.defects ?? []).slice(0, IMPACT_DEFECT_LIMIT);
76
62
  if (0 === list.length)
@@ -95,7 +81,7 @@ function defects(scope, dashboardUrl) {
95
81
  : `<a class="reticle-report-defects-more" href="${esc(dashboardUrl)}" target="_blank" rel="noreferrer noopener">${REPORT_TEXT.DEFECTS_MORE}${scope.counts.failed > list.length ? ` (${String(scope.counts.failed)})` : ''}</a>`;
96
82
  return `<div class="reticle-report-defects-wrap"><span class="reticle-report-section">${REPORT_TEXT.DEFECTS}</span><ul class="reticle-report-defects">${rows}</ul>${more}</div>`;
97
83
  }
98
- export function reportBodyHtml(scope, dashboardUrl, account) {
84
+ export function reportBodyHtml(scope, dashboardUrl, account, projectName) {
99
85
  const c = scope.counts;
100
86
  if (0 === c.calls)
101
87
  return `<p class="reticle-report-empty">${REPORT_TEXT.EMPTY}</p>`;
@@ -118,7 +104,8 @@ export function reportBodyHtml(scope, dashboardUrl, account) {
118
104
  card(compactNumber(scope.savings.tokens.value), REPORT_TEXT.SAVED_TOKENS, scope.savings.tokens.basis),
119
105
  card(compactDuration(scope.savings.minutes.value * 60_000), REPORT_TEXT.SAVED_MINUTES, scope.savings.minutes.basis),
120
106
  ].join('');
121
- return `${streak}${hero}${verdicts}<div class="reticle-report-grid">${cards}</div>${defects(scope, dashboardUrl)}${chart(scope)}${localOnly(scope, dashboardUrl, account)}`;
107
+ const identity = `<div class="reticle-report-identity">${accountControlHtml(account, { dashboardUrl, projectName, verdicts: c.verdicts, defects: c.failed })}${syncButtonHtml(dashboardUrl)}</div>`;
108
+ return `${identity}${streak}${hero}${verdicts}<div class="reticle-report-grid">${cards}</div>${defects(scope, dashboardUrl)}${chart(scope)}${localOnly(scope, dashboardUrl, account)}`;
122
109
  }
123
110
  function localOnly(scope, dashboardUrl, account) {
124
111
  if (dashboardUrl !== undefined)
@@ -132,6 +119,8 @@ function localOnly(scope, dashboardUrl, account) {
132
119
  : [REPORT_TEXT.LOCAL_ONLY, REPORT_TEXT.LOCAL_ONLY_ACTION, REPORT_TEXT.LOCAL_ONLY_TAIL];
133
120
  return `<p class="reticle-report-local-only">${lead} <code>${action}</code> ${tail}</p>`;
134
121
  }
122
+ const SYNC_SPIN_MS = 1_200;
123
+ const SYNCING_ATTR = 'data-syncing';
135
124
  export class PresenterReport {
136
125
  #panel;
137
126
  #body;
@@ -175,6 +164,24 @@ export class PresenterReport {
175
164
  e.stopPropagation();
176
165
  void this.#copy(buildReferralText(), e.currentTarget);
177
166
  });
167
+ root.addEventListener('click', (e) => {
168
+ const target = e.target;
169
+ if (!(target instanceof Element))
170
+ return;
171
+ const signin = target.closest(`[${ACCOUNT_SIGNIN_ATTR}]`);
172
+ if (signin !== null) {
173
+ e.stopPropagation();
174
+ void this.#copy(ACCOUNT_TEXT.SIGNIN_COMMAND, signin);
175
+ return;
176
+ }
177
+ const sync = target.closest(`[${SYNC_BTN_ATTR}]`);
178
+ if (sync !== null) {
179
+ e.stopPropagation();
180
+ this.#host.onSyncNow?.();
181
+ sync.setAttribute(SYNCING_ATTR, '1');
182
+ setTimeout(() => sync.removeAttribute(SYNCING_ATTR), SYNC_SPIN_MS);
183
+ }
184
+ });
178
185
  }
179
186
  setSnapshot(snapshot) {
180
187
  this.#snapshot = snapshot;
@@ -229,7 +236,7 @@ export class PresenterReport {
229
236
  this.#body.innerHTML =
230
237
  scope === undefined
231
238
  ? `<p class="reticle-report-empty">${REPORT_TEXT.EMPTY}</p>`
232
- : reportBodyHtml(scope, this.#snapshot?.dashboardUrl, this.#snapshot?.account);
239
+ : reportBodyHtml(scope, this.#snapshot?.dashboardUrl, this.#snapshot?.account, this.#snapshot?.projectName);
233
240
  }
234
241
  #openShare(url) {
235
242
  window.open(url, '_blank', 'noopener,noreferrer');
@@ -1,5 +1,5 @@
1
1
  import { SessionState } from '@reticlehq/core';
2
- import { type LogKind, type LogResult } from './presenter-log.js';
2
+ import { type LogKind, type LogResult } from './chrome/presenter-log.js';
3
3
  import { type Capabilities } from '../registry/capabilities.js';
4
4
  export interface PresenterRunState {
5
5
  session: string;
@@ -1,5 +1,5 @@
1
1
  import { SessionState } from '@reticlehq/core';
2
- import { LOG_KIND, LOG_RESULT } from './presenter-log.js';
2
+ import { LOG_KIND, LOG_RESULT } from './chrome/presenter-log.js';
3
3
  import { getCapabilities } from '../registry/capabilities.js';
4
4
  export function buildRunState(input) {
5
5
  const start = input.startMs ?? input.now;
@@ -1,5 +1,5 @@
1
1
  import { SETTINGS_PANEL_ATTR, SETTINGS_ATTR, DOCK_ATTR, DOCK_ALIGN_ATTR, SETTINGS_PLACEMENT_ATTR, } from './presenter-config.js';
2
- import { HUD_DROP_SHADOW, HUD_SURFACE_CLASS, HUD_SURFACE_PAINT } from './presenter-hud-chrome.js';
2
+ import { HUD_DROP_SHADOW, HUD_SURFACE_CLASS, HUD_SURFACE_PAINT, } from './chrome/presenter-hud-chrome.js';
3
3
  export const SETTINGS_CSS = `
4
4
  [${'data-reticle-overlay'}][${SETTINGS_ATTR}="1"] [${'data-reticle-chat-panel'}]{
5
5
  display:none !important;visibility:hidden !important;pointer-events:none !important;}
@@ -149,4 +149,5 @@ export const SETTINGS_CSS = `
149
149
  display:none;pointer-events:none;}
150
150
  [${'data-reticle-overlay'}][data-reticle-hidden="1"] [${DOCK_ATTR}],
151
151
  [${'data-reticle-overlay'}][data-reticle-hidden="1"] [${SETTINGS_PANEL_ATTR}]{display:none !important;}
152
+
152
153
  `;
@@ -1,4 +1,6 @@
1
1
  import { SETTINGS_CSS } from './presenter-settings-styles.js';
2
+ import type { AccountState } from '@reticlehq/core';
3
+ import { type AccountDetails } from './presenter-account.js';
2
4
  export { SETTINGS_CSS };
3
5
  export declare const OutputDetail: {
4
6
  readonly MINIMAL: "minimal";
@@ -38,6 +40,8 @@ export interface PresenterSettings {
38
40
  }
39
41
  export declare function getPresenterSettings(): PresenterSettings;
40
42
  export declare function loadPresenterSettings(): PresenterSettings;
43
+ export declare const SETTINGS_ACCOUNT_ATTR = "data-reticle-settings-account";
44
+ export declare function paintSettingsAccount(root: HTMLElement, account: AccountState | undefined, dashboardUrl: string | undefined, details?: AccountDetails): void;
41
45
  export declare function settingsPanelHtml(): string;
42
46
  export interface SettingsHost {
43
47
  onHideUntilRestart?: () => void;
@@ -1,9 +1,10 @@
1
1
  import { SETTINGS_ATTR, SETTINGS_BTN_ATTR, SETTINGS_PANEL_ATTR, SETTINGS_CLOSE_ATTR, SETTING_KEY_ATTR, SETTINGS_STORAGE_KEY, AMBIENT_GLOW_ATTR, BLOCK_ATTR, HIDDEN_UNTIL_RESTART_ATTR, LOG_TIMESTAMPS_ATTR, REDUCE_MOTION_ATTR, DOCK_ATTR, MCP_DOCS_URL, } from './presenter-config.js';
2
- import { PresenterIcon, PRESENTER_ICON_SIZE, setHiIcon, hiIconHtml } from './presenter-icons.js';
3
- import { HUD_SURFACE_CLASS } from './presenter-hud-chrome.js';
2
+ import { PresenterIcon, PRESENTER_ICON_SIZE, setHiIcon, hiIconHtml, } from './icons/presenter-icons.js';
3
+ 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 { accountControlHtml } from './presenter-account.js';
7
8
  export { SETTINGS_CSS };
8
9
  export const OutputDetail = {
9
10
  MINIMAL: 'minimal',
@@ -146,6 +147,18 @@ function settingsCheckRow(key, label, checked) {
146
147
  <span class="reticle-settings-check-label">${label}</span>
147
148
  </label>`;
148
149
  }
150
+ export const SETTINGS_ACCOUNT_ATTR = 'data-reticle-settings-account';
151
+ const SETTINGS_ACCOUNT_ROW_ATTR = 'data-reticle-settings-account-row';
152
+ const ACCOUNT_HELP = 'Whether this machine is signed in to a Reticle workspace. Signing in happens in your terminal.';
153
+ export function paintSettingsAccount(root, account, dashboardUrl, details = {}) {
154
+ const row = root.querySelector(`[${SETTINGS_ACCOUNT_ROW_ATTR}]`);
155
+ const slot = root.querySelector(`[${SETTINGS_ACCOUNT_ATTR}]`);
156
+ if (!(row instanceof HTMLElement) || !(slot instanceof HTMLElement))
157
+ return;
158
+ const html = accountControlHtml(account, { ...details, dashboardUrl }, true);
159
+ row.hidden = 0 === html.length;
160
+ slot.innerHTML = html;
161
+ }
149
162
  export function settingsPanelHtml() {
150
163
  const close = hiIconHtml(PresenterIcon.REMOVE, PRESENTER_ICON_SIZE.MIN);
151
164
  const caret = hiIconHtml(PresenterIcon.CARET_RIGHT, PRESENTER_ICON_SIZE.HELP);
@@ -179,6 +192,11 @@ export function settingsPanelHtml() {
179
192
  ${settingsCheckRow('clearOnCopy', 'Clear on copy/send', false)}
180
193
  ${settingsToggleRow('hideUntilRestart', 'Hide Until Restart', hideHelp)}
181
194
  ${settingsToggleRow('reduceMotion', 'Reduce motion', motionHelp)}
195
+ <div class="reticle-settings-section">Account</div>
196
+ <div class="reticle-settings-row" ${SETTINGS_ACCOUNT_ROW_ATTR} hidden>
197
+ ${settingsLabel('Workspace', ACCOUNT_HELP)}
198
+ <span ${SETTINGS_ACCOUNT_ATTR}></span>
199
+ </div>
182
200
  <div class="reticle-settings-section">Status theme</div>
183
201
  ${settingsToggleRow('ambientGlow', 'Page glow', glowHelp)}
184
202
  <div class="reticle-settings-themes" data-reticle-settings-themes></div>
@@ -1,7 +1,7 @@
1
- import { LOG_TIME_ATTR } from './presenter-log.js';
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 './presenter-hud-chrome.js';
4
+ import { HUD_DROP_SHADOW, 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';
@@ -86,8 +86,8 @@ export const SHELL_CSS = `
86
86
  /**
87
87
  * Minimising the chat leaves a CAPSULE, not a hole: the same glass, the same state dot, the last
88
88
  * thing the agent did - sitting directly above the toolbar capsule and reopening the panel when
89
- * clicked. Minimise used to leave the toolbar alone above an empty gap, so a minimised session
90
- * looked identical to no session at all.
89
+ * clicked. Leaving the toolbar alone above an empty gap would make a minimised session look
90
+ * identical to no session at all.
91
91
  */
92
92
  [${CHAT_PILL_ATTR}]{
93
93
  display:none;position:absolute;right:0;left:auto;bottom:calc(100% + 8px);z-index:5;
@@ -115,6 +115,13 @@ export const SHELL_CSS = `
115
115
  flex:none;display:flex;align-items:center;gap:8px;padding:11px 44px 9px 14px;
116
116
  border-bottom:1px solid rgba(255,255,255,.06);}
117
117
  [${CHAT_PANEL}] .reticle-chat-brand{display:inline-flex;align-items:center;gap:7px;color:var(--reticle-fg);}
118
+ /* The account capsule lives in the header, not the toolbar. The toolbar row is already 13px over its
119
+ own width, so a 20px avatar there pushed the close button outside the panel. The header has the
120
+ space, is visible under exactly the same condition, and is where an account avatar belongs anyway.
121
+ margin-left:auto right-aligns it; the head reserves 44px of right padding for the caret above it,
122
+ so the two never overlap. The :empty rule keeps the gap closed when no account state was sent. */
123
+ [${CHAT_PANEL}] .reticle-head-account{margin-left:auto;display:inline-flex;align-items:center;}
124
+ [${CHAT_PANEL}] .reticle-head-account:empty{display:none;}
118
125
  [${CHAT_PANEL}] .reticle-chat-brand .reticle-mark{height:14px;width:auto;}
119
126
  [${CHAT_PANEL}] .reticle-chat-brandname{font-size:12.5px;font-weight:600;letter-spacing:.01em;}
120
127
  /* No state word here: the activity strip immediately below already says "idle · 21s", and the
@@ -234,6 +241,7 @@ export const SHELL_CSS = `
234
241
  padding:2px;border-radius:999px;background:rgba(0,0,0,.2);}
235
242
  [${HUD}] .reticle-toolbar-chrome{display:inline-flex;align-items:center;gap:2px;flex:none;}
236
243
  [${HUD}] .reticle-tb-sep{width:1px;height:14px;background:rgba(255,255,255,.12);margin:0 4px;flex:none;align-self:center;}
244
+
237
245
  [${HUD}] .reticle-tb-wrap{position:relative;display:flex;align-items:center;justify-content:center;overflow:visible;}
238
246
  [${HUD}] .reticle-tb-btn{
239
247
  flex:none;display:inline-flex;align-items:center;justify-content:center;
@@ -259,10 +267,9 @@ export const SHELL_CSS = `
259
267
  [${HUD}] .reticle-hi-toggle .reticle-hi-icon--solid svg{
260
268
  transform:scale(1.12);transform-origin:center;}
261
269
  /*
262
- * Active toggles keep the OUTLINE icon. They used to swap to the solid heroicon, which is a filled
263
- * glyph next to 1.5px strokes everywhere else, so the pressed button read as a heavier typeface
264
- * rather than as a state. The state is already carried by accent colour and a background above,
265
- * which is enough and does not change the icon's weight.
270
+ * Active toggles keep the OUTLINE icon. The solid heroicon is a filled glyph next to 1.5px strokes
271
+ * everywhere else, so a pressed button reads as a heavier typeface rather than as a state. Accent
272
+ * colour and the background above already carry the state without changing the icon's weight.
266
273
  */
267
274
  [${HUD}] .reticle-tb-btn--toggle[data-active="1"] .reticle-hi-icon--outline{opacity:1;}
268
275
  [${HUD}] .reticle-tb-btn--toggle .reticle-hi-icon--solid{opacity:0;}
@@ -274,10 +281,10 @@ export const SHELL_CSS = `
274
281
  /**
275
282
  * The toolbar is a FIXED number of slots.
276
283
  *
277
- * Copy and Export appear when a session ends, and they used to be added to a bar that was already
278
- * full - eleven icons in a pill sized for nine, so the last one rendered outside the rounded box.
279
- * They take the two slots that Pause and End vacate: neither can do anything to a session that has
280
- * already ended, so the bar swaps two dead controls for two live ones and never changes width.
284
+ * Copy and Export appear when a session ends, and the bar is already full - eleven icons in a pill
285
+ * sized for nine renders the last one outside the rounded box. They take the two slots Pause and End
286
+ * vacate: neither can do anything to an ended session, so the bar swaps two dead controls for two
287
+ * live ones and never changes width.
281
288
  */
282
289
  [${HUD}] .reticle-tb-btn--export{display:none;}
283
290
  [${OVERLAY}][${STATE}="ended"] [${HUD}] .reticle-tb-btn--export{display:inline-flex;}
@@ -307,9 +314,8 @@ export const SHELL_CSS = `
307
314
  * The mode, ON the status row rather than under it.
308
315
  *
309
316
  * The action text beside it is flex:1, so this claims the right edge of the row and takes its space
310
- * from the elided action text. Nothing above or below moves: the panel used to grow and shrink by
311
- * the height of this pill on every single tool call, which is what made one status line look like a
312
- * second UI appearing and leaving.
317
+ * from the elided action text. Nothing above or below moves: growing and shrinking by the height of
318
+ * this pill on every tool call makes one status line look like a second UI appearing and leaving.
313
319
  */
314
320
  [${DOCK_ATTR}] .reticle-chip{display:none;flex:none;align-items:center;gap:4px;font-size:8px;font-weight:600;letter-spacing:.06em;
315
321
  height:16px;padding:0 7px;line-height:1;border-radius:999px;text-transform:uppercase;
@@ -1,4 +1,6 @@
1
1
  import { PresenterReport } from './presenter-report.js';
2
+ import type { AccountState } from '@reticlehq/core';
3
+ import { type AccountDetails } from './presenter-account.js';
2
4
  import { type SettingsHost } from './presenter-settings.js';
3
5
  interface HudShellCallbacks {
4
6
  onChatOpen?: () => void;
@@ -6,10 +8,12 @@ interface HudShellCallbacks {
6
8
  onExpand?: () => void;
7
9
  onAnnotateToggle?: (on: boolean) => void;
8
10
  onCollapse?: () => void;
11
+ onSyncNow?: () => void;
9
12
  settings?: SettingsHost;
10
13
  }
11
14
  export declare class HudShell {
12
15
  #private;
16
+ paintAccount(account: AccountState | undefined, dashboardUrl: string | undefined, details?: AccountDetails): void;
13
17
  constructor(callbacks?: HudShellCallbacks);
14
18
  static dockHtml(actStripHtml: string, bannerHtml: string, logAttr: string, flowsHtml: string, footHtml: string): string;
15
19
  get report(): PresenterReport;
@@ -1,11 +1,14 @@
1
1
  import { PresenterReport, reportPanelHtml } from './presenter-report.js';
2
+ import { paintToolbarAccount, TOOLBAR_ACCOUNT_ATTR } from './presenter-workspace.js';
3
+ import { mountAccountControl } from './presenter-account.js';
4
+ import { paintSettingsAccount } from './presenter-settings.js';
2
5
  import { CHAT_MIN_ATTR, CHAT_PILL_ATTR, REPORT_ATTR, REPORT_BTN_ATTR, ANNOTATE_BTN_ATTR, CHAT_ATTR, CHAT_TOGGLE_ATTR, CHAT_PANEL_ATTR, DOCK_ATTR, FAB_ATTR, MIN_ATTR, SETTINGS_ATTR, SETTINGS_BTN_ATTR, } from './presenter-config.js';
3
- import { BRAND_NAME, FAB_TOGGLE_HTML, MARK_SVG } from './presenter-brand.js';
4
- import { settleLogAtLatest } from './presenter-log.js';
6
+ import { BRAND_NAME, FAB_TOGGLE_HTML, MARK_SVG } from './chrome/presenter-brand.js';
7
+ import { settleLogAtLatest } from './chrome/presenter-log.js';
5
8
  import { installHudDragHandles, installHudPositionGuards } from './presenter-drag.js';
6
9
  import { scheduleSyncDockLayout } from './presenter-dock-layout.js';
7
- import { hiIconHtml, hiToggleIconHtml, PRESENTER_ICON_SIZE, PresenterIcon, } from './presenter-icons.js';
8
- import { HUD_SURFACE_CLASS, HUD_LOG_WELL_CLASS } from './presenter-hud-chrome.js';
10
+ import { hiIconHtml, hiToggleIconHtml, PRESENTER_ICON_SIZE, PresenterIcon, } from './icons/presenter-icons.js';
11
+ import { HUD_SURFACE_CLASS, HUD_LOG_WELL_CLASS } from './chrome/presenter-hud-chrome.js';
9
12
  import { CONTROLS_TOOLBAR_HTML } from './presenter-controls.js';
10
13
  import { PresenterSettingsPanel, settingsPanelHtml, } from './presenter-settings.js';
11
14
  const DRAG_HANDLE_CLASS = 'reticle-toolbar-drag';
@@ -37,8 +40,18 @@ export class HudShell {
37
40
  this.#settings.close();
38
41
  this.closeChat();
39
42
  },
43
+ onSyncNow: () => this.#callbacks.onSyncNow?.(),
40
44
  });
41
45
  #callbacks;
46
+ #pushedAccount;
47
+ #accountTeardown;
48
+ paintAccount(account, dashboardUrl, details = {}) {
49
+ this.#pushedAccount = { account, dashboardUrl, details };
50
+ if (this.#root === undefined)
51
+ return;
52
+ paintToolbarAccount(this.#root, account, dashboardUrl, details);
53
+ paintSettingsAccount(this.#root, account, dashboardUrl, details);
54
+ }
42
55
  constructor(callbacks = {}) {
43
56
  this.#callbacks = callbacks;
44
57
  this.#settings = new PresenterSettingsPanel({
@@ -59,6 +72,7 @@ export class HudShell {
59
72
  <div ${CHAT_PANEL_ATTR} class="reticle-chat-panel ${HUD_SURFACE_CLASS}" role="region" aria-label="Reticle session" aria-hidden="true">
60
73
  <div class="reticle-chat-head">
61
74
  <span class="reticle-chat-brand">${MARK_SVG}<span class="reticle-chat-brandname">${BRAND_NAME}</span></span>
75
+ <span ${TOOLBAR_ACCOUNT_ATTR} class="reticle-head-account"></span>
62
76
  </div>
63
77
  <button type="button" ${CHAT_MIN_ATTR} class="reticle-chat-min" title="${CHAT_MIN_LABEL}" aria-label="${CHAT_MIN_LABEL}">${hiIconHtml(PresenterIcon.CARET_DOWN, PRESENTER_ICON_SIZE.TOOLBAR)}</button>
64
78
  ${actStripHtml}
@@ -209,8 +223,15 @@ export class HudShell {
209
223
  }
210
224
  document.addEventListener('pointerdown', this.#onDocPointerDown, { signal });
211
225
  document.addEventListener('keydown', this.#onKeyDown, { signal });
226
+ this.#accountTeardown = mountAccountControl(root);
227
+ if (this.#pushedAccount !== undefined) {
228
+ const pushed = this.#pushedAccount;
229
+ this.paintAccount(pushed.account, pushed.dashboardUrl, pushed.details);
230
+ }
212
231
  }
213
232
  teardown() {
233
+ this.#accountTeardown?.();
234
+ this.#accountTeardown = undefined;
214
235
  this.#listeners?.abort();
215
236
  this.#listeners = undefined;
216
237
  this.#toggleSync?.disconnect();
@@ -1,2 +1,3 @@
1
- export declare const Z_OVERLAY = 2147483600;
1
+ import { Z_OVERLAY } from './chrome/layers.js';
2
+ export { Z_OVERLAY };
2
3
  export declare const PRESENTER_CSS: string;