@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,3 +1,4 @@
1
+ import { ReticleStorageKey } from '../storage-keys.js';
1
2
  export const BorderMode = { SESSION: 'session', BUSY: 'busy' };
2
3
  export const DEFAULT_BORDER_MODE = BorderMode.SESSION;
3
4
  export const DATA_BUSY = 'data-busy';
@@ -24,6 +25,7 @@ export const ACT_STRIP = {
24
25
  STOPPED_PREFIX: 'stopped · ',
25
26
  NOW: 'now',
26
27
  SINCE_LAST: ' since last action',
28
+ harnessTesting: (steps) => `Testing · Harness · ${String(steps)} step${1 === steps ? '' : 's'}`,
27
29
  };
28
30
  export const IDLE_END_MS = 300_000;
29
31
  export const IDLE_END_MIN_MS = 5_000;
@@ -60,8 +62,8 @@ export const SETTINGS_BTN_ATTR = 'data-reticle-settings-btn';
60
62
  export const SETTINGS_PANEL_ATTR = 'data-reticle-settings-panel';
61
63
  export const SETTINGS_CLOSE_ATTR = 'data-reticle-settings-close';
62
64
  export const SETTING_KEY_ATTR = 'data-reticle-setting';
63
- export const SETTINGS_STORAGE_KEY = 'reticle-presenter-settings';
64
- export const MINIMISED_STORAGE_KEY = 'reticle-presenter-minimised';
65
+ export const SETTINGS_STORAGE_KEY = ReticleStorageKey.PRESENTER_SETTINGS;
66
+ export const MINIMISED_STORAGE_KEY = ReticleStorageKey.PRESENTER_MINIMISED;
65
67
  export const AMBIENT_GLOW_ATTR = 'data-reticle-ambient-glow';
66
68
  export const LIVENESS_ATTR = 'data-reticle-live';
67
69
  export const BLOCK_ATTR = 'data-reticle-block';
@@ -74,6 +76,6 @@ export const HUD_POS_X_VAR = '--reticle-hud-x';
74
76
  export const HUD_POS_Y_VAR = '--reticle-hud-y';
75
77
  export const HUD_DRAG_THRESHOLD_PX = 4;
76
78
  export const HUD_DOCK_MARGIN_PX = 8;
77
- export const HUD_DRAG_IGNORE_SEL = '[data-reticle-pause], [data-reticle-annotate-btn], [data-reticle-markers-btn], [data-reticle-clear-marks], [data-reticle-copy-marks], [data-reticle-end], [data-reticle-min-btn], [data-reticle-settings-btn], [data-reticle-settings-panel], [data-reticle-report-btn], [data-reticle-report-panel], [data-reticle-chat-panel], [data-reticle-chat-toggle], [data-reticle-workspace-btn], [data-reticle-workspace-menu], [data-reticle-copy], [data-reticle-export], input, textarea, select, a, .reticle-head-ctl, [data-reticle-tally], .reticle-maxhint';
79
+ export const HUD_DRAG_IGNORE_SEL = '[data-reticle-pause], [data-reticle-annotate-btn], [data-reticle-markers-btn], [data-reticle-clear-marks], [data-reticle-copy-marks], [data-reticle-end], [data-reticle-min-btn], [data-reticle-settings-btn], [data-reticle-settings-panel], [data-reticle-report-btn], [data-reticle-report-panel], [data-reticle-chat-panel], [data-reticle-chat-toggle], [data-reticle-workspace-btn], [data-reticle-workspace-menu], [data-reticle-copy], [data-reticle-export], .reticle-toolbar-chrome button, .reticle-toolbar-actions button, input, textarea, select, a, .reticle-head-ctl, [data-reticle-tally], .reticle-maxhint';
78
80
  export const THROTTLED_ATTR = 'data-reticle-throttled';
79
81
  export const Z_HUD_TOOLTIP = 100;
@@ -1,11 +1,11 @@
1
1
  import { HumanControlKind, PresenterTone, SessionState } from '@reticlehq/core';
2
2
  export type { ControlIntent, ControlHandler } from './presenter-config.js';
3
3
  export declare const ENDED_FADE_MS = 4000;
4
+ export declare function flowTitle(name: string): string;
4
5
  export declare const CONTROLS_CSS: string;
5
- export declare const CONTROLS_TOOLBAR_HTML: string;
6
+ export declare const CONTROLS_STATUS_HTML: string;
6
7
  export declare const CONTROLS_BANNER_HTML = "<div data-reticle-banner class=\"reticle-banner\">Session ended</div>";
7
8
  export declare const CONTROLS_MARKS_HTML: string;
8
- export declare const CONTROLS_FLOWS_HTML = "<div data-reticle-flows class=\"reticle-flows\"><span class=\"reticle-flows-cap\">Replay a flow</span></div>";
9
9
  export declare const CONTROLS_FOOT_HTML: string;
