@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.
- package/dist/actions/action-report.d.ts +54 -0
- package/dist/actions/action-report.js +60 -0
- package/dist/actions/actions-dom.d.ts +1 -0
- package/dist/actions/actions-dom.js +56 -43
- package/dist/actions/actions-press.d.ts +11 -4
- package/dist/actions/actions-press.js +132 -17
- package/dist/actions/actions.d.ts +1 -35
- package/dist/actions/actions.js +32 -65
- package/dist/actions/appeared-text.js +3 -0
- package/dist/actions/danger-context.d.ts +2 -0
- package/dist/actions/danger-context.js +29 -3
- package/dist/actions/value-input.d.ts +1 -0
- package/dist/actions/value-input.js +11 -0
- package/dist/commands/commands.js +4 -0
- package/dist/connect-options.d.ts +1 -0
- package/dist/dom/a11y.js +264 -14
- package/dist/dom/addressing/coverage-markers.d.ts +6 -0
- package/dist/dom/addressing/coverage-markers.js +13 -0
- package/dist/dom/addressing/refs.js +2 -1
- package/dist/dom/addressing/testid-attr.d.ts +4 -0
- package/dist/dom/addressing/testid-attr.js +14 -0
- package/dist/dom/dom-ignore.d.ts +2 -0
- package/dist/dom/dom-ignore.js +15 -2
- package/dist/dom/query.d.ts +2 -1
- package/dist/dom/query.js +116 -27
- package/dist/dom/render-page.d.ts +12 -0
- package/dist/dom/render-page.js +135 -0
- package/dist/dom/snapshot.js +56 -7
- package/dist/observers/departure.js +51 -9
- package/dist/observers/dom.js +35 -18
- package/dist/observers/field.js +5 -21
- package/dist/observers/health.js +63 -2
- package/dist/observers/network-stream.d.ts +1 -1
- package/dist/observers/network-stream.js +3 -3
- package/dist/observers/network.js +57 -8
- package/dist/observers/perf.js +53 -1
- package/dist/observers/route.js +6 -5
- package/dist/observers/storage.js +6 -1
- package/dist/presenter/carousel/carousel.d.ts +5 -2
- package/dist/presenter/carousel/carousel.js +30 -8
- package/dist/presenter/carousel/panel-slides.d.ts +3 -4
- package/dist/presenter/carousel/panel-slides.js +48 -9
- package/dist/presenter/carousel/talk-card.d.ts +1 -1
- package/dist/presenter/chrome/log-kinds.d.ts +5 -0
- package/dist/presenter/chrome/log-kinds.js +1 -0
- package/dist/presenter/chrome/log-memory.d.ts +13 -0
- package/dist/presenter/chrome/log-memory.js +39 -0
- package/dist/presenter/chrome/presenter-hud-chrome.d.ts +1 -0
- package/dist/presenter/chrome/presenter-hud-chrome.js +7 -1
- package/dist/presenter/chrome/presenter-log.d.ts +12 -4
- package/dist/presenter/chrome/presenter-log.js +100 -10
- package/dist/presenter/chrome/presenter-morph.d.ts +1 -0
- package/dist/presenter/chrome/presenter-morph.js +34 -0
- package/dist/presenter/chrome/presenter-report-copy.d.ts +28 -5
- package/dist/presenter/chrome/presenter-report-copy.js +40 -5
- package/dist/presenter/hud-telemetry.js +83 -3
- package/dist/presenter/icons/presenter-heroicons-data.d.ts +1 -0
- package/dist/presenter/icons/presenter-heroicons-data.js +4 -3
- package/dist/presenter/icons/presenter-icons.d.ts +2 -0
- package/dist/presenter/icons/presenter-icons.js +2 -0
- package/dist/presenter/presenter-account-styles.js +3 -3
- package/dist/presenter/presenter-account.d.ts +31 -8
- package/dist/presenter/presenter-account.js +105 -15
- package/dist/presenter/presenter-agent-link.d.ts +9 -0
- package/dist/presenter/presenter-agent-link.js +62 -0
- package/dist/presenter/presenter-chat-views.d.ts +43 -0
- package/dist/presenter/presenter-chat-views.js +622 -0
- package/dist/presenter/presenter-config.d.ts +5 -2
- package/dist/presenter/presenter-config.js +5 -3
- package/dist/presenter/presenter-controls.d.ts +3 -2
- package/dist/presenter/presenter-controls.js +163 -61
- package/dist/presenter/presenter-design-tokens.d.ts +1 -0
- package/dist/presenter/presenter-design-tokens.js +34 -0
- package/dist/presenter/presenter-dock-layout.js +1 -1
- package/dist/presenter/presenter-frame.d.ts +12 -0
- package/dist/presenter/presenter-frame.js +187 -0
- package/dist/presenter/presenter-harness-row.d.ts +67 -0
- package/dist/presenter/presenter-harness-row.js +209 -0
- package/dist/presenter/presenter-personas.d.ts +35 -0
- package/dist/presenter/presenter-personas.js +95 -0
- package/dist/presenter/presenter-plan.d.ts +10 -0
- package/dist/presenter/presenter-plan.js +186 -0
- package/dist/presenter/presenter-report-styles.js +46 -21
- package/dist/presenter/presenter-report.d.ts +2 -1
- package/dist/presenter/presenter-report.js +123 -20
- package/dist/presenter/presenter-settings-styles.js +18 -4
- package/dist/presenter/presenter-settings.d.ts +8 -2
- package/dist/presenter/presenter-settings.js +134 -33
- package/dist/presenter/presenter-shell-styles.js +48 -42
- package/dist/presenter/presenter-shell.d.ts +17 -4
- package/dist/presenter/presenter-shell.js +139 -79
- package/dist/presenter/presenter-styles.js +9 -3
- package/dist/presenter/presenter-workspace.js +10 -3
- package/dist/presenter/presenter.d.ts +6 -4
- package/dist/presenter/presenter.js +171 -22
- package/dist/presenter/tour/tour.d.ts +4 -1
- package/dist/presenter/tour/tour.js +6 -4
- package/dist/recorder/recorder.js +2 -2
- package/dist/registry/auto-testids.js +3 -4
- package/dist/registry/declared-platform.js +4 -0
- package/dist/reticle-inject.js +938 -494
- package/dist/reticle.d.ts +10 -1
- package/dist/reticle.js +109 -11
- package/dist/review/annotator-styles.d.ts +1 -1
- package/dist/review/annotator.d.ts +7 -0
- package/dist/review/annotator.js +20 -2
- package/dist/review/mark-anchor.js +2 -2
- package/dist/session-continuity.js +2 -1
- package/dist/storage-keys.d.ts +15 -0
- package/dist/storage-keys.js +21 -0
- package/dist/transport/transport.js +5 -3
- package/dist/transport/unreachable-message.d.ts +1 -1
- package/dist/transport/unreachable-message.js +3 -5
- package/package.json +3 -2
- package/dist/presenter/carousel/offer-card.d.ts +0 -21
- package/dist/presenter/carousel/offer-card.js +0 -55
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { ScriptStatus } from '@reticlehq/core';
|
|
2
|
+
export const PLAN_ATTR = 'data-reticle-plan';
|
|
3
|
+
const CLOSE_ATTR = 'data-reticle-plan-close';
|
|
4
|
+
const JOURNEY_ATTR = 'data-reticle-plan-journey';
|
|
5
|
+
export const PLAN_HTML = `<div ${PLAN_ATTR} class="reticle-plan" hidden></div>`;
|
|
6
|
+
const TEXT = {
|
|
7
|
+
TITLE: 'Harness plan',
|
|
8
|
+
CLOSE: 'Hide the plan',
|
|
9
|
+
WAITS: 'waits on',
|
|
10
|
+
AT_ONCE: 'at once',
|
|
11
|
+
LANE: 'Lane',
|
|
12
|
+
};
|
|
13
|
+
const GLYPH = {
|
|
14
|
+
[ScriptStatus.PENDING]: '○',
|
|
15
|
+
[ScriptStatus.RUNNING]: '◐',
|
|
16
|
+
[ScriptStatus.PASSED]: '✓',
|
|
17
|
+
[ScriptStatus.FAILED]: '✗',
|
|
18
|
+
[ScriptStatus.BLOCKED]: '⊘',
|
|
19
|
+
[ScriptStatus.NOT_TAKEN]: '–',
|
|
20
|
+
[ScriptStatus.UNDECIDED]: '?',
|
|
21
|
+
};
|
|
22
|
+
export const PLAN_CSS = `
|
|
23
|
+
[data-reticle-page-panel="harness"] .reticle-plan{flex:none;margin:0 10px 8px;padding:8px 10px;border:1px solid var(--reticle-hud-border);border-radius:10px;background:rgba(255,255,255,.03);}
|
|
24
|
+
[data-reticle-page-panel="harness"] .reticle-plan[hidden]{display:none;}
|
|
25
|
+
[data-reticle-page-panel="harness"] .reticle-plan-head{display:flex;align-items:center;gap:8px;margin-bottom:6px;font-size:var(--reticle-hud-size-xs);color:var(--reticle-hud-text-muted);}
|
|
26
|
+
[data-reticle-page-panel="harness"] .reticle-plan-head strong{color:var(--reticle-hud-text);font-size:var(--reticle-hud-size-sm);font-weight:600;}
|
|
27
|
+
[data-reticle-page-panel="harness"] .reticle-plan-head span{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
28
|
+
[data-reticle-page-panel="harness"] .reticle-plan-close{flex:none;border:0;background:none;color:inherit;cursor:pointer;font-size:14px;line-height:1;padding:2px 4px;}
|
|
29
|
+
[data-reticle-page-panel="harness"] .reticle-plan-lanes{display:flex;flex-direction:column;gap:6px;max-height:240px;overflow:auto;scrollbar-width:none;}
|
|
30
|
+
[data-reticle-page-panel="harness"] .reticle-plan-lanes::-webkit-scrollbar{display:none;}
|
|
31
|
+
[data-reticle-page-panel="harness"] .reticle-plan-lane{display:flex;gap:6px;align-items:flex-start;min-width:0;}
|
|
32
|
+
[data-reticle-page-panel="harness"] .reticle-plan-lane-name{flex:none;width:16px;padding-top:6px;font-size:11px;font-weight:600;text-align:center;color:var(--reticle-hud-text-muted);}
|
|
33
|
+
[data-reticle-page-panel="harness"] .reticle-plan-lane-journeys{display:flex;flex:1;min-width:0;flex-direction:column;gap:4px;}
|
|
34
|
+
[data-reticle-page-panel="harness"] .reticle-plan-journey{display:block;width:100%;text-align:left;border:1px solid transparent;border-radius:8px;padding:5px 7px;background:rgba(255,255,255,.04);color:var(--reticle-hud-text);font:inherit;font-size:var(--reticle-hud-size-xs);cursor:pointer;}
|
|
35
|
+
[data-reticle-page-panel="harness"] .reticle-plan-journey[data-status="running"]{border-color:var(--reticle-c-active);box-shadow:0 0 12px -4px var(--reticle-c-active);}
|
|
36
|
+
[data-reticle-page-panel="harness"] .reticle-plan-journey[data-status="blocked"],[data-reticle-page-panel="harness"] .reticle-plan-journey[data-status="not-taken"]{opacity:.55;}
|
|
37
|
+
[data-reticle-page-panel="harness"] .reticle-plan-row{display:flex;gap:6px;align-items:baseline;min-width:0;}
|
|
38
|
+
[data-reticle-page-panel="harness"] .reticle-plan-row b{flex:1;min-width:0;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
39
|
+
[data-reticle-page-panel="harness"] .reticle-plan-glyph{flex:none;width:12px;text-align:center;}
|
|
40
|
+
[data-reticle-page-panel="harness"] [data-status="running"]>.reticle-plan-row>.reticle-plan-glyph,[data-reticle-page-panel="harness"] .reticle-plan-step[data-status="running"]>.reticle-plan-glyph{color:var(--reticle-c-active);animation:reticle-flow-pulse 1.2s ease-in-out infinite;}
|
|
41
|
+
[data-reticle-page-panel="harness"] [data-status="passed"]>.reticle-plan-row>.reticle-plan-glyph,[data-reticle-page-panel="harness"] .reticle-plan-step[data-status="passed"]>.reticle-plan-glyph{color:#4ade80;}
|
|
42
|
+
[data-reticle-page-panel="harness"] [data-status="failed"]>.reticle-plan-row>.reticle-plan-glyph,[data-reticle-page-panel="harness"] .reticle-plan-step[data-status="failed"]>.reticle-plan-glyph{color:#f87171;}
|
|
43
|
+
[data-reticle-page-panel="harness"] .reticle-plan-meta{display:block;margin:2px 0 0 18px;color:var(--reticle-hud-text-muted);font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
44
|
+
[data-reticle-page-panel="harness"] .reticle-plan-steps{margin:4px 0 0 18px;display:flex;flex-direction:column;gap:2px;}
|
|
45
|
+
[data-reticle-page-panel="harness"] .reticle-plan-step{display:flex;gap:6px;color:var(--reticle-hud-text-muted);font-size:11px;min-width:0;}
|
|
46
|
+
[data-reticle-page-panel="harness"] .reticle-plan-step span:last-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
47
|
+
`;
|
|
48
|
+
const STATUSES = new Set(Object.values(ScriptStatus));
|
|
49
|
+
function statusOf(value) {
|
|
50
|
+
return 'string' === typeof value && STATUSES.has(value)
|
|
51
|
+
? value
|
|
52
|
+
: ScriptStatus.PENDING;
|
|
53
|
+
}
|
|
54
|
+
function textOf(value) {
|
|
55
|
+
return 'string' === typeof value ? value : '';
|
|
56
|
+
}
|
|
57
|
+
function listOf(value) {
|
|
58
|
+
return Array.isArray(value)
|
|
59
|
+
? value.filter((v) => 'object' === typeof v && null !== v)
|
|
60
|
+
: [];
|
|
61
|
+
}
|
|
62
|
+
export function parsePlanView(args) {
|
|
63
|
+
const lanes = listOf(args['lanes']).map((lane) => ({
|
|
64
|
+
id: textOf(lane['id']),
|
|
65
|
+
journeys: listOf(lane['journeys']).map((j) => ({
|
|
66
|
+
id: textOf(j['id']),
|
|
67
|
+
title: textOf(j['title']),
|
|
68
|
+
...('string' === typeof j['persona'] ? { persona: j['persona'] } : {}),
|
|
69
|
+
waitsOn: Array.isArray(j['waitsOn']) ? j['waitsOn'].map(textOf) : [],
|
|
70
|
+
status: statusOf(j['status']),
|
|
71
|
+
steps: listOf(j['steps']).map((s) => ({
|
|
72
|
+
label: textOf(s['label']),
|
|
73
|
+
status: statusOf(s['status']),
|
|
74
|
+
})),
|
|
75
|
+
})),
|
|
76
|
+
}));
|
|
77
|
+
if (0 === lanes.length)
|
|
78
|
+
return undefined;
|
|
79
|
+
const parallel = 'number' === typeof args['parallel'] ? args['parallel'] : 1;
|
|
80
|
+
return { parallel, lanes };
|
|
81
|
+
}
|
|
82
|
+
function el(tag, className, text) {
|
|
83
|
+
const node = document.createElement(tag);
|
|
84
|
+
node.className = className;
|
|
85
|
+
if (text !== undefined)
|
|
86
|
+
node.textContent = text;
|
|
87
|
+
return node;
|
|
88
|
+
}
|
|
89
|
+
export class PlanBoard {
|
|
90
|
+
#host;
|
|
91
|
+
#view;
|
|
92
|
+
#dismissed = false;
|
|
93
|
+
#toggled = new Map();
|
|
94
|
+
mount(root) {
|
|
95
|
+
this.#host = root.querySelector(`[${PLAN_ATTR}]`) ?? undefined;
|
|
96
|
+
this.#host?.addEventListener('click', (event) => {
|
|
97
|
+
const target = event.target;
|
|
98
|
+
if (!(target instanceof Element))
|
|
99
|
+
return;
|
|
100
|
+
if (null !== target.closest(`[${CLOSE_ATTR}]`)) {
|
|
101
|
+
this.#dismissed = true;
|
|
102
|
+
this.#paint();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const card = target.closest(`[${JOURNEY_ATTR}]`);
|
|
106
|
+
const key = card?.getAttribute(JOURNEY_ATTR);
|
|
107
|
+
if (null === key || key === undefined)
|
|
108
|
+
return;
|
|
109
|
+
this.#toggled.set(key, 'true' !== card?.getAttribute('aria-expanded'));
|
|
110
|
+
this.#paint();
|
|
111
|
+
});
|
|
112
|
+
this.#paint();
|
|
113
|
+
}
|
|
114
|
+
paint(view) {
|
|
115
|
+
const fresh = view.lanes.every((lane) => lane.journeys.every((j) => ScriptStatus.PENDING === j.status));
|
|
116
|
+
if (fresh) {
|
|
117
|
+
this.#dismissed = false;
|
|
118
|
+
this.#toggled.clear();
|
|
119
|
+
}
|
|
120
|
+
this.#view = view;
|
|
121
|
+
this.#paint();
|
|
122
|
+
}
|
|
123
|
+
#paint() {
|
|
124
|
+
const host = this.#host;
|
|
125
|
+
const view = this.#view;
|
|
126
|
+
if (host === undefined)
|
|
127
|
+
return;
|
|
128
|
+
host.replaceChildren();
|
|
129
|
+
host.hidden = view === undefined || this.#dismissed;
|
|
130
|
+
if (view === undefined || this.#dismissed)
|
|
131
|
+
return;
|
|
132
|
+
const journeys = view.lanes.flatMap((lane) => lane.journeys);
|
|
133
|
+
const done = journeys.filter((j) => ScriptStatus.PASSED === j.status).length;
|
|
134
|
+
const head = el('div', 'reticle-plan-head');
|
|
135
|
+
head.append(el('strong', '', TEXT.TITLE), el('span', '', `${String(done)} of ${String(journeys.length)} passed · ${String(view.lanes.length)} lane(s)` +
|
|
136
|
+
(1 < view.parallel ? `, ${String(view.parallel)} ${TEXT.AT_ONCE}` : '')));
|
|
137
|
+
const close = el('button', 'reticle-plan-close', '×');
|
|
138
|
+
close.setAttribute('type', 'button');
|
|
139
|
+
close.setAttribute(CLOSE_ATTR, '');
|
|
140
|
+
close.setAttribute('aria-label', TEXT.CLOSE);
|
|
141
|
+
close.setAttribute('title', TEXT.CLOSE);
|
|
142
|
+
head.append(close);
|
|
143
|
+
const lanes = el('div', 'reticle-plan-lanes');
|
|
144
|
+
for (const lane of view.lanes) {
|
|
145
|
+
const row = el('div', 'reticle-plan-lane');
|
|
146
|
+
const name = el('span', 'reticle-plan-lane-name', lane.id);
|
|
147
|
+
name.setAttribute('title', `${TEXT.LANE} ${lane.id}`);
|
|
148
|
+
const journeys = el('div', 'reticle-plan-lane-journeys');
|
|
149
|
+
for (const journey of lane.journeys)
|
|
150
|
+
journeys.append(this.#card(lane.id, journey));
|
|
151
|
+
row.append(name, journeys);
|
|
152
|
+
lanes.append(row);
|
|
153
|
+
}
|
|
154
|
+
host.append(head, lanes);
|
|
155
|
+
}
|
|
156
|
+
#card(laneId, journey) {
|
|
157
|
+
const key = `${laneId}/${journey.id}`;
|
|
158
|
+
const open = this.#toggled.get(key) ??
|
|
159
|
+
(ScriptStatus.RUNNING === journey.status || ScriptStatus.FAILED === journey.status);
|
|
160
|
+
const card = el('button', 'reticle-plan-journey');
|
|
161
|
+
card.setAttribute('type', 'button');
|
|
162
|
+
card.setAttribute(JOURNEY_ATTR, key);
|
|
163
|
+
card.setAttribute('data-status', journey.status);
|
|
164
|
+
card.setAttribute('aria-expanded', String(open));
|
|
165
|
+
const row = el('span', 'reticle-plan-row');
|
|
166
|
+
row.append(el('span', 'reticle-plan-glyph', GLYPH[journey.status]), el('b', '', journey.title));
|
|
167
|
+
card.append(row);
|
|
168
|
+
const meta = [
|
|
169
|
+
journey.persona,
|
|
170
|
+
0 < journey.waitsOn.length ? `${TEXT.WAITS} ${journey.waitsOn.join(', ')}` : undefined,
|
|
171
|
+
].filter((part) => part !== undefined && 0 < part.length);
|
|
172
|
+
if (0 < meta.length)
|
|
173
|
+
card.append(el('span', 'reticle-plan-meta', meta.join(' · ')));
|
|
174
|
+
if (open && 0 < journey.steps.length) {
|
|
175
|
+
const steps = el('span', 'reticle-plan-steps');
|
|
176
|
+
for (const step of journey.steps) {
|
|
177
|
+
const line = el('span', 'reticle-plan-step');
|
|
178
|
+
line.setAttribute('data-status', step.status);
|
|
179
|
+
line.append(el('span', 'reticle-plan-glyph', GLYPH[step.status]), el('span', '', step.label));
|
|
180
|
+
steps.append(line);
|
|
181
|
+
}
|
|
182
|
+
card.append(steps);
|
|
183
|
+
}
|
|
184
|
+
return card;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -26,17 +26,17 @@ export const REPORT_CSS = `
|
|
|
26
26
|
[${REPORT_PANEL_ATTR}] .reticle-report-scope{
|
|
27
27
|
margin-left:auto;padding:3px 9px;border-radius:999px;cursor:pointer;
|
|
28
28
|
border:1px solid rgba(255,255,255,.14);background:rgba(255,255,255,.04);
|
|
29
|
-
color:var(--reticle-muted);font:inherit;font-size:
|
|
29
|
+
color:var(--reticle-muted);font:inherit;font-size:11px;}
|
|
30
30
|
[${REPORT_PANEL_ATTR}] .reticle-report-scope:hover{background:rgba(255,255,255,.08);color:var(--reticle-fg);}
|
|
31
31
|
[${REPORT_PANEL_ATTR}] .reticle-report-close{
|
|
32
32
|
flex:none;display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;
|
|
33
33
|
border:none;background:transparent;color:var(--reticle-faint);cursor:pointer;line-height:0;}
|
|
34
34
|
[${REPORT_PANEL_ATTR}] .reticle-report-close svg{display:block;fill:none;stroke:currentColor;stroke-width:1.6;}
|
|
35
35
|
[${REPORT_PANEL_ATTR}] .reticle-report-body{
|
|
36
|
-
min-height:0;overflow-y:auto;padding:12px 14px 6px;
|
|
36
|
+
flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:12px 14px 6px;
|
|
37
37
|
scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.14) transparent;}
|
|
38
38
|
[${REPORT_PANEL_ATTR}] .reticle-report-empty{
|
|
39
|
-
margin:0;padding:22px 6px;text-align:center;color:var(--reticle-faint);font-size:
|
|
39
|
+
margin:0;padding:22px 6px;text-align:center;color:var(--reticle-faint);font-size:12px;}
|
|
40
40
|
/* The streak sits above the hero, as a flame and a number: it is the one stat that is about
|
|
41
41
|
TODAY, so it reads first and never competes with the count it sits over. */
|
|
42
42
|
[${REPORT_PANEL_ATTR}] .reticle-report-streak{
|
|
@@ -45,14 +45,36 @@ export const REPORT_CSS = `
|
|
|
45
45
|
border:1px solid color-mix(in srgb,var(--reticle-c-active) 22%,transparent);
|
|
46
46
|
font-size:12px;font-weight:600;font-variant-numeric:tabular-nums;}
|
|
47
47
|
[${REPORT_PANEL_ATTR}] .reticle-report-streak-label{
|
|
48
|
-
color:var(--reticle-faint);font-size:
|
|
48
|
+
color:var(--reticle-faint);font-size:11px;font-weight:400;}
|
|
49
49
|
[${REPORT_PANEL_ATTR}] .reticle-report-hero{display:flex;flex-direction:column;gap:2px;margin-bottom:10px;}
|
|
50
50
|
[${REPORT_PANEL_ATTR}] .reticle-report-hero-value{
|
|
51
51
|
font-size:34px;font-weight:700;line-height:1;letter-spacing:-.02em;color:var(--reticle-c-active);}
|
|
52
|
-
[${REPORT_PANEL_ATTR}] .reticle-report-hero-label{color:var(--reticle-muted);font-size:
|
|
52
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-hero-label{color:var(--reticle-muted);font-size:12px;}
|
|
53
53
|
[${REPORT_PANEL_ATTR}] .reticle-report-verdicts{display:flex;gap:6px;margin-bottom:12px;}
|
|
54
|
+
[${REPORT_PANEL_ATTR}] .reticle-sync-status{font-size:11px;color:var(--reticle-faint);font-variant-numeric:tabular-nums;white-space:nowrap;}
|
|
55
|
+
[${REPORT_PANEL_ATTR}] .reticle-sync-status[data-reticle-sync-status="refused"]{color:var(--reticle-c-error,#f87171);}
|
|
56
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-coverage{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 10px;margin-bottom:12px;padding:8px 10px;border:1px solid var(--reticle-hud-border);border-radius:var(--reticle-hud-radius-sm,8px);}
|
|
57
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-coverage-value{font-size:18px;font-weight:600;font-variant-numeric:tabular-nums;color:var(--reticle-hud-text);}
|
|
58
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-coverage-levels{display:flex;flex-wrap:wrap;gap:4px 10px;width:100%;font-size:11px;color:var(--reticle-faint);font-variant-numeric:tabular-nums;}
|
|
59
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-instrumentation{margin-bottom:12px;padding:10px;border:1px solid var(--reticle-hud-border);border-radius:var(--reticle-hud-radius-md);}
|
|
60
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-instrumentation-head{display:flex;align-items:baseline;justify-content:space-between;gap:8px;}
|
|
61
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-instrumentation-head .reticle-report-section{margin-bottom:0;}
|
|
62
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-instrumentation-value{font-size:13px;font-weight:600;font-variant-numeric:tabular-nums;color:var(--reticle-hud-text);}
|
|
63
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-caps{display:grid;grid-template-columns:1fr 1fr;gap:4px 10px;margin:8px 0 0;padding:0;list-style:none;}
|
|
64
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-cap{display:flex;flex-wrap:wrap;align-items:baseline;gap:0 6px;min-width:0;font-size:11px;line-height:1.4;color:var(--reticle-muted);}
|
|
65
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-cap-mark{flex:none;width:10px;text-align:center;}
|
|
66
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-cap-name{flex:1;min-width:0;}
|
|
67
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-cap[data-state="seen"] .reticle-report-cap-mark{color:#86efac;}
|
|
68
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-cap[data-state="missing"]{grid-column:1/-1;color:var(--reticle-hud-text);}
|
|
69
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-cap[data-state="missing"] .reticle-report-cap-mark{color:#fca5a5;}
|
|
70
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-cap[data-state="unseen"]{color:var(--reticle-faint);}
|
|
71
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-cap-cost{flex-basis:100%;padding-left:16px;color:var(--reticle-faint);}
|
|
72
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-caps-note{display:block;margin-top:6px;color:var(--reticle-faint);font-size:11px;}
|
|
73
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-copy-prompt{display:block;width:100%;margin-top:10px;padding:6px 10px;border:1px solid color-mix(in srgb,var(--reticle-hud-brand) 50%,transparent);
|
|
74
|
+
border-radius:var(--reticle-hud-radius-sm);background:var(--reticle-hud-brand-soft);color:var(--reticle-hud-brand);font:inherit;font-size:12px;font-weight:600;cursor:pointer;}
|
|
75
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-copy-prompt:hover{filter:brightness(1.15);}
|
|
54
76
|
[${REPORT_PANEL_ATTR}] .reticle-report-verdict{
|
|
55
|
-
flex:1;padding:5px 8px;border-radius:8px;font-size:
|
|
77
|
+
flex:1;padding:5px 8px;border-radius:8px;font-size:11px;text-align:center;
|
|
56
78
|
background:rgba(255,255,255,.05);color:var(--reticle-muted);}
|
|
57
79
|
[${REPORT_PANEL_ATTR}] .reticle-report-verdict[data-kind="fail"]{color:#fca5a5;}
|
|
58
80
|
[${REPORT_PANEL_ATTR}] .reticle-report-verdict[data-kind="unknown"]{color:#fcd34d;}
|
|
@@ -62,11 +84,11 @@ export const REPORT_CSS = `
|
|
|
62
84
|
display:flex;flex-direction:column;gap:2px;padding:8px;border-radius:10px;
|
|
63
85
|
background:rgba(255,255,255,.04);}
|
|
64
86
|
[${REPORT_PANEL_ATTR}] .reticle-report-value{font-size:15px;font-weight:600;font-variant-numeric:tabular-nums;}
|
|
65
|
-
[${REPORT_PANEL_ATTR}] .reticle-report-label{color:var(--reticle-faint);font-size:
|
|
87
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-label{color:var(--reticle-faint);font-size:11px;line-height:1.3;}
|
|
66
88
|
/* An estimate is labelled as one, and its label carries what it is measured against. */
|
|
67
89
|
[${REPORT_PANEL_ATTR}] .reticle-report-basis{
|
|
68
90
|
display:block;margin-top:2px;color:var(--reticle-c-active);opacity:.8;
|
|
69
|
-
font-size:
|
|
91
|
+
font-size:11px;letter-spacing:.06em;text-transform:uppercase;cursor:help;}
|
|
70
92
|
[${REPORT_PANEL_ATTR}] .reticle-report-defects-wrap{margin-top:14px;}
|
|
71
93
|
/* The heading and its push control on one line, so the button reads as belonging to this section
|
|
72
94
|
rather than floating above the list. */
|
|
@@ -82,9 +104,9 @@ export const REPORT_CSS = `
|
|
|
82
104
|
[${REPORT_PANEL_ATTR}] .reticle-report-defect-title{
|
|
83
105
|
font-size:11px;line-height:1.35;overflow-wrap:anywhere;}
|
|
84
106
|
[${REPORT_PANEL_ATTR}] .reticle-report-defect-detail{
|
|
85
|
-
color:var(--reticle-faint);font-size:
|
|
107
|
+
color:var(--reticle-faint);font-size:11px;line-height:1.35;overflow-wrap:anywhere;}
|
|
86
108
|
[${REPORT_PANEL_ATTR}] .reticle-report-defect-source{
|
|
87
|
-
color:var(--reticle-faint);font-size:
|
|
109
|
+
color:var(--reticle-faint);font-size:11px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
|
|
88
110
|
overflow-wrap:anywhere;}
|
|
89
111
|
.reticle-report-defect-link{flex:none;display:inline-flex;align-items:center;justify-content:center;
|
|
90
112
|
width:20px;height:20px;border-radius:6px;color:var(--reticle-faint);pointer-events:auto;
|
|
@@ -93,17 +115,19 @@ export const REPORT_CSS = `
|
|
|
93
115
|
.reticle-report-defect-link:focus-visible{opacity:1;}
|
|
94
116
|
.reticle-report-defect-link:hover{color:var(--reticle-fg);background:rgba(255,255,255,.07);}
|
|
95
117
|
[${REPORT_PANEL_ATTR}] .reticle-report-defects-more{
|
|
96
|
-
display:inline-block;margin-top:8px;font-size:
|
|
118
|
+
display:inline-block;margin-top:8px;font-size:11px;color:var(--reticle-c-active);
|
|
97
119
|
text-decoration:none;}
|
|
98
120
|
[${REPORT_PANEL_ATTR}] .reticle-report-defects-more:hover{text-decoration:underline;}
|
|
121
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-defects-all{display:inline-block;margin-top:8px;border:0;padding:0;background:none;cursor:pointer;font:inherit;font-size:11px;font-weight:500;color:var(--reticle-c-active);}
|
|
122
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-defects-all:hover{text-decoration:underline;}
|
|
99
123
|
[${REPORT_PANEL_ATTR}] .reticle-report-local-only{
|
|
100
124
|
margin:14px 0 0;padding-top:10px;border-top:1px solid var(--reticle-line);
|
|
101
|
-
color:var(--reticle-faint);font-size:
|
|
125
|
+
color:var(--reticle-faint);font-size:11px;line-height:1.45;}
|
|
102
126
|
[${REPORT_PANEL_ATTR}] .reticle-report-local-only code{
|
|
103
127
|
font-family:ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--reticle-c-active);}
|
|
104
128
|
[${REPORT_PANEL_ATTR}] .reticle-report-chart-wrap{margin-top:12px;}
|
|
105
129
|
[${REPORT_PANEL_ATTR}] .reticle-report-section{
|
|
106
|
-
display:block;margin-bottom:6px;color:var(--reticle-faint);font-size:
|
|
130
|
+
display:block;margin-bottom:6px;color:var(--reticle-faint);font-size:11px;
|
|
107
131
|
letter-spacing:.06em;text-transform:uppercase;}
|
|
108
132
|
/* Thirty fixed slots, not a flex row: one recorded day is one thin bar on a month's axis, and a
|
|
109
133
|
single stretched block would read as "a month of solid work" on the day you install it. */
|
|
@@ -113,19 +137,20 @@ export const REPORT_CSS = `
|
|
|
113
137
|
min-width:2px;border-radius:2px 2px 0 0;
|
|
114
138
|
background:color-mix(in srgb,var(--reticle-c-active) 55%,transparent);}
|
|
115
139
|
[${REPORT_PANEL_ATTR}] .reticle-report-bar[data-hot="1"]{background:#f87171;}
|
|
140
|
+
/* Sharing is a quiet row under the record, never the loudest thing on the panel. */
|
|
116
141
|
[${REPORT_PANEL_ATTR}] .reticle-report-foot{
|
|
117
|
-
flex:none;display:flex;flex-wrap:
|
|
142
|
+
flex:none;display:flex;flex-wrap:nowrap;align-items:center;gap:2px;padding:6px 10px 2px 14px;
|
|
118
143
|
border-top:1px solid rgba(255,255,255,.06);}
|
|
144
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-share-label{margin-right:4px;color:var(--reticle-faint);font-size:11px;}
|
|
119
145
|
[${REPORT_PANEL_ATTR}] .reticle-report-share{
|
|
120
|
-
padding:
|
|
121
|
-
|
|
122
|
-
[${REPORT_PANEL_ATTR}] .reticle-report-share:hover{background:rgba(255,255,255,.
|
|
123
|
-
[${REPORT_PANEL_ATTR}] .reticle-report-refer{
|
|
124
|
-
border-color:color-mix(in srgb,var(--reticle-c-active) 45%,transparent);color:var(--reticle-fg);}
|
|
146
|
+
flex:none;padding:3px 6px;border:0;border-radius:6px;cursor:pointer;font:inherit;font-size:11px;
|
|
147
|
+
background:transparent;color:var(--reticle-muted);white-space:nowrap;}
|
|
148
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-share:hover{background:rgba(255,255,255,.08);color:var(--reticle-fg);}
|
|
149
|
+
[${REPORT_PANEL_ATTR}] .reticle-report-refer{margin-left:auto;color:var(--reticle-hud-brand);}
|
|
125
150
|
[${REPORT_PANEL_ATTR}] .reticle-report-links{
|
|
126
|
-
flex:none;display:flex;gap:12px;padding:
|
|
151
|
+
flex:none;display:flex;gap:12px;padding:2px 14px 10px;}
|
|
127
152
|
[${REPORT_PANEL_ATTR}] .reticle-report-links a{
|
|
128
|
-
color:var(--reticle-faint);font-size:
|
|
153
|
+
color:var(--reticle-faint);font-size:11px;text-decoration:none;}
|
|
129
154
|
[${REPORT_PANEL_ATTR}] .reticle-report-links a:hover{color:var(--reticle-fg);text-decoration:underline;}
|
|
130
155
|
|
|
131
156
|
/* Who this record belongs to, and the push-now control. Tokens only — no literal colours. */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type AccountState, type ImpactScope, type ImpactSnapshot } from '@reticlehq/core';
|
|
2
2
|
export declare function reportPanelHtml(): string;
|
|
3
|
-
export declare function
|
|
3
|
+
export declare function instrumentationHtml(instrumentation: Readonly<Record<string, unknown>> | undefined): string;
|
|
4
|
+
export declare function reportBodyHtml(scope: ImpactScope, dashboardUrl?: string, account?: AccountState, projectName?: string, coverage?: Readonly<Record<string, number>>, sync?: Readonly<Record<string, unknown>>, instrumentation?: Readonly<Record<string, unknown>>): string;
|
|
4
5
|
interface ReportHost {
|
|
5
6
|
onBeforeOpen?: () => void;
|
|
6
7
|
onSyncNow?: () => void;
|
|
@@ -4,8 +4,9 @@ import { HUD_SURFACE_CLASS } from './chrome/presenter-hud-chrome.js';
|
|
|
4
4
|
import { esc, isSafeDashboardUrl } from './chrome/presenter-safe-html.js';
|
|
5
5
|
import { ACCOUNT_SIGNIN_ATTR, ACCOUNT_TEXT, SYNC_BTN_ATTR, accountControlHtml, syncButtonHtml, } from './presenter-account.js';
|
|
6
6
|
import { REPORT_PANEL_ATTR, REPORT_ATTR, REPORT_CLOSE_ATTR } from './presenter-config.js';
|
|
7
|
-
import { REPORT_LINKS, REPORT_TEXT, buildLinkedInShareUrl, buildReferralText, buildShareText, buildXShareUrl, compactDuration, compactNumber, } from './chrome/presenter-report-copy.js';
|
|
7
|
+
import { REPORT_LINKS, REPORT_TEXT, buildLinkedInShareUrl, buildReferralText, buildShareText, buildXShareUrl, compactDuration, compactNumber, COVERAGE_LEVELS, CAPABILITY_WORDS, } from './chrome/presenter-report-copy.js';
|
|
8
8
|
const SCOPE_ATTR = 'data-reticle-report-scope';
|
|
9
|
+
const SCOPE = { PROJECT: 'project', MACHINE: 'machine' };
|
|
9
10
|
const SHARE_X_ATTR = 'data-reticle-share-x';
|
|
10
11
|
const SHARE_IN_ATTR = 'data-reticle-share-in';
|
|
11
12
|
const SHARE_COPY_ATTR = 'data-reticle-share-copy';
|
|
@@ -17,13 +18,14 @@ export function reportPanelHtml() {
|
|
|
17
18
|
<div class="reticle-report-inner">
|
|
18
19
|
<div class="reticle-report-head">
|
|
19
20
|
<span class="reticle-report-title">${REPORT_TEXT.TITLE}</span>
|
|
20
|
-
<button type="button" ${SCOPE_ATTR}
|
|
21
|
+
<span class="reticle-segmented reticle-report-scopes" role="group" aria-label="${REPORT_TEXT.SCOPE_LABEL}"><button type="button" ${SCOPE_ATTR}="${SCOPE.PROJECT}" aria-pressed="true">${REPORT_TEXT.PROJECT}</button><button type="button" ${SCOPE_ATTR}="${SCOPE.MACHINE}" aria-pressed="false" title="${REPORT_TEXT.GLOBAL_TITLE}">${REPORT_TEXT.GLOBAL}</button></span>
|
|
21
22
|
<button type="button" ${REPORT_CLOSE_ATTR} class="reticle-report-close" title="Close" aria-label="Close impact">${close}</button>
|
|
22
23
|
</div>
|
|
23
24
|
<div class="reticle-report-body" data-reticle-report-body></div>
|
|
24
|
-
<div class="reticle-report-foot">
|
|
25
|
-
<
|
|
26
|
-
<button type="button" ${
|
|
25
|
+
<div class="reticle-report-foot" role="group" aria-label="${REPORT_TEXT.SHARE_LABEL}">
|
|
26
|
+
<span class="reticle-report-share-label">${REPORT_TEXT.SHARE_LABEL}</span>
|
|
27
|
+
<button type="button" ${SHARE_X_ATTR} class="reticle-report-share">${REPORT_TEXT.POST_X}</button>
|
|
28
|
+
<button type="button" ${SHARE_IN_ATTR} class="reticle-report-share">${REPORT_TEXT.POST_IN}</button>
|
|
27
29
|
<button type="button" ${SHARE_COPY_ATTR} class="reticle-report-share">${REPORT_TEXT.COPY}</button>
|
|
28
30
|
<button type="button" ${REFER_ATTR} class="reticle-report-share reticle-report-refer">${REPORT_TEXT.REFER}</button>
|
|
29
31
|
</div>
|
|
@@ -76,20 +78,111 @@ function defects(scope, dashboardUrl) {
|
|
|
76
78
|
return `<li class="reticle-report-defect"><span class="reticle-report-defect-title">${esc(d.title)}</span>${detail}${source}${rowLink}</li>`;
|
|
77
79
|
})
|
|
78
80
|
.join('');
|
|
81
|
+
const hidden = scope.counts.failed > list.length;
|
|
79
82
|
const more = !linked
|
|
80
|
-
?
|
|
81
|
-
|
|
83
|
+
? !hidden
|
|
84
|
+
? ''
|
|
85
|
+
: `<button type="button" data-reticle-defects-all ${ACCOUNT_SIGNIN_ATTR} class="reticle-report-defects-all" title="${REPORT_TEXT.DEFECTS_SEE_ALL_TITLE}">${REPORT_TEXT.DEFECTS_SEE_ALL} (${String(scope.counts.failed)}) →</button>`
|
|
86
|
+
: `<a class="reticle-report-defects-more" data-reticle-link="dashboard" href="${esc(dashboardUrl)}" target="_blank" rel="noreferrer noopener">${REPORT_TEXT.DEFECTS_MORE}${hidden ? ` (${String(scope.counts.failed)})` : ''}</a>`;
|
|
82
87
|
const sync = syncButtonHtml(dashboardUrl);
|
|
83
88
|
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>`;
|
|
84
89
|
}
|
|
85
|
-
|
|
90
|
+
function syncStatusHtml(sync) {
|
|
91
|
+
if (sync === undefined)
|
|
92
|
+
return '';
|
|
93
|
+
const num = (key) => ('number' === typeof sync[key] ? sync[key] : 0);
|
|
94
|
+
const status = sync['status'];
|
|
95
|
+
const keyRefused = 'string' === typeof sync['keyRefused'] ? sync['keyRefused'] : undefined;
|
|
96
|
+
const label = keyRefused !== undefined
|
|
97
|
+
? REPORT_TEXT.KEY_REFUSED
|
|
98
|
+
: 'refused' === status
|
|
99
|
+
? `${String(num('refused'))} refused`
|
|
100
|
+
: 'pending' === status
|
|
101
|
+
? `${String(num('pending'))} waiting`
|
|
102
|
+
: 'on-platform' === status
|
|
103
|
+
? `Synced · ${String(num('onPlatform'))}`
|
|
104
|
+
: 'local-only' === status
|
|
105
|
+
? `${String(num('pending'))} ${REPORT_TEXT.NOT_SENT}`
|
|
106
|
+
: undefined;
|
|
107
|
+
if (label === undefined)
|
|
108
|
+
return '';
|
|
109
|
+
const said = keyRefused !== undefined
|
|
110
|
+
? keyRefused
|
|
111
|
+
: 'local-only' === status
|
|
112
|
+
? REPORT_TEXT.NOT_SENT_HELP
|
|
113
|
+
: 'string' === typeof sync['said']
|
|
114
|
+
? sync['said']
|
|
115
|
+
: '';
|
|
116
|
+
const runUrl = sync['runUrl'];
|
|
117
|
+
const seeRun = 'string' === typeof runUrl && isSafeDashboardUrl(runUrl)
|
|
118
|
+
? ` <a class="reticle-sync-run" data-reticle-link="run" href="${esc(runUrl)}" target="_blank" rel="noreferrer noopener">${REPORT_TEXT.SEE_RUN}</a>`
|
|
119
|
+
: '';
|
|
120
|
+
return `<span class="reticle-sync-status" data-reticle-sync-status="${esc(String(status))}" title="${esc(said)}">${label}</span>${seeRun}`;
|
|
121
|
+
}
|
|
122
|
+
const capabilityWords = (name) => 'string' === typeof name ? esc(CAPABILITY_WORDS[name] ?? name) : undefined;
|
|
123
|
+
function capabilityItem(state, name, cost = '') {
|
|
124
|
+
const mark = 'seen' === state ? '✓' : 'missing' === state ? '✕' : '○';
|
|
125
|
+
const why = '' === cost ? '' : `<span class="reticle-report-cap-cost">${cost}</span>`;
|
|
126
|
+
return `<li class="reticle-report-cap" data-state="${state}"><span class="reticle-report-cap-mark" aria-hidden="true">${mark}</span><span class="reticle-report-cap-name">${name}</span>${why}</li>`;
|
|
127
|
+
}
|
|
128
|
+
export function instrumentationHtml(instrumentation) {
|
|
129
|
+
if (instrumentation === undefined)
|
|
130
|
+
return '';
|
|
131
|
+
const covered = instrumentation['covered'];
|
|
132
|
+
const total = instrumentation['total'];
|
|
133
|
+
if ('number' !== typeof covered || 'number' !== typeof total)
|
|
134
|
+
return '';
|
|
135
|
+
const list = (key) => {
|
|
136
|
+
const value = instrumentation[key];
|
|
137
|
+
return Array.isArray(value) ? value : [];
|
|
138
|
+
};
|
|
139
|
+
const seen = list('seen').flatMap((n) => {
|
|
140
|
+
const name = capabilityWords(n);
|
|
141
|
+
return name === undefined ? [] : [capabilityItem('seen', name)];
|
|
142
|
+
});
|
|
143
|
+
const missing = list('missing').flatMap((m) => {
|
|
144
|
+
if ('object' !== typeof m || null === m)
|
|
145
|
+
return [];
|
|
146
|
+
const gap = m;
|
|
147
|
+
const name = capabilityWords(gap['capability']);
|
|
148
|
+
const cost = 'string' === typeof gap['cost'] ? esc(gap['cost']) : '';
|
|
149
|
+
return name === undefined ? [] : [capabilityItem('missing', name, cost)];
|
|
150
|
+
});
|
|
151
|
+
const unseen = list('notSeenYet').flatMap((n) => {
|
|
152
|
+
const name = capabilityWords(n);
|
|
153
|
+
return name === undefined ? [] : [capabilityItem('unseen', name)];
|
|
154
|
+
});
|
|
155
|
+
const items = [...missing, ...seen, ...unseen];
|
|
156
|
+
const caps = 0 === items.length
|
|
157
|
+
? ''
|
|
158
|
+
: `<ul class="reticle-report-caps">${items.join('')}</ul>${0 === unseen.length ? '' : `<span class="reticle-report-caps-note">○ ${REPORT_TEXT.NOT_SEEN_YET}</span>`}`;
|
|
159
|
+
const copy = 'string' === typeof instrumentation['prompt']
|
|
160
|
+
? `<button type="button" class="reticle-report-copy-prompt" ${COPY_PROMPT_ATTR}>${REPORT_TEXT.COPY_PROMPT}</button>`
|
|
161
|
+
: '';
|
|
162
|
+
return `<div class="reticle-report-instrumentation" title="${REPORT_TEXT.INSTRUMENTATION_HELP}"><div class="reticle-report-instrumentation-head"><span class="reticle-report-section">${REPORT_TEXT.INSTRUMENTATION}</span><span class="reticle-report-instrumentation-value">${REPORT_TEXT.covered(Math.round(covered), Math.round(total))}</span></div>${caps}${copy}</div>`;
|
|
163
|
+
}
|
|
164
|
+
const COPY_PROMPT_ATTR = 'data-reticle-copy-prompt';
|
|
165
|
+
function coverageHtml(coverage) {
|
|
166
|
+
if (coverage === undefined)
|
|
167
|
+
return '';
|
|
168
|
+
const known = COVERAGE_LEVELS.filter((l) => 'number' === typeof coverage[l.key]);
|
|
169
|
+
if (0 === known.length)
|
|
170
|
+
return '';
|
|
171
|
+
const pct = (key) => `${String(Math.round(coverage[key] ?? 0))}%`;
|
|
172
|
+
const head = 'number' === typeof coverage['proved'] ? pct('proved') : pct(known[0]?.key ?? '');
|
|
173
|
+
const rows = known
|
|
174
|
+
.map((l) => `<span class="reticle-report-coverage-level">${pct(l.key)} ${l.label}</span>`)
|
|
175
|
+
.join('');
|
|
176
|
+
return `<div class="reticle-report-coverage" title="${REPORT_TEXT.COVERAGE_HELP}"><span class="reticle-report-section">${REPORT_TEXT.COVERAGE}</span><span class="reticle-report-coverage-value">${head}</span><div class="reticle-report-coverage-levels">${rows}</div></div>`;
|
|
177
|
+
}
|
|
178
|
+
export function reportBodyHtml(scope, dashboardUrl, account, projectName, coverage, sync, instrumentation) {
|
|
86
179
|
const c = scope.counts;
|
|
87
180
|
if (0 === c.calls)
|
|
88
181
|
return `<p class="reticle-report-empty">${REPORT_TEXT.EMPTY}</p>`;
|
|
89
182
|
const streak = scope.records.streakDays > 0
|
|
90
183
|
? `<span class="reticle-report-streak" title="Consecutive days with at least one verdict">🔥 ${String(scope.records.streakDays)} <span class="reticle-report-streak-label">${REPORT_TEXT.STREAK}</span></span>`
|
|
91
184
|
: '';
|
|
92
|
-
const hero = `<div class="reticle-report-hero"><span class="reticle-report-hero-value">${compactNumber(c.failed)}</span><span class="reticle-report-hero-label">${REPORT_TEXT.
|
|
185
|
+
const hero = `<div class="reticle-report-hero"><span class="reticle-report-hero-value">${compactNumber(c.failed)}</span><span class="reticle-report-hero-label">${REPORT_TEXT.heroDefects(c.failed)}</span></div>`;
|
|
93
186
|
const verdicts = `<div class="reticle-report-verdicts" title="${REPORT_TEXT.UNKNOWN_HELP}">
|
|
94
187
|
<span class="reticle-report-verdict" data-kind="pass">${compactNumber(c.passed)} ${REPORT_TEXT.PASSED}</span>
|
|
95
188
|
<span class="reticle-report-verdict" data-kind="fail">${compactNumber(c.failed)} ${REPORT_TEXT.FAILED}</span>
|
|
@@ -105,8 +198,8 @@ export function reportBodyHtml(scope, dashboardUrl, account, projectName) {
|
|
|
105
198
|
card(compactNumber(scope.savings.tokens.value), REPORT_TEXT.SAVED_TOKENS, scope.savings.tokens.basis),
|
|
106
199
|
card(compactDuration(scope.savings.minutes.value * 60_000), REPORT_TEXT.SAVED_MINUTES, scope.savings.minutes.basis),
|
|
107
200
|
].join('');
|
|
108
|
-
const identity = `<div class="reticle-report-identity">${accountControlHtml(account, { dashboardUrl, projectName, verdicts: c.verdicts, defects: c.failed })}${syncButtonHtml(dashboardUrl)}</div>`;
|
|
109
|
-
return
|
|
201
|
+
const identity = `<div class="reticle-report-identity">${accountControlHtml(account, { dashboardUrl, projectName, verdicts: c.verdicts, defects: c.failed })}${syncButtonHtml(dashboardUrl)}${syncStatusHtml(sync)}</div>`;
|
|
202
|
+
return `<div class="reticle-report-top">${streak}${identity}</div>${hero}${verdicts}${instrumentationHtml(instrumentation)}${coverageHtml(coverage)}<div class="reticle-report-grid">${cards}</div>${defects(scope, dashboardUrl)}${chart(scope)}${localOnly(scope, dashboardUrl, account)}`;
|
|
110
203
|
}
|
|
111
204
|
function localOnly(scope, dashboardUrl, account) {
|
|
112
205
|
if (dashboardUrl !== undefined)
|
|
@@ -140,14 +233,17 @@ export class PresenterReport {
|
|
|
140
233
|
e.stopPropagation();
|
|
141
234
|
this.close();
|
|
142
235
|
});
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
236
|
+
const scopeBtns = root.querySelectorAll(`[${SCOPE_ATTR}]`);
|
|
237
|
+
for (const btn of scopeBtns) {
|
|
238
|
+
btn.addEventListener('click', (e) => {
|
|
239
|
+
e.stopPropagation();
|
|
240
|
+
this.#global = SCOPE.MACHINE === btn.getAttribute(SCOPE_ATTR);
|
|
241
|
+
for (const other of scopeBtns) {
|
|
242
|
+
other.setAttribute('aria-pressed', other === btn ? 'true' : 'false');
|
|
243
|
+
}
|
|
244
|
+
this.#paint();
|
|
245
|
+
});
|
|
246
|
+
}
|
|
151
247
|
root.querySelector(`[${SHARE_X_ATTR}]`)?.addEventListener('click', (e) => {
|
|
152
248
|
e.stopPropagation();
|
|
153
249
|
this.#openShare(buildXShareUrl(this.shareText()));
|
|
@@ -175,6 +271,13 @@ export class PresenterReport {
|
|
|
175
271
|
void this.#copy(ACCOUNT_TEXT.SIGNIN_COMMAND, signin);
|
|
176
272
|
return;
|
|
177
273
|
}
|
|
274
|
+
const copyPrompt = target.closest(`[${COPY_PROMPT_ATTR}]`);
|
|
275
|
+
const prompt = this.#snapshot?.instrumentation?.['prompt'];
|
|
276
|
+
if (copyPrompt !== null && 'string' === typeof prompt) {
|
|
277
|
+
e.stopPropagation();
|
|
278
|
+
void this.#copy(prompt, copyPrompt);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
178
281
|
const sync = target.closest(`[${SYNC_BTN_ATTR}]`);
|
|
179
282
|
if (sync !== null) {
|
|
180
283
|
e.stopPropagation();
|
|
@@ -237,7 +340,7 @@ export class PresenterReport {
|
|
|
237
340
|
this.#body.innerHTML =
|
|
238
341
|
scope === undefined
|
|
239
342
|
? `<p class="reticle-report-empty">${REPORT_TEXT.EMPTY}</p>`
|
|
240
|
-
: reportBodyHtml(scope, this.#snapshot?.dashboardUrl, this.#snapshot?.account, this.#snapshot?.projectName);
|
|
343
|
+
: reportBodyHtml(scope, this.#snapshot?.dashboardUrl, this.#snapshot?.account, this.#snapshot?.projectName, this.#global ? undefined : this.#snapshot?.coverage, this.#global ? undefined : this.#snapshot?.sync, this.#global ? undefined : this.#snapshot?.instrumentation);
|
|
241
344
|
}
|
|
242
345
|
#openShare(url) {
|
|
243
346
|
window.open(url, '_blank', 'noopener,noreferrer');
|
|
@@ -56,8 +56,10 @@ export const SETTINGS_CSS = `
|
|
|
56
56
|
[${SETTINGS_PANEL_ATTR}] .reticle-settings-row{
|
|
57
57
|
display:flex;align-items:center;justify-content:space-between;gap:12px;
|
|
58
58
|
padding:8px 14px;min-height:30px;}
|
|
59
|
+
[${SETTINGS_PANEL_ATTR}] .reticle-settings-labelcol{display:flex;flex:1;min-width:0;flex-direction:column;gap:2px;}
|
|
59
60
|
[${SETTINGS_PANEL_ATTR}] .reticle-settings-label{
|
|
60
|
-
display:inline-flex;align-items:center;gap:6px;color:rgba(255,255,255,.
|
|
61
|
+
display:inline-flex;align-items:center;gap:6px;color:rgba(255,255,255,.86);font-size:13px;font-weight:500;}
|
|
62
|
+
[${SETTINGS_PANEL_ATTR}] .reticle-settings-desc{color:rgba(255,255,255,.5);font-size:11px;line-height:1.35;}
|
|
61
63
|
[${SETTINGS_PANEL_ATTR}] .reticle-settings-help{
|
|
62
64
|
display:inline-flex;align-items:center;justify-content:center;
|
|
63
65
|
width:14px;height:14px;border:none;border-radius:999px;padding:0;cursor:help;
|
|
@@ -86,7 +88,7 @@ export const SETTINGS_CSS = `
|
|
|
86
88
|
[${SETTINGS_PANEL_ATTR}] .reticle-settings-toggle:focus-visible{outline:2px solid rgba(59,130,246,.55);outline-offset:2px;}
|
|
87
89
|
[${SETTINGS_PANEL_ATTR}] .reticle-settings-row[data-disabled="1"]{opacity:.45;pointer-events:none;}
|
|
88
90
|
[${SETTINGS_PANEL_ATTR}] .reticle-settings-section{
|
|
89
|
-
padding:10px 14px 6px;color:rgba(255,255,255,.38);font-size:
|
|
91
|
+
padding:10px 14px 6px;color:rgba(255,255,255,.38);font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;}
|
|
90
92
|
[${SETTINGS_PANEL_ATTR}] .reticle-settings-swatches{
|
|
91
93
|
display:flex;gap:8px;flex-wrap:wrap;padding:0 14px 10px;}
|
|
92
94
|
[${SETTINGS_PANEL_ATTR}] .reticle-settings-swatch{
|
|
@@ -98,13 +100,13 @@ export const SETTINGS_CSS = `
|
|
|
98
100
|
border-color:#fff;box-shadow:0 0 0 1px rgba(0,0,0,.35);}
|
|
99
101
|
[${SETTINGS_PANEL_ATTR}] .reticle-settings-swatch:focus-visible{outline:2px solid rgba(59,130,246,.65);outline-offset:2px;}
|
|
100
102
|
[${SETTINGS_PANEL_ATTR}] .reticle-settings-checkrow{
|
|
101
|
-
display:flex;align-items:center;gap:10px;padding:7px 14px;color:rgba(255,255,255,.
|
|
103
|
+
display:flex;align-items:center;gap:10px;padding:7px 14px;color:rgba(255,255,255,.86);font-size:13px;
|
|
102
104
|
cursor:pointer;user-select:none;margin:0;transition:background .12s;}
|
|
103
105
|
[${SETTINGS_PANEL_ATTR}] .reticle-settings-checkrow:hover{background:rgba(255,255,255,.03);}
|
|
104
106
|
[${SETTINGS_PANEL_ATTR}] .reticle-settings-check{
|
|
105
107
|
width:16px;height:16px;border-radius:5px;border:1px solid rgba(255,255,255,.22);
|
|
106
108
|
background:rgba(255,255,255,.04);display:inline-flex;align-items:center;justify-content:center;
|
|
107
|
-
color:transparent;font-size:
|
|
109
|
+
color:transparent;font-size:11px;line-height:1;flex:none;transition:background .12s,border-color .12s,color .12s;}
|
|
108
110
|
[${SETTINGS_PANEL_ATTR}] .reticle-settings-check[data-on="1"]{
|
|
109
111
|
background:#fafafa;border-color:#fafafa;color:#111;}
|
|
110
112
|
[${SETTINGS_PANEL_ATTR}] .reticle-settings-check:focus-visible{outline:2px solid rgba(59,130,246,.55);outline-offset:2px;}
|
|
@@ -125,6 +127,18 @@ export const SETTINGS_CSS = `
|
|
|
125
127
|
[${SETTINGS_PANEL_ATTR}] .reticle-settings-link-caret{display:inline-flex;align-items:center;flex:none;opacity:.55;}
|
|
126
128
|
[${SETTINGS_PANEL_ATTR}] .reticle-settings-link:hover{background:rgba(255,255,255,.04);color:#fff;}
|
|
127
129
|
[${SETTINGS_PANEL_ATTR}] .reticle-settings-link:focus-visible{outline:2px solid rgba(59,130,246,.55);outline-offset:-2px;}
|
|
130
|
+
[${SETTINGS_PANEL_ATTR}] .reticle-settings-kill-row{padding:12px 14px 14px;border-top:1px solid rgba(255,255,255,.08);}
|
|
131
|
+
[${SETTINGS_PANEL_ATTR}] .reticle-settings-kill{
|
|
132
|
+
width:100%;display:flex;align-items:center;justify-content:center;
|
|
133
|
+
border:1px solid rgba(239,68,68,.55);border-radius:8px;background:rgba(239,68,68,.12);color:#f87171;
|
|
134
|
+
font:inherit;font-size:13px;font-weight:600;padding:8px 12px;cursor:pointer;
|
|
135
|
+
transition:background .12s,border-color .12s,color .12s;}
|
|
136
|
+
[${SETTINGS_PANEL_ATTR}] .reticle-settings-kill:hover{background:rgba(239,68,68,.22);border-color:#ef4444;color:#fca5a5;}
|
|
137
|
+
[${SETTINGS_PANEL_ATTR}] .reticle-settings-kill[data-armed="1"]{background:#ef4444;border-color:#ef4444;color:#fff;}
|
|
138
|
+
[${SETTINGS_PANEL_ATTR}] .reticle-settings-kill:focus-visible{outline:2px solid rgba(239,68,68,.6);outline-offset:2px;}
|
|
139
|
+
[${SETTINGS_PANEL_ATTR}] .reticle-settings-kill-sub{margin:8px 0 0;font-size:11px;line-height:1.45;color:rgba(255,255,255,.55);}
|
|
140
|
+
/* The SDK version line sits straight in the foot, so it takes the rows' inset rather than none. */
|
|
141
|
+
[${SETTINGS_PANEL_ATTR}] .reticle-settings-foot > .reticle-settings-kill-sub{padding:0 14px;}
|
|
128
142
|
[${'data-reticle-overlay'}][${SETTINGS_ATTR}="1"] [${'data-reticle-hud'}] .reticle-tb-tip{
|
|
129
143
|
opacity:0;visibility:hidden;transition-delay:0s;}
|
|
130
144
|
/* The dock's accent IS the theme's active colour, published as --reticle-accent on the overlay by
|