@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,9 +1,14 @@
1
1
  import { HUD_KEYED_ATTRS, isHudControl } from '@reticlehq/core/hud';
2
- import { HudPanel, HudToggle, HudView } from '@reticlehq/core';
2
+ import { HudPanel, HudPromptSurface, HudToggle, HudView } from '@reticlehq/core';
3
3
  import { isReticleUi } from '../dom/dom-ignore.js';
4
4
  import { nativeSetTimeout } from '../timers/native/native-timers.js';
5
5
  import { CHAT_ATTR, MIN_ATTR, REPORT_ATTR, SETTINGS_ATTR } from './presenter-config.js';
6
+ import { CAROUSEL_SLIDE_ATTR } from './carousel/carousel.js';
7
+ import { ACCOUNT_MENU_ATTR, ACCOUNT_SIGNIN_ATTR, TEAM_SAVE_ATTR } from './presenter-account.js';
8
+ import { HARNESS_LINK_ATTR } from './presenter-harness-row.js';
9
+ import { RAIL_SIGNIN_ATTR } from './presenter-frame.js';
6
10
  const PREFIX = 'data-reticle-';
11
+ const PAGE_OPEN_ATTR = 'data-reticle-page-open';
7
12
  const TOGGLE_ROLES = new Set(['switch', 'checkbox']);
