@reticlehq/browser 3.1.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 +2 -1
- package/dist/actions/actions-dom.js +28 -41
- package/dist/actions/actions.d.ts +0 -1
- package/dist/actions/actions.js +13 -7
- 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 +28 -9
- package/dist/dom/snapshot.d.ts +1 -1
- package/dist/observers/console.js +38 -13
- package/dist/observers/departure.js +16 -8
- 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/network.js +49 -8
- 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-styles.js +4 -0
- package/dist/presenter/presenter-report.js +8 -7
- package/dist/presenter/presenter-settings-styles.js +2 -1
- package/dist/presenter/presenter-settings.d.ts +6 -4
- package/dist/presenter/presenter-settings.js +51 -5
- package/dist/presenter/presenter-shell-styles.js +16 -0
- package/dist/presenter/presenter-shell.d.ts +7 -0
- package/dist/presenter/presenter-shell.js +65 -5
- package/dist/presenter/presenter-styles.js +6 -0
- package/dist/presenter/presenter-workspace.d.ts +0 -7
- package/dist/presenter/presenter-workspace.js +7 -7
- package/dist/presenter/presenter.js +46 -10
- package/dist/presenter/tour/tour-view.js +7 -3
- package/dist/presenter/tour/tour.d.ts +3 -0
- package/dist/presenter/tour/tour.js +26 -2
- 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 +11 -1
- 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
|
@@ -1,10 +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 { 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';
|
|
6
10
|
import { BRAND_NAME, FAB_TOGGLE_HTML, MARK_SVG } from './chrome/presenter-brand.js';
|
|
7
|
-
import { settleLogAtLatest } from './chrome/presenter-log.js';
|
|
11
|
+
import { DATA_RETICLE_LOG, settleLogAtLatest } from './chrome/presenter-log.js';
|
|
8
12
|
import { installHudDragHandles, installHudPositionGuards } from './presenter-drag.js';
|
|
9
13
|
import { scheduleSyncDockLayout } from './presenter-dock-layout.js';
|
|
10
14
|
import { hiIconHtml, hiToggleIconHtml, PRESENTER_ICON_SIZE, PresenterIcon, } from './icons/presenter-icons.js';
|
|
@@ -45,6 +49,9 @@ export class HudShell {
|
|
|
45
49
|
#callbacks;
|
|
46
50
|
#pushedAccount;
|
|
47
51
|
#accountTeardown;
|
|
52
|
+
paintHarness(config) {
|
|
53
|
+
this.#settings.paintHarness(config);
|
|
54
|
+
}
|
|
48
55
|
paintAccount(account, dashboardUrl, details = {}) {
|
|
49
56
|
this.#pushedAccount = { account, dashboardUrl, details };
|
|
50
57
|
if (this.#root === undefined)
|
|
@@ -52,6 +59,34 @@ export class HudShell {
|
|
|
52
59
|
paintToolbarAccount(this.#root, account, dashboardUrl, details);
|
|
53
60
|
paintSettingsAccount(this.#root, account, dashboardUrl, details);
|
|
54
61
|
}
|
|
62
|
+
#pushedOffer;
|
|
63
|
+
paintOffer(offer) {
|
|
64
|
+
this.#pushedOffer = offer;
|
|
65
|
+
this.#paintCarousel();
|
|
66
|
+
}
|
|
67
|
+
#paintCarousel() {
|
|
68
|
+
const log = this.#root?.querySelector(`[${DATA_RETICLE_LOG}]`);
|
|
69
|
+
if (!(log instanceof HTMLElement))
|
|
70
|
+
return;
|
|
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
|
+
}
|
|
81
|
+
}
|
|
82
|
+
#storage() {
|
|
83
|
+
try {
|
|
84
|
+
return globalThis.localStorage;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
55
90
|
constructor(callbacks = {}) {
|
|
56
91
|
this.#callbacks = callbacks;
|
|
57
92
|
this.#settings = new PresenterSettingsPanel({
|
|
@@ -228,6 +263,7 @@ export class HudShell {
|
|
|
228
263
|
const pushed = this.#pushedAccount;
|
|
229
264
|
this.paintAccount(pushed.account, pushed.dashboardUrl, pushed.details);
|
|
230
265
|
}
|
|
266
|
+
this.#paintCarousel();
|
|
231
267
|
}
|
|
232
268
|
teardown() {
|
|
233
269
|
this.#accountTeardown?.();
|
|
@@ -261,6 +297,7 @@ export class HudShell {
|
|
|
261
297
|
return;
|
|
262
298
|
this.#lockTransition();
|
|
263
299
|
this.#root.setAttribute(MIN_ATTR, '0');
|
|
300
|
+
rememberMinimised(false);
|
|
264
301
|
if (this.#fab !== undefined)
|
|
265
302
|
this.#fab.setAttribute('aria-expanded', 'true');
|
|
266
303
|
this.#callbacks.onExpand?.();
|
|
@@ -275,6 +312,7 @@ export class HudShell {
|
|
|
275
312
|
this.closeChat();
|
|
276
313
|
this.#settings.close();
|
|
277
314
|
this.#root.setAttribute(MIN_ATTR, '1');
|
|
315
|
+
rememberMinimised(true);
|
|
278
316
|
if (this.#fab !== undefined)
|
|
279
317
|
this.#fab.setAttribute('aria-expanded', 'false');
|
|
280
318
|
this.#callbacks.onCollapse?.();
|
|
@@ -345,6 +383,10 @@ export class HudShell {
|
|
|
345
383
|
#onKeyDown = (e) => {
|
|
346
384
|
if ('Escape' !== e.key || this.#root === undefined)
|
|
347
385
|
return;
|
|
386
|
+
if (e.defaultPrevented)
|
|
387
|
+
return;
|
|
388
|
+
if (appModalOpen(document))
|
|
389
|
+
return;
|
|
348
390
|
const target = e.target;
|
|
349
391
|
if (target instanceof HTMLElement &&
|
|
350
392
|
('INPUT' === target.tagName ||
|
|
@@ -353,18 +395,36 @@ export class HudShell {
|
|
|
353
395
|
return;
|
|
354
396
|
}
|
|
355
397
|
if (this.isChatOpen()) {
|
|
356
|
-
e.preventDefault();
|
|
357
398
|
this.closeChat();
|
|
358
399
|
return;
|
|
359
400
|
}
|
|
360
401
|
if (this.#settings.isOpen()) {
|
|
361
|
-
e.preventDefault();
|
|
362
402
|
this.#settings.close();
|
|
363
403
|
return;
|
|
364
404
|
}
|
|
365
405
|
if (!this.isCollapsed()) {
|
|
366
|
-
e.preventDefault();
|
|
367
406
|
this.collapse();
|
|
368
407
|
}
|
|
369
408
|
};
|
|
370
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,6 +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 './carousel/offer-card.js';
|
|
10
|
+
import { TALK_CSS } from './carousel/talk-card.js';
|
|
11
|
+
import { CAROUSEL_CSS } from './carousel/carousel.js';
|
|
9
12
|
export { Z_OVERLAY };
|
|
10
13
|
export const PRESENTER_CSS = `
|
|
11
14
|
[data-reticle-overlay]{position:fixed;inset:0;pointer-events:none;z-index:${String(Z_OVERLAY)};}
|
|
@@ -63,5 +66,8 @@ ${SHELL_CSS}
|
|
|
63
66
|
${SETTINGS_CSS}
|
|
64
67
|
${REPORT_CSS}
|
|
65
68
|
${ACCOUNT_CSS}
|
|
69
|
+
${OFFER_CSS}
|
|
70
|
+
${TALK_CSS}
|
|
71
|
+
${CAROUSEL_CSS}
|
|
66
72
|
${LOG_CSS}
|
|
67
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) => {
|
|
@@ -93,6 +99,7 @@ export class Presenter {
|
|
|
93
99
|
},
|
|
94
100
|
onHideUntilRestart: () => this.#applyHideUntilRestart(),
|
|
95
101
|
onSettingsChange: (s) => this.#onSettingsChange(s),
|
|
102
|
+
onHarness: (enabled) => this.#onControl?.({ kind: HumanControlKind.HARNESS, text: enabled ? 'on' : 'off' }),
|
|
96
103
|
},
|
|
97
104
|
});
|
|
98
105
|
}
|
|
@@ -119,6 +126,8 @@ export class Presenter {
|
|
|
119
126
|
const snapshot = parseImpactSnapshot(a['snapshot']);
|
|
120
127
|
if (snapshot !== undefined) {
|
|
121
128
|
this.#shell.report.setSnapshot(snapshot);
|
|
129
|
+
this.#shell.paintOffer(snapshot.harnessOffer);
|
|
130
|
+
this.#shell.paintHarness(snapshot.harnessConfig);
|
|
122
131
|
this.#shell.paintAccount(snapshot.account, snapshot.dashboardUrl, {
|
|
123
132
|
projectName: snapshot.projectName,
|
|
124
133
|
dashboardUrl: snapshot.dashboardUrl,
|
|
@@ -156,7 +165,7 @@ export class Presenter {
|
|
|
156
165
|
<div data-reticle-glow></div>
|
|
157
166
|
<div data-reticle-cursor></div>
|
|
158
167
|
<div data-reticle-ring></div>
|
|
159
|
-
${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)}`;
|
|
160
169
|
document.body.appendChild(root);
|
|
161
170
|
this.#root = root;
|
|
162
171
|
this.#glow = root.querySelector('[data-reticle-glow]') ?? undefined;
|
|
@@ -176,6 +185,9 @@ export class Presenter {
|
|
|
176
185
|
syncPageBlocker(root, getPresenterSettings(), false);
|
|
177
186
|
this.#glowCtl.setElements(this.#glow, this.#cursor);
|
|
178
187
|
this.#panel.mount(root, this.#glow);
|
|
188
|
+
if (this.#onHudUse !== undefined) {
|
|
189
|
+
this.#hudTelemetryTeardown = installHudTelemetry(document, root, this.#onHudUse);
|
|
190
|
+
}
|
|
179
191
|
this.setMode(this.#mode);
|
|
180
192
|
this.#renderTally();
|
|
181
193
|
}
|
|
@@ -187,7 +199,23 @@ export class Presenter {
|
|
|
187
199
|
const markers = root.querySelector(`[${MARKERS_BTN_ATTR}]`);
|
|
188
200
|
const clear = root.querySelector(`[${CLEAR_MARKS_ATTR}]`);
|
|
189
201
|
const count = root.querySelector(`[${MARK_COUNT_ATTR}]`);
|
|
202
|
+
const copy = root.querySelector(`[${COPY_MARKS_ATTR}]`);
|
|
203
|
+
const row = root.querySelector(`[${MARKS_ROW_ATTR}]`);
|
|
190
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
|
+
}
|
|
191
219
|
if (markers instanceof HTMLElement)
|
|
192
220
|
chrome.markersBtn = markers;
|
|
193
221
|
if (clear instanceof HTMLElement)
|
|
@@ -198,6 +226,14 @@ export class Presenter {
|
|
|
198
226
|
annotator.setAccent(statusTheme(getPresenterSettings().statusThemeId).active);
|
|
199
227
|
this.#syncAnnotator();
|
|
200
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
|
+
}
|
|
201
237
|
#syncAnnotator() {
|
|
202
238
|
const live = !this.#shell.isCollapsed() && this.#shell.isAnnotateOn();
|
|
203
239
|
this.#annotator?.toggle(live);
|
|
@@ -210,6 +246,8 @@ export class Presenter {
|
|
|
210
246
|
if (this.#heartbeatTimer !== undefined)
|
|
211
247
|
nativeClearTimeout(this.#heartbeatTimer);
|
|
212
248
|
this.#heartbeatTimer = undefined;
|
|
249
|
+
this.#hudTelemetryTeardown?.();
|
|
250
|
+
this.#hudTelemetryTeardown = undefined;
|
|
213
251
|
this.#shell.teardown();
|
|
214
252
|
this.#panel.teardown();
|
|
215
253
|
this.#sessionActive = false;
|
|
@@ -234,7 +272,7 @@ export class Presenter {
|
|
|
234
272
|
this.#showSession();
|
|
235
273
|
this.#glowCtl.resetActivity(this.#now());
|
|
236
274
|
this.#startHeartbeat();
|
|
237
|
-
if (getPresenterSettings().autoOpenChat) {
|
|
275
|
+
if (shouldAutoOpenChat(getPresenterSettings().autoOpenChat)) {
|
|
238
276
|
this.#shell.openChat();
|
|
239
277
|
}
|
|
240
278
|
}
|
|
@@ -247,7 +285,7 @@ export class Presenter {
|
|
|
247
285
|
this.#hud?.setAttribute(DATA_ON, GLOW_ON);
|
|
248
286
|
this.#lastActionText = unreachableStripText(url, attempts);
|
|
249
287
|
this.#paintActStrip(this.#lastActionText, true);
|
|
250
|
-
if (getPresenterSettings().autoOpenChat)
|
|
288
|
+
if (shouldAutoOpenChat(getPresenterSettings().autoOpenChat))
|
|
251
289
|
this.#shell.openChat();
|
|
252
290
|
}
|
|
253
291
|
#showSession() {
|
|
@@ -410,7 +448,7 @@ export class Presenter {
|
|
|
410
448
|
this.#runLog = [];
|
|
411
449
|
this.#tallied = { passes: 0, fails: 0 };
|
|
412
450
|
if (this.#log !== undefined)
|
|
413
|
-
this.#log
|
|
451
|
+
clearLogRows(this.#log);
|
|
414
452
|
this.#renderTally();
|
|
415
453
|
}
|
|
416
454
|
#applyHideUntilRestart() {
|
|
@@ -474,9 +512,7 @@ export class Presenter {
|
|
|
474
512
|
#pruneLog() {
|
|
475
513
|
if (this.#log === undefined)
|
|
476
514
|
return;
|
|
477
|
-
|
|
478
|
-
this.#log.firstElementChild?.remove();
|
|
479
|
-
}
|
|
515
|
+
trimLogRows(this.#log, this.#logMax);
|
|
480
516
|
}
|
|
481
517
|
setThrottled(throttled) {
|
|
482
518
|
this.#root?.setAttribute(THROTTLED_ATTR, throttled ? '1' : '0');
|
|
@@ -111,9 +111,13 @@ export const TOUR_CSS = `
|
|
|
111
111
|
[${TOUR_ATTR}] .reticle-tour-blocker{position:absolute;pointer-events:auto;z-index:0;}
|
|
112
112
|
/* Ringed slides dim through the ring's own 9999px shadow, which leaves a hole over the thing being
|
|
113
113
|
pointed at. The scrim would cover that hole -- dimming the HUD the slide is ABOUT, and washing
|
|
114
|
-
the page twice (~80% rather than 55%).
|
|
115
|
-
|
|
116
|
-
|
|
114
|
+
the page twice (~80% rather than 55%).
|
|
115
|
+
It is cleared for the clicks too, and that half was missing. A scrim blocks only while it is
|
|
116
|
+
VISIBLY blocking: reported from the field on 3.1.0 by several users, one of whom removed the
|
|
117
|
+
plugin, because a cleared scrim went on eating every press on a page that looked lit and
|
|
118
|
+
reachable. An overlay that looks transparent and behaves opaque reads as the app being broken. */
|
|
119
|
+
[${TOUR_ATTR}] .reticle-tour-scrim.is-clear{background:transparent;backdrop-filter:none;
|
|
120
|
+
pointer-events:none;}
|
|
117
121
|
/* Above the ring, and the z-index is the whole point.
|
|
118
122
|
The ring is appended after the card, and both are positioned with an auto z-index, so the ring
|
|
119
123
|
painted on top of it -- and the ring's dimming IS a 9999px shadow, which therefore fell across the
|
|
@@ -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;
|
|
@@ -92,15 +95,27 @@ export function mountTour(deps) {
|
|
|
92
95
|
let index = 0;
|
|
93
96
|
let open = true;
|
|
94
97
|
let detachKeys = () => undefined;
|
|
95
|
-
|
|
98
|
+
let stopWatch = () => undefined;
|
|
99
|
+
const remove = () => {
|
|
96
100
|
if (!open)
|
|
97
101
|
return;
|
|
98
102
|
open = false;
|
|
99
103
|
detachKeys();
|
|
100
|
-
|
|
104
|
+
stopWatch();
|
|
101
105
|
root.remove();
|
|
102
106
|
style.remove();
|
|
103
107
|
};
|
|
108
|
+
const close = () => {
|
|
109
|
+
if (!open)
|
|
110
|
+
return;
|
|
111
|
+
markSeen(deps.storage, deps.projectId);
|
|
112
|
+
remove();
|
|
113
|
+
};
|
|
114
|
+
const yieldToDrive = () => {
|
|
115
|
+
if (!deps.isDriving())
|
|
116
|
+
return;
|
|
117
|
+
remove();
|
|
118
|
+
};
|
|
104
119
|
const draw = () => {
|
|
105
120
|
const slide = slides[index];
|
|
106
121
|
if (undefined === slide)
|
|
@@ -208,6 +223,8 @@ export function mountTour(deps) {
|
|
|
208
223
|
const onKey = (event) => {
|
|
209
224
|
if (!open)
|
|
210
225
|
return;
|
|
226
|
+
if (appModalOpen(doc))
|
|
227
|
+
return;
|
|
211
228
|
const step = 'ArrowRight' === event.key ? 1 : 'ArrowLeft' === event.key ? -1 : 0;
|
|
212
229
|
if ('Escape' === event.key) {
|
|
213
230
|
event.preventDefault();
|
|
@@ -229,6 +246,13 @@ export function mountTour(deps) {
|
|
|
229
246
|
};
|
|
230
247
|
draw();
|
|
231
248
|
doc.body.appendChild(root);
|
|
249
|
+
const overlay = doc.querySelector('[data-reticle-overlay]');
|
|
250
|
+
if (null !== overlay) {
|
|
251
|
+
const observer = new MutationObserver(yieldToDrive);
|
|
252
|
+
observer.observe(overlay, { attributes: true, attributeFilter: ['data-reticle-mode'] });
|
|
253
|
+
stopWatch = () => observer.disconnect();
|
|
254
|
+
yieldToDrive();
|
|
255
|
+
}
|
|
232
256
|
return {
|
|
233
257
|
destroy: close,
|
|
234
258
|
isOpen: () => open,
|
|
@@ -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;
|