@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
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import { ReticleStorageKey } from '../../storage-keys.js';
|
|
1
2
|
export const CAROUSEL_ATTR = 'data-reticle-carousel';
|
|
2
3
|
export const CAROUSEL_SLIDE_ATTR = 'data-reticle-carousel-slide';
|
|
3
4
|
export const CAROUSEL_DOT_ATTR = 'data-reticle-carousel-dot';
|
|
4
5
|
export const CAROUSEL_CLOSE_ATTR = 'data-reticle-carousel-close';
|
|
5
|
-
export const
|
|
6
|
+
export const CAROUSEL_PREV_ATTR = 'data-reticle-carousel-prev';
|
|
7
|
+
export const CAROUSEL_NEXT_ATTR = 'data-reticle-carousel-next';
|
|
8
|
+
export const CAROUSEL_POSITION_ATTR = 'data-reticle-carousel-position';
|
|
9
|
+
export const CAROUSEL_DISMISSED_KEY = ReticleStorageKey.CAROUSEL_DISMISSED;
|
|
6
10
|
const CLOSE_LABEL = 'Close';
|
|
7
11
|
export const ROTATE_MS = 6000;
|
|
8
12
|
const rotations = new WeakMap();
|
|
@@ -22,7 +26,11 @@ function prefersReducedMotion() {
|
|
|
22
26
|
}
|
|
23
27
|
function show(root, index) {
|
|
24
28
|
root.setAttribute('data-active', String(index));
|
|
25
|
-
root.querySelectorAll(`[${CAROUSEL_SLIDE_ATTR}]`)
|
|
29
|
+
const slides = root.querySelectorAll(`[${CAROUSEL_SLIDE_ATTR}]`);
|
|
30
|
+
const position = root.querySelector(`[${CAROUSEL_POSITION_ATTR}]`);
|
|
31
|
+
if (position !== null)
|
|
32
|
+
position.textContent = `${String(index + 1)} / ${String(slides.length)}`;
|
|
33
|
+
slides.forEach((slide, i) => {
|
|
26
34
|
slide.hidden = i !== index;
|
|
27
35
|
});
|
|
28
36
|
root.querySelectorAll(`[${CAROUSEL_DOT_ATTR}]`).forEach((dot, i) => {
|
|
@@ -37,15 +45,15 @@ export function carouselHtml(slides, active) {
|
|
|
37
45
|
const track = slides
|
|
38
46
|
.map((slide, i) => `<div ${CAROUSEL_SLIDE_ATTR}="${slide.id}" class="reticle-carousel-slide"${i === current ? '' : ' hidden'}>${slide.html}</div>`)
|
|
39
47
|
.join('');
|
|
40
|
-
const
|
|
48
|
+
const controls = 1 === slides.length
|
|
41
49
|
? ''
|
|
42
|
-
: `<div class="reticle-carousel-dots">${slides
|
|
43
|
-
.map((slide, i) => `<button type="button" ${CAROUSEL_DOT_ATTR}="${String(i)}" class="reticle-carousel-dot" aria-label="${slide.id}" aria-current="${String(i === current)}"></button>`)
|
|
44
|
-
.join('')}</div>`;
|
|
50
|
+
: `<div class="reticle-carousel-controls"><button type="button" ${CAROUSEL_PREV_ATTR} aria-label="Previous message" class="reticle-carousel-arrow">‹</button><div class="reticle-carousel-dots">${slides
|
|
51
|
+
.map((slide, i) => `<button type="button" ${CAROUSEL_DOT_ATTR}="${String(i)}" class="reticle-carousel-dot" aria-label="Show ${slide.id}" aria-current="${String(i === current)}"></button>`)
|
|
52
|
+
.join('')}</div><span ${CAROUSEL_POSITION_ATTR} aria-live="polite">${String(current + 1)} / ${String(slides.length)}</span><button type="button" ${CAROUSEL_NEXT_ATTR} aria-label="Next message" class="reticle-carousel-arrow">›</button></div>`;
|
|
45
53
|
return `<div ${CAROUSEL_ATTR} class="reticle-carousel" role="region" aria-label="Reticle" data-active="${String(current)}">
|
|
46
54
|
<button type="button" ${CAROUSEL_CLOSE_ATTR} class="reticle-carousel-close" aria-label="${CLOSE_LABEL}" title="${CLOSE_LABEL}">×</button>
|
|
47
55
|
<div class="reticle-carousel-track">${track}</div>
|
|
48
|
-
${
|
|
56
|
+
${controls}
|
|
49
57
|
</div>`;
|
|
50
58
|
}
|
|
51
59
|
function dismissed(storage) {
|
|
@@ -88,6 +96,16 @@ export function paintCarousel(log, slides, storage) {
|
|
|
88
96
|
show(root, Number(dot.getAttribute(CAROUSEL_DOT_ATTR)));
|
|
89
97
|
});
|
|
90
98
|
}
|
|
99
|
+
root.querySelector(`[${CAROUSEL_PREV_ATTR}]`)?.addEventListener('click', (event) => {
|
|
100
|
+
event.preventDefault();
|
|
101
|
+
event.stopPropagation();
|
|
102
|
+
show(root, (Number(root.getAttribute('data-active')) + slides.length - 1) % slides.length);
|
|
103
|
+
});
|
|
104
|
+
root.querySelector(`[${CAROUSEL_NEXT_ATTR}]`)?.addEventListener('click', (event) => {
|
|
105
|
+
event.preventDefault();
|
|
106
|
+
event.stopPropagation();
|
|
107
|
+
show(root, (Number(root.getAttribute('data-active')) + 1) % slides.length);
|
|
108
|
+
});
|
|
91
109
|
startRotation(log, root, slides.length);
|
|
92
110
|
}
|
|
93
111
|
function startRotation(log, root, count) {
|
|
@@ -117,10 +135,14 @@ function startRotation(log, root, count) {
|
|
|
117
135
|
export const CAROUSEL_CSS = `
|
|
118
136
|
.reticle-carousel{position:relative;margin:8px 10px;padding:10px 12px 8px;border:1px solid var(--reticle-line,#2a2f3a);border-radius:10px;background:var(--reticle-surface-inset,rgba(255,255,255,.03));}
|
|
119
137
|
.reticle-carousel-track{height:100px;overflow:hidden;}
|
|
120
|
-
.reticle-carousel-slide .reticle-
|
|
138
|
+
.reticle-carousel-slide .reticle-talk{margin:0;padding:0;border:0;background:none;}
|
|
121
139
|
.reticle-carousel-close{position:absolute;top:4px;right:6px;width:20px;height:20px;padding:0;border:0;background:none;color:inherit;opacity:.55;cursor:pointer;font-size:14px;line-height:20px;}
|
|
122
140
|
.reticle-carousel-close:hover{opacity:1;}
|
|
123
141
|
.reticle-carousel-dots{display:flex;justify-content:center;gap:6px;margin-top:6px;}
|
|
124
142
|
.reticle-carousel-dot{width:6px;height:6px;padding:0;border:0;border-radius:999px;background:currentColor;opacity:.3;cursor:pointer;}
|
|
125
143
|
.reticle-carousel-dot[aria-current="true"]{opacity:.9;}
|
|
144
|
+
.reticle-carousel-controls{display:flex;align-items:center;gap:8px;margin-top:6px;}
|
|
145
|
+
.reticle-carousel-arrow{width:24px;height:24px;flex:none;display:inline-flex;align-items:center;justify-content:center;border:1px solid var(--reticle-line);border-radius:6px;background:rgba(255,255,255,.04);color:var(--reticle-fg);font:inherit;font-size:18px;line-height:1;cursor:pointer;}
|
|
146
|
+
.reticle-carousel-arrow:hover{border-color:var(--reticle-accent);background:var(--reticle-accent-soft);}
|
|
147
|
+
.reticle-carousel-position{margin-left:auto;color:var(--reticle-muted);font-size:11px;font-variant-numeric:tabular-nums;white-space:nowrap;}
|
|
126
148
|
`;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { type OfferState } from './offer-card.js';
|
|
2
1
|
import type { Slide } from './carousel.js';
|
|
3
2
|
export declare const SLIDE_ID: {
|
|
4
|
-
readonly
|
|
5
|
-
readonly
|
|
3
|
+
readonly REPLAY: "replay-confidence";
|
|
4
|
+
readonly NOTES: "notes-for-agent";
|
|
6
5
|
};
|
|
7
|
-
export declare function panelSlides(
|
|
6
|
+
export declare function panelSlides(notices?: readonly unknown[], base?: string): Slide[];
|
|
@@ -1,10 +1,49 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import { HudNoticeSchema } from '@reticlehq/core/hud';
|
|
2
|
+
import { esc } from '../chrome/presenter-safe-html.js';
|
|
3
|
+
import { DEFAULT_PLATFORM_URL } from '@reticlehq/core';
|
|
4
|
+
const HARNESS_PATH = '/settings?group=billing';
|
|
5
|
+
export const SLIDE_ID = {
|
|
6
|
+
REPLAY: 'replay-confidence',
|
|
7
|
+
NOTES: 'notes-for-agent',
|
|
8
|
+
};
|
|
9
|
+
const SLIDE_TEXT = {
|
|
10
|
+
REPLAY_TITLE: 'Replay what your agent verified',
|
|
11
|
+
REPLAY_DETAIL: 'Saved flows re-run after each edit.',
|
|
12
|
+
REPLAY_LINK: 'See how →',
|
|
13
|
+
NOTES_TITLE: 'Point your agent at anything',
|
|
14
|
+
NOTES_DETAIL: 'Open Notes, then click an element.',
|
|
15
|
+
};
|
|
16
|
+
const valueSlides = (base) => [
|
|
17
|
+
{
|
|
18
|
+
id: SLIDE_ID.REPLAY,
|
|
19
|
+
html: `<div class="reticle-promo-copy"><strong class="reticle-promo-title">${SLIDE_TEXT.REPLAY_TITLE}</strong><span class="reticle-promo-detail">${SLIDE_TEXT.REPLAY_DETAIL}</span><a data-reticle-promo-link class="reticle-promo-link" href="${base}${HARNESS_PATH}" target="_blank" rel="noopener noreferrer">${SLIDE_TEXT.REPLAY_LINK}</a></div>`,
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: SLIDE_ID.NOTES,
|
|
23
|
+
html: `<div class="reticle-promo-copy"><strong class="reticle-promo-title">${SLIDE_TEXT.NOTES_TITLE}</strong><span class="reticle-promo-detail">${SLIDE_TEXT.NOTES_DETAIL}</span></div>`,
|
|
24
|
+
},
|
|
25
|
+
];
|
|
26
|
+
const HARNESS_NOTICE_PREFIX = 'harness';
|
|
27
|
+
const NOTICE_SLIDE_PREFIX = 'notice-';
|
|
28
|
+
function noticeSlide(notice) {
|
|
29
|
+
const kicker = notice.kicker === undefined
|
|
30
|
+
? ''
|
|
31
|
+
: `<span class="reticle-promo-kicker">${esc(notice.kicker)}</span>`;
|
|
32
|
+
const detail = notice.detail === undefined
|
|
33
|
+
? ''
|
|
34
|
+
: `<span class="reticle-promo-detail" title="${esc(notice.detail)}">${esc(notice.detail)}</span>`;
|
|
35
|
+
const link = notice.cta === undefined
|
|
36
|
+
? ''
|
|
37
|
+
: `<a data-reticle-promo-link class="reticle-promo-link" href="${esc(notice.cta.url)}" target="_blank" rel="noopener noreferrer">${esc(notice.cta.label)} →</a>`;
|
|
38
|
+
return {
|
|
39
|
+
id: `${NOTICE_SLIDE_PREFIX}${notice.id}`,
|
|
40
|
+
html: `<div class="reticle-promo-copy">${kicker}<strong class="reticle-promo-title">${esc(notice.title)}</strong>${detail}${link}</div>`,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export function panelSlides(notices = [], base = DEFAULT_PLATFORM_URL) {
|
|
44
|
+
const valid = notices.flatMap((n) => {
|
|
45
|
+
const parsed = HudNoticeSchema.safeParse(n);
|
|
46
|
+
return parsed.success && !parsed.data.id.startsWith(HARNESS_NOTICE_PREFIX) ? [parsed.data] : [];
|
|
47
|
+
});
|
|
48
|
+
return 0 < valid.length ? valid.map(noticeSlide) : [...valueSlides(base)];
|
|
10
49
|
}
|
|
@@ -5,4 +5,4 @@ export declare const TALK_TEXT: {
|
|
|
5
5
|
readonly BOOK: "Book a call";
|
|
6
6
|
};
|
|
7
7
|
export declare function talkCardHtml(): string;
|
|
8
|
-
export declare const TALK_CSS
|
|
8
|
+
export declare const TALK_CSS: string;
|
|
@@ -10,3 +10,8 @@ export declare const LOG_RESULT: {
|
|
|
10
10
|
readonly FAIL: "fail";
|
|
11
11
|
};
|
|
12
12
|
export type LogResult = (typeof LOG_RESULT)[keyof typeof LOG_RESULT];
|
|
13
|
+
export declare const LOG_ACTOR: {
|
|
14
|
+
readonly AGENT: "agent";
|
|
15
|
+
readonly HARNESS: "harness";
|
|
16
|
+
};
|
|
17
|
+
export type LogActor = (typeof LOG_ACTOR)[keyof typeof LOG_ACTOR];
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { LogActor, LogKind, LogResult } from './presenter-log.js';
|
|
2
|
+
export interface RememberedRow {
|
|
3
|
+
kind: LogKind;
|
|
4
|
+
text: string;
|
|
5
|
+
ts: string;
|
|
6
|
+
at: number;
|
|
7
|
+
actor?: LogActor;
|
|
8
|
+
summary?: true;
|
|
9
|
+
result?: LogResult;
|
|
10
|
+
}
|
|
11
|
+
export declare function readRememberedLog(): RememberedRow[];
|
|
12
|
+
export declare function rememberLog(rows: readonly RememberedRow[]): void;
|
|
13
|
+
export declare function claimLog(owner: string | undefined): boolean;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ReticleStorageKey } from '../../storage-keys.js';
|
|
2
|
+
const LOG_STORAGE_KEY = ReticleStorageKey.PRESENTER_LOG;
|
|
3
|
+
export function readRememberedLog() {
|
|
4
|
+
try {
|
|
5
|
+
const raw = JSON.parse(globalThis.sessionStorage.getItem(LOG_STORAGE_KEY) ?? '[]');
|
|
6
|
+
return Array.isArray(raw)
|
|
7
|
+
? raw.filter((row) => 'object' === typeof row &&
|
|
8
|
+
null !== row &&
|
|
9
|
+
'string' === typeof row.text &&
|
|
10
|
+
'string' === typeof row.kind &&
|
|
11
|
+
'string' === typeof row.ts &&
|
|
12
|
+
'number' === typeof row.at)
|
|
13
|
+
: [];
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export function rememberLog(rows) {
|
|
20
|
+
try {
|
|
21
|
+
globalThis.sessionStorage.setItem(LOG_STORAGE_KEY, JSON.stringify(rows));
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function claimLog(owner) {
|
|
27
|
+
try {
|
|
28
|
+
const now = owner ?? '';
|
|
29
|
+
const before = globalThis.sessionStorage.getItem(ReticleStorageKey.PRESENTER_LOG_OWNER);
|
|
30
|
+
globalThis.sessionStorage.setItem(ReticleStorageKey.PRESENTER_LOG_OWNER, now);
|
|
31
|
+
if (null === before || before === now)
|
|
32
|
+
return false;
|
|
33
|
+
globalThis.sessionStorage.removeItem(LOG_STORAGE_KEY);
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -2,6 +2,7 @@ export declare const HUD_SURFACE_CLASS = "reticle-hud-surface";
|
|
|
2
2
|
export declare const HUD_SURFACE_FILL: string;
|
|
3
3
|
export declare const HUD_SURFACE_PAINT: string;
|
|
4
4
|
export declare const HUD_DROP_SHADOW: string;
|
|
5
|
+
export declare const HUD_GLASS_PAINT: string;
|
|
5
6
|
export declare const HUD_CHROME_CSS: string;
|
|
6
7
|
export declare const HUD_LOG_WELL_CLASS = "reticle-hud-log-well";
|
|
7
8
|
export declare const HUD_LOG_WELL_CSS: string;
|
|
@@ -2,6 +2,12 @@ export const HUD_SURFACE_CLASS = 'reticle-hud-surface';
|
|
|
2
2
|
export const HUD_SURFACE_FILL = `linear-gradient(165deg,#121218 0%,#09090c 42%,#000 100%)`;
|
|
3
3
|
export const HUD_SURFACE_PAINT = `background:${HUD_SURFACE_FILL};box-shadow:inset 0 1px 0 rgba(255,255,255,.09),0 0 0 1px rgba(255,255,255,.09);`;
|
|
4
4
|
export const HUD_DROP_SHADOW = '0 16px 40px rgba(0,0,0,.58),0 0 0 1px rgba(255,255,255,.08)';
|
|
5
|
+
export const HUD_GLASS_PAINT = [
|
|
6
|
+
'background:radial-gradient(130% 80% at 50% 0%,color-mix(in srgb,var(--reticle-c-active) 20%,transparent),transparent 62%),',
|
|
7
|
+
'linear-gradient(180deg,#121a2e,#080b14);',
|
|
8
|
+
'border:1px solid color-mix(in srgb,var(--reticle-c-active) 30%,rgba(255,255,255,.1));',
|
|
9
|
+
`box-shadow:${HUD_DROP_SHADOW},0 0 64px -12px color-mix(in srgb,var(--reticle-c-active) 75%,transparent);`,
|
|
10
|
+
].join('');
|
|
5
11
|
export const HUD_CHROME_CSS = `
|
|
6
12
|
.${HUD_SURFACE_CLASS}{
|
|
7
13
|
position:relative;overflow:hidden;
|
|
@@ -25,7 +31,7 @@ export const HUD_LOG_WELL_CSS = `
|
|
|
25
31
|
as tall as its rows, which left the empty-state line floating near the top of an otherwise
|
|
26
32
|
empty panel instead of centred in it. */
|
|
27
33
|
position:relative;display:flex;flex-direction:column;
|
|
28
|
-
flex:1 1 auto;height:
|
|
34
|
+
flex:1 1 auto;height:240px;min-height:120px;max-height:300px;overflow:hidden;
|
|
29
35
|
contain:layout style paint;
|
|
30
36
|
margin:0 6px;border-radius:12px;background:transparent;}
|
|
31
37
|
.${HUD_LOG_WELL_CLASS} > *{position:relative;z-index:1;}`;
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import { PresenterMode } from '@reticlehq/core';
|
|
2
|
-
import { LOG_KIND, LOG_RESULT, type LogKind, type LogResult } from './log-kinds.js';
|
|
3
|
-
export { LOG_KIND, LOG_RESULT };
|
|
4
|
-
export type { LogKind, LogResult };
|
|
2
|
+
import { LOG_ACTOR, LOG_KIND, LOG_RESULT, type LogActor, type LogKind, type LogResult } from './log-kinds.js';
|
|
3
|
+
export { LOG_ACTOR, LOG_KIND, LOG_RESULT };
|
|
4
|
+
export type { LogActor, LogKind, LogResult };
|
|
5
5
|
export declare const CHIP_LABEL: Record<PresenterMode, string>;
|
|
6
|
+
export declare const LAST_ACTOR_ATTR = "data-reticle-log-actor";
|
|
6
7
|
export declare const DATA_RETICLE_LOG = "data-reticle-log";
|
|
7
8
|
export declare const LOG_TIME_ATTR = "data-reticle-log-time";
|
|
9
|
+
export declare const LOG_EMPTY_TEXT: {
|
|
10
|
+
readonly TITLE: "Reticle checks your app from the inside.";
|
|
11
|
+
readonly OFFLINE_TITLE: "Not connected yet.";
|
|
12
|
+
readonly OFFLINE_BODY: "Once Reticle is running, this page connects by itself and the steps show here.";
|
|
13
|
+
readonly BODY: "Ask your coding agent to verify a change with Reticle: each step shows here, live.\\AOr open Harness below: Reticle clicks through the app as the person you pick and reports what breaks.";
|
|
14
|
+
};
|
|
8
15
|
export declare const LOG_CSS: string;
|
|
16
|
+
export declare const LOG_SCROLLABLE_ATTR = "data-reticle-log-scrollable";
|
|
9
17
|
export declare function settleLogAtLatest(container: HTMLElement): void;
|
|
10
18
|
export interface LogHandle {
|
|
11
19
|
result(r: LogResult): void;
|
|
@@ -15,4 +23,4 @@ export declare function clearLogRows(container: HTMLElement): void;
|
|
|
15
23
|
export declare function clampLogMax(n: number | undefined): number;
|
|
16
24
|
export declare function humanDuration(ms: number): string;
|
|
17
25
|
export declare function formatElapsed(ms: number): string;
|
|
18
|
-
export declare function appendLogRow(container: HTMLElement, kind: LogKind, text: string, ts: string, logMax: number): LogHandle;
|
|
26
|
+
export declare function appendLogRow(container: HTMLElement, kind: LogKind, text: string, ts: string, logMax: number, actor?: LogActor): LogHandle;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { PresenterMode } from '@reticlehq/core';
|
|
2
|
-
import { LOG_KIND, LOG_RESULT } from './log-kinds.js';
|
|
2
|
+
import { LOG_ACTOR, LOG_KIND, LOG_RESULT, } from './log-kinds.js';
|
|
3
3
|
import { nativeSetTimeout } from '../../timers/native/native-timers.js';
|
|
4
4
|
import { PresenterIcon, PRESENTER_ICON_SIZE, hiIcon, } from '../icons/presenter-icons.js';
|
|
5
5
|
const DEFAULT_LOG_MAX = 50;
|
|
6
|
-
export { LOG_KIND, LOG_RESULT };
|
|
6
|
+
export { LOG_ACTOR, LOG_KIND, LOG_RESULT };
|
|
7
7
|
const LOG_CHIP = { read: 'READ', act: 'ACT', narration: '', human: '' };
|
|
8
8
|
const LOG_CHIP_ICON = {
|
|
9
9
|
read: PresenterIcon.VIEW,
|
|
@@ -20,25 +20,44 @@ const LOG_CHIP_MODE = {
|
|
|
20
20
|
narration: PresenterMode.IDLE,
|
|
21
21
|
human: PresenterMode.IDLE,
|
|
22
22
|
};
|
|
23
|
-
const RESULT_GLYPH = { pass: '', fail: 'Fail' };
|
|
23
|
+
const RESULT_GLYPH = { pass: '✓', fail: 'Fail' };
|
|
24
|
+
const HANDOVER_TEXT = {
|
|
25
|
+
agent: 'Your agent is driving',
|
|
26
|
+
harness: 'Reticle Harness is driving',
|
|
27
|
+
};
|
|
28
|
+
export const LAST_ACTOR_ATTR = 'data-reticle-log-actor';
|
|
29
|
+
const DATA_ACTOR = 'data-actor';
|
|
30
|
+
const DATA_STATE = 'data-state';
|
|
31
|
+
const HANDOVER_KIND = 'handover';
|
|
24
32
|
const RESULT_CLASS = { pass: 'reticle-pass', fail: 'reticle-fail' };
|
|
25
33
|
export const DATA_RETICLE_LOG = 'data-reticle-log';
|
|
26
34
|
const DATA_RETICLE_LOG_ROW = 'data-reticle-log-row';
|
|
27
35
|
export const LOG_TIME_ATTR = 'data-reticle-log-time';
|
|
28
36
|
const DATA_KIND = 'data-kind';
|
|
29
37
|
const LOG_TEXT_CLASS = 'reticle-log-text';
|
|
38
|
+
const REPEAT_ATTR = 'data-reticle-repeat';
|
|
39
|
+
const LOG_REPEAT_CLASS = 'reticle-log-repeat';
|
|
30
40
|
const LOG_RES_CLASS = 'reticle-res';
|
|
31
41
|
const LOG_CHIP_CLASS = 'reticle-chip';
|
|
32
|
-
const
|
|
42
|
+
export const LOG_EMPTY_TEXT = {
|
|
43
|
+
TITLE: 'Reticle checks your app from the inside.',
|
|
44
|
+
OFFLINE_TITLE: 'Not connected yet.',
|
|
45
|
+
OFFLINE_BODY: 'Once Reticle is running, this page connects by itself and the steps show here.',
|
|
46
|
+
BODY: 'Ask your coding agent to verify a change with Reticle: each step shows here, live.\\AOr open Harness below: Reticle clicks through the app as the person you pick and reports what breaks.',
|
|
47
|
+
};
|
|
33
48
|
const LOG_SETTLE_MS = 160;
|
|
34
49
|
export const LOG_CSS = `
|
|
35
50
|
[data-reticle-log]{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;pointer-events:auto;touch-action:pan-y;
|
|
36
51
|
display:flex;flex-direction:column;
|
|
37
52
|
gap:4px;padding:8px 10px 10px;scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.16) transparent;
|
|
38
53
|
background:transparent;}
|
|
39
|
-
[data-reticle-log]:empty{align-items:center;justify-content:center;}
|
|
40
|
-
[data-reticle-log]:empty::
|
|
41
|
-
color:var(--reticle-
|
|
54
|
+
[data-reticle-log]:empty{align-items:center;justify-content:center;gap:6px;padding:16px 18px;}
|
|
55
|
+
[data-reticle-log]:empty::before{content:"${LOG_EMPTY_TEXT.TITLE}";display:block;text-align:center;
|
|
56
|
+
color:var(--reticle-hud-text);font-size:var(--reticle-hud-size-base);font-weight:600;line-height:1.4;}
|
|
57
|
+
[data-reticle-log]:empty::after{content:"${LOG_EMPTY_TEXT.BODY}";display:block;max-width:260px;text-align:center;white-space:pre-line;
|
|
58
|
+
color:var(--reticle-hud-text-muted);font-size:var(--reticle-hud-size-sm);line-height:1.5;}
|
|
59
|
+
[data-reticle-state="unreachable"] [data-reticle-log]:empty::before{content:"${LOG_EMPTY_TEXT.OFFLINE_TITLE}";}
|
|
60
|
+
[data-reticle-state="unreachable"] [data-reticle-log]:empty::after{content:"${LOG_EMPTY_TEXT.OFFLINE_BODY}";}
|
|
42
61
|
[data-reticle-log]::-webkit-scrollbar{width:8px;}
|
|
43
62
|
[data-reticle-log]::-webkit-scrollbar-thumb{background:rgba(255,255,255,.14);border-radius:8px;border:2px solid transparent;background-clip:content-box;}
|
|
44
63
|
[data-reticle-log]::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,.26);background-clip:content-box;}
|
|
@@ -61,18 +80,43 @@ export const LOG_CSS = `
|
|
|
61
80
|
[data-reticle-log] [data-reticle-log-row] .reticle-chip-label{
|
|
62
81
|
position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;}
|
|
63
82
|
[data-reticle-log] [data-reticle-log-row][data-kind="read"] .reticle-chip{opacity:.5;}
|
|
64
|
-
[${LOG_TIME_ATTR}]{flex:none;color:var(--reticle-faint);font-size:
|
|
83
|
+
[${LOG_TIME_ATTR}]{flex:none;color:var(--reticle-faint);font-size:11px;font-variant-numeric:tabular-nums;padding-top:2px;min-width:2em;opacity:.85;}
|
|
65
84
|
[data-reticle-log] .reticle-log-text{flex:1;min-width:0;color:var(--reticle-muted);overflow-wrap:anywhere;word-break:break-word;}
|
|
85
|
+
[data-reticle-log] .reticle-log-repeat{flex:none;color:var(--reticle-muted);font-variant-numeric:tabular-nums;opacity:.7;}
|
|
66
86
|
[data-reticle-log] .reticle-res{flex:none;font-size:7.5px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:var(--reticle-bad);opacity:.75;padding-top:2px;}
|
|
67
87
|
[data-reticle-log] .reticle-res.reticle-pass{display:none;}
|
|
68
88
|
[data-reticle-log-row][data-kind="human"]{align-self:flex-end;max-width:78%;margin:6px 0 2px;
|
|
69
89
|
padding:8px 12px;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.1);
|
|
70
90
|
border-radius:16px 16px 4px 16px;box-shadow:inset 0 1px 0 rgba(255,255,255,.08);}
|
|
71
91
|
[data-reticle-log-row][data-kind="human"] .reticle-log-text{color:var(--reticle-fg);font-size:12px;line-height:1.45;}
|
|
92
|
+
/* Rows arrive, rather than appear: the person watching should see the log move. */
|
|
93
|
+
[data-reticle-log-row]{animation:reticle-row-in .22s var(--reticle-hud-ease,ease) both;}
|
|
94
|
+
@keyframes reticle-row-in{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
|
|
95
|
+
@media (prefers-reduced-motion:reduce){[data-reticle-log-row]{animation:none;}}
|
|
96
|
+
/* The step in flight pulses until its verdict lands; then it says how it ended. */
|
|
97
|
+
[data-reticle-log-row][data-state="running"] .reticle-res{display:inline-block;width:6px;height:6px;margin-top:5px;
|
|
98
|
+
border-radius:50%;background:var(--reticle-c-active);opacity:1;animation:reticle-row-pulse 1s ease-in-out infinite;}
|
|
99
|
+
@keyframes reticle-row-pulse{0%,100%{opacity:.35;transform:scale(.8)}50%{opacity:1;transform:scale(1)}}
|
|
100
|
+
[data-reticle-log] .reticle-res.reticle-pass{display:inline;color:#4ade80;font-size:11px;opacity:.9;}
|
|
101
|
+
[data-reticle-log] .reticle-res.reticle-fail{color:#fca5a5;background:rgba(239,68,68,.16);border-radius:999px;padding:1px 6px;font-size:8px;opacity:1;}
|
|
102
|
+
/* The Harness's rows wear its own colour, so two drivers never read as one. */
|
|
103
|
+
[data-reticle-log-row][data-actor="harness"]{--reticle-row-accent:#a78bfa;}
|
|
104
|
+
[data-reticle-log-row][data-actor="harness"] .reticle-chip{color:#a78bfa;}
|
|
105
|
+
[data-reticle-log-row][data-actor="harness"]:not([data-kind="handover"]){box-shadow:inset 2px 0 0 color-mix(in srgb,#a78bfa 55%,transparent);padding-left:8px;}
|
|
106
|
+
[data-reticle-log-row][data-kind="handover"]{align-items:center;gap:8px;margin:8px 0 4px;padding:0;
|
|
107
|
+
font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--reticle-row-accent,var(--reticle-c-active));}
|
|
108
|
+
[data-reticle-log-row][data-kind="handover"]::before,[data-reticle-log-row][data-kind="handover"]::after{
|
|
109
|
+
content:"";flex:1;height:1px;background:color-mix(in srgb,var(--reticle-row-accent,var(--reticle-c-active)) 40%,transparent);}
|
|
110
|
+
[data-reticle-log-row][data-kind="handover"] .reticle-log-text{flex:none;color:inherit;}
|
|
111
|
+
/* What the Harness or the daemon says about a drive reads as a notice, not an aside. */
|
|
112
|
+
[data-reticle-log-row][data-kind="narration"]{font-style:normal;color:var(--reticle-fg);margin:4px 0;padding:7px 10px;
|
|
113
|
+
border-radius:10px;background:rgba(255,255,255,.05);border:1px solid rgba(255,255,255,.08);}
|
|
72
114
|
`;
|
|
73
115
|
function scrollLogToLatest(container) {
|
|
74
116
|
container.scrollTop = container.scrollHeight;
|
|
117
|
+
container.toggleAttribute(LOG_SCROLLABLE_ATTR, container.scrollHeight > container.clientHeight + 1);
|
|
75
118
|
}
|
|
119
|
+
export const LOG_SCROLLABLE_ATTR = 'data-reticle-log-scrollable';
|
|
76
120
|
export function settleLogAtLatest(container) {
|
|
77
121
|
scrollLogToLatest(container);
|
|
78
122
|
requestAnimationFrame(() => {
|
|
@@ -111,10 +155,44 @@ export function humanDuration(ms) {
|
|
|
111
155
|
export function formatElapsed(ms) {
|
|
112
156
|
return humanDuration(ms);
|
|
113
157
|
}
|
|
114
|
-
export function appendLogRow(container, kind, text, ts, logMax) {
|
|
158
|
+
export function appendLogRow(container, kind, text, ts, logMax, actor = LOG_ACTOR.AGENT) {
|
|
159
|
+
const repeated = kind === LOG_KIND.READ ? sameReadAsLast(container, text, actor) : undefined;
|
|
160
|
+
if (repeated !== undefined) {
|
|
161
|
+
const times = Number(repeated.getAttribute(REPEAT_ATTR) ?? '1') + 1;
|
|
162
|
+
repeated.setAttribute(REPEAT_ATTR, String(times));
|
|
163
|
+
let count = repeated.querySelector(`.${LOG_REPEAT_CLASS}`);
|
|
164
|
+
if (null === count) {
|
|
165
|
+
count = document.createElement('span');
|
|
166
|
+
count.className = LOG_REPEAT_CLASS;
|
|
167
|
+
repeated.querySelector(`.${LOG_TEXT_CLASS}`)?.after(count);
|
|
168
|
+
}
|
|
169
|
+
count.textContent = `×${String(times)}`;
|
|
170
|
+
const time = repeated.querySelector(`[${LOG_TIME_ATTR}]`);
|
|
171
|
+
if (null !== time)
|
|
172
|
+
time.textContent = ts;
|
|
173
|
+
const res = repeated.querySelector(`.${LOG_RES_CLASS}`);
|
|
174
|
+
return null === res ? { result: () => undefined } : rowHandle(repeated, res);
|
|
175
|
+
}
|
|
176
|
+
const driven = kind === LOG_KIND.ACT || kind === LOG_KIND.READ;
|
|
177
|
+
if (driven && (container.getAttribute(LAST_ACTOR_ATTR) ?? LOG_ACTOR.AGENT) !== actor) {
|
|
178
|
+
const divider = document.createElement('div');
|
|
179
|
+
divider.setAttribute(DATA_RETICLE_LOG_ROW, '');
|
|
180
|
+
divider.setAttribute(DATA_KIND, HANDOVER_KIND);
|
|
181
|
+
divider.setAttribute(DATA_ACTOR, actor);
|
|
182
|
+
const label = document.createElement('span');
|
|
183
|
+
label.className = LOG_TEXT_CLASS;
|
|
184
|
+
label.textContent = HANDOVER_TEXT[actor];
|
|
185
|
+
divider.appendChild(label);
|
|
186
|
+
container.appendChild(divider);
|
|
187
|
+
}
|
|
188
|
+
if (driven)
|
|
189
|
+
container.setAttribute(LAST_ACTOR_ATTR, actor);
|
|
115
190
|
const row = document.createElement('div');
|
|
116
191
|
row.setAttribute(DATA_RETICLE_LOG_ROW, '');
|
|
117
192
|
row.setAttribute(DATA_KIND, kind);
|
|
193
|
+
row.setAttribute(DATA_ACTOR, actor);
|
|
194
|
+
if (kind === LOG_KIND.ACT)
|
|
195
|
+
row.setAttribute(DATA_STATE, 'running');
|
|
118
196
|
const tsEl = document.createElement('span');
|
|
119
197
|
tsEl.setAttribute(LOG_TIME_ATTR, '');
|
|
120
198
|
tsEl.textContent = ts;
|
|
@@ -151,10 +229,22 @@ export function appendLogRow(container, kind, text, ts, logMax) {
|
|
|
151
229
|
requestAnimationFrame(() => {
|
|
152
230
|
scrollLogToLatest(container);
|
|
153
231
|
});
|
|
232
|
+
return rowHandle(row, resEl);
|
|
233
|
+
}
|
|
234
|
+
function sameReadAsLast(container, text, actor) {
|
|
235
|
+
const last = container.lastElementChild;
|
|
236
|
+
if (!(last instanceof HTMLElement) || !last.hasAttribute(DATA_RETICLE_LOG_ROW))
|
|
237
|
+
return undefined;
|
|
238
|
+
if (last.getAttribute(DATA_KIND) !== LOG_KIND.READ || last.getAttribute(DATA_ACTOR) !== actor)
|
|
239
|
+
return undefined;
|
|
240
|
+
return last.querySelector(`.${LOG_TEXT_CLASS}`)?.textContent === text ? last : undefined;
|
|
241
|
+
}
|
|
242
|
+
function rowHandle(row, resEl) {
|
|
154
243
|
return {
|
|
155
244
|
result: (r) => {
|
|
245
|
+
row.setAttribute(DATA_STATE, r);
|
|
156
246
|
if (r === LOG_RESULT.PASS) {
|
|
157
|
-
resEl.textContent =
|
|
247
|
+
resEl.textContent = RESULT_GLYPH.pass;
|
|
158
248
|
resEl.className = `${LOG_RES_CLASS} ${RESULT_CLASS.pass}`;
|
|
159
249
|
return;
|
|
160
250
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function morphHtml(target: Element, html: string): void;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export function morphHtml(target, html) {
|
|
2
|
+
const next = document.createElement('template');
|
|
3
|
+
next.innerHTML = html;
|
|
4
|
+
morphChildren(target, next.content);
|
|
5
|
+
}
|
|
6
|
+
function morphChildren(from, to) {
|
|
7
|
+
const have = [...from.childNodes];
|
|
8
|
+
const want = [...to.childNodes];
|
|
9
|
+
want.forEach((node, i) => {
|
|
10
|
+
const old = have[i];
|
|
11
|
+
if (old === undefined)
|
|
12
|
+
from.appendChild(node);
|
|
13
|
+
else if (old.nodeType === node.nodeType && old.nodeName === node.nodeName)
|
|
14
|
+
morphNode(old, node);
|
|
15
|
+
else
|
|
16
|
+
from.replaceChild(node, old);
|
|
17
|
+
});
|
|
18
|
+
for (const extra of have.slice(want.length))
|
|
19
|
+
from.removeChild(extra);
|
|
20
|
+
}
|
|
21
|
+
function morphNode(old, node) {
|
|
22
|
+
if (!(old instanceof Element) || !(node instanceof Element)) {
|
|
23
|
+
if (old.nodeValue !== node.nodeValue)
|
|
24
|
+
old.nodeValue = node.nodeValue;
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
for (const { name } of [...old.attributes])
|
|
28
|
+
if (!node.hasAttribute(name))
|
|
29
|
+
old.removeAttribute(name);
|
|
30
|
+
for (const { name, value } of [...node.attributes])
|
|
31
|
+
if (old.getAttribute(name) !== value)
|
|
32
|
+
old.setAttribute(name, value);
|
|
33
|
+
morphChildren(old, node);
|
|
34
|
+
}
|
|
@@ -7,9 +7,13 @@ export declare const REPORT_LINKS: {
|
|
|
7
7
|
};
|
|
8
8
|
export declare const REPORT_TEXT: {
|
|
9
9
|
readonly TITLE: "Impact";
|
|
10
|
+
readonly COVERAGE: "Reticle Coverage";
|
|
11
|
+
readonly COVERAGE_HELP: "How much of this app Reticle has reached, touched and proved, from the coverage ledger in .reticle";
|
|
10
12
|
readonly PROJECT: "This project";
|
|
11
|
-
readonly GLOBAL: "
|
|
12
|
-
readonly
|
|
13
|
+
readonly GLOBAL: "All projects";
|
|
14
|
+
readonly GLOBAL_TITLE: "Every project on this computer";
|
|
15
|
+
readonly SCOPE_LABEL: "Show impact for";
|
|
16
|
+
readonly heroDefects: (n: number) => string;
|
|
13
17
|
readonly VERDICTS: "Verdicts";
|
|
14
18
|
readonly PASSED: "passed";
|
|
15
19
|
readonly FAILED: "failed";
|
|
@@ -27,20 +31,35 @@ export declare const REPORT_TEXT: {
|
|
|
27
31
|
readonly CHART: "Verdicts, last 30 days";
|
|
28
32
|
readonly DEFECTS: "What broke";
|
|
29
33
|
readonly DEFECTS_MORE: "Manage all of them on the dashboard — triage, assign, and push to GitHub";
|
|
34
|
+
readonly DEFECTS_SEE_ALL: "See all on the dashboard";
|
|
35
|
+
readonly DEFECTS_SEE_ALL_TITLE: "Sign up free, link this project, and see every bug Reticle caught";
|
|
30
36
|
readonly DEFECT_LINK_TITLE: "Open on the dashboard to triage or push to GitHub";
|
|
31
37
|
readonly LOCAL_ONLY: "This record stops at this machine.";
|
|
32
|
-
readonly LOCAL_ONLY_ACTION: "reticle
|
|
38
|
+
readonly LOCAL_ONLY_ACTION: "reticle connect";
|
|
33
39
|
readonly LOCAL_ONLY_TAIL: "keeps it, and lets a team see it.";
|
|
34
40
|
readonly UNLINKED: "Signed in, but this repo is not linked.";
|
|
35
|
-
readonly UNLINKED_ACTION: "reticle
|
|
41
|
+
readonly UNLINKED_ACTION: "reticle connect";
|
|
36
42
|
readonly UNLINKED_TAIL: "sends this record to your workspace.";
|
|
37
43
|
readonly DEFECTS_NONE: "Nothing has failed a declared consequence yet.";
|
|
38
44
|
readonly EMPTY: "Nothing recorded yet. Drive the app once and this fills in.";
|
|
39
45
|
readonly SHARE: "Share";
|
|
40
46
|
readonly COPY: "Copy";
|
|
41
47
|
readonly COPIED: "Copied";
|
|
42
|
-
readonly
|
|
48
|
+
readonly SEE_RUN: "See it in your dashboard";
|
|
49
|
+
readonly KEY_REFUSED: "Key refused";
|
|
50
|
+
readonly NOT_SENT: "not synced";
|
|
51
|
+
readonly NOT_SENT_HELP: "Runs saved on this machine but not on your dashboard yet. Run reticle connect in this project to send them.";
|
|
52
|
+
readonly INSTRUMENTATION: "What Reticle can see";
|
|
53
|
+
readonly INSTRUMENTATION_HELP: "What Reticle can see in this app, from its handshake and the gaps its verdicts recorded";
|
|
54
|
+
readonly covered: (covered: number, total: number) => string;
|
|
55
|
+
readonly NOT_SEEN_YET: "not checked yet";
|
|
56
|
+
readonly COPY_PROMPT: "Copy prompt for your coding agent";
|
|
57
|
+
readonly SHARE_LABEL: "Share";
|
|
58
|
+
readonly POST_X: "X";
|
|
59
|
+
readonly POST_IN: "LinkedIn";
|
|
60
|
+
readonly REFER: "Invite a friend";
|
|
43
61
|
};
|
|
62
|
+
export declare const CAPABILITY_WORDS: Readonly<Record<string, string>>;
|
|
44
63
|
export declare function compactNumber(n: number): string;
|
|
45
64
|
export declare function compactDuration(ms: number): string;
|
|
46
65
|
export declare function buildShareText(scope: ImpactScope, projectName?: string): string;
|
|
@@ -48,3 +67,7 @@ export declare function buildXShareUrl(text: string): string;
|
|
|
48
67
|
export declare function buildLinkedInShareUrl(): string;
|
|
49
68
|
export declare function buildReferralText(): string;
|
|
50
69
|
export declare function parseImpactSnapshot(value: unknown): ImpactSnapshot | undefined;
|
|
70
|
+
export declare const COVERAGE_LEVELS: readonly {
|
|
71
|
+
key: string;
|
|
72
|
+
label: string;
|
|
73
|
+
}[];
|
|
@@ -8,9 +8,13 @@ export const REPORT_LINKS = {
|
|
|
8
8
|
const SHARE_VIA_HANDLE = '';
|
|
9
9
|
export const REPORT_TEXT = {
|
|
10
10
|
TITLE: 'Impact',
|
|
11
|
+
COVERAGE: 'Reticle Coverage',
|
|
12
|
+
COVERAGE_HELP: 'How much of this app Reticle has reached, touched and proved, from the coverage ledger in .reticle',
|
|
11
13
|
PROJECT: 'This project',
|
|
12
|
-
GLOBAL: '
|
|
13
|
-
|
|
14
|
+
GLOBAL: 'All projects',
|
|
15
|
+
GLOBAL_TITLE: 'Every project on this computer',
|
|
16
|
+
SCOPE_LABEL: 'Show impact for',
|
|
17
|
+
heroDefects: (n) => `failed check${1 === n ? '' : 's'} caught before you shipped`,
|
|
14
18
|
VERDICTS: 'Verdicts',
|
|
15
19
|
PASSED: 'passed',
|
|
16
20
|
FAILED: 'failed',
|
|
@@ -28,19 +32,43 @@ export const REPORT_TEXT = {
|
|
|
28
32
|
CHART: 'Verdicts, last 30 days',
|
|
29
33
|
DEFECTS: 'What broke',
|
|
30
34
|
DEFECTS_MORE: 'Manage all of them on the dashboard — triage, assign, and push to GitHub',
|
|
35
|
+
DEFECTS_SEE_ALL: 'See all on the dashboard',
|
|
36
|
+
DEFECTS_SEE_ALL_TITLE: 'Sign up free, link this project, and see every bug Reticle caught',
|
|
31
37
|
DEFECT_LINK_TITLE: 'Open on the dashboard to triage or push to GitHub',
|
|
32
38
|
LOCAL_ONLY: 'This record stops at this machine.',
|
|
33
|
-
LOCAL_ONLY_ACTION: 'reticle
|
|
39
|
+
LOCAL_ONLY_ACTION: 'reticle connect',
|
|
34
40
|
LOCAL_ONLY_TAIL: 'keeps it, and lets a team see it.',
|
|
35
41
|
UNLINKED: 'Signed in, but this repo is not linked.',
|
|
36
|
-
UNLINKED_ACTION: 'reticle
|
|
42
|
+
UNLINKED_ACTION: 'reticle connect',
|
|
37
43
|
UNLINKED_TAIL: 'sends this record to your workspace.',
|
|
38
44
|
DEFECTS_NONE: 'Nothing has failed a declared consequence yet.',
|
|
39
45
|
EMPTY: 'Nothing recorded yet. Drive the app once and this fills in.',
|
|
40
46
|
SHARE: 'Share',
|
|
41
47
|
COPY: 'Copy',
|
|
42
48
|
COPIED: 'Copied',
|
|
43
|
-
|
|
49
|
+
SEE_RUN: 'See it in your dashboard',
|
|
50
|
+
KEY_REFUSED: 'Key refused',
|
|
51
|
+
NOT_SENT: 'not synced',
|
|
52
|
+
NOT_SENT_HELP: 'Runs saved on this machine but not on your dashboard yet. Run reticle connect in this project to send them.',
|
|
53
|
+
INSTRUMENTATION: 'What Reticle can see',
|
|
54
|
+
INSTRUMENTATION_HELP: 'What Reticle can see in this app, from its handshake and the gaps its verdicts recorded',
|
|
55
|
+
covered: (covered, total) => `${String(covered)} of ${String(total)}`,
|
|
56
|
+
NOT_SEEN_YET: 'not checked yet',
|
|
57
|
+
COPY_PROMPT: 'Copy prompt for your coding agent',
|
|
58
|
+
SHARE_LABEL: 'Share',
|
|
59
|
+
POST_X: 'X',
|
|
60
|
+
POST_IN: 'LinkedIn',
|
|
61
|
+
REFER: 'Invite a friend',
|
|
62
|
+
};
|
|
63
|
+
export const CAPABILITY_WORDS = {
|
|
64
|
+
'page connected': 'Page connected',
|
|
65
|
+
'DOM actions': 'Clicks and typing',
|
|
66
|
+
network: 'Network requests',
|
|
67
|
+
console: 'Console errors',
|
|
68
|
+
'app state (store registered)': 'App state',
|
|
69
|
+
'signals on mutation': 'Success signals from your code',
|
|
70
|
+
'file:line source mapping': 'Source file and line',
|
|
71
|
+
'stable test ids': 'Stable test ids',
|
|
44
72
|
};
|
|
45
73
|
export function compactNumber(n) {
|
|
46
74
|
if (n < 1000)
|
|
@@ -93,3 +121,10 @@ export function parseImpactSnapshot(value) {
|
|
|
93
121
|
const parsed = ImpactSnapshotSchema.safeParse(value);
|
|
94
122
|
return parsed.success ? parsed.data : undefined;
|
|
95
123
|
}
|
|
124
|
+
export const COVERAGE_LEVELS = [
|
|
125
|
+
{ key: 'reached', label: 'routes reached' },
|
|
126
|
+
{ key: 'touched', label: 'controls touched' },
|
|
127
|
+
{ key: 'proved', label: 'controls proved' },
|
|
128
|
+
{ key: 'branched', label: 'write failures driven' },
|
|
129
|
+
{ key: 'executed', label: 'code executed' },
|
|
130
|
+
];
|