@reticlehq/browser 3.2.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/actions-dom.d.ts +1 -1
- package/dist/actions/actions-dom.js +5 -4
- package/dist/actions/actions.d.ts +0 -1
- package/dist/actions/actions.js +4 -2
- package/dist/actions/value-input.js +14 -5
- package/dist/connect-options.d.ts +1 -0
- package/dist/dom/addressing/source.d.ts +2 -1
- package/dist/dom/dom-ignore.d.ts +1 -0
- package/dist/dom/dom-ignore.js +13 -0
- package/dist/dom/query.js +6 -4
- package/dist/dom/snapshot.d.ts +1 -1
- package/dist/observers/console.js +38 -13
- package/dist/observers/dom.js +3 -1
- package/dist/observers/download.js +3 -2
- package/dist/observers/error-repeats.d.ts +18 -0
- package/dist/observers/error-repeats.js +41 -0
- package/dist/observers/field.d.ts +2 -0
- package/dist/observers/field.js +86 -0
- package/dist/observers/frames.js +3 -4
- package/dist/observers/install-all.js +2 -0
- package/dist/observers/net-detail/network-body.d.ts +2 -0
- package/dist/observers/net-detail/network-body.js +24 -7
- package/dist/observers/route.js +4 -4
- package/dist/observers/state.js +18 -8
- package/dist/presenter/carousel/carousel.d.ts +13 -0
- package/dist/presenter/carousel/carousel.js +126 -0
- package/dist/presenter/carousel/offer-card.d.ts +21 -0
- package/dist/presenter/carousel/offer-card.js +55 -0
- package/dist/presenter/carousel/panel-slides.d.ts +7 -0
- package/dist/presenter/carousel/panel-slides.js +10 -0
- package/dist/presenter/carousel/talk-card.d.ts +8 -0
- package/dist/presenter/carousel/talk-card.js +20 -0
- package/dist/presenter/chrome/presenter-log.d.ts +2 -1
- package/dist/presenter/chrome/presenter-log.js +14 -3
- package/dist/presenter/chrome/presenter-report-copy.d.ts +0 -1
- package/dist/presenter/chrome/presenter-report-copy.js +1 -1
- package/dist/presenter/hud-telemetry.d.ts +3 -0
- package/dist/presenter/hud-telemetry.js +87 -0
- package/dist/presenter/presenter-account.js +1 -1
- package/dist/presenter/presenter-config.d.ts +5 -1
- package/dist/presenter/presenter-config.js +4 -1
- package/dist/presenter/presenter-controls.d.ts +1 -0
- package/dist/presenter/presenter-controls.js +12 -1
- package/dist/presenter/presenter-report.js +6 -6
- package/dist/presenter/presenter-settings-styles.js +2 -1
- package/dist/presenter/presenter-settings.d.ts +2 -4
- package/dist/presenter/presenter-settings.js +13 -4
- package/dist/presenter/presenter-shell-styles.js +4 -0
- package/dist/presenter/presenter-shell.d.ts +4 -1
- package/dist/presenter/presenter-shell.js +49 -11
- package/dist/presenter/presenter-styles.js +5 -1
- package/dist/presenter/presenter-workspace.d.ts +0 -7
- package/dist/presenter/presenter-workspace.js +7 -7
- package/dist/presenter/presenter.js +43 -10
- package/dist/presenter/tour/tour.d.ts +3 -0
- package/dist/presenter/tour/tour.js +5 -0
- package/dist/recorder/recorder.js +25 -22
- package/dist/reticle-inject.d.ts +2 -0
- package/dist/reticle-inject.js +1337 -0
- package/dist/reticle.js +6 -0
- package/dist/review/annotator.d.ts +3 -0
- package/dist/review/annotator.js +37 -37
- package/dist/review/marks-for-agent.d.ts +9 -0
- package/dist/review/marks-for-agent.js +12 -0
- package/dist/util/captured-method.d.ts +0 -1
- package/dist/util/captured-method.js +1 -1
- package/package.json +8 -4
- package/dist/presenter/presenter-offer.d.ts +0 -25
- package/dist/presenter/presenter-offer.js +0 -87
|
@@ -4,6 +4,7 @@ import { HUD_SURFACE_CLASS } from './chrome/presenter-hud-chrome.js';
|
|
|
4
4
|
import { resetHudDockPosition } from './presenter-drag.js';
|
|
5
5
|
import { findDock, scheduleSyncDockLayout } from './presenter-dock-layout.js';
|
|
6
6
|
import { SETTINGS_CSS } from './presenter-settings-styles.js';
|
|
7
|
+
import { DISCOVERY_CALL_URL, FOUNDER_EMAIL, FOUNDER_MAILTO } from '@reticlehq/core';
|
|
7
8
|
import { accountControlHtml } from './presenter-account.js';
|
|
8
9
|
export { SETTINGS_CSS };
|
|
9
10
|
export const OutputDetail = {
|
|
@@ -11,14 +12,14 @@ export const OutputDetail = {
|
|
|
11
12
|
STANDARD: 'standard',
|
|
12
13
|
VERBOSE: 'verbose',
|
|
13
14
|
};
|
|
14
|
-
|
|
15
|
+
const StatusThemeId = {
|
|
15
16
|
SIGNAL: 'signal',
|
|
16
17
|
TRAFFIC: 'traffic',
|
|
17
18
|
MONO: 'mono',
|
|
18
19
|
NEON: 'neon',
|
|
19
20
|
EMBER: 'ember',
|
|
20
21
|
};
|
|
21
|
-
|
|
22
|
+
const STATUS_THEMES = [
|
|
22
23
|
{
|
|
23
24
|
id: StatusThemeId.SIGNAL,
|
|
24
25
|
label: 'Signal',
|
|
@@ -132,7 +133,7 @@ function settingsHelpIcon() {
|
|
|
132
133
|
}
|
|
133
134
|
function settingsLabel(text, helpTitle) {
|
|
134
135
|
const help = settingsHelpIcon();
|
|
135
|
-
return `<span class="reticle-settings-label">${text}<button type="button" class="reticle-settings-help" title="${helpTitle}" aria-label="${helpTitle}">${help}</button></span>`;
|
|
136
|
+
return `<span class="reticle-settings-label">${text}<button type="button" class="reticle-settings-help" data-reticle-settings-help title="${helpTitle}" aria-label="${helpTitle}">${help}</button></span>`;
|
|
136
137
|
}
|
|
137
138
|
function settingsToggleRow(key, label, helpTitle, extra = '') {
|
|
138
139
|
return `<div class="reticle-settings-row" ${extra}>
|
|
@@ -181,6 +182,12 @@ export function paintSettingsAccount(root, account, dashboardUrl, details = {})
|
|
|
181
182
|
row.hidden = 0 === html.length;
|
|
182
183
|
slot.innerHTML = html;
|
|
183
184
|
}
|
|
185
|
+
const FEEDBACK_TEXT = {
|
|
186
|
+
EMAIL: 'Email the founder',
|
|
187
|
+
EMAIL_TITLE: `Write to ${FOUNDER_EMAIL}: a problem, a wish, anything`,
|
|
188
|
+
CALL: 'Book a call',
|
|
189
|
+
CALL_TITLE: 'Pick a time to talk it through',
|
|
190
|
+
};
|
|
184
191
|
export function settingsPanelHtml() {
|
|
185
192
|
const close = hiIconHtml(PresenterIcon.REMOVE, PRESENTER_ICON_SIZE.MIN);
|
|
186
193
|
const caret = hiIconHtml(PresenterIcon.CARET_RIGHT, PRESENTER_ICON_SIZE.HELP);
|
|
@@ -228,11 +235,13 @@ export function settingsPanelHtml() {
|
|
|
228
235
|
<div class="reticle-settings-foot">
|
|
229
236
|
<button type="button" class="reticle-settings-reset" data-reticle-settings-reset>Reset HUD position</button>
|
|
230
237
|
<button type="button" class="reticle-settings-link" data-reticle-settings-mcp>Manage MCP & Webhooks<span class="reticle-settings-link-caret" aria-hidden="true">${caret}</span></button>
|
|
238
|
+
<a class="reticle-settings-link" data-reticle-feedback-email href="${FOUNDER_MAILTO}" target="_blank" rel="noopener noreferrer" title="${FEEDBACK_TEXT.EMAIL_TITLE}">${FEEDBACK_TEXT.EMAIL}<span class="reticle-settings-link-caret" aria-hidden="true">${caret}</span></a>
|
|
239
|
+
<a class="reticle-settings-link" data-reticle-feedback-call href="${DISCOVERY_CALL_URL}" target="_blank" rel="noopener noreferrer" title="${FEEDBACK_TEXT.CALL_TITLE}">${FEEDBACK_TEXT.CALL}<span class="reticle-settings-link-caret" aria-hidden="true">${caret}</span></a>
|
|
231
240
|
</div>
|
|
232
241
|
</div>
|
|
233
242
|
</div>`;
|
|
234
243
|
}
|
|
235
|
-
|
|
244
|
+
function applyPresenterSettings(root, settings) {
|
|
236
245
|
const theme = statusTheme(settings.statusThemeId);
|
|
237
246
|
root.style.setProperty('--reticle-mark-accent', theme.active);
|
|
238
247
|
root.style.setProperty('--reticle-accent', theme.active);
|
|
@@ -246,6 +246,10 @@ export const SHELL_CSS = `
|
|
|
246
246
|
transition:opacity .2s var(--reticle-shell-ease),transform .18s var(--reticle-shell-ease);}
|
|
247
247
|
[${OVERLAY}][${MIN_ATTR}="0"] [${HUD}] .reticle-toolbar{
|
|
248
248
|
display:flex;opacity:1;transform:scale(1);pointer-events:auto;}
|
|
249
|
+
/* The HUD block is transformed, which makes it a stacking context at level 0 inside the dock, so a
|
|
250
|
+
tooltip's z-index only ranked it within the block and it drew behind the chat and settings panels,
|
|
251
|
+
its siblings. Lift the block while a tooltip can show. */
|
|
252
|
+
[${HUD}]:hover,[${HUD}]:focus-within{z-index:${String(Z_HUD_TOOLTIP)};}
|
|
249
253
|
[${HUD}] .reticle-toolbar-drag{cursor:grab;touch-action:none;user-select:none;}
|
|
250
254
|
[${HUD}] .reticle-toolbar-drag.reticle-drag-handle--dragging{cursor:grabbing;}
|
|
251
255
|
[${HUD}] .reticle-toolbar-actions{
|
|
@@ -2,7 +2,7 @@ import type { HarnessConfig } from '@reticlehq/core';
|
|
|
2
2
|
import { PresenterReport } from './presenter-report.js';
|
|
3
3
|
import type { AccountState } from '@reticlehq/core';
|
|
4
4
|
import { type AccountDetails } from './presenter-account.js';
|
|
5
|
-
import { type OfferState } from './
|
|
5
|
+
import { type OfferState } from './carousel/offer-card.js';
|
|
6
6
|
import { type SettingsHost } from './presenter-settings.js';
|
|
7
7
|
interface HudShellCallbacks {
|
|
8
8
|
onChatOpen?: () => void;
|
|
@@ -35,4 +35,7 @@ export declare class HudShell {
|
|
|
35
35
|
showChat(): void;
|
|
36
36
|
pulseFab(active: boolean): void;
|
|
37
37
|
}
|
|
38
|
+
export declare function wasMinimised(): boolean;
|
|
39
|
+
export declare function rememberMinimised(minimised: boolean): void;
|
|
40
|
+
export declare function shouldAutoOpenChat(autoOpenChat: boolean): boolean;
|
|
38
41
|
export {};
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
import { appModalOpen } from '../dom/dom-ignore.js';
|
|
1
2
|
import { PresenterReport, reportPanelHtml } from './presenter-report.js';
|
|
2
3
|
import { paintToolbarAccount, TOOLBAR_ACCOUNT_ATTR } from './presenter-workspace.js';
|
|
3
4
|
import { mountAccountControl } from './presenter-account.js';
|
|
4
5
|
import { paintSettingsAccount } from './presenter-settings.js';
|
|
5
|
-
import { CHAT_MIN_ATTR, CHAT_PILL_ATTR, REPORT_ATTR, REPORT_BTN_ATTR, ANNOTATE_BTN_ATTR, CHAT_ATTR, CHAT_TOGGLE_ATTR, CHAT_PANEL_ATTR, DOCK_ATTR, FAB_ATTR, MIN_ATTR, SETTINGS_ATTR, SETTINGS_BTN_ATTR, } from './presenter-config.js';
|
|
6
|
-
import {
|
|
6
|
+
import { CHAT_MIN_ATTR, CHAT_PILL_ATTR, REPORT_ATTR, REPORT_BTN_ATTR, ANNOTATE_BTN_ATTR, CHAT_ATTR, CHAT_TOGGLE_ATTR, CHAT_PANEL_ATTR, DOCK_ATTR, FAB_ATTR, MIN_ATTR, SETTINGS_ATTR, SETTINGS_BTN_ATTR, MINIMISED_STORAGE_KEY, } from './presenter-config.js';
|
|
7
|
+
import { offerDismissed } from './carousel/offer-card.js';
|
|
8
|
+
import { paintCarousel } from './carousel/carousel.js';
|
|
9
|
+
import { panelSlides } from './carousel/panel-slides.js';
|
|
7
10
|
import { BRAND_NAME, FAB_TOGGLE_HTML, MARK_SVG } from './chrome/presenter-brand.js';
|
|
8
|
-
import { settleLogAtLatest } from './chrome/presenter-log.js';
|
|
11
|
+
import { DATA_RETICLE_LOG, settleLogAtLatest } from './chrome/presenter-log.js';
|
|
9
12
|
import { installHudDragHandles, installHudPositionGuards } from './presenter-drag.js';
|
|
10
13
|
import { scheduleSyncDockLayout } from './presenter-dock-layout.js';
|
|
11
14
|
import { hiIconHtml, hiToggleIconHtml, PRESENTER_ICON_SIZE, PresenterIcon, } from './icons/presenter-icons.js';
|
|
@@ -59,9 +62,22 @@ export class HudShell {
|
|
|
59
62
|
#pushedOffer;
|
|
60
63
|
paintOffer(offer) {
|
|
61
64
|
this.#pushedOffer = offer;
|
|
62
|
-
|
|
65
|
+
this.#paintCarousel();
|
|
66
|
+
}
|
|
67
|
+
#paintCarousel() {
|
|
68
|
+
const log = this.#root?.querySelector(`[${DATA_RETICLE_LOG}]`);
|
|
69
|
+
if (!(log instanceof HTMLElement))
|
|
63
70
|
return;
|
|
64
|
-
|
|
71
|
+
const declined = offerDismissed(this.#storage());
|
|
72
|
+
paintCarousel(log, panelSlides(this.#pushedOffer, declined), this.#sessionStorage());
|
|
73
|
+
}
|
|
74
|
+
#sessionStorage() {
|
|
75
|
+
try {
|
|
76
|
+
return globalThis.sessionStorage;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
65
81
|
}
|
|
66
82
|
#storage() {
|
|
67
83
|
try {
|
|
@@ -97,7 +113,6 @@ export class HudShell {
|
|
|
97
113
|
${actStripHtml}
|
|
98
114
|
<span class="reticle-tally" data-reticle-tally hidden></span>
|
|
99
115
|
${bannerHtml}
|
|
100
|
-
<div ${OFFER_SLOT_ATTR}></div>
|
|
101
116
|
<div class="${HUD_LOG_WELL_CLASS}"><div ${logAttr}></div></div>
|
|
102
117
|
${flowsHtml}
|
|
103
118
|
${footHtml}
|
|
@@ -248,8 +263,7 @@ export class HudShell {
|
|
|
248
263
|
const pushed = this.#pushedAccount;
|
|
249
264
|
this.paintAccount(pushed.account, pushed.dashboardUrl, pushed.details);
|
|
250
265
|
}
|
|
251
|
-
|
|
252
|
-
this.paintOffer(this.#pushedOffer);
|
|
266
|
+
this.#paintCarousel();
|
|
253
267
|
}
|
|
254
268
|
teardown() {
|
|
255
269
|
this.#accountTeardown?.();
|
|
@@ -283,6 +297,7 @@ export class HudShell {
|
|
|
283
297
|
return;
|
|
284
298
|
this.#lockTransition();
|
|
285
299
|
this.#root.setAttribute(MIN_ATTR, '0');
|
|
300
|
+
rememberMinimised(false);
|
|
286
301
|
if (this.#fab !== undefined)
|
|
287
302
|
this.#fab.setAttribute('aria-expanded', 'true');
|
|
288
303
|
this.#callbacks.onExpand?.();
|
|
@@ -297,6 +312,7 @@ export class HudShell {
|
|
|
297
312
|
this.closeChat();
|
|
298
313
|
this.#settings.close();
|
|
299
314
|
this.#root.setAttribute(MIN_ATTR, '1');
|
|
315
|
+
rememberMinimised(true);
|
|
300
316
|
if (this.#fab !== undefined)
|
|
301
317
|
this.#fab.setAttribute('aria-expanded', 'false');
|
|
302
318
|
this.#callbacks.onCollapse?.();
|
|
@@ -367,6 +383,10 @@ export class HudShell {
|
|
|
367
383
|
#onKeyDown = (e) => {
|
|
368
384
|
if ('Escape' !== e.key || this.#root === undefined)
|
|
369
385
|
return;
|
|
386
|
+
if (e.defaultPrevented)
|
|
387
|
+
return;
|
|
388
|
+
if (appModalOpen(document))
|
|
389
|
+
return;
|
|
370
390
|
const target = e.target;
|
|
371
391
|
if (target instanceof HTMLElement &&
|
|
372
392
|
('INPUT' === target.tagName ||
|
|
@@ -375,18 +395,36 @@ export class HudShell {
|
|
|
375
395
|
return;
|
|
376
396
|
}
|
|
377
397
|
if (this.isChatOpen()) {
|
|
378
|
-
e.preventDefault();
|
|
379
398
|
this.closeChat();
|
|
380
399
|
return;
|
|
381
400
|
}
|
|
382
401
|
if (this.#settings.isOpen()) {
|
|
383
|
-
e.preventDefault();
|
|
384
402
|
this.#settings.close();
|
|
385
403
|
return;
|
|
386
404
|
}
|
|
387
405
|
if (!this.isCollapsed()) {
|
|
388
|
-
e.preventDefault();
|
|
389
406
|
this.collapse();
|
|
390
407
|
}
|
|
391
408
|
};
|
|
392
409
|
}
|
|
410
|
+
export function wasMinimised() {
|
|
411
|
+
try {
|
|
412
|
+
return '1' === globalThis.sessionStorage.getItem(MINIMISED_STORAGE_KEY);
|
|
413
|
+
}
|
|
414
|
+
catch {
|
|
415
|
+
return false;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
export function rememberMinimised(minimised) {
|
|
419
|
+
try {
|
|
420
|
+
if (minimised)
|
|
421
|
+
globalThis.sessionStorage.setItem(MINIMISED_STORAGE_KEY, '1');
|
|
422
|
+
else
|
|
423
|
+
globalThis.sessionStorage.removeItem(MINIMISED_STORAGE_KEY);
|
|
424
|
+
}
|
|
425
|
+
catch {
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
export function shouldAutoOpenChat(autoOpenChat) {
|
|
429
|
+
return autoOpenChat && !wasMinimised();
|
|
430
|
+
}
|
|
@@ -6,7 +6,9 @@ import { SETTINGS_CSS } from './presenter-settings-styles.js';
|
|
|
6
6
|
import { ACCOUNT_CSS } from './presenter-account-styles.js';
|
|
7
7
|
import { HUD_CHROME_CSS, HUD_LOG_WELL_CSS } from './chrome/presenter-hud-chrome.js';
|
|
8
8
|
import { REPORT_CSS } from './presenter-report-styles.js';
|
|
9
|
-
import { OFFER_CSS } from './
|
|
9
|
+
import { OFFER_CSS } from './carousel/offer-card.js';
|
|
10
|
+
import { TALK_CSS } from './carousel/talk-card.js';
|
|
11
|
+
import { CAROUSEL_CSS } from './carousel/carousel.js';
|
|
10
12
|
export { Z_OVERLAY };
|
|
11
13
|
export const PRESENTER_CSS = `
|
|
12
14
|
[data-reticle-overlay]{position:fixed;inset:0;pointer-events:none;z-index:${String(Z_OVERLAY)};}
|
|
@@ -65,5 +67,7 @@ ${SETTINGS_CSS}
|
|
|
65
67
|
${REPORT_CSS}
|
|
66
68
|
${ACCOUNT_CSS}
|
|
67
69
|
${OFFER_CSS}
|
|
70
|
+
${TALK_CSS}
|
|
71
|
+
${CAROUSEL_CSS}
|
|
68
72
|
${LOG_CSS}
|
|
69
73
|
${CONTROLS_CSS}`;
|
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
import type { AccountState } from '@reticlehq/core';
|
|
2
2
|
import { type AccountDetails } from './presenter-account.js';
|
|
3
3
|
export declare const TOOLBAR_ACCOUNT_ATTR = "data-reticle-toolbar-account";
|
|
4
|
-
export declare function readWorkspaceRoot(): string | undefined;
|
|
5
4
|
export declare function workspaceFolderLabel(root: string): string;
|
|
6
|
-
export declare function readProjectIdFromUrl(): string | undefined;
|
|
7
|
-
export declare function workspaceSummary(): {
|
|
8
|
-
folder: string;
|
|
9
|
-
root?: string;
|
|
10
|
-
projectId?: string;
|
|
11
|
-
};
|
|
12
5
|
export declare function workspaceRowHtml(): string;
|
|
13
6
|
export declare function paintWorkspace(root: HTMLElement): void;
|
|
14
7
|
export declare function mountWorkspaceSelector(root: HTMLElement): () => void;
|
|
@@ -14,7 +14,7 @@ const PROJECT_LABEL = 'Project';
|
|
|
14
14
|
const COPY_PATH_LABEL = 'Copy path';
|
|
15
15
|
const COPIED_PATH_LABEL = 'Copied';
|
|
16
16
|
const WORKSPACE_FALLBACK = 'This page';
|
|
17
|
-
|
|
17
|
+
function readWorkspaceRoot() {
|
|
18
18
|
const value = globalThis[RETICLE_ROOT_GLOBAL];
|
|
19
19
|
return 'string' === typeof value && value.length > 0 ? value : undefined;
|
|
20
20
|
}
|
|
@@ -24,13 +24,13 @@ export function workspaceFolderLabel(root) {
|
|
|
24
24
|
const last = parts[parts.length - 1];
|
|
25
25
|
return last !== undefined && last.length > 0 ? last : WORKSPACE_FALLBACK;
|
|
26
26
|
}
|
|
27
|
-
|
|
27
|
+
function readProjectIdFromUrl() {
|
|
28
28
|
if ('undefined' === typeof window)
|
|
29
29
|
return undefined;
|
|
30
30
|
const id = new URLSearchParams(window.location.search).get(RETICLE_URL_PARAM.PROJECT);
|
|
31
31
|
return id !== null && id.length > 0 ? id : undefined;
|
|
32
32
|
}
|
|
33
|
-
|
|
33
|
+
function workspaceSummary() {
|
|
34
34
|
const root = readWorkspaceRoot();
|
|
35
35
|
const projectId = readProjectIdFromUrl();
|
|
36
36
|
const folder = root !== undefined ? workspaceFolderLabel(root) : WORKSPACE_FALLBACK;
|
|
@@ -106,6 +106,7 @@ export function paintWorkspace(root) {
|
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
export function mountWorkspaceSelector(root) {
|
|
109
|
+
const ac = new AbortController();
|
|
109
110
|
paintWorkspace(root);
|
|
110
111
|
const btn = root.querySelector(`[${WORKSPACE_BTN_ATTR}]`);
|
|
111
112
|
const menu = root.querySelector(`[${WORKSPACE_MENU_ATTR}]`);
|
|
@@ -183,13 +184,12 @@ export function mountWorkspaceSelector(root) {
|
|
|
183
184
|
btn.addEventListener('click', onBtnClick);
|
|
184
185
|
menu.addEventListener('pointerdown', (e) => e.stopPropagation());
|
|
185
186
|
copyBtn?.addEventListener('click', onCopy);
|
|
186
|
-
document.addEventListener('pointerdown', onDocPointer);
|
|
187
|
-
document.addEventListener('keydown', onKeyDown);
|
|
187
|
+
document.addEventListener('pointerdown', onDocPointer, { signal: ac.signal });
|
|
188
|
+
document.addEventListener('keydown', onKeyDown, { signal: ac.signal });
|
|
188
189
|
return () => {
|
|
189
190
|
btn.removeEventListener('click', onBtnClick);
|
|
190
191
|
copyBtn?.removeEventListener('click', onCopy);
|
|
191
|
-
|
|
192
|
-
document.removeEventListener('keydown', onKeyDown);
|
|
192
|
+
ac.abort();
|
|
193
193
|
};
|
|
194
194
|
}
|
|
195
195
|
export function paintToolbarAccount(root, account, dashboardUrl, details = {}) {
|
|
@@ -3,19 +3,22 @@ import { refs } from '../dom/addressing/refs.js';
|
|
|
3
3
|
import { unreachableStripText } from '../transport/unreachable-message.js';
|
|
4
4
|
import { actionVerb } from './chrome/presenter-verbs.js';
|
|
5
5
|
import { nativeSetTimeout, nativeClearTimeout, nativeNow } from '../timers/native/native-timers.js';
|
|
6
|
-
import { LOG_KIND, CHIP_LABEL, DATA_RETICLE_LOG, clampLogMax, formatElapsed, humanDuration, appendLogRow, } from './chrome/presenter-log.js';
|
|
6
|
+
import { LOG_KIND, CHIP_LABEL, DATA_RETICLE_LOG, clampLogMax, formatElapsed, humanDuration, appendLogRow, clearLogRows, trimLogRows, } from './chrome/presenter-log.js';
|
|
7
7
|
import { PRESENTER_CSS } from './presenter-styles.js';
|
|
8
8
|
import { HudShell } from './presenter-shell.js';
|
|
9
9
|
import { parseImpactSnapshot } from './chrome/presenter-report-copy.js';
|
|
10
|
-
import { BorderMode, DEFAULT_BORDER_MODE, DATA_BUSY, BUSY_OFF, effectivePaceMs, IDLE_AFTER_MS, HEARTBEAT_MS, IDLE_NOTICE_MS, IDLE_END_MS, IDLE_END_MIN_MS, ACT_STRIP, STATE_ATTR, UNREACHABLE_STATE, GLOW_FADE_MS, GLOW_ON, GLOW_OFF, DATA_ON, THROTTLED_ATTR, LIVENESS_ATTR, MARKERS_BTN_ATTR, CLEAR_MARKS_ATTR, MARK_COUNT_ATTR, } from './presenter-config.js';
|
|
10
|
+
import { BorderMode, DEFAULT_BORDER_MODE, DATA_BUSY, BUSY_OFF, effectivePaceMs, IDLE_AFTER_MS, HEARTBEAT_MS, IDLE_NOTICE_MS, IDLE_END_MS, IDLE_END_MIN_MS, ACT_STRIP, STATE_ATTR, UNREACHABLE_STATE, GLOW_FADE_MS, GLOW_ON, GLOW_OFF, DATA_ON, THROTTLED_ATTR, LIVENESS_ATTR, MARKERS_BTN_ATTR, CLEAR_MARKS_ATTR, COPY_MARKS_ATTR, MARKS_ROW_ATTR, MARK_COUNT_ATTR, } from './presenter-config.js';
|
|
11
11
|
import { buildRunState } from './presenter-run-state.js';
|
|
12
12
|
import { moveCursor, ringAround, spawnRipple, pace } from './chrome/presenter-effects.js';
|
|
13
13
|
import { GlowController } from './presenter-glow.js';
|
|
14
14
|
import { PresenterIcon, PRESENTER_ICON_SIZE, hiIcon } from './icons/presenter-icons.js';
|
|
15
15
|
import { renderTally } from './chrome/presenter-tally.js';
|
|
16
|
-
import { CONTROLS_BANNER_HTML, CONTROLS_FLOWS_HTML, CONTROLS_FOOT_HTML, ENDED_FADE_MS, ControlPanel, } from './presenter-controls.js';
|
|
16
|
+
import { CONTROLS_BANNER_HTML, CONTROLS_FLOWS_HTML, CONTROLS_MARKS_HTML, CONTROLS_FOOT_HTML, ENDED_FADE_MS, ControlPanel, } from './presenter-controls.js';
|
|
17
17
|
import { statusTheme, blockerHtml, getPresenterSettings, OutputDetail, syncPageBlocker, } from './presenter-settings.js';
|
|
18
18
|
import { Annotator } from '../review/annotator.js';
|
|
19
|
+
import { shouldAutoOpenChat } from './presenter-shell.js';
|
|
20
|
+
import { installHudTelemetry } from './hud-telemetry.js';
|
|
21
|
+
const COPIED_FLASH_MS = 1600;
|
|
19
22
|
export { GlowPhase } from './presenter-config.js';
|
|
20
23
|
export { LOG_KIND, LOG_RESULT } from './chrome/presenter-log.js';
|
|
21
24
|
export class Presenter {
|
|
@@ -50,6 +53,8 @@ export class Presenter {
|
|
|
50
53
|
#log;
|
|
51
54
|
#logBaseMs;
|
|
52
55
|
#onControl;
|
|
56
|
+
#onHudUse;
|
|
57
|
+
#hudTelemetryTeardown;
|
|
53
58
|
#panel;
|
|
54
59
|
#shell;
|
|
55
60
|
#annotator;
|
|
@@ -70,6 +75,7 @@ export class Presenter {
|
|
|
70
75
|
});
|
|
71
76
|
this.#logMax = clampLogMax(options.logMax);
|
|
72
77
|
this.#onControl = options.onControl;
|
|
78
|
+
this.#onHudUse = options.onHudUse;
|
|
73
79
|
this.#panel = new ControlPanel({
|
|
74
80
|
emit: (kind, text) => this.#onControl?.(text !== undefined ? { kind, text } : { kind }),
|
|
75
81
|
logHuman: (text) => {
|
|
@@ -159,7 +165,7 @@ export class Presenter {
|
|
|
159
165
|
<div data-reticle-glow></div>
|
|
160
166
|
<div data-reticle-cursor></div>
|
|
161
167
|
<div data-reticle-ring></div>
|
|
162
|
-
${HudShell.dockHtml(actStrip, CONTROLS_BANNER_HTML, DATA_RETICLE_LOG, CONTROLS_FLOWS_HTML, CONTROLS_FOOT_HTML)}`;
|
|
168
|
+
${HudShell.dockHtml(actStrip, CONTROLS_BANNER_HTML, DATA_RETICLE_LOG, CONTROLS_MARKS_HTML + CONTROLS_FLOWS_HTML, CONTROLS_FOOT_HTML)}`;
|
|
163
169
|
document.body.appendChild(root);
|
|
164
170
|
this.#root = root;
|
|
165
171
|
this.#glow = root.querySelector('[data-reticle-glow]') ?? undefined;
|
|
@@ -179,6 +185,9 @@ export class Presenter {
|
|
|
179
185
|
syncPageBlocker(root, getPresenterSettings(), false);
|
|
180
186
|
this.#glowCtl.setElements(this.#glow, this.#cursor);
|
|
181
187
|
this.#panel.mount(root, this.#glow);
|
|
188
|
+
if (this.#onHudUse !== undefined) {
|
|
189
|
+
this.#hudTelemetryTeardown = installHudTelemetry(document, root, this.#onHudUse);
|
|
190
|
+
}
|
|
182
191
|
this.setMode(this.#mode);
|
|
183
192
|
this.#renderTally();
|
|
184
193
|
}
|
|
@@ -190,7 +199,23 @@ export class Presenter {
|
|
|
190
199
|
const markers = root.querySelector(`[${MARKERS_BTN_ATTR}]`);
|
|
191
200
|
const clear = root.querySelector(`[${CLEAR_MARKS_ATTR}]`);
|
|
192
201
|
const count = root.querySelector(`[${MARK_COUNT_ATTR}]`);
|
|
202
|
+
const copy = root.querySelector(`[${COPY_MARKS_ATTR}]`);
|
|
203
|
+
const row = root.querySelector(`[${MARKS_ROW_ATTR}]`);
|
|
193
204
|
const chrome = {};
|
|
205
|
+
if (row instanceof HTMLElement)
|
|
206
|
+
chrome.copyRow = row;
|
|
207
|
+
let marks = annotator.markCount;
|
|
208
|
+
chrome.onCount = (n) => {
|
|
209
|
+
if (0 === marks && n > 0)
|
|
210
|
+
this.#shell.openChat();
|
|
211
|
+
marks = n;
|
|
212
|
+
};
|
|
213
|
+
if (copy instanceof HTMLElement) {
|
|
214
|
+
copy.addEventListener('click', (e) => {
|
|
215
|
+
e.stopPropagation();
|
|
216
|
+
this.#copyMarks(annotator, copy);
|
|
217
|
+
});
|
|
218
|
+
}
|
|
194
219
|
if (markers instanceof HTMLElement)
|
|
195
220
|
chrome.markersBtn = markers;
|
|
196
221
|
if (clear instanceof HTMLElement)
|
|
@@ -201,6 +226,14 @@ export class Presenter {
|
|
|
201
226
|
annotator.setAccent(statusTheme(getPresenterSettings().statusThemeId).active);
|
|
202
227
|
this.#syncAnnotator();
|
|
203
228
|
}
|
|
229
|
+
#copyMarks(annotator, btn) {
|
|
230
|
+
const text = annotator.agentPrompt(window.location.href);
|
|
231
|
+
void navigator.clipboard?.writeText(text).catch(() => undefined);
|
|
232
|
+
btn.setAttribute('data-copied', '1');
|
|
233
|
+
nativeSetTimeout(() => btn.removeAttribute('data-copied'), COPIED_FLASH_MS);
|
|
234
|
+
if (getPresenterSettings().clearOnCopy)
|
|
235
|
+
annotator.clearAll();
|
|
236
|
+
}
|
|
204
237
|
#syncAnnotator() {
|
|
205
238
|
const live = !this.#shell.isCollapsed() && this.#shell.isAnnotateOn();
|
|
206
239
|
this.#annotator?.toggle(live);
|
|
@@ -213,6 +246,8 @@ export class Presenter {
|
|
|
213
246
|
if (this.#heartbeatTimer !== undefined)
|
|
214
247
|
nativeClearTimeout(this.#heartbeatTimer);
|
|
215
248
|
this.#heartbeatTimer = undefined;
|
|
249
|
+
this.#hudTelemetryTeardown?.();
|
|
250
|
+
this.#hudTelemetryTeardown = undefined;
|
|
216
251
|
this.#shell.teardown();
|
|
217
252
|
this.#panel.teardown();
|
|
218
253
|
this.#sessionActive = false;
|
|
@@ -237,7 +272,7 @@ export class Presenter {
|
|
|
237
272
|
this.#showSession();
|
|
238
273
|
this.#glowCtl.resetActivity(this.#now());
|
|
239
274
|
this.#startHeartbeat();
|
|
240
|
-
if (getPresenterSettings().autoOpenChat) {
|
|
275
|
+
if (shouldAutoOpenChat(getPresenterSettings().autoOpenChat)) {
|
|
241
276
|
this.#shell.openChat();
|
|
242
277
|
}
|
|
243
278
|
}
|
|
@@ -250,7 +285,7 @@ export class Presenter {
|
|
|
250
285
|
this.#hud?.setAttribute(DATA_ON, GLOW_ON);
|
|
251
286
|
this.#lastActionText = unreachableStripText(url, attempts);
|
|
252
287
|
this.#paintActStrip(this.#lastActionText, true);
|
|
253
|
-
if (getPresenterSettings().autoOpenChat)
|
|
288
|
+
if (shouldAutoOpenChat(getPresenterSettings().autoOpenChat))
|
|
254
289
|
this.#shell.openChat();
|
|
255
290
|
}
|
|
256
291
|
#showSession() {
|
|
@@ -413,7 +448,7 @@ export class Presenter {
|
|
|
413
448
|
this.#runLog = [];
|
|
414
449
|
this.#tallied = { passes: 0, fails: 0 };
|
|
415
450
|
if (this.#log !== undefined)
|
|
416
|
-
this.#log
|
|
451
|
+
clearLogRows(this.#log);
|
|
417
452
|
this.#renderTally();
|
|
418
453
|
}
|
|
419
454
|
#applyHideUntilRestart() {
|
|
@@ -477,9 +512,7 @@ export class Presenter {
|
|
|
477
512
|
#pruneLog() {
|
|
478
513
|
if (this.#log === undefined)
|
|
479
514
|
return;
|
|
480
|
-
|
|
481
|
-
this.#log.firstElementChild?.remove();
|
|
482
|
-
}
|
|
515
|
+
trimLogRows(this.#log, this.#logMax);
|
|
483
516
|
}
|
|
484
517
|
setThrottled(throttled) {
|
|
485
518
|
this.#root?.setAttribute(THROTTLED_ATTR, throttled ? '1' : '0');
|
|
@@ -11,6 +11,9 @@ export interface TourDeps {
|
|
|
11
11
|
readonly projectId: string;
|
|
12
12
|
readonly isDriving: () => boolean;
|
|
13
13
|
readonly search?: string;
|
|
14
|
+
readonly navigator?: {
|
|
15
|
+
readonly webdriver?: boolean;
|
|
16
|
+
};
|
|
14
17
|
readonly copy?: (text: string) => Promise<boolean> | boolean;
|
|
15
18
|
readonly select?: (element: Element) => void;
|
|
16
19
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { COPIED_LABEL, COPY_FLASH_MS, COPY_LABEL, COPY_MANUAL_LABEL, TOUR_ATTR, TOUR_CSS, TOUR_PROMPT_ATTR, TOUR_TARGET_ATTR, holeRects, isInteractive, isLastSlide, nextIndex, slideHtml, tourSlides, } from './tour-view.js';
|
|
2
2
|
import { TourAnchor } from '@reticlehq/core/tour';
|
|
3
3
|
import { RETICLE_URL_PARAM } from '@reticlehq/core';
|
|
4
|
+
import { appModalOpen } from '../../dom/dom-ignore.js';
|
|
4
5
|
export const TOUR_SEEN_KEY_PREFIX = 'reticle.tour.seen.';
|
|
5
6
|
const APP_SELECTORS = ['main', '#root', '#app'];
|
|
6
7
|
const HUD_PARTS = ['[data-reticle-hud]', '[data-reticle-chat-panel]'];
|
|
@@ -79,6 +80,8 @@ export function mountTour(deps) {
|
|
|
79
80
|
return undefined;
|
|
80
81
|
if (openedByReticle(deps.search))
|
|
81
82
|
return undefined;
|
|
83
|
+
if (true === deps.navigator?.webdriver)
|
|
84
|
+
return undefined;
|
|
82
85
|
if (tourAlreadySeen(deps.storage, deps.projectId))
|
|
83
86
|
return undefined;
|
|
84
87
|
const doc = deps.document;
|
|
@@ -220,6 +223,8 @@ export function mountTour(deps) {
|
|
|
220
223
|
const onKey = (event) => {
|
|
221
224
|
if (!open)
|
|
222
225
|
return;
|
|
226
|
+
if (appModalOpen(doc))
|
|
227
|
+
return;
|
|
223
228
|
const step = 'ArrowRight' === event.key ? 1 : 'ArrowLeft' === event.key ? -1 : 0;
|
|
224
229
|
if ('Escape' === event.key) {
|
|
225
230
|
event.preventDefault();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ActionType, AnchorKind, AnnotationKind, DEGRADED_ANCHOR_ROLE, EventType, FLOW_FILE_VERSION, FlowStepTool, RecorderPhase, } from '@reticlehq/core';
|
|
1
|
+
import { ActionType, AnchorKind, AnnotationKind, DEGRADED_ANCHOR_ROLE, EventType, FLOW_FILE_VERSION, FlowStepTool, RecorderPhase, PredicateKind, } from '@reticlehq/core';
|
|
2
2
|
import { REDACTED_VALUE } from '@reticlehq/core';
|
|
3
3
|
import { sourceFromDom } from '../dom/addressing/source.js';
|
|
4
4
|
import { getAccessibleName, getRole, isSensitiveField } from '../dom/a11y.js';
|
|
@@ -83,17 +83,20 @@ export function compileRecording(name, steps, annotations, createdAt, startPath)
|
|
|
83
83
|
}
|
|
84
84
|
if (ann.kind === AnnotationKind.SUCCESS_STATE) {
|
|
85
85
|
if (ann.signal !== undefined)
|
|
86
|
-
success = {
|
|
86
|
+
success = { kind: PredicateKind.SIGNAL, name: ann.signal };
|
|
87
87
|
continue;
|
|
88
88
|
}
|
|
89
89
|
const target = out.at(-1);
|
|
90
90
|
if (target === undefined)
|
|
91
91
|
continue;
|
|
92
92
|
if (ann.kind === AnnotationKind.ASSERT_SIGNAL && ann.signal !== undefined) {
|
|
93
|
-
target.expect =
|
|
93
|
+
target.expect = alsoAssert(target.expect, { kind: PredicateKind.SIGNAL, name: ann.signal });
|
|
94
94
|
}
|
|
95
95
|
else if (ann.kind === AnnotationKind.ASSERT_VISIBLE) {
|
|
96
|
-
target.expect =
|
|
96
|
+
target.expect = alsoAssert(target.expect, {
|
|
97
|
+
kind: PredicateKind.ELEMENT,
|
|
98
|
+
query: anchorToElement(ann.anchor),
|
|
99
|
+
});
|
|
97
100
|
}
|
|
98
101
|
}
|
|
99
102
|
const flow = { version: FLOW_FILE_VERSION, name, createdAt, steps: out };
|
|
@@ -105,6 +108,14 @@ export function compileRecording(name, steps, annotations, createdAt, startPath)
|
|
|
105
108
|
flow.success = success;
|
|
106
109
|
return flow;
|
|
107
110
|
}
|
|
111
|
+
function alsoAssert(existing, clause) {
|
|
112
|
+
if (existing === undefined)
|
|
113
|
+
return clause;
|
|
114
|
+
if (existing.kind === PredicateKind.ALL_OF) {
|
|
115
|
+
return { kind: PredicateKind.ALL_OF, predicates: [...existing.predicates, clause] };
|
|
116
|
+
}
|
|
117
|
+
return { kind: PredicateKind.ALL_OF, predicates: [existing, clause] };
|
|
118
|
+
}
|
|
108
119
|
function anchorToElement(anchor) {
|
|
109
120
|
if (anchor.kind === AnchorKind.TESTID)
|
|
110
121
|
return { testid: anchor.value };
|
|
@@ -122,7 +133,7 @@ class Recorder {
|
|
|
122
133
|
#startPath;
|
|
123
134
|
#annotations = [];
|
|
124
135
|
#pendingFill;
|
|
125
|
-
#
|
|
136
|
+
#captureAbort;
|
|
126
137
|
#root;
|
|
127
138
|
#statusEl;
|
|
128
139
|
#menuEl;
|
|
@@ -145,9 +156,8 @@ class Recorder {
|
|
|
145
156
|
this.#installCapture();
|
|
146
157
|
}
|
|
147
158
|
destroy() {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
this.#teardowns = [];
|
|
159
|
+
this.#captureAbort?.abort();
|
|
160
|
+
this.#captureAbort = undefined;
|
|
151
161
|
this.#root?.remove();
|
|
152
162
|
this.#root = undefined;
|
|
153
163
|
this.#statusEl = undefined;
|
|
@@ -159,20 +169,13 @@ class Recorder {
|
|
|
159
169
|
this.#pendingFill = undefined;
|
|
160
170
|
}
|
|
161
171
|
#installCapture() {
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
document.addEventListener('
|
|
167
|
-
document.addEventListener('
|
|
168
|
-
document.addEventListener('
|
|
169
|
-
document.addEventListener('submit', onSubmit, true);
|
|
170
|
-
this.#teardowns.push(() => {
|
|
171
|
-
document.removeEventListener('click', onClick, true);
|
|
172
|
-
document.removeEventListener('input', onInput, true);
|
|
173
|
-
document.removeEventListener('change', onChange, true);
|
|
174
|
-
document.removeEventListener('submit', onSubmit, true);
|
|
175
|
-
});
|
|
172
|
+
const ac = new AbortController();
|
|
173
|
+
this.#captureAbort = ac;
|
|
174
|
+
const opts = { capture: true, signal: ac.signal };
|
|
175
|
+
document.addEventListener('click', (ev) => this.#onClick(ev), opts);
|
|
176
|
+
document.addEventListener('input', (ev) => this.#onInput(ev), opts);
|
|
177
|
+
document.addEventListener('change', (ev) => this.#onChange(ev), opts);
|
|
178
|
+
document.addEventListener('submit', (ev) => this.#onSubmit(ev), opts);
|
|
176
179
|
}
|
|
177
180
|
#ignore(ev) {
|
|
178
181
|
const target = ev.target;
|