10
10
  interface ControlPanelHost {
11
11
  emit: (kind: HumanControlKind, text?: string) => void;
@@ -21,6 +21,7 @@ export declare class ControlPanel {
21
21
  get state(): SessionState;
22
22
  mount(root: HTMLElement, glow: HTMLElement | undefined): void;
23
23
  teardown(): void;
24
+ setFlowProgress(args: Record<string, unknown>): void;
24
25
  setFlows(flows: unknown): void;
25
26
  refilterFlows(): void;
26
27
  setState(state: SessionState, text?: string, tone?: PresenterTone): void;
@@ -1,9 +1,11 @@
1
- import { HumanControlKind, PresenterTone, SessionState } from '@reticlehq/core';
1
+ import { readTestId, testIdSelector } from '../dom/addressing/testid-attr.js';
2
+ import { FlowProgressStatus, HumanControlKind, PresenterTone, SessionState, } from '@reticlehq/core';
2
3
  import { nativeSetTimeout, nativeClearTimeout } from '../timers/native/native-timers.js';
3
- import { CHAT_TOGGLE_ATTR, CLEAR_MARKS_ATTR, COPY_MARKS_ATTR, MARKERS_BTN_ATTR, MARKS_ROW_ATTR, } from './presenter-config.js';
4
- import { PresenterIcon, PRESENTER_ICON_SIZE, hiIcon, hiIconHtml, hiToggleIconHtml, } from './icons/presenter-icons.js';
4
+ import { COPY_MARKS_ATTR, MARKS_ROW_ATTR } from './presenter-config.js';
5
+ import { PresenterIcon, PRESENTER_ICON_SIZE, hiIcon, hiIconHtml } from './icons/presenter-icons.js';
5
6
  import { getPresenterSettings } from './presenter-settings.js';
6
7
  import { mountWorkspaceSelector, workspaceRowHtml } from './presenter-workspace.js';
8
+ import { CHAT_VIEWS_HTML } from './presenter-chat-views.js';
7
9
  const DATA_RETICLE_STATE = 'data-reticle-state';
8
10
  const DATA_RETICLE_TONE = 'data-reticle-tone';
9
11
  const DATA_ON = 'data-on';
@@ -18,25 +20,55 @@ const PAUSED_BADGE_TEXT = 'PAUSED';
18
20
  const ENDED_BANNER_TEXT = 'Session ended';
19
21
  const COPY_LABEL = 'Copy run';
20
22
  const EXPORT_LABEL = 'Export';
21
- const FLOWS_LABEL = 'Replay a flow';
23
+ const PROGRESS_LINGER_MS = 6000;
24
+ const PROGRESS_GLYPH = {
25
+ [FlowProgressStatus.PLAYING]: '⏵',
26
+ [FlowProgressStatus.PASSED]: '✓',
27
+ [FlowProgressStatus.FAILED]: '✗',
28
+ };
29
+ function parseFlowProgress(args) {
30
+ const { name, done, total, status } = args;
31
+ if ('string' !== typeof name || 'number' !== typeof done || 'number' !== typeof total)
32
+ return undefined;
33
+ if (!Object.values(FlowProgressStatus).includes(status))
34
+ return undefined;
35
+ return { name, done, total, status: status };
36
+ }
22
37
  const COPIED_TEXT = 'Copied ✓';
23
38
  const RUN_FILENAME = 'reticle-run.json';
24
39
  export const ENDED_FADE_MS = 4000;
40
+ const FLOW_TEXT = {
41
+ REPLAYABLE: 'Replay without an agent',
42
+ ELSEWHERE: 'Starts on another page',
43
+ EMPTY_TITLE: 'No saved flows yet',
44
+ EMPTY_HINT: 'Every journey your agent verifies is saved here, ready to replay in one click.',
45
+ };
46
+ export function flowTitle(name) {
47
+ const words = name.replace(/[-_]+/g, ' ').trim();
48
+ return 0 === words.length ? name : words.charAt(0).toUpperCase() + words.slice(1);
49
+ }
25
50
  export const CONTROLS_CSS = `
26
- [data-reticle-chat-panel] [data-reticle-foot]{flex:none;padding:8px 10px 10px;border-top:1px solid rgba(255,255,255,.07);
51
+ [data-reticle-chat-panel] [data-reticle-foot]{flex:none;padding:6px 10px 8px;border-top:1px solid rgba(255,255,255,.07);
27
52
  background:linear-gradient(180deg,rgba(0,0,0,.35) 0%,rgba(0,0,0,.55) 100%);pointer-events:auto;}
53
+ [data-reticle-chat-panel] .reticle-foot-workspace-row{display:flex;flex-wrap:wrap;align-items:center;min-width:0;}
28
54
  [data-reticle-chat-panel] .reticle-hud-log-well{margin:0 0 4px;}
29
55
  [data-reticle-chat-panel] .reticle-workspace-wrap{position:relative;align-self:flex-start;max-width:100%;}
56
+ [data-reticle-chat-panel] .reticle-workspace-wrap[hidden]{display:none;}
30
57
  [data-reticle-chat-panel] .reticle-workspace{
31
58
  display:inline-flex;align-items:center;gap:5px;max-width:100%;padding:3px 8px 3px 6px;border-radius:999px;cursor:pointer;
32
59
  border:1px solid rgba(255,255,255,.1);background:rgba(255,255,255,.04);color:rgba(255,255,255,.78);
33
60
  font:inherit;font-size:11px;font-weight:500;line-height:1.2;
34
61
  box-shadow:inset 0 1px 0 rgba(255,255,255,.05);transition:background .15s,border-color .15s,color .15s;}
62
+ [data-reticle-chat-panel] .reticle-workspace-k{color:var(--reticle-hud-text-faint);font-weight:400;}
35
63
  [data-reticle-chat-panel] .reticle-workspace:hover{background:rgba(255,255,255,.07);border-color:rgba(255,255,255,.14);color:#fff;}
36
64
  [data-reticle-chat-panel] .reticle-workspace[aria-expanded="true"]{background:rgba(255,255,255,.08);border-color:rgba(255,255,255,.16);color:#fff;}
37
65
  [data-reticle-chat-panel] .reticle-workspace-icon,
38
66
  [data-reticle-chat-panel] .reticle-workspace-caret{display:inline-flex;align-items:center;opacity:.72;flex:none;}
39
67
  [data-reticle-chat-panel] .reticle-workspace-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:min(200px,calc(100vw - 120px));}
68
+ @media (max-width:480px){
69
+ [data-reticle-chat-panel] .reticle-workspace-wrap{flex:none;}
70
+ [data-reticle-chat-panel] .reticle-workspace-name{max-width:88px;}
71
+ }
40
72
  [data-reticle-chat-panel] .reticle-workspace[aria-expanded="true"] .reticle-workspace-caret{transform:rotate(180deg);}
41
73
  [data-reticle-chat-panel] .reticle-workspace-caret{transition:transform .18s ease;}
42
74
  [data-reticle-chat-panel] .reticle-workspace-menu{
@@ -51,7 +83,7 @@ export const CONTROLS_CSS = `
51
83
  [data-reticle-chat-panel] .reticle-workspace-menu-head{
52
84
  display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px;}
53
85
  [data-reticle-chat-panel] .reticle-workspace-menu-title{
54
- color:rgba(255,255,255,.42);font-size:10px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;}
86
+ color:rgba(255,255,255,.42);font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;}
55
87
  [data-reticle-chat-panel] .reticle-workspace-copy{
56
88
  display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;padding:0;border:none;border-radius:6px;
57
89
  background:rgba(255,255,255,.06);color:rgba(255,255,255,.7);cursor:pointer;line-height:0;transition:background .12s,color .12s;}
@@ -65,9 +97,9 @@ export const CONTROLS_CSS = `
65
97
  [data-reticle-chat-panel] .reticle-workspace-menu-row{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;padding:4px 0;}
66
98
  [data-reticle-chat-panel] .reticle-workspace-menu-k{flex:none;color:rgba(255,255,255,.42);}
67
99
  [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;}
68
- [data-reticle-chat-panel] .reticle-banner{display:none;flex:none;align-items:center;gap:8px;padding:10px 14px;color:var(--reticle-fg);
69
- font-size:12px;font-weight:500;border-bottom:1px solid var(--reticle-line2);background:var(--reticle-surface);}
70
- [data-reticle-overlay][data-reticle-state="ended"] [data-reticle-chat-panel] .reticle-banner{display:block;}
100
+ [data-reticle-chat-panel] .reticle-banner{display:none;flex:none;align-items:center;gap:8px;min-height:38px;box-sizing:border-box;padding:8px 14px;
101
+ color:var(--reticle-hud-text);font-size:12px;font-weight:600;border-bottom:1px solid var(--reticle-hud-border);background:var(--reticle-hud-inset);line-height:1.35;}
102
+ [data-reticle-overlay][data-reticle-state="ended"] [data-reticle-chat-panel] .reticle-banner{display:flex;}
71
103
  [data-reticle-overlay][data-reticle-state="paused"] [data-reticle-glow][data-on="1"]{
72
104
  box-shadow:inset 0 0 0 2px rgba(255,255,255,.2);}
73
105
  [data-reticle-overlay][data-reticle-state="ended"] [data-reticle-glow][data-on="1"]{
@@ -87,13 +119,19 @@ export const CONTROLS_CSS = `
87
119
  [data-reticle-overlay][data-reticle-tone="ask"] [data-reticle-glow][data-on="1"],
88
120
  [data-reticle-overlay][data-reticle-tone="warn"] [data-reticle-glow][data-on="1"]{
89
121
  box-shadow:inset 0 0 0 2px rgba(255,255,255,.22);}
90
- [data-reticle-chat-panel] .reticle-flows{display:none;flex:none;flex-wrap:wrap;align-content:flex-start;gap:6px;
91
- padding:9px 12px;border-top:1px solid var(--reticle-line2);max-height:88px;overflow-y:auto;overscroll-behavior:contain;
92
- pointer-events:auto;touch-action:pan-y;}
93
- [data-reticle-chat-panel] .reticle-flows[data-has="1"]{display:flex;}
94
- [data-reticle-chat-panel] .reticle-flows::-webkit-scrollbar{width:9px;}
95
- [data-reticle-chat-panel] .reticle-flows::-webkit-scrollbar-thumb{background:rgba(255,255,255,.14);border-radius:9px;border:2px solid transparent;background-clip:content-box;}
96
- [data-reticle-chat-panel] .reticle-flows-cap{flex:0 0 100%;margin-bottom:1px;color:var(--reticle-faint);font-size:9.5px;letter-spacing:.09em;text-transform:uppercase;}
122
+ /* A replay the person started: the chip becomes a player, its bar filling step by step. */
123
+ [data-reticle-chat-panel] :is(.reticle-flow,.reticle-flow-row)[data-state]{position:relative;overflow:hidden;
124
+ border-color:color-mix(in srgb,var(--reticle-c-active) 55%,transparent);color:var(--reticle-fg);}
125
+ [data-reticle-chat-panel] :is(.reticle-flow,.reticle-flow-row)[data-state]::after{content:"";position:absolute;left:0;bottom:0;height:2px;
126
+ width:var(--reticle-flow-progress,4%);background:var(--reticle-c-active);transition:width .25s var(--reticle-hud-ease,ease);}
127
+ [data-reticle-chat-panel] :is(.reticle-flow,.reticle-flow-row)[data-state="playing"]::after{animation:reticle-flow-pulse 1.2s ease-in-out infinite;}
128
+ @keyframes reticle-flow-pulse{50%{opacity:.55}}
129
+ [data-reticle-chat-panel] :is(.reticle-flow,.reticle-flow-row)[data-state="passed"]::after{background:#4ade80;}
130
+ [data-reticle-chat-panel] :is(.reticle-flow,.reticle-flow-row)[data-state="failed"]::after{background:#f87171;}
131
+ [data-reticle-chat-panel] .reticle-all-flows{flex:1;min-height:0;overflow-y:auto;padding:8px 12px;display:flex;flex-direction:column;align-items:stretch;gap:5px;}
132
+ [data-reticle-chat-panel] .reticle-all-flows .reticle-flow{width:100%;height:auto;min-height:32px;text-align:left;justify-content:flex-start;}
133
+ [data-reticle-chat-panel] .reticle-all-flows .reticle-flow:disabled{opacity:.45;cursor:not-allowed;}
134
+ [data-reticle-chat-panel] .reticle-flows-empty{padding:25px 8px;color:var(--reticle-faint);font-size:11px;text-align:center;}
97
135
  [data-reticle-chat-panel] .reticle-flow{pointer-events:auto;cursor:pointer;display:inline-flex;align-items:center;gap:5px;height:24px;padding:0 10px;
98
136
  border-radius:7px;border:1px solid var(--reticle-line);background:rgba(255,255,255,.04);color:var(--reticle-muted);
99
137
  font-family:var(--reticle-font);font-size:11px;font-weight:500;transition:background .15s,color .15s,border-color .15s,transform .1s;}
@@ -122,35 +160,12 @@ function paintPauseBtn(btn, paused) {
122
160
  btn.classList.toggle('reticle-tb-btn--primary', paused);
123
161
  btn.setAttribute('data-active', paused ? '1' : '0');
124
162
  }
125
- const CHAT_LABEL = 'Agent chat';
126
- const MARKERS_LABEL = 'Hide markers';
127
- const CLEAR_MARKS_LABEL = 'Clear all';
128
163
  const COPY_MARKS_LABEL = 'Copy for your agent';
129
164
  const TB = PRESENTER_ICON_SIZE.TOOLBAR;
130
- function tbWrap(btn, tip, kbd) {
131
- const shortcut = kbd === undefined ? '' : `<span class="reticle-tb-kbd">${kbd}</span>`;
132
- return `<div class="reticle-tb-wrap">${btn}<span class="reticle-tb-tip">${tip}${shortcut}</span></div>`;
133
- }
134
- function pauseToolbarHtml() {
135
- const btn = `<button type="button" data-reticle-pause class="reticle-tb-btn" title="${CONTROL_LABEL.PAUSE}" aria-label="${CONTROL_LABEL.PAUSE}">${hiIconHtml(PresenterIcon.PAUSE, TB)}</button>`;
136
- const badge = `<span data-reticle-badge class="reticle-pause-badge">${PAUSED_BADGE_TEXT}</span>`;
137
- const tip = `<span class="reticle-tb-tip">${CONTROL_LABEL.PAUSE}</span>`;
138
- return `<div class="reticle-tb-wrap reticle-tb-wrap--pause">${btn}${badge}${tip}</div>`;
139
- }
140
- export const CONTROLS_TOOLBAR_HTML = [
141
- pauseToolbarHtml(),
142
- tbWrap(`<button type="button" ${CHAT_TOGGLE_ATTR} class="reticle-tb-btn reticle-tb-btn--toggle" title="${CHAT_LABEL}" aria-label="${CHAT_LABEL}" aria-pressed="false" data-active="0">${hiToggleIconHtml(PresenterIcon.MESSAGE, TB)}</button>`, CHAT_LABEL),
143
- tbWrap(`<button type="button" ${MARKERS_BTN_ATTR} class="reticle-tb-btn reticle-tb-btn--toggle" title="${MARKERS_LABEL}" aria-label="${MARKERS_LABEL}" aria-pressed="false" data-active="0" disabled>${hiToggleIconHtml(PresenterIcon.VIEW, TB)}</button>`, MARKERS_LABEL, 'H'),
144
- tbWrap(`<button type="button" data-reticle-end class="reticle-tb-btn" title="${CONTROL_LABEL.END}" aria-label="${CONTROL_LABEL.END}">${hiIconHtml(PresenterIcon.STOP, TB)}</button>`, CONTROL_LABEL.END),
145
- tbWrap(`<button type="button" ${CLEAR_MARKS_ATTR} class="reticle-tb-btn" title="${CLEAR_MARKS_LABEL}" aria-label="${CLEAR_MARKS_LABEL}" data-danger disabled>${hiIconHtml(PresenterIcon.TRASH, TB)}</button>`, CLEAR_MARKS_LABEL, 'X'),
146
- `<button type="button" data-reticle-copy class="reticle-tb-btn reticle-tb-btn--export" title="${COPY_LABEL}" aria-label="${COPY_LABEL}" hidden>${hiIconHtml(PresenterIcon.COPY, TB)}</button>`,
147
- `<button type="button" data-reticle-export class="reticle-tb-btn reticle-tb-btn--export" title="${EXPORT_LABEL}" aria-label="${EXPORT_LABEL}" hidden>${hiIconHtml(PresenterIcon.DOWNLOAD, TB)}</button>`,
148
- '<span data-reticle-export-msg class="reticle-export-msg" aria-live="polite"></span>',
149
- ].join('');
165
+ export const CONTROLS_STATUS_HTML = `<button type="button" data-reticle-pause class="reticle-status-btn" title="${CONTROL_LABEL.PAUSE}" aria-label="${CONTROL_LABEL.PAUSE}">${hiIconHtml(PresenterIcon.PAUSE, TB)}</button><span data-reticle-badge class="reticle-pause-badge">${PAUSED_BADGE_TEXT}</span><button type="button" data-reticle-end class="reticle-status-btn reticle-status-stop" title="${CONTROL_LABEL.END}" aria-label="${CONTROL_LABEL.END}">${hiIconHtml(PresenterIcon.STOP, TB)}</button><button type="button" data-reticle-copy class="reticle-status-btn" title="${COPY_LABEL}" aria-label="${COPY_LABEL}" hidden>${hiIconHtml(PresenterIcon.COPY, TB)}</button><button type="button" data-reticle-export class="reticle-status-btn" title="${EXPORT_LABEL}" aria-label="${EXPORT_LABEL}" hidden>${hiIconHtml(PresenterIcon.DOWNLOAD, TB)}</button><span data-reticle-export-msg class="reticle-export-msg" aria-live="polite"></span>`;
150
166
  export const CONTROLS_BANNER_HTML = `<div data-reticle-banner class="reticle-banner">${ENDED_BANNER_TEXT}</div>`;
151
167
  export const CONTROLS_MARKS_HTML = `<div ${MARKS_ROW_ATTR} class="reticle-marks-row" hidden><span data-reticle-marks-text class="reticle-marks-text"></span><button type="button" ${COPY_MARKS_ATTR} class="reticle-marks-copy" title="${COPY_MARKS_LABEL}">${hiIconHtml(PresenterIcon.COPY, PRESENTER_ICON_SIZE.HELP)}<span>${COPY_MARKS_LABEL}</span></button></div>`;
152
- export const CONTROLS_FLOWS_HTML = `<div data-reticle-flows class="reticle-flows"><span class="reticle-flows-cap">${FLOWS_LABEL}</span></div>`;
153
- export const CONTROLS_FOOT_HTML = `<div data-reticle-foot><div class="reticle-foot-stack">${workspaceRowHtml()}</div></div>`;
168
+ export const CONTROLS_FOOT_HTML = `<div data-reticle-foot><div class="reticle-foot-stack">${CHAT_VIEWS_HTML}<div class="reticle-foot-workspace-row">${workspaceRowHtml()}</div></div></div>`;
154
169
  function queryControlRefs(root) {
155
170
  return {
156
171
  pauseBtn: root.querySelector('[data-reticle-pause]') ?? undefined,
@@ -159,7 +174,7 @@ function queryControlRefs(root) {
159
174
  copyBtn: root.querySelector('[data-reticle-copy]') ?? undefined,
160
175
  exportBtn: root.querySelector('[data-reticle-export]') ?? undefined,
161
176
  exportMsg: root.querySelector('[data-reticle-export-msg]') ?? undefined,
162
- flows: root.querySelector('[data-reticle-flows]') ?? undefined,
177
+ allFlows: root.querySelector('[data-reticle-all-flows]') ?? undefined,
163
178
  };
164
179
  }
165
180
  export class ControlPanel {
@@ -170,13 +185,15 @@ export class ControlPanel {
170
185
  copyBtn: undefined,
171
186
  exportBtn: undefined,
172
187
  exportMsg: undefined,
173
- flows: undefined,
188
+ allFlows: undefined,
174
189
  };
175
190
  #state = SessionState.ACTIVE;
176
191
  #fadeTimer;
177
192
  #root;
178
193
  #glow;
179
194
  #flowItems = [];
195
+ #progress;
196
+ #progressTimer;
180
197
  #workspaceTeardown;
181
198
  #listeners;
182
199
  #host;
@@ -198,18 +215,23 @@ export class ControlPanel {
198
215
  }
199
216
  this.#refs.pauseBtn?.addEventListener('click', () => this.#onPauseToggle(), { signal });
200
217
  this.#refs.endBtn?.addEventListener('click', () => this.#onEnd(), { signal });
201
- this.#refs.flows?.addEventListener('click', (e) => {
218
+ const onReplay = (e) => {
202
219
  const target = e.target;
203
220
  if (!(target instanceof HTMLElement))
204
221
  return;
205
- const name = target.closest('[data-reticle-replay]')?.getAttribute('data-reticle-replay');
206
- if (name !== null && name !== undefined && name.length > 0) {
222
+ const button = target.closest('[data-reticle-replay]');
223
+ if (null === button || button.disabled)
224
+ return;
225
+ const name = button.getAttribute('data-reticle-replay');
226
+ if (name !== null && name.length > 0) {
207
227
  this.#host.emit(HumanControlKind.REPLAY, name);
208
228
  }
209
- }, { signal });
229
+ };
230
+ this.#refs.allFlows?.addEventListener('click', onReplay, { signal });
210
231
  this.#refs.copyBtn?.addEventListener('click', () => this.#onCopy(), { signal });
211
232
  this.#refs.exportBtn?.addEventListener('click', () => this.#onExport(), { signal });
212
233
  this.#workspaceTeardown = mountWorkspaceSelector(root);
234
+ this.#renderFlows();
213
235
  this.setState(SessionState.ACTIVE);
214
236
  }
215
237
  #runJson() {
@@ -264,6 +286,50 @@ export class ControlPanel {
264
286
  this.#host.emit(HumanControlKind.END);
265
287
  this.setState(SessionState.ENDED);
266
288
  }
289
+ setFlowProgress(args) {
290
+ const progress = parseFlowProgress(args);
291
+ if (progress === undefined)
292
+ return;
293
+ this.#progress = progress;
294
+ if (this.#progressTimer !== undefined)
295
+ nativeClearTimeout(this.#progressTimer);
296
+ this.#progressTimer =
297
+ FlowProgressStatus.PLAYING === progress.status
298
+ ? undefined
299
+ : nativeSetTimeout(() => {
300
+ this.#progress = undefined;
301
+ this.#paintProgress();
302
+ }, PROGRESS_LINGER_MS);
303
+ this.#paintProgress();
304
+ }
305
+ #paintProgress() {
306
+ const progress = this.#progress;
307
+ {
308
+ for (const btn of this.#refs.allFlows?.querySelectorAll('[data-reticle-replay]') ?? []) {
309
+ const name = btn.getAttribute('data-reticle-replay') ?? '';
310
+ const mine = progress !== undefined && progress.name === name;
311
+ if (!mine) {
312
+ btn.removeAttribute('data-state');
313
+ btn.style.removeProperty('--reticle-flow-progress');
314
+ if (btn.classList.contains('reticle-flow'))
315
+ btn.textContent = `▶ ${name}`;
316
+ continue;
317
+ }
318
+ const share = 0 < progress.total ? Math.round((100 * progress.done) / progress.total) : 4;
319
+ btn.setAttribute('data-state', progress.status);
320
+ btn.style.setProperty('--reticle-flow-progress', `${String(Math.max(4, share))}%`);
321
+ const glyph = PROGRESS_GLYPH[progress.status];
322
+ if (btn.classList.contains('reticle-flow'))
323
+ btn.textContent = `${glyph} ${name}`;
324
+ const play = btn.querySelector('.reticle-flow-play');
325
+ if (play !== null)
326
+ play.textContent = glyph;
327
+ const meta = btn.querySelector('.reticle-flow-meta');
328
+ if (meta !== null && 0 < progress.total)
329
+ meta.textContent = `Step ${String(progress.done)} of ${String(progress.total)}`;
330
+ }
331
+ }
332
+ }
267
333
  setFlows(flows) {
268
334
  const list = Array.isArray(flows) ? flows : [];
269
335
  this.#flowItems = list
@@ -276,7 +342,12 @@ export class ControlPanel {
276
342
  if (typeof name !== 'string' || 0 === name.length)
277
343
  return null;
278
344
  const start = rec['start'];
279
- return 'string' === typeof start && start.length > 0 ? { name, start } : { name };
345
+ const createdAt = rec['createdAt'];
346
+ return {
347
+ name,
348
+ ...('string' === typeof start && start.length > 0 ? { start } : {}),
349
+ ...('number' === typeof createdAt && Number.isFinite(createdAt) ? { createdAt } : {}),
350
+ };
280
351
  }
281
352
  return null;
282
353
  })
@@ -287,22 +358,53 @@ export class ControlPanel {
287
358
  this.#renderFlows();
288
359
  }
289
360
  #renderFlows() {
290
- const el = this.#refs.flows;
291
- if (el === undefined)
361
+ const all = this.#refs.allFlows;
362
+ if (all === undefined)
292
363
  return;
293
- const doc = el.ownerDocument;
294
- const testids = new Set(Array.from(doc.querySelectorAll('[data-testid]')).map((n) => n.getAttribute('data-testid')));
295
- const visible = this.#flowItems.filter((f) => f.start === undefined || testids.has(f.start));
296
- el.querySelectorAll('[data-reticle-replay]').forEach((b) => b.remove());
297
- for (const flow of visible) {
364
+ const doc = all.ownerDocument;
365
+ const testids = new Set(Array.from(doc.querySelectorAll(testIdSelector())).map((n) => readTestId(n)));
366
+ const playable = (f) => f.start === undefined || testids.has(f.start);
367
+ all.replaceChildren();
368
+ const addRow = (container, flow, enabled) => {
298
369
  const btn = doc.createElement('button');
299
370
  btn.type = 'button';
300
- btn.className = 'reticle-flow';
371
+ btn.className = 'reticle-flow-row';
301
372
  btn.setAttribute('data-reticle-replay', flow.name);
302
- btn.textContent = `▶ ${flow.name}`;
303
- el.appendChild(btn);
373
+ btn.title = flow.name;
374
+ btn.disabled = !enabled;
375
+ const play = doc.createElement('span');
376
+ play.className = 'reticle-flow-play';
377
+ play.setAttribute('aria-hidden', 'true');
378
+ play.textContent = '▶';
379
+ const text = doc.createElement('span');
380
+ text.className = 'reticle-flow-text';
381
+ const name = doc.createElement('span');
382
+ name.className = 'reticle-flow-name';
383
+ name.textContent = flowTitle(flow.name);
384
+ const meta = doc.createElement('span');
385
+ meta.className = 'reticle-flow-meta';
386
+ meta.textContent = enabled ? FLOW_TEXT.REPLAYABLE : FLOW_TEXT.ELSEWHERE;
387
+ text.append(name, meta);
388
+ btn.append(play, text);
389
+ container.appendChild(btn);
390
+ };
391
+ {
392
+ if (0 === this.#flowItems.length) {
393
+ const empty = doc.createElement('div');
394
+ empty.className = 'reticle-flows-empty';
395
+ const title = doc.createElement('strong');
396
+ title.textContent = FLOW_TEXT.EMPTY_TITLE;
397
+ const hint = doc.createElement('span');
398
+ hint.textContent = FLOW_TEXT.EMPTY_HINT;
399
+ empty.append(title, hint);
400
+ all.appendChild(empty);
401
+ }
402
+ else {
403
+ for (const flow of [...this.#flowItems].sort((a, b) => (b.createdAt ?? 0) - (a.createdAt ?? 0)))
404
+ addRow(all, flow, playable(flow));
405
+ }
304
406
  }
305
- el.setAttribute('data-has', visible.length > 0 ? '1' : '0');
407
+ this.#paintProgress();
306
408
  }
307
409
  setState(state, text, tone) {
308
410
  this.#state = state;
@@ -320,10 +422,10 @@ export class ControlPanel {
320
422
  const ended = state === SessionState.ENDED;
321
423
  if (refs.pauseBtn !== undefined) {
322
424
  paintPauseBtn(refs.pauseBtn, state === SessionState.PAUSED);
323
- refs.pauseBtn.disabled = ended;
425
+ refs.pauseBtn.hidden = ended;
324
426
  }
325
427
  if (refs.endBtn !== undefined)
326
- refs.endBtn.disabled = ended;
428
+ refs.endBtn.hidden = ended;
327
429
  if (refs.copyBtn !== undefined) {
328
430
  if (ended)
329
431
  refs.copyBtn.removeAttribute('hidden');
@@ -0,0 +1 @@
1
+ export declare const PRESENTER_DESIGN_TOKENS_CSS: string;
@@ -0,0 +1,34 @@
1
+ export const PRESENTER_DESIGN_TOKENS_CSS = `
2
+ [data-reticle-overlay]{
3
+ --reticle-hud-space-0:0px;
4
+ --reticle-hud-space-1:4px;
5
+ --reticle-hud-space-2:8px;
6
+ --reticle-hud-space-3:12px;
7
+ --reticle-hud-space-4:16px;
8
+ --reticle-hud-radius-sm:6px;
9
+ --reticle-hud-radius-md:10px;
10
+ --reticle-hud-radius-lg:14px;
11
+ --reticle-hud-radius-pill:9999px;
12
+ --reticle-hud-size-xs:11px;
13
+ --reticle-hud-size-sm:12px;
14
+ --reticle-hud-size-base:13px;
15
+ --reticle-hud-icon-size:18px;
16
+ --reticle-hud-control-size:36px;
17
+ --reticle-hud-control-gap:4px;
18
+ /* Compact: the HUD sits over the user's own app. 400×560 covered too much of it. */
19
+ --reticle-hud-dock-width:320px;
20
+ --reticle-hud-panel-height:440px;
21
+ --reticle-hud-ground:#0b0b0c;
22
+ --reticle-hud-surface:#151417;
23
+ --reticle-hud-inset:#232227;
24
+ --reticle-hud-hover:#1e1d22;
25
+ --reticle-hud-border:#29282d;
26
+ --reticle-hud-border-strong:#3a3941;
27
+ --reticle-hud-text:#f5f3ef;
28
+ --reticle-hud-text-muted:#a6a29a;
29
+ --reticle-hud-text-faint:#8b867d;
30
+ --reticle-hud-brand:#f0872e;
31
+ --reticle-hud-brand-soft:#2a1b0d;
32
+ --reticle-hud-ease:cubic-bezier(.4,0,.2,1);
33
+ }
34
+ `;
@@ -5,7 +5,7 @@ const DOCK_PANEL_GAP_PX = 8;
5
5
  const DOCK_SETTINGS_GAP_PX = 12;
6
6
  const HUD_BAR_HEIGHT_PX = 44;
7
7
  const CHAT_MIN_HEIGHT_PX = 160;
8
- const CHAT_DEFAULT_MAX_HEIGHT_PX = 420;
8
+ const CHAT_DEFAULT_MAX_HEIGHT_PX = 440;
9
9
  const SETTINGS_DEFAULT_MAX_HEIGHT_PX = 520;
10
10
  const SETTINGS_MIN_HEIGHT_PX = 200;
11
11
  function unionRects(rects) {
@@ -0,0 +1,12 @@
1
+ import type { AccountState } from '@reticlehq/core';
2
+ export declare const RAIL_ATTR = "data-reticle-rail";
3
+ export declare const RAIL_SIGNIN_ATTR = "data-reticle-rail-signin";
4
+ export declare const RAIL_PROMO_ATTR = "data-reticle-rail-promo";
5
+ export declare const RAIL_TEXT: {
6
+ readonly TITLE: "Sign in to try Reticle Harness";
7
+ readonly DETAIL: "Free Harness credits to start.";
8
+ readonly CTA: "Sign in";
9
+ };
10
+ export declare const RAIL_HTML: string;
11
+ export declare function paintRail(root: HTMLElement, account: AccountState | undefined): void;
12
+ export declare const FRAME_CSS: string;