8
13
  export function controlIdOf(el) {
9
14
  for (const attr of Array.from(el.attributes)) {
@@ -28,6 +33,14 @@ function controlOf(path) {
28
33
  return undefined;
29
34
  }
30
35
  function viewOf(root) {
36
+ const page = pageOf(root);
37
+ if (HudView.BUBBLE === page.view)
38
+ return page;
39
+ return null === root.querySelector(`[${ACCOUNT_MENU_ATTR}]:not([hidden])`)
40
+ ? page
41
+ : { view: page.view, panel: HudPanel.ACCOUNT };
42
+ }
43
+ function pageOf(root) {
31
44
  if ('1' === root.getAttribute(MIN_ATTR))
32
45
  return { view: HudView.BUBBLE, panel: HudPanel.NONE };
33
46
  if ('1' === root.getAttribute(SETTINGS_ATTR)) {
@@ -36,10 +49,35 @@ function viewOf(root) {
36
49
  if ('1' === root.getAttribute(REPORT_ATTR)) {
37
50
  return { view: HudView.EXPANDED, panel: HudPanel.REPORT };
38
51
  }
52
+ const page = root.getAttribute(PAGE_OPEN_ATTR);
53
+ if ('flows' === page)
54
+ return { view: HudView.EXPANDED, panel: HudPanel.FLOWS };
55
+ if ('annotations' === page)
56
+ return { view: HudView.EXPANDED, panel: HudPanel.NOTES };
57
+ if ('harness' === page)
58
+ return { view: HudView.EXPANDED, panel: HudPanel.HARNESS };
39
59
  if ('1' === root.getAttribute(CHAT_ATTR))
40
60
  return { view: HudView.EXPANDED, panel: HudPanel.CHAT };
41
61
  return { view: HudView.COLLAPSED, panel: HudPanel.NONE };
42
62
  }
63
+ const PROMPTS = [
64
+ {
65
+ surface: HudPromptSurface.RAIL,
66
+ selector: `[${RAIL_SIGNIN_ATTR}]`,
67
+ shown: (at) => HudView.EXPANDED === at.view,
68
+ },
69
+ {
70
+ surface: HudPromptSurface.HARNESS,
71
+ selector: `[data-reticle-page-panel="harness"] :is([${ACCOUNT_SIGNIN_ATTR}],[${HARNESS_LINK_ATTR}])`,
72
+ shown: (at) => HudPanel.HARNESS === at.panel,
73
+ },
74
+ {
75
+ surface: HudPromptSurface.HEADER,
76
+ selector: `[${TEAM_SAVE_ATTR}]`,
77
+ shown: (at) => HudView.BUBBLE !== at.view,
78
+ },
79
+ ];
80
+ const visible = (el) => null === el.closest('[hidden]');
43
81
  export function installHudTelemetry(doc, root, report) {
44
82
  const send = (use) => {
45
83
  try {
@@ -73,15 +111,57 @@ export function installHudTelemetry(doc, root, report) {
73
111
  last = key;
74
112
  send(now);
75
113
  };
114
+ let lastSlide = '';
115
+ const onSlide = () => {
116
+ if (HudPanel.CHAT !== viewOf(root).panel)
117
+ return;
118
+ const shown = root.querySelector(`[${CAROUSEL_SLIDE_ATTR}]:not([hidden])`);
119
+ const slide = shown?.getAttribute(CAROUSEL_SLIDE_ATTR) ?? '';
120
+ if (0 === slide.length || slide === lastSlide)
121
+ return;
122
+ lastSlide = slide;
123
+ send({ slide });
124
+ };
125
+ const prompted = new Set();
126
+ const onPrompt = () => {
127
+ const at = viewOf(root);
128
+ for (const prompt of PROMPTS) {
129
+ if (prompted.has(prompt.surface) || !prompt.shown(at))
130
+ continue;
131
+ const el = root.querySelector(prompt.selector);
132
+ if (null === el || !visible(el))
133
+ continue;
134
+ prompted.add(prompt.surface);
135
+ send({ prompt: prompt.surface });
136
+ }
137
+ };
138
+ const inside = new MutationObserver(() => {
139
+ onSlide();
140
+ onChange();
141
+ onPrompt();
142
+ });
143
+ inside.observe(root, {
144
+ subtree: true,
145
+ childList: true,
146
+ attributes: true,
147
+ attributeFilter: ['data-active', 'hidden'],
148
+ });
76
149
  doc.addEventListener('click', onPress, true);
77
- const observer = new MutationObserver(onChange);
150
+ const observer = new MutationObserver((records) => {
151
+ onChange();
152
+ if (0 < records.length)
153
+ onSlide();
154
+ onPrompt();
155
+ });
78
156
  observer.observe(root, {
79
157
  attributes: true,
80
- attributeFilter: [MIN_ATTR, CHAT_ATTR, SETTINGS_ATTR, REPORT_ATTR],
158
+ attributeFilter: [MIN_ATTR, CHAT_ATTR, SETTINGS_ATTR, REPORT_ATTR, PAGE_OPEN_ATTR],
81
159
  });
82
160
  onChange();
161
+ onPrompt();
83
162
  return () => {
84
163
  doc.removeEventListener('click', onPress, true);
85
164
  observer.disconnect();
165
+ inside.disconnect();
86
166
  };
87
167
  }
@@ -14,6 +14,7 @@ declare const BODIES: {
14
14
  message: string;
15
15
  gear: string;
16
16
  layout: string;
17
+ notes: string;
17
18
  trash: string;
18
19
  help: string;
19
20
  'caret-right': string;
@@ -6,14 +6,15 @@ const BODIES = {
6
6
  'caret-down': '<path d="m19 9l-7 7l-7-7"/>',
7
7
  check: '<path d="m5 13l4 4L19 7"/>',
8
8
  remove: '<path d="m4.5 19.5l15-15m-15 0l15 15"/>',
9
- pause: '<path d="M10 9v6m4-6v6m7-3a9 9 0 1 1-18 0a9 9 0 0 1 18 0"/>',
10
- play: '<g><path d="m14.752 11.168l-3.197-2.132A1 1 0 0 0 10 9.87v4.263a1 1 0 0 0 1.555.832l3.197-2.132a1 1 0 0 0 0-1.664"/><path d="M21 12a9 9 0 1 1-18 0a9 9 0 0 1 18 0"/></g>',
11
- stop: '<g><path d="M21 12a9 9 0 1 1-18 0a9 9 0 0 1 18 0"/><path d="M9 10a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1h-4a1 1 0 0 1-1-1z"/></g>',
9
+ pause: '<path d="M8 5v14M16 5v14"/>',
10
+ play: '<path d="M8 5.75v12.5L18 12 8 5.75Z"/>',
11
+ stop: '<rect x="6.5" y="6.5" width="11" height="11" rx="2"/>',
12
12
  copy: '<path d="M8 7v8a2 2 0 0 0 2 2h6M8 7V5a2 2 0 0 1 2-2h4.586a1 1 0 0 1 .707.293l4.414 4.414a1 1 0 0 1 .293.707V15a2 2 0 0 1-2 2h-2M8 7H6a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2v-2"/>',
13
13
  download: '<path d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5M16.5 12L12 16.5m0 0L7.5 12m4.5 4.5V3"/>',
14
14
  message: '<path d="M8.625 9.75a.375.375 0 1 1-.75 0a.375.375 0 0 1 .75 0Zm0 0H8.25m4.125 0a.375.375 0 1 1-.75 0a.375.375 0 0 1 .75 0Zm0 0H12m4.125 0a.375.375 0 1 1-.75 0a.375.375 0 0 1 .75 0Zm0 0h-.375m-13.5 3.01c0 1.6 1.123 2.994 2.707 3.227c1.087.16 2.185.283 3.293.369V21l4.184-4.183a1.14 1.14 0 0 1 .778-.332a48.294 48.294 0 0 0 5.83-.498c1.585-.233 2.708-1.626 2.708-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0 0 12 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018Z"/>',
15
15
  gear: '<g><path d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87c.074.04.147.083.22.127c.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 0 1 0 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 0 1 0-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124c.072-.044.146-.087.22-.128c.332-.183.582-.495.644-.869l.214-1.281Z"/><path d="M15 12a3 3 0 1 1-6 0a3 3 0 0 1 6 0Z"/></g>',
16
16
  layout: '<path d="M3.75 6A2.25 2.25 0 0 1 6 3.75h2.25A2.25 2.25 0 0 1 10.5 6v2.25a2.25 2.25 0 0 1-2.25 2.25H6a2.25 2.25 0 0 1-2.25-2.25V6Zm0 9.75A2.25 2.25 0 0 1 6 13.5h2.25a2.25 2.25 0 0 1 2.25 2.25V18a2.25 2.25 0 0 1-2.25 2.25H6A2.25 2.25 0 0 1 3.75 18v-2.25ZM13.5 6a2.25 2.25 0 0 1 2.25-2.25H18A2.25 2.25 0 0 1 20.25 6v2.25A2.25 2.25 0 0 1 18 10.5h-2.25a2.25 2.25 0 0 1-2.25-2.25V6Zm0 9.75a2.25 2.25 0 0 1 2.25-2.25H18a2.25 2.25 0 0 1 2.25 2.25V18A2.25 2.25 0 0 1 18 20.25h-2.25A2.25 2.25 0 0 1 13.5 18v-2.25Z"/>',
17
+ notes: '<path d="M9 12h3.75M9 15h3.75M9 18h3.75m3 .75H18a2.25 2.25 0 0 0 2.25-2.25V6.108c0-1.135-.845-2.098-1.976-2.192a48.424 48.424 0 0 0-1.123-.08m-5.801 0c-.065.21-.1.433-.1.664c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75a2.25 2.25 0 0 0-.1-.664m-5.8 0A2.251 2.251 0 0 1 13.5 2.25H15a2.25 2.25 0 0 1 2.15 1.586m-5.8 0c-.376.023-.75.05-1.124.08C9.095 4.01 8.25 4.973 8.25 6.108V8.25m0 0H4.875c-.621 0-1.125.504-1.125 1.125v11.25c0 .621.504 1.125 1.125 1.125h9.75c.621 0 1.125-.504 1.125-1.125V9.375c0-.621-.504-1.125-1.125-1.125H8.25ZM6.75 12h.008v.008H6.75V12Zm0 3h.008v.008H6.75V15Zm0 3h.008v.008H6.75V18Z"/>',
17
18
  trash: '<path d="m19 7l-.867 12.142A2 2 0 0 1 16.138 21H7.862a2 2 0 0 1-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v3M4 7h16"/>',
18
19
  help: '<path d="M8.228 9c.549-1.165 2.03-2 3.772-2c2.21 0 4 1.343 4 3c0 1.4-1.278 2.575-3.006 2.907c-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 1 1-18 0a9 9 0 0 1 18 0"/>',
19
20
  'caret-right': '<path d="m9 5l7 7l-7 7"/>',
@@ -13,6 +13,7 @@ export declare const PresenterIcon: {
13
13
  readonly COPY: "copy";
14
14
  readonly DOWNLOAD: "download";
15
15
  readonly MESSAGE: "message";
16
+ readonly NOTES: "notes";
16
17
  readonly GEAR: "gear";
17
18
  readonly LAYOUT: "layout";
18
19
  readonly TRASH: "trash";
@@ -30,6 +31,7 @@ export declare const PRESENTER_ICON_SIZE: {
30
31
  readonly SEND: 14;
31
32
  readonly MIN: 12;
32
33
  readonly HELP: 12;
34
+ readonly NAV: 18;
33
35
  readonly TOOLBAR: 18;
34
36
  readonly FAB: 22;
35
37
  };
@@ -13,6 +13,7 @@ export const PresenterIcon = {
13
13
  COPY: 'copy',
14
14
  DOWNLOAD: 'download',
15
15
  MESSAGE: 'message',
16
+ NOTES: 'notes',
16
17
  GEAR: 'gear',
17
18
  LAYOUT: 'layout',
18
19
  TRASH: 'trash',
@@ -29,6 +30,7 @@ export const PRESENTER_ICON_SIZE = {
29
30
  SEND: 14,
30
31
  MIN: 12,
31
32
  HELP: 12,
33
+ NAV: 18,
32
34
  TOOLBAR: 18,
33
35
  FAB: 22,
34
36
  };
@@ -36,7 +36,7 @@ export const ACCOUNT_CSS = `
36
36
  .reticle-account-trigger[aria-expanded="true"]{box-shadow:0 0 0 2px rgba(255,255,255,.28);}
37
37
 
38
38
  .reticle-account-signin{
39
- border:none;border-radius:6px;padding:2px 9px;cursor:pointer;font:inherit;font-size:10.5px;
39
+ border:none;border-radius:6px;padding:2px 9px;cursor:pointer;font:inherit;font-size:11px;
40
40
  background:rgba(255,255,255,.06);color:rgba(255,255,255,.75);transition:background .12s,color .12s;}
41
41
  .reticle-account-signin:hover{background:rgba(255,255,255,.12);color:#fff;}
42
42
 
@@ -64,7 +64,7 @@ export const ACCOUNT_CSS = `
64
64
  font-size:11.5px;font-weight:600;color:rgba(255,255,255,.95);
65
65
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
66
66
  .reticle-account-email{
67
- font-size:10.5px;color:rgba(255,255,255,.55);
67
+ font-size:11px;color:rgba(255,255,255,.55);
68
68
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
69
69
 
70
70
  .reticle-account-rows{
@@ -90,7 +90,7 @@ export const ACCOUNT_CSS = `
90
90
  /* The unlinked hint is a sentence, not a verb: it wraps rather than truncating, because a clipped
91
91
  instruction is worse than a tall menu. */
92
92
  .reticle-account-action--hint{
93
- color:rgba(255,255,255,.5);font-size:10.5px;line-height:1.35;white-space:normal;cursor:pointer;}
93
+ color:rgba(255,255,255,.5);font-size:11px;line-height:1.35;white-space:normal;cursor:pointer;}
94
94
 
95
95
  .reticle-account-dashboard{
96
96
  display:inline-flex;align-items:center;justify-content:center;
@@ -1,14 +1,23 @@
1
- import type { AccountState } from '@reticlehq/core';
1
+ import { type AccountState, type HarnessConfig } from '@reticlehq/core';
2
2
  export declare const ACCOUNT_ROOT_ATTR = "data-reticle-account";
3
3
  export declare const ACCOUNT_SIGNIN_ATTR = "data-reticle-account-signin";
4
4
  export declare const SYNC_BTN_ATTR = "data-reticle-sync-now";
5
5
  export declare const ACCOUNT_TRIGGER_ATTR = "data-reticle-account-trigger";
6
6
  export declare const ACCOUNT_MENU_ATTR = "data-reticle-account-menu";
7
7
  export declare const ACCOUNT_SIGNOUT_ATTR = "data-reticle-account-signout";
8
+ export declare const TEAM_OPEN_ATTR = "data-reticle-team-open";
9
+ export declare const TEAM_SAVE_ATTR = "data-reticle-team-save";
10
+ export declare const TEAM_SLOT_ATTR = "data-reticle-team";
11
+ export declare const TEAM_TEXT: {
12
+ readonly OPEN: "Open on dashboard ↗";
13
+ readonly OPEN_TITLE: "This project's runs, kept for your team on the Reticle dashboard";
14
+ readonly SAVE_LINK: "Save runs for your team";
15
+ readonly SAVE_TITLE: `Keep every run, flow and Harness result on a dashboard your team shares. On Reticle, ${string}.`;
16
+ };
8
17
  export declare const ACCOUNT_TEXT: {
9
18
  readonly SIGNED_OUT: "Sign in";
10
- readonly SIGNIN_TITLE: "Run `reticle login` in your terminal — click to copy the command";
11
- readonly SIGNIN_COMMAND: "reticle login";
19
+ readonly SIGNIN_TITLE: "Sign in through your browser. Also copies `reticle connect`, for a terminal";
20
+ readonly SIGNIN_COMMAND: "reticle connect";
12
21
  readonly DASHBOARD_TITLE: "Open this project on the dashboard";
13
22
  readonly SYNC_TITLE: "Push to the dashboard now, instead of waiting for the next sync";
14
23
  readonly COPIED: "Copied";
@@ -17,24 +26,38 @@ export declare const ACCOUNT_TEXT: {
17
26
  readonly TRIGGER_TITLE: "Your Reticle account";
18
27
  readonly SIGNED_IN_AS: "Signed in";
19
28
  readonly DASHBOARD_ACTION: "Open dashboard";
20
- readonly LINK_HINT: "Not linked — run `reticle link` to keep a record on the dashboard";
21
- readonly LINK_COMMAND: "reticle link";
22
- readonly SIGNOUT_ACTION: "Sign out";
23
- readonly SIGNOUT_TITLE: "Run `reticle logout` in your terminal — click to copy the command";
29
+ readonly LINK_HINT: "Not linked — run `reticle connect` to keep a record on the dashboard";
30
+ readonly LINK_COMMAND: "reticle connect";
31
+ readonly SIGNOUT_ACTION: "Copy sign-out command";
32
+ readonly SIGNOUT_TITLE: "Copy `reticle logout` to run in your terminal";
24
33
  readonly SIGNOUT_COMMAND: "reticle logout";
34
+ readonly SIGNOUT_COPY_FAILED: "Run reticle logout in terminal";
25
35
  readonly PROJECT_LABEL: "Project";
26
36
  readonly HOST_LABEL: "Host";
27
37
  readonly VERDICTS_LABEL: "Verdicts";
28
38
  readonly DEFECTS_LABEL: "Defects found";
39
+ readonly PLAN_LABEL: "Plan";
40
+ readonly CREDITS_LABEL: "Credits";
41
+ readonly HARNESS_RUNS_LABEL: "Harness runs";
42
+ readonly MEMORY_LABEL: "Memory";
43
+ readonly ON_PLATFORM_LABEL: "Runs on dashboard";
29
44
  };
30
45
  export interface AccountDetails {
31
46
  projectName?: string | undefined;
32
47
  dashboardUrl?: string | undefined;
33
48
  verdicts?: number | undefined;
34
49
  defects?: number | undefined;
50
+ plan?: string | undefined;
51
+ credits?: string | undefined;
52
+ harnessRuns?: string | undefined;
53
+ memory?: string | undefined;
54
+ onPlatform?: string | undefined;
35
55
  }
36
56
  export declare function accountInitials(org: string | undefined): string;
37
57
  export declare function accountControlHtml(account: AccountState | undefined, details?: AccountDetails, offerSignIn?: boolean): string;
58
+ export declare function platformDetails(config: HarnessConfig | undefined, sync: Readonly<Record<string, unknown>> | undefined): Pick<AccountDetails, 'plan' | 'credits' | 'harnessRuns' | 'memory' | 'onPlatform'>;
59
+ export declare function teamLinkHtml(account: AccountState | undefined, dashboardUrl: string | undefined): string;
60
+ export declare function paintAccountSlot(slot: HTMLElement, html: string): void;
38
61
  export declare function accountCapsuleHtml(account: AccountState | undefined, dashboardUrl?: string, offerSignIn?: boolean): string;
39
62
  export declare function syncButtonHtml(dashboardUrl: string | undefined): string;
40
- export declare function mountAccountControl(root: HTMLElement): () => void;
63
+ export declare function mountAccountControl(root: HTMLElement, onSignIn?: () => void): () => void;
@@ -1,15 +1,27 @@
1
+ import { CreditKind } from '@reticlehq/core';
2
+ import { TRIAL_OFFER } from '@reticlehq/core/hud';
1
3
  import { hiIconHtml, PRESENTER_ICON_SIZE, PresenterIcon } from './icons/presenter-icons.js';
2
4
  import { esc, isSafeDashboardUrl } from './chrome/presenter-safe-html.js';
5
+ import { morphHtml } from './chrome/presenter-morph.js';
3
6
  export const ACCOUNT_ROOT_ATTR = 'data-reticle-account';
4
7
  export const ACCOUNT_SIGNIN_ATTR = 'data-reticle-account-signin';
5
8
  export const SYNC_BTN_ATTR = 'data-reticle-sync-now';
6
9
  export const ACCOUNT_TRIGGER_ATTR = 'data-reticle-account-trigger';
7
10
  export const ACCOUNT_MENU_ATTR = 'data-reticle-account-menu';
8
11
  export const ACCOUNT_SIGNOUT_ATTR = 'data-reticle-account-signout';
12
+ export const TEAM_OPEN_ATTR = 'data-reticle-team-open';
13
+ export const TEAM_SAVE_ATTR = 'data-reticle-team-save';
14
+ export const TEAM_SLOT_ATTR = 'data-reticle-team';
15
+ export const TEAM_TEXT = {
16
+ OPEN: 'Open on dashboard ↗',
17
+ OPEN_TITLE: "This project's runs, kept for your team on the Reticle dashboard",
18
+ SAVE_LINK: 'Save runs for your team',
19
+ SAVE_TITLE: `Keep every run, flow and Harness result on a dashboard your team shares. On Reticle, ${TRIAL_OFFER}.`,
20
+ };
9
21
  export const ACCOUNT_TEXT = {
10
22
  SIGNED_OUT: 'Sign in',
11
- SIGNIN_TITLE: 'Run `reticle login` in your terminal — click to copy the command',
12
- SIGNIN_COMMAND: 'reticle login',
23
+ SIGNIN_TITLE: 'Sign in through your browser. Also copies `reticle connect`, for a terminal',
24
+ SIGNIN_COMMAND: 'reticle connect',
13
25
  DASHBOARD_TITLE: 'Open this project on the dashboard',
14
26
  SYNC_TITLE: 'Push to the dashboard now, instead of waiting for the next sync',
15
27
  COPIED: 'Copied',
@@ -18,15 +30,21 @@ export const ACCOUNT_TEXT = {
18
30
  TRIGGER_TITLE: 'Your Reticle account',
19
31
  SIGNED_IN_AS: 'Signed in',
20
32
  DASHBOARD_ACTION: 'Open dashboard',
21
- LINK_HINT: 'Not linked — run `reticle link` to keep a record on the dashboard',
22
- LINK_COMMAND: 'reticle link',
23
- SIGNOUT_ACTION: 'Sign out',
24
- SIGNOUT_TITLE: 'Run `reticle logout` in your terminal — click to copy the command',
33
+ LINK_HINT: 'Not linked — run `reticle connect` to keep a record on the dashboard',
34
+ LINK_COMMAND: 'reticle connect',
35
+ SIGNOUT_ACTION: 'Copy sign-out command',
36
+ SIGNOUT_TITLE: 'Copy `reticle logout` to run in your terminal',
25
37
  SIGNOUT_COMMAND: 'reticle logout',
38
+ SIGNOUT_COPY_FAILED: 'Run reticle logout in terminal',
26
39
  PROJECT_LABEL: 'Project',
27
40
  HOST_LABEL: 'Host',
28
41
  VERDICTS_LABEL: 'Verdicts',
29
42
  DEFECTS_LABEL: 'Defects found',
43
+ PLAN_LABEL: 'Plan',
44
+ CREDITS_LABEL: 'Credits',
45
+ HARNESS_RUNS_LABEL: 'Harness runs',
46
+ MEMORY_LABEL: 'Memory',
47
+ ON_PLATFORM_LABEL: 'Runs on dashboard',
30
48
  };
31
49
  const ACCOUNT_COPIED_MS = 1200;
32
50
  export function accountInitials(org) {
@@ -69,12 +87,20 @@ function accountMenuHtml(account, details) {
69
87
  `<span class="reticle-account-org">${org.length > 0 ? esc(org) : ACCOUNT_TEXT.SIGNED_IN_AS}</span>` +
70
88
  (account.email !== undefined && account.email.length > 0
71
89
  ? `<span class="reticle-account-email" title="${esc(account.email)}">${esc(account.email)}</span>`
72
- : `<span class="reticle-account-email">${ACCOUNT_TEXT.SIGNED_IN_AS}</span>`) +
90
+ :
91
+ org.length > 0
92
+ ? `<span class="reticle-account-email">${ACCOUNT_TEXT.SIGNED_IN_AS}</span>`
93
+ : '') +
73
94
  `</span></div>`;
74
95
  const rows = detailRow(ACCOUNT_TEXT.HOST_LABEL, account.host) +
75
96
  detailRow(ACCOUNT_TEXT.PROJECT_LABEL, details.projectName) +
76
97
  detailRow(ACCOUNT_TEXT.VERDICTS_LABEL, details.verdicts !== undefined ? String(details.verdicts) : undefined) +
77
- detailRow(ACCOUNT_TEXT.DEFECTS_LABEL, details.defects !== undefined ? String(details.defects) : undefined);
98
+ detailRow(ACCOUNT_TEXT.DEFECTS_LABEL, details.defects !== undefined ? String(details.defects) : undefined) +
99
+ detailRow(ACCOUNT_TEXT.ON_PLATFORM_LABEL, details.onPlatform) +
100
+ detailRow(ACCOUNT_TEXT.PLAN_LABEL, details.plan) +
101
+ detailRow(ACCOUNT_TEXT.CREDITS_LABEL, details.credits) +
102
+ detailRow(ACCOUNT_TEXT.HARNESS_RUNS_LABEL, details.harnessRuns) +
103
+ detailRow(ACCOUNT_TEXT.MEMORY_LABEL, details.memory);
78
104
  const linked = details.dashboardUrl !== undefined && isSafeDashboardUrl(details.dashboardUrl);
79
105
  const dashboard = linked
80
106
  ? `<a class="reticle-account-action" data-reticle-account-dashboard href="${esc(details.dashboardUrl ?? '')}" target="_blank" rel="noreferrer noopener" title="${ACCOUNT_TEXT.DASHBOARD_TITLE}">${hiIconHtml(PresenterIcon.VIEW, PRESENTER_ICON_SIZE.HELP)}<span>${ACCOUNT_TEXT.DASHBOARD_ACTION}</span></a>`
@@ -87,6 +113,58 @@ function accountMenuHtml(account, details) {
87
113
  `<div class="reticle-account-actions">${dashboard}${signOut}</div>` +
88
114
  `</div>`);
89
115
  }
116
+ const PLAN_NAME = {
117
+ [CreditKind.FREE]: 'Free',
118
+ [CreditKind.TRIAL]: 'Trial',
119
+ [CreditKind.PAID]: 'Pro',
120
+ };
121
+ const plural = (n, one, many) => `${String(n)} ${1 === n ? one : many}`;
122
+ export function platformDetails(config, sync) {
123
+ const plan = config?.plan;
124
+ const onPlatform = sync?.['onPlatform'];
125
+ return {
126
+ ...(plan === undefined
127
+ ? {}
128
+ : {
129
+ plan: [
130
+ PLAN_NAME[plan.kind],
131
+ plan.daysLeft === undefined ? '' : `${plural(plan.daysLeft, 'day', 'days')} left`,
132
+ plan.seats === undefined ? '' : plural(plan.seats, 'seat', 'seats'),
133
+ ]
134
+ .filter((part) => part.length > 0)
135
+ .join(' · '),
136
+ }),
137
+ ...(config?.credits === undefined
138
+ ? {}
139
+ : {
140
+ credits: `${Math.max(0, config.credits.limit - config.credits.used).toLocaleString('en-US')} of ${config.credits.limit.toLocaleString('en-US')} left`,
141
+ }),
142
+ ...(config?.harnessRuns === undefined ? {} : { harnessRuns: String(config.harnessRuns) }),
143
+ ...(config?.memoryEntries === undefined
144
+ ? {}
145
+ : { memory: plural(config.memoryEntries, 'entry', 'entries') }),
146
+ ...('number' === typeof onPlatform ? { onPlatform: String(onPlatform) } : {}),
147
+ };
148
+ }
149
+ export function teamLinkHtml(account, dashboardUrl) {
150
+ if (account === undefined || !account.signedIn)
151
+ return '';
152
+ if (dashboardUrl !== undefined && isSafeDashboardUrl(dashboardUrl))
153
+ return `<a ${TEAM_OPEN_ATTR} class="reticle-team-link" href="${esc(dashboardUrl)}" target="_blank" rel="noreferrer noopener" title="${esc(TEAM_TEXT.OPEN_TITLE)}">${TEAM_TEXT.OPEN}</a>`;
154
+ return `<button type="button" ${TEAM_SAVE_ATTR} class="reticle-team-link" title="${esc(TEAM_TEXT.SAVE_TITLE)}"><span>${TEAM_TEXT.SAVE_LINK}</span></button>`;
155
+ }
156
+ export function paintAccountSlot(slot, html) {
157
+ const open = null !== slot.querySelector(`[${ACCOUNT_MENU_ATTR}]:not([hidden])`);
158
+ morphHtml(slot, html);
159
+ if (!open)
160
+ return;
161
+ const menu = slot.querySelector(`[${ACCOUNT_MENU_ATTR}]`);
162
+ if (null === menu)
163
+ return;
164
+ menu.removeAttribute('hidden');
165
+ menu.setAttribute('aria-hidden', 'false');
166
+ slot.querySelector(`[${ACCOUNT_TRIGGER_ATTR}]`)?.setAttribute('aria-expanded', 'true');
167
+ }
90
168
  export function accountCapsuleHtml(account, dashboardUrl, offerSignIn = false) {
91
169
  return accountControlHtml(account, { dashboardUrl }, offerSignIn);
92
170
  }
@@ -95,7 +173,7 @@ export function syncButtonHtml(dashboardUrl) {
95
173
  return '';
96
174
  return `<button type="button" class="reticle-sync-now" ${SYNC_BTN_ATTR} title="${ACCOUNT_TEXT.SYNC_TITLE}" aria-label="${ACCOUNT_TEXT.SYNC_TITLE}">${hiIconHtml(PresenterIcon.SYNC, PRESENTER_ICON_SIZE.HELP)}</button>`;
97
175
  }
98
- export function mountAccountControl(root) {
176
+ export function mountAccountControl(root, onSignIn) {
99
177
  const controller = new AbortController();
100
178
  const { signal } = controller;
101
179
  const closeAll = (except) => {
@@ -111,14 +189,27 @@ export function mountAccountControl(root) {
111
189
  trigger.setAttribute('aria-expanded', 'false');
112
190
  }
113
191
  };
114
- const flashCopied = (el) => {
192
+ const flashLabel = (el, value) => {
115
193
  const label = el.querySelector('span') ?? el;
116
194
  const previous = label.textContent;
117
- label.textContent = ACCOUNT_TEXT.COPIED;
195
+ label.textContent = value;
118
196
  setTimeout(() => {
119
197
  label.textContent = previous;
120
198
  }, ACCOUNT_COPIED_MS);
121
199
  };
200
+ const copyCommand = async (command, el) => {
201
+ try {
202
+ if (navigator.clipboard === undefined)
203
+ throw new Error('Clipboard unavailable');
204
+ await navigator.clipboard.writeText(command);
205
+ flashLabel(el, ACCOUNT_TEXT.COPIED);
206
+ }
207
+ catch {
208
+ flashLabel(el, command === ACCOUNT_TEXT.SIGNOUT_COMMAND
209
+ ? ACCOUNT_TEXT.SIGNOUT_COPY_FAILED
210
+ : `Run ${command} in terminal`);
211
+ }
212
+ };
122
213
  root.addEventListener('click', (event) => {
123
214
  const target = event.target;
124
215
  if (!(target instanceof Element))
@@ -145,16 +236,15 @@ export function mountAccountControl(root) {
145
236
  event.stopPropagation();
146
237
  const command = copier.getAttribute('data-reticle-copy') ?? '';
147
238
  if (command.length > 0)
148
- void navigator.clipboard?.writeText(command).catch(() => undefined);
149
- flashCopied(copier);
239
+ void copyCommand(command, copier);
150
240
  return;
151
241
  }
152
242
  const signin = target.closest(`[${ACCOUNT_SIGNIN_ATTR}]`);
153
243
  if (signin instanceof HTMLElement) {
154
244
  event.preventDefault();
155
245
  event.stopPropagation();
156
- void navigator.clipboard?.writeText(ACCOUNT_TEXT.SIGNIN_COMMAND).catch(() => undefined);
157
- flashCopied(signin);
246
+ onSignIn?.();
247
+ void copyCommand(ACCOUNT_TEXT.SIGNIN_COMMAND, signin);
158
248
  return;
159
249
  }
160
250
  closeAll();
@@ -0,0 +1,9 @@
1
+ import { type AgentLink } from '@reticlehq/core';
2
+ export declare const AGENT_LINK_HTML: string;
3
+ export declare const AGENT_LINK_CSS = "\n[data-reticle-chat-panel] .reticle-agent-spot{flex:none;display:flex;flex-direction:column;gap:3px;}\n[data-reticle-chat-panel] .reticle-agent-spot[hidden]{display:none;}\n[data-reticle-chat-panel] .reticle-agent-row{display:flex;align-items:center;gap:8px;min-width:0;min-height:30px;padding:0 var(--reticle-hud-space-2);}\n[data-reticle-chat-panel] .reticle-agent-dot{flex:none;width:7px;height:7px;border-radius:50%;background:var(--reticle-hud-text-faint);}\n[data-reticle-chat-panel] .reticle-agent-row[data-connected=\"1\"] .reticle-agent-dot{background:#4ade80;box-shadow:0 0 0 3px rgba(74,222,128,.15);}\n[data-reticle-chat-panel] .reticle-agent-said{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--reticle-hud-size-sm);color:var(--reticle-hud-text-muted);}\n[data-reticle-chat-panel] .reticle-agent-copy:focus-visible{outline:2px solid var(--reticle-hud-brand);outline-offset:0;}\n[data-reticle-chat-panel] .reticle-agent-copy{flex:none;height:22px;padding:0 9px;border:1px solid var(--reticle-hud-border-strong);border-radius:11px;background:transparent;color:var(--reticle-hud-text);font:inherit;font-size:11px;font-weight:600;cursor:pointer;white-space:nowrap;}\n[data-reticle-chat-panel] .reticle-agent-copy:hover{border-color:var(--reticle-hud-brand);color:var(--reticle-hud-brand);}\n[data-reticle-chat-panel] .reticle-agent-copy[hidden]{display:none;}\n";
4
+ export declare class AgentLinkView {
5
+ #private;
6
+ mount(root: HTMLElement, signal: AbortSignal): void;
7
+ teardown(): void;
8
+ paint(link: AgentLink | undefined): void;
9
+ }
@@ -0,0 +1,62 @@
1
+ import {} from '@reticlehq/core';
2
+ import { REPORT_TEXT } from './chrome/presenter-report-copy.js';
3
+ const COPIED_FLASH_MS = 1600;
4
+ const TEXT = {
5
+ NONE: 'No coding agent connected',
6
+ named: (names) => `${names} connected`,
7
+ COPY: 'Copy prompt',
8
+ prompt: (url) => `Use Reticle to check ${url} in my running app, then tell me the verdict.`,
9
+ };
10
+ export const AGENT_LINK_HTML = `<div data-reticle-agent-spot class="reticle-agent-spot" hidden>
11
+ <div class="reticle-agent-row"><span data-reticle-agent-dot class="reticle-agent-dot" aria-hidden="true"></span><span data-reticle-agent-said class="reticle-agent-said">${TEXT.NONE}</span><button type="button" data-reticle-agent-copy class="reticle-agent-copy" aria-label="${REPORT_TEXT.COPY_PROMPT}" title="${REPORT_TEXT.COPY_PROMPT}">${TEXT.COPY}</button></div>
12
+ </div>`;
13
+ export const AGENT_LINK_CSS = `
14
+ [data-reticle-chat-panel] .reticle-agent-spot{flex:none;display:flex;flex-direction:column;gap:3px;}
15
+ [data-reticle-chat-panel] .reticle-agent-spot[hidden]{display:none;}
16
+ [data-reticle-chat-panel] .reticle-agent-row{display:flex;align-items:center;gap:8px;min-width:0;min-height:30px;padding:0 var(--reticle-hud-space-2);}
17
+ [data-reticle-chat-panel] .reticle-agent-dot{flex:none;width:7px;height:7px;border-radius:50%;background:var(--reticle-hud-text-faint);}
18
+ [data-reticle-chat-panel] .reticle-agent-row[data-connected="1"] .reticle-agent-dot{background:#4ade80;box-shadow:0 0 0 3px rgba(74,222,128,.15);}
19
+ [data-reticle-chat-panel] .reticle-agent-said{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--reticle-hud-size-sm);color:var(--reticle-hud-text-muted);}
20
+ [data-reticle-chat-panel] .reticle-agent-copy:focus-visible{outline:2px solid var(--reticle-hud-brand);outline-offset:0;}
21
+ [data-reticle-chat-panel] .reticle-agent-copy{flex:none;height:22px;padding:0 9px;border:1px solid var(--reticle-hud-border-strong);border-radius:11px;background:transparent;color:var(--reticle-hud-text);font:inherit;font-size:11px;font-weight:600;cursor:pointer;white-space:nowrap;}
22
+ [data-reticle-chat-panel] .reticle-agent-copy:hover{border-color:var(--reticle-hud-brand);color:var(--reticle-hud-brand);}
23
+ [data-reticle-chat-panel] .reticle-agent-copy[hidden]{display:none;}
24
+ `;
25
+ export class AgentLinkView {
26
+ #root;
27
+ mount(root, signal) {
28
+ this.#root = root;
29
+ root.querySelector('[data-reticle-agent-copy]')?.addEventListener('click', (event) => {
30
+ const button = event.currentTarget;
31
+ void navigator.clipboard
32
+ ?.writeText(TEXT.prompt(location.href))
33
+ .catch(() => undefined)
34
+ .finally(() => {
35
+ if (!(button instanceof HTMLElement))
36
+ return;
37
+ button.textContent = REPORT_TEXT.COPIED;
38
+ window.setTimeout(() => (button.textContent = REPORT_TEXT.COPY_PROMPT), COPIED_FLASH_MS);
39
+ });
40
+ }, { signal });
41
+ }
42
+ teardown() {
43
+ this.#root = undefined;
44
+ }
45
+ paint(link) {
46
+ const root = this.#root;
47
+ const spot = root?.querySelector('[data-reticle-agent-spot]');
48
+ if (root === undefined || null === spot || spot === undefined)
49
+ return;
50
+ spot.hidden = link === undefined;
51
+ if (link === undefined)
52
+ return;
53
+ const connected = 0 < link.agents.length;
54
+ spot.querySelector('.reticle-agent-row')?.setAttribute('data-connected', connected ? '1' : '0');
55
+ const said = spot.querySelector('[data-reticle-agent-said]');
56
+ if (said !== null)
57
+ said.textContent = connected ? TEXT.named(link.agents.join(', ')) : TEXT.NONE;
58
+ const copy = spot.querySelector('[data-reticle-agent-copy]');
59
+ if (copy !== null)
60
+ copy.hidden = connected;
61
+ }
62
+ }
@@ -0,0 +1,43 @@
1
+ import { type AccountState, type HarnessConfig, type AgentLink, type HarnessDrive } from '@reticlehq/core';
2
+ import type { AnnotationItem } from '../review/annotator.js';
3
+ import { type HarnessGateView } from './presenter-harness-row.js';
4
+ export type ChatView = 'activity' | 'flows' | 'annotations' | 'harness';
5
+ export declare function platformBase(config: HarnessConfig | undefined, account: AccountState): string;
6
+ export interface HudDriveHost {
7
+ run(persona?: string): void;
8
+ stop(): void;
9
+ setHarness(on: boolean): void;
10
+ link(): void;
11
+ }
12
+ export declare const CHAT_VIEWS_HTML: string;
13
+ export declare const FLOWS_PAGE_HTML: string;
14
+ export declare const NAV_LABEL: {
15
+ readonly LOG: "Log";
16
+ readonly FLOWS: "Flows";
17
+ readonly NOTES: "Notes";
18
+ readonly IMPACT: "Impact";
19
+ readonly HARNESS: "Harness";
20
+ readonly SETTINGS: "Settings";
21
+ readonly HIDE: "Hide";
22
+ };
23
+ export declare const navLabelHtml: (text: string) => string;
24
+ export declare const CHAT_VIEWS_NAV_HTML: string;
25
+ export declare const HARNESS_PAGE_HTML: string;
26
+ export declare const ANNOTATIONS_HTML: string;
27
+ export declare const CHAT_VIEWS_CSS: string;
28
+ export declare class ChatViews {
29
+ #private;
30
+ constructor(onImpact: () => void, onOpenChat: (view: ChatView) => void, drive?: HudDriveHost);
31
+ mount(root: HTMLElement): void;
32
+ teardown(): void;
33
+ open(view: ChatView): void;
34
+ closePage(): void;
35
+ setProjectId(projectId: string | undefined): void;
36
+ paintAgent(link: AgentLink | undefined): void;
37
+ paintAccount(account: AccountState | undefined): void;
38
+ paintDrive(drive: HarnessDrive | undefined): void;
39
+ paintHarness(config: HarnessConfig | undefined, gate?: HarnessGateView): void;
40
+ paintImpact(verdicts: number): void;
41
+ setImpactOpen(open: boolean): void;
42
+ paintAnnotations(items: readonly AnnotationItem[]): void;
43
+ }