@reticlehq/browser 2.14.0 → 3.2.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.
- package/dist/actions/actions-dom.d.ts +9 -0
- package/dist/actions/actions-dom.js +81 -28
- package/dist/actions/actions-press.d.ts +4 -0
- package/dist/actions/actions-press.js +46 -0
- package/dist/actions/actions.d.ts +1 -0
- package/dist/actions/actions.js +50 -13
- package/dist/actions/appeared-text.js +6 -0
- package/dist/actions/scroll.d.ts +8 -1
- package/dist/actions/scroll.js +28 -10
- package/dist/commands/commands.js +10 -3
- package/dist/dom/a11y.js +2 -2
- package/dist/dom/desktop-capture.js +1 -1
- package/dist/dom/dom-ignore.js +1 -1
- package/dist/dom/occlusion.js +31 -5
- package/dist/dom/paint-context.d.ts +6 -0
- package/dist/dom/paint-context.js +38 -0
- package/dist/dom/query.js +24 -7
- package/dist/dom/snapshot.js +1 -1
- package/dist/edit-epoch.d.ts +1 -1
- package/dist/edit-epoch.js +1 -1
- package/dist/index.d.ts +4 -5
- package/dist/index.js +3 -5
- package/dist/observers/animation.js +1 -1
- package/dist/observers/blind-spots.js +1 -1
- package/dist/observers/departure.js +16 -8
- package/dist/observers/dialogs.js +1 -1
- package/dist/observers/dom.js +28 -8
- package/dist/observers/download.js +1 -1
- package/dist/observers/health.js +1 -1
- package/dist/observers/{network-body.js → net-detail/network-body.js} +2 -2
- package/dist/observers/network-stream.js +2 -2
- package/dist/observers/network.d.ts +1 -1
- package/dist/observers/network.js +51 -10
- package/dist/observers/scroll.js +2 -2
- package/dist/presenter/chrome/layers.d.ts +2 -0
- package/dist/presenter/chrome/layers.js +2 -0
- package/dist/presenter/chrome/log-kinds.d.ts +12 -0
- package/dist/presenter/chrome/log-kinds.js +7 -0
- package/dist/presenter/{presenter-effects.js → chrome/presenter-effects.js} +1 -1
- package/dist/presenter/{presenter-log.d.ts → chrome/presenter-log.d.ts} +3 -12
- package/dist/presenter/{presenter-log.js → chrome/presenter-log.js} +4 -9
- package/dist/presenter/chrome/presenter-safe-html.d.ts +2 -0
- package/dist/presenter/chrome/presenter-safe-html.js +16 -0
- package/dist/presenter/{presenter-tally.js → chrome/presenter-tally.js} +1 -1
- package/dist/presenter/{presenter-heroicons-data.d.ts → icons/presenter-heroicons-data.d.ts} +1 -0
- package/dist/presenter/{presenter-heroicons-data.js → icons/presenter-heroicons-data.js} +1 -0
- package/dist/presenter/{presenter-icons.d.ts → icons/presenter-icons.d.ts} +1 -0
- package/dist/presenter/{presenter-icons.js → icons/presenter-icons.js} +1 -0
- package/dist/presenter/presenter-account-styles.d.ts +1 -0
- package/dist/presenter/presenter-account-styles.js +99 -0
- package/dist/presenter/presenter-account.d.ts +40 -0
- package/dist/presenter/presenter-account.js +178 -0
- package/dist/presenter/presenter-config.d.ts +9 -2
- package/dist/presenter/presenter-config.js +3 -1
- package/dist/presenter/presenter-controls.d.ts +1 -6
- package/dist/presenter/presenter-controls.js +7 -2
- package/dist/presenter/presenter-dock-layout.js +1 -1
- package/dist/presenter/presenter-drag.d.ts +1 -19
- package/dist/presenter/presenter-drag.js +3 -50
- package/dist/presenter/presenter-glow.js +1 -1
- package/dist/presenter/presenter-hud-position.d.ts +18 -0
- package/dist/presenter/presenter-hud-position.js +50 -0
- package/dist/presenter/presenter-offer.d.ts +25 -0
- package/dist/presenter/presenter-offer.js +87 -0
- package/dist/presenter/presenter-report-styles.js +20 -1
- package/dist/presenter/presenter-report.d.ts +2 -1
- package/dist/presenter/presenter-report.js +31 -23
- package/dist/presenter/presenter-run-state.d.ts +1 -1
- package/dist/presenter/presenter-run-state.js +1 -1
- package/dist/presenter/presenter-settings-styles.js +2 -1
- package/dist/presenter/presenter-settings.d.ts +8 -0
- package/dist/presenter/presenter-settings.js +58 -3
- package/dist/presenter/presenter-shell-styles.js +33 -15
- package/dist/presenter/presenter-shell.d.ts +8 -0
- package/dist/presenter/presenter-shell.js +47 -4
- package/dist/presenter/presenter-styles.d.ts +2 -1
- package/dist/presenter/presenter-styles.js +8 -3
- package/dist/presenter/presenter-workspace.d.ts +4 -0
- package/dist/presenter/presenter-workspace.js +29 -2
- package/dist/presenter/presenter.d.ts +3 -3
- package/dist/presenter/presenter.js +28 -14
- package/dist/presenter/tour/tour-view.d.ts +33 -0
- package/dist/presenter/tour/tour-view.js +192 -0
- package/dist/presenter/tour/tour.d.ts +25 -0
- package/dist/presenter/tour/tour.js +264 -0
- package/dist/recorder/recorder.js +6 -0
- package/dist/registry/declared-channels.d.ts +2 -0
- package/dist/registry/declared-channels.js +14 -0
- package/dist/registry/declared-platform.d.ts +4 -0
- package/dist/registry/declared-platform.js +34 -0
- package/dist/registry/emitter.js +1 -1
- package/dist/registry/store-adapters.js +1 -1
- package/dist/registry/stores.js +1 -1
- package/dist/reticle-presenter-helpers.js +10 -6
- package/dist/reticle-singleton.d.ts +2 -0
- package/dist/reticle-singleton.js +3 -0
- package/dist/reticle.js +99 -35
- package/dist/review/annotator-styles.js +1 -1
- package/dist/review/annotator.js +2 -2
- package/dist/review/mark-anchor.js +3 -3
- package/dist/transport/transport.js +2 -2
- package/package.json +5 -4
- package/dist/presenter/presenter-test-helpers.d.ts +0 -14
- package/dist/presenter/presenter-test-helpers.js +0 -39
- /package/dist/actions/{synthetic-input.d.ts → synthetic/synthetic-input.d.ts} +0 -0
- /package/dist/actions/{synthetic-input.js → synthetic/synthetic-input.js} +0 -0
- /package/dist/dom/{auto-anchor.d.ts → addressing/auto-anchor.d.ts} +0 -0
- /package/dist/dom/{auto-anchor.js → addressing/auto-anchor.js} +0 -0
- /package/dist/dom/{refs.d.ts → addressing/refs.d.ts} +0 -0
- /package/dist/dom/{refs.js → addressing/refs.js} +0 -0
- /package/dist/dom/{source.d.ts → addressing/source.d.ts} +0 -0
- /package/dist/dom/{source.js → addressing/source.js} +0 -0
- /package/dist/observers/{network-body.d.ts → net-detail/network-body.d.ts} +0 -0
- /package/dist/observers/{network-redact.d.ts → net-detail/network-redact.d.ts} +0 -0
- /package/dist/observers/{network-redact.js → net-detail/network-redact.js} +0 -0
- /package/dist/presenter/{overlay.d.ts → chrome/overlay.d.ts} +0 -0
- /package/dist/presenter/{overlay.js → chrome/overlay.js} +0 -0
- /package/dist/presenter/{presenter-brand.d.ts → chrome/presenter-brand.d.ts} +0 -0
- /package/dist/presenter/{presenter-brand.js → chrome/presenter-brand.js} +0 -0
- /package/dist/presenter/{presenter-effects.d.ts → chrome/presenter-effects.d.ts} +0 -0
- /package/dist/presenter/{presenter-hud-chrome.d.ts → chrome/presenter-hud-chrome.d.ts} +0 -0
- /package/dist/presenter/{presenter-hud-chrome.js → chrome/presenter-hud-chrome.js} +0 -0
- /package/dist/presenter/{presenter-report-copy.d.ts → chrome/presenter-report-copy.d.ts} +0 -0
- /package/dist/presenter/{presenter-report-copy.js → chrome/presenter-report-copy.js} +0 -0
- /package/dist/presenter/{presenter-tally.d.ts → chrome/presenter-tally.d.ts} +0 -0
- /package/dist/presenter/{presenter-verbs.d.ts → chrome/presenter-verbs.d.ts} +0 -0
- /package/dist/presenter/{presenter-verbs.js → chrome/presenter-verbs.js} +0 -0
- /package/dist/registry/{adapters.d.ts → stores/adapters.d.ts} +0 -0
- /package/dist/registry/{adapters.js → stores/adapters.js} +0 -0
- /package/dist/timers/{native-console.d.ts → console/native-console.d.ts} +0 -0
- /package/dist/timers/{native-console.js → console/native-console.js} +0 -0
- /package/dist/timers/{native-timers.d.ts → native/native-timers.d.ts} +0 -0
- /package/dist/timers/{native-timers.js → native/native-timers.js} +0 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export const OFFER_ATTR = 'data-reticle-offer';
|
|
2
|
+
export const OFFER_SLOT_ATTR = 'data-reticle-offer-slot';
|
|
3
|
+
export const OFFER_CLAIM_ATTR = 'data-reticle-offer-claim';
|
|
4
|
+
export const OFFER_DISMISS_ATTR = 'data-reticle-offer-dismiss';
|
|
5
|
+
export const OFFER_DISMISSED_KEY = 'reticle.harnessOffer.dismissed';
|
|
6
|
+
export const OFFER_TEXT = {
|
|
7
|
+
HEADLINE: 'Reticle drives your app for you',
|
|
8
|
+
BODY: 'The harness explores your app, proves what works, and saves every journey as a test that replays for free. Built on Jev — no model API key needed.',
|
|
9
|
+
FREE: 'Free for 3 months',
|
|
10
|
+
CLAIM: 'Claim now',
|
|
11
|
+
DISMISS: 'Not now',
|
|
12
|
+
claimed: (days) => `Harness active — ${String(days)} days left`,
|
|
13
|
+
ending: (days) => 1 >= days
|
|
14
|
+
? 'Harness free period ends today'
|
|
15
|
+
: `Harness free period ends in ${String(days)} days`,
|
|
16
|
+
};
|
|
17
|
+
const ENDING_SOON_DAYS = 14;
|
|
18
|
+
export function offerDismissed(storage) {
|
|
19
|
+
try {
|
|
20
|
+
return '1' === storage?.getItem(OFFER_DISMISSED_KEY);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function dismissOffer(storage) {
|
|
27
|
+
try {
|
|
28
|
+
storage?.setItem(OFFER_DISMISSED_KEY, '1');
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const escape = (text) => text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
34
|
+
export function offerHtml(offer, dismissed) {
|
|
35
|
+
if (offer === undefined)
|
|
36
|
+
return '';
|
|
37
|
+
if (offer.claimed) {
|
|
38
|
+
const days = offer.daysRemaining;
|
|
39
|
+
if (days === undefined || 0 > days)
|
|
40
|
+
return '';
|
|
41
|
+
const text = ENDING_SOON_DAYS >= days ? OFFER_TEXT.ending(days) : OFFER_TEXT.claimed(days);
|
|
42
|
+
return `<div ${OFFER_ATTR} class="reticle-offer reticle-offer-claimed">${escape(text)}</div>`;
|
|
43
|
+
}
|
|
44
|
+
if (false === offer.eligible)
|
|
45
|
+
return '';
|
|
46
|
+
if (dismissed)
|
|
47
|
+
return '';
|
|
48
|
+
const url = offer.claimUrl;
|
|
49
|
+
if (url === undefined || 0 === url.length)
|
|
50
|
+
return '';
|
|
51
|
+
return `<div ${OFFER_ATTR} class="reticle-offer" role="note">
|
|
52
|
+
<div class="reticle-offer-head">
|
|
53
|
+
<span class="reticle-offer-title">${escape(OFFER_TEXT.HEADLINE)}</span>
|
|
54
|
+
<span class="reticle-offer-free">${escape(OFFER_TEXT.FREE)}</span>
|
|
55
|
+
</div>
|
|
56
|
+
<p class="reticle-offer-body">${escape(OFFER_TEXT.BODY)}</p>
|
|
57
|
+
<div class="reticle-offer-actions">
|
|
58
|
+
<a ${OFFER_CLAIM_ATTR} class="reticle-offer-claim" href="${escape(url)}" target="_blank" rel="noopener noreferrer">${escape(OFFER_TEXT.CLAIM)}</a>
|
|
59
|
+
<button type="button" ${OFFER_DISMISS_ATTR} class="reticle-offer-dismiss">${escape(OFFER_TEXT.DISMISS)}</button>
|
|
60
|
+
</div>
|
|
61
|
+
</div>`;
|
|
62
|
+
}
|
|
63
|
+
export function paintOffer(root, offer, storage) {
|
|
64
|
+
const slot = root.querySelector(`[${OFFER_SLOT_ATTR}]`);
|
|
65
|
+
if (!(slot instanceof HTMLElement))
|
|
66
|
+
return;
|
|
67
|
+
slot.innerHTML = offerHtml(offer, offerDismissed(storage));
|
|
68
|
+
const dismiss = slot.querySelector(`[${OFFER_DISMISS_ATTR}]`);
|
|
69
|
+
dismiss?.addEventListener('click', (event) => {
|
|
70
|
+
event.preventDefault();
|
|
71
|
+
event.stopPropagation();
|
|
72
|
+
dismissOffer(storage);
|
|
73
|
+
slot.innerHTML = '';
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
export const OFFER_CSS = `
|
|
77
|
+
.reticle-offer{margin:8px 10px;padding:10px 12px;border:1px solid var(--reticle-line,#2a2f3a);border-radius:10px;background:var(--reticle-surface-inset,rgba(255,255,255,.03));}
|
|
78
|
+
.reticle-offer-head{display:flex;align-items:center;justify-content:space-between;gap:8px;}
|
|
79
|
+
.reticle-offer-title{font-weight:600;font-size:12px;}
|
|
80
|
+
.reticle-offer-free{font-size:10px;padding:2px 6px;border-radius:999px;background:var(--reticle-accent,#4f7cff);color:#fff;white-space:nowrap;}
|
|
81
|
+
.reticle-offer-body{margin:6px 0 8px;font-size:11px;line-height:1.45;opacity:.8;}
|
|
82
|
+
.reticle-offer-actions{display:flex;align-items:center;gap:8px;}
|
|
83
|
+
.reticle-offer-claim{font-size:11px;font-weight:600;padding:4px 10px;border-radius:6px;background:var(--reticle-accent,#4f7cff);color:#fff;text-decoration:none;}
|
|
84
|
+
.reticle-offer-dismiss{font-size:11px;padding:4px 8px;border-radius:6px;background:none;border:0;color:inherit;opacity:.6;cursor:pointer;}
|
|
85
|
+
.reticle-offer-dismiss:hover{opacity:1;}
|
|
86
|
+
.reticle-offer-claimed{margin:6px 10px;font-size:10px;opacity:.6;}
|
|
87
|
+
`;
|
|
@@ -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 = `
|
|
@@ -68,6 +68,10 @@ export const REPORT_CSS = `
|
|
|
68
68
|
display:block;margin-top:2px;color:var(--reticle-c-active);opacity:.8;
|
|
69
69
|
font-size:8.5px;letter-spacing:.06em;text-transform:uppercase;cursor:help;}
|
|
70
70
|
[${REPORT_PANEL_ATTR}] .reticle-report-defects-wrap{margin-top:14px;}
|
|
71
|
+
/* The heading and its push control on one line, so the button reads as belonging to this section
|
|
72
|
+
rather than floating above the list. */
|
|
73
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-defects-head{
|
|
74
|
+
display:flex;align-items:center;justify-content:space-between;gap:8px;}
|
|
71
75
|
[${REPORT_PANEL_ATTR}] .reticle-report-defects{
|
|
72
76
|
display:flex;flex-direction:column;gap:6px;margin:0;padding:0;list-style:none;}
|
|
73
77
|
/* A left rule rather than a card per row: ten cards in a 320px panel is a wall, and the rule keeps
|
|
@@ -123,4 +127,19 @@ export const REPORT_CSS = `
|
|
|
123
127
|
[${REPORT_PANEL_ATTR}] .reticle-report-links a{
|
|
124
128
|
color:var(--reticle-faint);font-size:10.5px;text-decoration:none;}
|
|
125
129
|
[${REPORT_PANEL_ATTR}] .reticle-report-links a:hover{color:var(--reticle-fg);text-decoration:underline;}
|
|
130
|
+
|
|
131
|
+
/* Who this record belongs to, and the push-now control. Tokens only — no literal colours. */
|
|
132
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-identity{
|
|
133
|
+
display:flex;align-items:center;justify-content:flex-end;gap:6px;padding:10px 14px 0;min-height:0;}
|
|
134
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-identity:empty{display:none;}
|
|
135
|
+
[${REPORT_PANEL_ATTR}] .reticle-sync-now{
|
|
136
|
+
display:inline-flex;align-items:center;justify-content:center;
|
|
137
|
+
border:none;background:transparent;color:var(--reticle-faint);cursor:pointer;line-height:0;padding:2px;}
|
|
138
|
+
[${REPORT_PANEL_ATTR}] .reticle-sync-now:hover{color:var(--reticle-fg);}
|
|
139
|
+
/* The one moment this control has an opinion: it says it is going, not that it has arrived. */
|
|
140
|
+
[${REPORT_PANEL_ATTR}] .reticle-sync-now[data-syncing="1"]{
|
|
141
|
+
color:var(--reticle-c-active);animation:reticle-sync-spin 900ms linear infinite;}
|
|
142
|
+
@keyframes reticle-sync-spin{to{transform:rotate(360deg);}}
|
|
143
|
+
@media (prefers-reduced-motion:reduce){
|
|
144
|
+
[${REPORT_PANEL_ATTR}] .reticle-sync-now[data-syncing="1"]{animation:none;}}
|
|
126
145
|
`;
|
|
@@ -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, '&')
|
|
61
|
-
.replace(/</g, '<')
|
|
62
|
-
.replace(/>/g, '>')
|
|
63
|
-
.replace(/"/g, '"');
|
|
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)
|
|
@@ -93,9 +79,10 @@ function defects(scope, dashboardUrl) {
|
|
|
93
79
|
const more = !linked
|
|
94
80
|
? ''
|
|
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
|
+
const sync = syncButtonHtml(dashboardUrl);
|
|
83
|
+
return `<div class="reticle-report-defects-wrap"><div class="reticle-report-defects-head"><span class="reticle-report-section">${REPORT_TEXT.DEFECTS}</span>${sync}</div><ul class="reticle-report-defects">${rows}</ul>${more}</div>`;
|
|
97
84
|
}
|
|
98
|
-
export function reportBodyHtml(scope, dashboardUrl, account) {
|
|
85
|
+
export function reportBodyHtml(scope, dashboardUrl, account, projectName) {
|
|
99
86
|
const c = scope.counts;
|
|
100
87
|
if (0 === c.calls)
|
|
101
88
|
return `<p class="reticle-report-empty">${REPORT_TEXT.EMPTY}</p>`;
|
|
@@ -118,7 +105,8 @@ export function reportBodyHtml(scope, dashboardUrl, account) {
|
|
|
118
105
|
card(compactNumber(scope.savings.tokens.value), REPORT_TEXT.SAVED_TOKENS, scope.savings.tokens.basis),
|
|
119
106
|
card(compactDuration(scope.savings.minutes.value * 60_000), REPORT_TEXT.SAVED_MINUTES, scope.savings.minutes.basis),
|
|
120
107
|
].join('');
|
|
121
|
-
|
|
108
|
+
const identity = `<div class="reticle-report-identity">${accountControlHtml(account, { dashboardUrl, projectName, verdicts: c.verdicts, defects: c.failed })}${syncButtonHtml(dashboardUrl)}</div>`;
|
|
109
|
+
return `${identity}${streak}${hero}${verdicts}<div class="reticle-report-grid">${cards}</div>${defects(scope, dashboardUrl)}${chart(scope)}${localOnly(scope, dashboardUrl, account)}`;
|
|
122
110
|
}
|
|
123
111
|
function localOnly(scope, dashboardUrl, account) {
|
|
124
112
|
if (dashboardUrl !== undefined)
|
|
@@ -132,6 +120,8 @@ function localOnly(scope, dashboardUrl, account) {
|
|
|
132
120
|
: [REPORT_TEXT.LOCAL_ONLY, REPORT_TEXT.LOCAL_ONLY_ACTION, REPORT_TEXT.LOCAL_ONLY_TAIL];
|
|
133
121
|
return `<p class="reticle-report-local-only">${lead} <code>${action}</code> ${tail}</p>`;
|
|
134
122
|
}
|
|
123
|
+
const SYNC_SPIN_MS = 1_200;
|
|
124
|
+
const SYNCING_ATTR = 'data-syncing';
|
|
135
125
|
export class PresenterReport {
|
|
136
126
|
#panel;
|
|
137
127
|
#body;
|
|
@@ -175,6 +165,24 @@ export class PresenterReport {
|
|
|
175
165
|
e.stopPropagation();
|
|
176
166
|
void this.#copy(buildReferralText(), e.currentTarget);
|
|
177
167
|
});
|
|
168
|
+
root.addEventListener('click', (e) => {
|
|
169
|
+
const target = e.target;
|
|
170
|
+
if (!(target instanceof Element))
|
|
171
|
+
return;
|
|
172
|
+
const signin = target.closest(`[${ACCOUNT_SIGNIN_ATTR}]`);
|
|
173
|
+
if (signin !== null) {
|
|
174
|
+
e.stopPropagation();
|
|
175
|
+
void this.#copy(ACCOUNT_TEXT.SIGNIN_COMMAND, signin);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const sync = target.closest(`[${SYNC_BTN_ATTR}]`);
|
|
179
|
+
if (sync !== null) {
|
|
180
|
+
e.stopPropagation();
|
|
181
|
+
this.#host.onSyncNow?.();
|
|
182
|
+
sync.setAttribute(SYNCING_ATTR, '1');
|
|
183
|
+
setTimeout(() => sync.removeAttribute(SYNCING_ATTR), SYNC_SPIN_MS);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
178
186
|
}
|
|
179
187
|
setSnapshot(snapshot) {
|
|
180
188
|
this.#snapshot = snapshot;
|
|
@@ -229,7 +237,7 @@ export class PresenterReport {
|
|
|
229
237
|
this.#body.innerHTML =
|
|
230
238
|
scope === undefined
|
|
231
239
|
? `<p class="reticle-report-empty">${REPORT_TEXT.EMPTY}</p>`
|
|
232
|
-
: reportBodyHtml(scope, this.#snapshot?.dashboardUrl, this.#snapshot?.account);
|
|
240
|
+
: reportBodyHtml(scope, this.#snapshot?.dashboardUrl, this.#snapshot?.account, this.#snapshot?.projectName);
|
|
233
241
|
}
|
|
234
242
|
#openShare(url) {
|
|
235
243
|
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,7 @@
|
|
|
1
|
+
import type { HarnessConfig } from '@reticlehq/core';
|
|
1
2
|
import { SETTINGS_CSS } from './presenter-settings-styles.js';
|
|
3
|
+
import type { AccountState } from '@reticlehq/core';
|
|
4
|
+
import { type AccountDetails } from './presenter-account.js';
|
|
2
5
|
export { SETTINGS_CSS };
|
|
3
6
|
export declare const OutputDetail: {
|
|
4
7
|
readonly MINIMAL: "minimal";
|
|
@@ -38,9 +41,13 @@ export interface PresenterSettings {
|
|
|
38
41
|
}
|
|
39
42
|
export declare function getPresenterSettings(): PresenterSettings;
|
|
40
43
|
export declare function loadPresenterSettings(): PresenterSettings;
|
|
44
|
+
export declare const SETTINGS_ACCOUNT_ATTR = "data-reticle-settings-account";
|
|
45
|
+
export declare function paintHarnessRow(root: ParentNode, config: HarnessConfig | undefined): void;
|
|
46
|
+
export declare function paintSettingsAccount(root: HTMLElement, account: AccountState | undefined, dashboardUrl: string | undefined, details?: AccountDetails): void;
|
|
41
47
|
export declare function settingsPanelHtml(): string;
|
|
42
48
|
export interface SettingsHost {
|
|
43
49
|
onHideUntilRestart?: () => void;
|
|
50
|
+
onHarness?: (enabled: boolean) => void;
|
|
44
51
|
onSettingsChange?: (settings: PresenterSettings) => void;
|
|
45
52
|
onBeforeOpen?: () => void;
|
|
46
53
|
}
|
|
@@ -52,6 +59,7 @@ export declare class PresenterSettingsPanel {
|
|
|
52
59
|
constructor(host?: SettingsHost);
|
|
53
60
|
contains(node: Node): boolean;
|
|
54
61
|
mount(root: HTMLElement): void;
|
|
62
|
+
paintHarness(config: HarnessConfig | undefined): void;
|
|
55
63
|
teardown(): void;
|
|
56
64
|
isOpen(): boolean;
|
|
57
65
|
open(): 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,12 +147,47 @@ 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 SETTINGS_HARNESS_ROW_ATTR = 'data-reticle-settings-harness-row';
|
|
153
|
+
const HARNESS_HELP = 'Let Reticle drive this app by itself to find defects. Set here or in your dashboard — both write to the same place.';
|
|
154
|
+
const HARNESS_LOCKED_HELP = 'Autonomous driving runs on Reticle’s model spend, so it needs a plan or the free period. Claim it from the chat panel.';
|
|
155
|
+
const ACCOUNT_HELP = 'Whether this machine is signed in to a Reticle workspace. Signing in happens in your terminal.';
|
|
156
|
+
export function paintHarnessRow(root, config) {
|
|
157
|
+
const row = root.querySelector(`[${SETTINGS_HARNESS_ROW_ATTR}]`);
|
|
158
|
+
if (!(row instanceof HTMLElement))
|
|
159
|
+
return;
|
|
160
|
+
if (config === undefined) {
|
|
161
|
+
row.hidden = true;
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
row.hidden = false;
|
|
165
|
+
const toggle = row.querySelector(`[${SETTING_KEY_ATTR}="harnessEnabled"]`);
|
|
166
|
+
if (!(toggle instanceof HTMLElement))
|
|
167
|
+
return;
|
|
168
|
+
const usable = config.harnessEntitled;
|
|
169
|
+
toggle.setAttribute('aria-checked', config.harnessEnabled && usable ? 'true' : 'false');
|
|
170
|
+
toggle.setAttribute('aria-disabled', usable ? 'false' : 'true');
|
|
171
|
+
const help = row.querySelector('[data-reticle-help]');
|
|
172
|
+
if (help instanceof HTMLElement)
|
|
173
|
+
help.title = usable ? HARNESS_HELP : HARNESS_LOCKED_HELP;
|
|
174
|
+
}
|
|
175
|
+
export function paintSettingsAccount(root, account, dashboardUrl, details = {}) {
|
|
176
|
+
const row = root.querySelector(`[${SETTINGS_ACCOUNT_ROW_ATTR}]`);
|
|
177
|
+
const slot = root.querySelector(`[${SETTINGS_ACCOUNT_ATTR}]`);
|
|
178
|
+
if (!(row instanceof HTMLElement) || !(slot instanceof HTMLElement))
|
|
179
|
+
return;
|
|
180
|
+
const html = accountControlHtml(account, { ...details, dashboardUrl }, true);
|
|
181
|
+
row.hidden = 0 === html.length;
|
|
182
|
+
slot.innerHTML = html;
|
|
183
|
+
}
|
|
149
184
|
export function settingsPanelHtml() {
|
|
150
185
|
const close = hiIconHtml(PresenterIcon.REMOVE, PRESENTER_ICON_SIZE.MIN);
|
|
151
186
|
const caret = hiIconHtml(PresenterIcon.CARET_RIGHT, PRESENTER_ICON_SIZE.HELP);
|
|
152
187
|
const outputHelp = 'How much detail is included when you copy or export the run';
|
|
153
188
|
const reactHelp = 'Include React component paths in exported run state when available';
|
|
154
189
|
const hideHelp = 'Hide the Reticle HUD until you reload the page';
|
|
190
|
+
const harnessHelp = HARNESS_HELP;
|
|
155
191
|
const tallyHelp = 'Show the pass/fail score pill in the toolbar';
|
|
156
192
|
const timestampsHelp = 'Show relative timestamps on each activity-log row';
|
|
157
193
|
const autoChatHelp = 'On by default. Opens the agent chat by itself when a session starts and when you expand the HUD. Off leaves the toolbar bare until you ask for the chat.';
|
|
@@ -178,7 +214,13 @@ export function settingsPanelHtml() {
|
|
|
178
214
|
${settingsCheckRow('blockPageInteractions', 'Block page interactions', true)}
|
|
179
215
|
${settingsCheckRow('clearOnCopy', 'Clear on copy/send', false)}
|
|
180
216
|
${settingsToggleRow('hideUntilRestart', 'Hide Until Restart', hideHelp)}
|
|
217
|
+
${settingsToggleRow('harnessEnabled', 'Autonomous driving', harnessHelp, `${SETTINGS_HARNESS_ROW_ATTR} hidden`)}
|
|
181
218
|
${settingsToggleRow('reduceMotion', 'Reduce motion', motionHelp)}
|
|
219
|
+
<div class="reticle-settings-section">Account</div>
|
|
220
|
+
<div class="reticle-settings-row" ${SETTINGS_ACCOUNT_ROW_ATTR} hidden>
|
|
221
|
+
${settingsLabel('Workspace', ACCOUNT_HELP)}
|
|
222
|
+
<span ${SETTINGS_ACCOUNT_ATTR}></span>
|
|
223
|
+
</div>
|
|
182
224
|
<div class="reticle-settings-section">Status theme</div>
|
|
183
225
|
${settingsToggleRow('ambientGlow', 'Page glow', glowHelp)}
|
|
184
226
|
<div class="reticle-settings-themes" data-reticle-settings-themes></div>
|
|
@@ -268,7 +310,14 @@ export class PresenterSettingsPanel {
|
|
|
268
310
|
if (!(toggle instanceof HTMLElement))
|
|
269
311
|
return;
|
|
270
312
|
const key = toggle.getAttribute(SETTING_KEY_ATTR);
|
|
271
|
-
if ('
|
|
313
|
+
if ('harnessEnabled' === key) {
|
|
314
|
+
if ('true' === toggle.getAttribute('aria-disabled'))
|
|
315
|
+
return;
|
|
316
|
+
const next = 'true' !== toggle.getAttribute('aria-checked');
|
|
317
|
+
toggle.setAttribute('aria-checked', next ? 'true' : 'false');
|
|
318
|
+
this.#host.onHarness?.(next);
|
|
319
|
+
}
|
|
320
|
+
else if ('reactComponents' === key) {
|
|
272
321
|
this.#update({ reactComponents: !activeSettings.reactComponents });
|
|
273
322
|
}
|
|
274
323
|
else if ('hideUntilRestart' === key) {
|
|
@@ -350,6 +399,12 @@ export class PresenterSettingsPanel {
|
|
|
350
399
|
applyPresenterSettings(root, activeSettings);
|
|
351
400
|
this.#host.onSettingsChange?.(activeSettings);
|
|
352
401
|
}
|
|
402
|
+
paintHarness(config) {
|
|
403
|
+
const root = this.#panel;
|
|
404
|
+
if (root === undefined)
|
|
405
|
+
return;
|
|
406
|
+
paintHarnessRow(root, config);
|
|
407
|
+
}
|
|
353
408
|
teardown() {
|
|
354
409
|
this.#root = undefined;
|
|
355
410
|
this.#panel = undefined;
|
|
@@ -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';
|
|
@@ -83,11 +83,23 @@ export const SHELL_CSS = `
|
|
|
83
83
|
right:auto;left:0;}
|
|
84
84
|
[${OVERLAY}][${CHAT_ATTR}="1"] [${CHAT_PANEL}]{
|
|
85
85
|
display:flex;pointer-events:auto;}
|
|
86
|
+
/**
|
|
87
|
+
* Idle, and not blocking the page: the card is not in use.
|
|
88
|
+
*
|
|
89
|
+
* The log fills the panel and sets pointer-events itself, so a click meant for a control sitting
|
|
90
|
+
* under the card landed on an empty transcript. Playwright named that div. The card passes the
|
|
91
|
+
* event through. The buttons stay clickable, which is the chrome a person still needs (#992).
|
|
92
|
+
*/
|
|
93
|
+
[${OVERLAY}][data-reticle-mode="idle"][data-reticle-block="0"] [${CHAT_PANEL}],
|
|
94
|
+
[${OVERLAY}][data-reticle-mode="idle"][data-reticle-block="0"] [${CHAT_PANEL}] [data-reticle-log]{
|
|
95
|
+
pointer-events:none;}
|
|
96
|
+
[${OVERLAY}][data-reticle-mode="idle"][data-reticle-block="0"] [${CHAT_PANEL}] :is(button,a,input,select,textarea){
|
|
97
|
+
pointer-events:auto;}
|
|
86
98
|
/**
|
|
87
99
|
* Minimising the chat leaves a CAPSULE, not a hole: the same glass, the same state dot, the last
|
|
88
100
|
* thing the agent did - sitting directly above the toolbar capsule and reopening the panel when
|
|
89
|
-
* clicked.
|
|
90
|
-
*
|
|
101
|
+
* clicked. Leaving the toolbar alone above an empty gap would make a minimised session look
|
|
102
|
+
* identical to no session at all.
|
|
91
103
|
*/
|
|
92
104
|
[${CHAT_PILL_ATTR}]{
|
|
93
105
|
display:none;position:absolute;right:0;left:auto;bottom:calc(100% + 8px);z-index:5;
|
|
@@ -115,6 +127,13 @@ export const SHELL_CSS = `
|
|
|
115
127
|
flex:none;display:flex;align-items:center;gap:8px;padding:11px 44px 9px 14px;
|
|
116
128
|
border-bottom:1px solid rgba(255,255,255,.06);}
|
|
117
129
|
[${CHAT_PANEL}] .reticle-chat-brand{display:inline-flex;align-items:center;gap:7px;color:var(--reticle-fg);}
|
|
130
|
+
/* The account capsule lives in the header, not the toolbar. The toolbar row is already 13px over its
|
|
131
|
+
own width, so a 20px avatar there pushed the close button outside the panel. The header has the
|
|
132
|
+
space, is visible under exactly the same condition, and is where an account avatar belongs anyway.
|
|
133
|
+
margin-left:auto right-aligns it; the head reserves 44px of right padding for the caret above it,
|
|
134
|
+
so the two never overlap. The :empty rule keeps the gap closed when no account state was sent. */
|
|
135
|
+
[${CHAT_PANEL}] .reticle-head-account{margin-left:auto;display:inline-flex;align-items:center;}
|
|
136
|
+
[${CHAT_PANEL}] .reticle-head-account:empty{display:none;}
|
|
118
137
|
[${CHAT_PANEL}] .reticle-chat-brand .reticle-mark{height:14px;width:auto;}
|
|
119
138
|
[${CHAT_PANEL}] .reticle-chat-brandname{font-size:12.5px;font-weight:600;letter-spacing:.01em;}
|
|
120
139
|
/* No state word here: the activity strip immediately below already says "idle · 21s", and the
|
|
@@ -234,6 +253,7 @@ export const SHELL_CSS = `
|
|
|
234
253
|
padding:2px;border-radius:999px;background:rgba(0,0,0,.2);}
|
|
235
254
|
[${HUD}] .reticle-toolbar-chrome{display:inline-flex;align-items:center;gap:2px;flex:none;}
|
|
236
255
|
[${HUD}] .reticle-tb-sep{width:1px;height:14px;background:rgba(255,255,255,.12);margin:0 4px;flex:none;align-self:center;}
|
|
256
|
+
|
|
237
257
|
[${HUD}] .reticle-tb-wrap{position:relative;display:flex;align-items:center;justify-content:center;overflow:visible;}
|
|
238
258
|
[${HUD}] .reticle-tb-btn{
|
|
239
259
|
flex:none;display:inline-flex;align-items:center;justify-content:center;
|
|
@@ -259,10 +279,9 @@ export const SHELL_CSS = `
|
|
|
259
279
|
[${HUD}] .reticle-hi-toggle .reticle-hi-icon--solid svg{
|
|
260
280
|
transform:scale(1.12);transform-origin:center;}
|
|
261
281
|
/*
|
|
262
|
-
* Active toggles keep the OUTLINE icon.
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
* which is enough and does not change the icon's weight.
|
|
282
|
+
* Active toggles keep the OUTLINE icon. The solid heroicon is a filled glyph next to 1.5px strokes
|
|
283
|
+
* everywhere else, so a pressed button reads as a heavier typeface rather than as a state. Accent
|
|
284
|
+
* colour and the background above already carry the state without changing the icon's weight.
|
|
266
285
|
*/
|
|
267
286
|
[${HUD}] .reticle-tb-btn--toggle[data-active="1"] .reticle-hi-icon--outline{opacity:1;}
|
|
268
287
|
[${HUD}] .reticle-tb-btn--toggle .reticle-hi-icon--solid{opacity:0;}
|
|
@@ -274,10 +293,10 @@ export const SHELL_CSS = `
|
|
|
274
293
|
/**
|
|
275
294
|
* The toolbar is a FIXED number of slots.
|
|
276
295
|
*
|
|
277
|
-
* Copy and Export appear when a session ends, and
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
296
|
+
* Copy and Export appear when a session ends, and the bar is already full - eleven icons in a pill
|
|
297
|
+
* sized for nine renders the last one outside the rounded box. They take the two slots Pause and End
|
|
298
|
+
* vacate: neither can do anything to an ended session, so the bar swaps two dead controls for two
|
|
299
|
+
* live ones and never changes width.
|
|
281
300
|
*/
|
|
282
301
|
[${HUD}] .reticle-tb-btn--export{display:none;}
|
|
283
302
|
[${OVERLAY}][${STATE}="ended"] [${HUD}] .reticle-tb-btn--export{display:inline-flex;}
|
|
@@ -307,9 +326,8 @@ export const SHELL_CSS = `
|
|
|
307
326
|
* The mode, ON the status row rather than under it.
|
|
308
327
|
*
|
|
309
328
|
* 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:
|
|
311
|
-
*
|
|
312
|
-
* second UI appearing and leaving.
|
|
329
|
+
* from the elided action text. Nothing above or below moves: growing and shrinking by the height of
|
|
330
|
+
* this pill on every tool call makes one status line look like a second UI appearing and leaving.
|
|
313
331
|
*/
|
|
314
332
|
[${DOCK_ATTR}] .reticle-chip{display:none;flex:none;align-items:center;gap:4px;font-size:8px;font-weight:600;letter-spacing:.06em;
|
|
315
333
|
height:16px;padding:0 7px;line-height:1;border-radius:999px;text-transform:uppercase;
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import type { HarnessConfig } from '@reticlehq/core';
|
|
1
2
|
import { PresenterReport } from './presenter-report.js';
|
|
3
|
+
import type { AccountState } from '@reticlehq/core';
|
|
4
|
+
import { type AccountDetails } from './presenter-account.js';
|
|
5
|
+
import { type OfferState } from './presenter-offer.js';
|
|
2
6
|
import { type SettingsHost } from './presenter-settings.js';
|
|
3
7
|
interface HudShellCallbacks {
|
|
4
8
|
onChatOpen?: () => void;
|
|
@@ -6,10 +10,14 @@ interface HudShellCallbacks {
|
|
|
6
10
|
onExpand?: () => void;
|
|
7
11
|
onAnnotateToggle?: (on: boolean) => void;
|
|
8
12
|
onCollapse?: () => void;
|
|
13
|
+
onSyncNow?: () => void;
|
|
9
14
|
settings?: SettingsHost;
|
|
10
15
|
}
|
|
11
16
|
export declare class HudShell {
|
|
12
17
|
#private;
|
|
18
|
+
paintHarness(config: HarnessConfig | undefined): void;
|
|
19
|
+
paintAccount(account: AccountState | undefined, dashboardUrl: string | undefined, details?: AccountDetails): void;
|
|
20
|
+
paintOffer(offer: OfferState | undefined): void;
|
|
13
21
|
constructor(callbacks?: HudShellCallbacks);
|
|
14
22
|
static dockHtml(actStripHtml: string, bannerHtml: string, logAttr: string, flowsHtml: string, footHtml: string): string;
|
|
15
23
|
get report(): PresenterReport;
|
|
@@ -1,11 +1,15 @@
|
|
|
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 {
|
|
4
|
-
import {
|
|
6
|
+
import { OFFER_SLOT_ATTR, paintOffer } from './presenter-offer.js';
|
|
7
|
+
import { BRAND_NAME, FAB_TOGGLE_HTML, MARK_SVG } from './chrome/presenter-brand.js';
|
|
8
|
+
import { settleLogAtLatest } from './chrome/presenter-log.js';
|
|
5
9
|
import { installHudDragHandles, installHudPositionGuards } from './presenter-drag.js';
|
|
6
10
|
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';
|
|
11
|
+
import { hiIconHtml, hiToggleIconHtml, PRESENTER_ICON_SIZE, PresenterIcon, } from './icons/presenter-icons.js';
|
|
12
|
+
import { HUD_SURFACE_CLASS, HUD_LOG_WELL_CLASS } from './chrome/presenter-hud-chrome.js';
|
|
9
13
|
import { CONTROLS_TOOLBAR_HTML } from './presenter-controls.js';
|
|
10
14
|
import { PresenterSettingsPanel, settingsPanelHtml, } from './presenter-settings.js';
|
|
11
15
|
const DRAG_HANDLE_CLASS = 'reticle-toolbar-drag';
|
|
@@ -37,8 +41,36 @@ export class HudShell {
|
|
|
37
41
|
this.#settings.close();
|
|
38
42
|
this.closeChat();
|
|
39
43
|
},
|
|
44
|
+
onSyncNow: () => this.#callbacks.onSyncNow?.(),
|
|
40
45
|
});
|
|
41
46
|
#callbacks;
|
|
47
|
+
#pushedAccount;
|
|
48
|
+
#accountTeardown;
|
|
49
|
+
paintHarness(config) {
|
|
50
|
+
this.#settings.paintHarness(config);
|
|
51
|
+
}
|
|
52
|
+
paintAccount(account, dashboardUrl, details = {}) {
|
|
53
|
+
this.#pushedAccount = { account, dashboardUrl, details };
|
|
54
|
+
if (this.#root === undefined)
|
|
55
|
+
return;
|
|
56
|
+
paintToolbarAccount(this.#root, account, dashboardUrl, details);
|
|
57
|
+
paintSettingsAccount(this.#root, account, dashboardUrl, details);
|
|
58
|
+
}
|
|
59
|
+
#pushedOffer;
|
|
60
|
+
paintOffer(offer) {
|
|
61
|
+
this.#pushedOffer = offer;
|
|
62
|
+
if (this.#root === undefined)
|
|
63
|
+
return;
|
|
64
|
+
paintOffer(this.#root, offer, this.#storage());
|
|
65
|
+
}
|
|
66
|
+
#storage() {
|
|
67
|
+
try {
|
|
68
|
+
return globalThis.localStorage;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
42
74
|
constructor(callbacks = {}) {
|
|
43
75
|
this.#callbacks = callbacks;
|
|
44
76
|
this.#settings = new PresenterSettingsPanel({
|
|
@@ -59,11 +91,13 @@ export class HudShell {
|
|
|
59
91
|
<div ${CHAT_PANEL_ATTR} class="reticle-chat-panel ${HUD_SURFACE_CLASS}" role="region" aria-label="Reticle session" aria-hidden="true">
|
|
60
92
|
<div class="reticle-chat-head">
|
|
61
93
|
<span class="reticle-chat-brand">${MARK_SVG}<span class="reticle-chat-brandname">${BRAND_NAME}</span></span>
|
|
94
|
+
<span ${TOOLBAR_ACCOUNT_ATTR} class="reticle-head-account"></span>
|
|
62
95
|
</div>
|
|
63
96
|
<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
97
|
${actStripHtml}
|
|
65
98
|
<span class="reticle-tally" data-reticle-tally hidden></span>
|
|
66
99
|
${bannerHtml}
|
|
100
|
+
<div ${OFFER_SLOT_ATTR}></div>
|
|
67
101
|
<div class="${HUD_LOG_WELL_CLASS}"><div ${logAttr}></div></div>
|
|
68
102
|
${flowsHtml}
|
|
69
103
|
${footHtml}
|
|
@@ -209,8 +243,17 @@ export class HudShell {
|
|
|
209
243
|
}
|
|
210
244
|
document.addEventListener('pointerdown', this.#onDocPointerDown, { signal });
|
|
211
245
|
document.addEventListener('keydown', this.#onKeyDown, { signal });
|
|
246
|
+
this.#accountTeardown = mountAccountControl(root);
|
|
247
|
+
if (this.#pushedAccount !== undefined) {
|
|
248
|
+
const pushed = this.#pushedAccount;
|
|
249
|
+
this.paintAccount(pushed.account, pushed.dashboardUrl, pushed.details);
|
|
250
|
+
}
|
|
251
|
+
if (this.#pushedOffer !== undefined)
|
|
252
|
+
this.paintOffer(this.#pushedOffer);
|
|
212
253
|
}
|
|
213
254
|
teardown() {
|
|
255
|
+
this.#accountTeardown?.();
|
|
256
|
+
this.#accountTeardown = undefined;
|
|
214
257
|
this.#listeners?.abort();
|
|
215
258
|
this.#listeners = undefined;
|
|
216
259
|
this.#toggleSync?.disconnect();
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Z_OVERLAY } from './chrome/layers.js';
|
|
2
|
+
import { LOG_CSS } from './chrome/presenter-log.js';
|
|
2
3
|
import { CONTROLS_CSS } from './presenter-controls.js';
|
|
3
4
|
import { SHELL_CSS } from './presenter-shell-styles.js';
|
|
4
5
|
import { SETTINGS_CSS } from './presenter-settings-styles.js';
|
|
5
|
-
import {
|
|
6
|
+
import { ACCOUNT_CSS } from './presenter-account-styles.js';
|
|
7
|
+
import { HUD_CHROME_CSS, HUD_LOG_WELL_CSS } from './chrome/presenter-hud-chrome.js';
|
|
6
8
|
import { REPORT_CSS } from './presenter-report-styles.js';
|
|
7
|
-
|
|
9
|
+
import { OFFER_CSS } from './presenter-offer.js';
|
|
10
|
+
export { Z_OVERLAY };
|
|
8
11
|
export const PRESENTER_CSS = `
|
|
9
12
|
[data-reticle-overlay]{position:fixed;inset:0;pointer-events:none;z-index:${String(Z_OVERLAY)};}
|
|
10
13
|
/**
|
|
@@ -60,5 +63,7 @@ ${HUD_LOG_WELL_CSS}
|
|
|
60
63
|
${SHELL_CSS}
|
|
61
64
|
${SETTINGS_CSS}
|
|
62
65
|
${REPORT_CSS}
|
|
66
|
+
${ACCOUNT_CSS}
|
|
67
|
+
${OFFER_CSS}
|
|
63
68
|
${LOG_CSS}
|
|
64
69
|
${CONTROLS_CSS}`;
|