@reticlehq/browser 2.14.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/actions-dom.d.ts +9 -0
- package/dist/actions/actions-dom.js +81 -28
- package/dist/actions/actions-press.d.ts +4 -0
- package/dist/actions/actions-press.js +46 -0
- package/dist/actions/actions.d.ts +1 -0
- package/dist/actions/actions.js +50 -13
- package/dist/actions/appeared-text.js +6 -0
- package/dist/actions/scroll.d.ts +8 -1
- package/dist/actions/scroll.js +28 -10
- package/dist/commands/commands.js +10 -3
- package/dist/dom/a11y.js +2 -2
- package/dist/dom/desktop-capture.js +1 -1
- package/dist/dom/dom-ignore.js +1 -1
- package/dist/dom/occlusion.js +31 -5
- package/dist/dom/paint-context.d.ts +6 -0
- package/dist/dom/paint-context.js +38 -0
- package/dist/dom/query.js +24 -7
- package/dist/dom/snapshot.js +1 -1
- package/dist/edit-epoch.d.ts +1 -1
- package/dist/edit-epoch.js +1 -1
- package/dist/index.d.ts +4 -5
- package/dist/index.js +3 -5
- package/dist/observers/animation.js +1 -1
- package/dist/observers/blind-spots.js +1 -1
- package/dist/observers/departure.js +16 -8
- package/dist/observers/dialogs.js +1 -1
- package/dist/observers/dom.js +28 -8
- package/dist/observers/download.js +1 -1
- package/dist/observers/health.js +1 -1
- package/dist/observers/{network-body.js → net-detail/network-body.js} +2 -2
- package/dist/observers/network-stream.js +2 -2
- package/dist/observers/network.d.ts +1 -1
- package/dist/observers/network.js +51 -10
- package/dist/observers/scroll.js +2 -2
- package/dist/presenter/chrome/layers.d.ts +2 -0
- package/dist/presenter/chrome/layers.js +2 -0
- package/dist/presenter/chrome/log-kinds.d.ts +12 -0
- package/dist/presenter/chrome/log-kinds.js +7 -0
- package/dist/presenter/{presenter-effects.js → chrome/presenter-effects.js} +1 -1
- package/dist/presenter/{presenter-log.d.ts → chrome/presenter-log.d.ts} +3 -12
- package/dist/presenter/{presenter-log.js → chrome/presenter-log.js} +4 -9
- package/dist/presenter/chrome/presenter-safe-html.d.ts +2 -0
- package/dist/presenter/chrome/presenter-safe-html.js +16 -0
- package/dist/presenter/{presenter-tally.js → chrome/presenter-tally.js} +1 -1
- package/dist/presenter/{presenter-heroicons-data.d.ts → icons/presenter-heroicons-data.d.ts} +1 -0
- package/dist/presenter/{presenter-heroicons-data.js → icons/presenter-heroicons-data.js} +1 -0
- package/dist/presenter/{presenter-icons.d.ts → icons/presenter-icons.d.ts} +1 -0
- package/dist/presenter/{presenter-icons.js → icons/presenter-icons.js} +1 -0
- package/dist/presenter/presenter-account-styles.d.ts +1 -0
- package/dist/presenter/presenter-account-styles.js +99 -0
- package/dist/presenter/presenter-account.d.ts +40 -0
- package/dist/presenter/presenter-account.js +178 -0
- package/dist/presenter/presenter-config.d.ts +9 -2
- package/dist/presenter/presenter-config.js +3 -1
- package/dist/presenter/presenter-controls.d.ts +1 -6
- package/dist/presenter/presenter-controls.js +7 -2
- package/dist/presenter/presenter-dock-layout.js +1 -1
- package/dist/presenter/presenter-drag.d.ts +1 -19
- package/dist/presenter/presenter-drag.js +3 -50
- package/dist/presenter/presenter-glow.js +1 -1
- package/dist/presenter/presenter-hud-position.d.ts +18 -0
- package/dist/presenter/presenter-hud-position.js +50 -0
- package/dist/presenter/presenter-offer.d.ts +25 -0
- package/dist/presenter/presenter-offer.js +87 -0
- package/dist/presenter/presenter-report-styles.js +20 -1
- package/dist/presenter/presenter-report.d.ts +2 -1
- package/dist/presenter/presenter-report.js +31 -23
- package/dist/presenter/presenter-run-state.d.ts +1 -1
- package/dist/presenter/presenter-run-state.js +1 -1
- package/dist/presenter/presenter-settings-styles.js +2 -1
- package/dist/presenter/presenter-settings.d.ts +8 -0
- package/dist/presenter/presenter-settings.js +58 -3
- package/dist/presenter/presenter-shell-styles.js +33 -15
- package/dist/presenter/presenter-shell.d.ts +8 -0
- package/dist/presenter/presenter-shell.js +47 -4
- package/dist/presenter/presenter-styles.d.ts +2 -1
- package/dist/presenter/presenter-styles.js +8 -3
- package/dist/presenter/presenter-workspace.d.ts +4 -0
- package/dist/presenter/presenter-workspace.js +29 -2
- package/dist/presenter/presenter.d.ts +3 -3
- package/dist/presenter/presenter.js +28 -14
- package/dist/presenter/tour/tour-view.d.ts +33 -0
- package/dist/presenter/tour/tour-view.js +192 -0
- package/dist/presenter/tour/tour.d.ts +25 -0
- package/dist/presenter/tour/tour.js +264 -0
- package/dist/recorder/recorder.js +6 -0
- package/dist/registry/declared-channels.d.ts +2 -0
- package/dist/registry/declared-channels.js +14 -0
- package/dist/registry/declared-platform.d.ts +4 -0
- package/dist/registry/declared-platform.js +34 -0
- package/dist/registry/emitter.js +1 -1
- package/dist/registry/store-adapters.js +1 -1
- package/dist/registry/stores.js +1 -1
- package/dist/reticle-presenter-helpers.js +10 -6
- package/dist/reticle-singleton.d.ts +2 -0
- package/dist/reticle-singleton.js +3 -0
- package/dist/reticle.js +99 -35
- package/dist/review/annotator-styles.js +1 -1
- package/dist/review/annotator.js +2 -2
- package/dist/review/mark-anchor.js +3 -3
- package/dist/transport/transport.js +2 -2
- package/package.json +5 -4
- package/dist/presenter/presenter-test-helpers.d.ts +0 -14
- package/dist/presenter/presenter-test-helpers.js +0 -39
- /package/dist/actions/{synthetic-input.d.ts → synthetic/synthetic-input.d.ts} +0 -0
- /package/dist/actions/{synthetic-input.js → synthetic/synthetic-input.js} +0 -0
- /package/dist/dom/{auto-anchor.d.ts → addressing/auto-anchor.d.ts} +0 -0
- /package/dist/dom/{auto-anchor.js → addressing/auto-anchor.js} +0 -0
- /package/dist/dom/{refs.d.ts → addressing/refs.d.ts} +0 -0
- /package/dist/dom/{refs.js → addressing/refs.js} +0 -0
- /package/dist/dom/{source.d.ts → addressing/source.d.ts} +0 -0
- /package/dist/dom/{source.js → addressing/source.js} +0 -0
- /package/dist/observers/{network-body.d.ts → net-detail/network-body.d.ts} +0 -0
- /package/dist/observers/{network-redact.d.ts → net-detail/network-redact.d.ts} +0 -0
- /package/dist/observers/{network-redact.js → net-detail/network-redact.js} +0 -0
- /package/dist/presenter/{overlay.d.ts → chrome/overlay.d.ts} +0 -0
- /package/dist/presenter/{overlay.js → chrome/overlay.js} +0 -0
- /package/dist/presenter/{presenter-brand.d.ts → chrome/presenter-brand.d.ts} +0 -0
- /package/dist/presenter/{presenter-brand.js → chrome/presenter-brand.js} +0 -0
- /package/dist/presenter/{presenter-effects.d.ts → chrome/presenter-effects.d.ts} +0 -0
- /package/dist/presenter/{presenter-hud-chrome.d.ts → chrome/presenter-hud-chrome.d.ts} +0 -0
- /package/dist/presenter/{presenter-hud-chrome.js → chrome/presenter-hud-chrome.js} +0 -0
- /package/dist/presenter/{presenter-report-copy.d.ts → chrome/presenter-report-copy.d.ts} +0 -0
- /package/dist/presenter/{presenter-report-copy.js → chrome/presenter-report-copy.js} +0 -0
- /package/dist/presenter/{presenter-tally.d.ts → chrome/presenter-tally.d.ts} +0 -0
- /package/dist/presenter/{presenter-verbs.d.ts → chrome/presenter-verbs.d.ts} +0 -0
- /package/dist/presenter/{presenter-verbs.js → chrome/presenter-verbs.js} +0 -0
- /package/dist/registry/{adapters.d.ts → stores/adapters.d.ts} +0 -0
- /package/dist/registry/{adapters.js → stores/adapters.js} +0 -0
- /package/dist/timers/{native-console.d.ts → console/native-console.d.ts} +0 -0
- /package/dist/timers/{native-console.js → console/native-console.js} +0 -0
- /package/dist/timers/{native-timers.d.ts → native/native-timers.d.ts} +0 -0
- /package/dist/timers/{native-timers.js → native/native-timers.js} +0 -0
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import type { AccountState } from '@reticlehq/core';
|
|
2
|
+
import { type AccountDetails } from './presenter-account.js';
|
|
3
|
+
export declare const TOOLBAR_ACCOUNT_ATTR = "data-reticle-toolbar-account";
|
|
1
4
|
export declare function readWorkspaceRoot(): string | undefined;
|
|
2
5
|
export declare function workspaceFolderLabel(root: string): string;
|
|
3
6
|
export declare function readProjectIdFromUrl(): string | undefined;
|
|
@@ -9,3 +12,4 @@ export declare function workspaceSummary(): {
|
|
|
9
12
|
export declare function workspaceRowHtml(): string;
|
|
10
13
|
export declare function paintWorkspace(root: HTMLElement): void;
|
|
11
14
|
export declare function mountWorkspaceSelector(root: HTMLElement): () => void;
|
|
15
|
+
export declare function paintToolbarAccount(root: HTMLElement, account: AccountState | undefined, dashboardUrl: string | undefined, details?: AccountDetails): void;
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { RETICLE_ROOT_GLOBAL, RETICLE_URL_PARAM } from '@reticlehq/core';
|
|
2
|
-
import { PresenterIcon, PRESENTER_ICON_SIZE, hiIconHtml } from './presenter-icons.js';
|
|
2
|
+
import { PresenterIcon, PRESENTER_ICON_SIZE, hiIconHtml } from './icons/presenter-icons.js';
|
|
3
|
+
import { ACCOUNT_SIGNIN_ATTR, ACCOUNT_TEXT, accountControlHtml, } from './presenter-account.js';
|
|
3
4
|
const WORKSPACE_BTN_ATTR = 'data-reticle-workspace-btn';
|
|
4
5
|
const WORKSPACE_MENU_ATTR = 'data-reticle-workspace-menu';
|
|
5
6
|
const WORKSPACE_NAME_ATTR = 'data-reticle-workspace-name';
|
|
6
7
|
const WORKSPACE_PATH_ATTR = 'data-reticle-workspace-path';
|
|
7
8
|
const WORKSPACE_PROJECT_ATTR = 'data-reticle-workspace-project';
|
|
8
9
|
const WORKSPACE_COPY_ATTR = 'data-reticle-workspace-copy';
|
|
10
|
+
export const TOOLBAR_ACCOUNT_ATTR = 'data-reticle-toolbar-account';
|
|
11
|
+
const COPIED_MS = 1_200;
|
|
9
12
|
const WORKSPACE_LABEL = 'Workspace';
|
|
10
13
|
const PROJECT_LABEL = 'Project';
|
|
11
14
|
const COPY_PATH_LABEL = 'Copy path';
|
|
@@ -53,7 +56,9 @@ export function workspaceRowHtml() {
|
|
|
53
56
|
<div class="reticle-workspace-menu" ${WORKSPACE_MENU_ATTR} role="region" aria-label="${WORKSPACE_LABEL}" aria-hidden="true" hidden>
|
|
54
57
|
<div class="reticle-workspace-menu-head">
|
|
55
58
|
<div class="reticle-workspace-menu-title">${WORKSPACE_LABEL}</div>
|
|
56
|
-
<
|
|
59
|
+
<div class="reticle-workspace-menu-actions">
|
|
60
|
+
<button type="button" class="reticle-workspace-copy" ${WORKSPACE_COPY_ATTR} title="${COPY_PATH_LABEL}" aria-label="${COPY_PATH_LABEL}">${copyIcon}</button>
|
|
61
|
+
</div>
|
|
57
62
|
</div>
|
|
58
63
|
<div class="reticle-workspace-menu-row"><span class="reticle-workspace-menu-k">Folder</span><span class="reticle-workspace-menu-v" data-reticle-workspace-folder></span></div>
|
|
59
64
|
<div class="reticle-workspace-menu-row"><span class="reticle-workspace-menu-k">Path</span><span class="reticle-workspace-menu-v reticle-workspace-menu-path" ${WORKSPACE_PATH_ATTR}></span></div>
|
|
@@ -128,6 +133,22 @@ export function mountWorkspaceSelector(root) {
|
|
|
128
133
|
e.stopPropagation();
|
|
129
134
|
toggle();
|
|
130
135
|
};
|
|
136
|
+
const onMenuClick = (e) => {
|
|
137
|
+
const target = e.target;
|
|
138
|
+
if (!(target instanceof Element))
|
|
139
|
+
return;
|
|
140
|
+
const signin = target.closest(`[${ACCOUNT_SIGNIN_ATTR}]`);
|
|
141
|
+
if (null === signin)
|
|
142
|
+
return;
|
|
143
|
+
e.stopPropagation();
|
|
144
|
+
void navigator.clipboard?.writeText(ACCOUNT_TEXT.SIGNIN_COMMAND).catch(() => undefined);
|
|
145
|
+
const previous = signin.textContent;
|
|
146
|
+
signin.textContent = ACCOUNT_TEXT.COPIED;
|
|
147
|
+
setTimeout(() => {
|
|
148
|
+
signin.textContent = previous;
|
|
149
|
+
}, COPIED_MS);
|
|
150
|
+
};
|
|
151
|
+
menu.addEventListener('click', onMenuClick);
|
|
131
152
|
const onDocPointer = (e) => {
|
|
132
153
|
const target = e.target;
|
|
133
154
|
if (!(target instanceof Node))
|
|
@@ -171,3 +192,9 @@ export function mountWorkspaceSelector(root) {
|
|
|
171
192
|
document.removeEventListener('keydown', onKeyDown);
|
|
172
193
|
};
|
|
173
194
|
}
|
|
195
|
+
export function paintToolbarAccount(root, account, dashboardUrl, details = {}) {
|
|
196
|
+
const slot = root.querySelector(`[${TOOLBAR_ACCOUNT_ATTR}]`);
|
|
197
|
+
if (!(slot instanceof HTMLElement))
|
|
198
|
+
return;
|
|
199
|
+
slot.innerHTML = accountControlHtml(account, { ...details, dashboardUrl }, true);
|
|
200
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { PresenterMode, SessionState, type PresenterTone } from '@reticlehq/core';
|
|
2
|
-
import { type LogKind, type LogResult, type LogHandle } from './presenter-log.js';
|
|
2
|
+
import { type LogKind, type LogResult, type LogHandle } from './chrome/presenter-log.js';
|
|
3
3
|
import { type PresenterOptions } from './presenter-config.js';
|
|
4
4
|
import { type PresenterRunState } from './presenter-run-state.js';
|
|
5
5
|
import { type ControlHandler } from './presenter-controls.js';
|
|
6
6
|
import { Annotator } from '../review/annotator.js';
|
|
7
7
|
export { GlowPhase } from './presenter-config.js';
|
|
8
8
|
export type { PresenterOptions } from './presenter-config.js';
|
|
9
|
-
export { LOG_KIND, LOG_RESULT } from './presenter-log.js';
|
|
10
|
-
export type { LogHandle } from './presenter-log.js';
|
|
9
|
+
export { LOG_KIND, LOG_RESULT } from './chrome/presenter-log.js';
|
|
10
|
+
export type { LogHandle } from './chrome/presenter-log.js';
|
|
11
11
|
export type { ControlIntent, ControlHandler } from './presenter-controls.js';
|
|
12
12
|
export declare class Presenter {
|
|
13
13
|
#private;
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import { ActionType, ReticleCommand, PresenterMode, SessionState, isPresenterTone, isSessionState, } from '@reticlehq/core';
|
|
2
|
-
import { refs } from '../dom/refs.js';
|
|
1
|
+
import { ActionType, HumanControlKind, ReticleCommand, PresenterMode, SessionState, isPresenterTone, isSessionState, } from '@reticlehq/core';
|
|
2
|
+
import { refs } from '../dom/addressing/refs.js';
|
|
3
3
|
import { unreachableStripText } from '../transport/unreachable-message.js';
|
|
4
|
-
import { actionVerb } from './presenter-verbs.js';
|
|
5
|
-
import { nativeSetTimeout, nativeClearTimeout, nativeNow } from '../timers/native-timers.js';
|
|
6
|
-
import { LOG_KIND, CHIP_LABEL, DATA_RETICLE_LOG, clampLogMax, formatElapsed, humanDuration, appendLogRow, } from './presenter-log.js';
|
|
4
|
+
import { actionVerb } from './chrome/presenter-verbs.js';
|
|
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';
|
|
7
7
|
import { PRESENTER_CSS } from './presenter-styles.js';
|
|
8
8
|
import { HudShell } from './presenter-shell.js';
|
|
9
|
-
import { parseImpactSnapshot } from './presenter-report-copy.js';
|
|
9
|
+
import { parseImpactSnapshot } from './chrome/presenter-report-copy.js';
|
|
10
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';
|
|
11
11
|
import { buildRunState } from './presenter-run-state.js';
|
|
12
|
-
import { moveCursor, ringAround, spawnRipple, pace } from './presenter-effects.js';
|
|
12
|
+
import { moveCursor, ringAround, spawnRipple, pace } from './chrome/presenter-effects.js';
|
|
13
13
|
import { GlowController } from './presenter-glow.js';
|
|
14
|
-
import { PresenterIcon, PRESENTER_ICON_SIZE, hiIcon } from './presenter-icons.js';
|
|
15
|
-
import { renderTally } from './presenter-tally.js';
|
|
14
|
+
import { PresenterIcon, PRESENTER_ICON_SIZE, hiIcon } from './icons/presenter-icons.js';
|
|
15
|
+
import { renderTally } from './chrome/presenter-tally.js';
|
|
16
16
|
import { CONTROLS_BANNER_HTML, CONTROLS_FLOWS_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
19
|
export { GlowPhase } from './presenter-config.js';
|
|
20
|
-
export { LOG_KIND, LOG_RESULT } from './presenter-log.js';
|
|
20
|
+
export { LOG_KIND, LOG_RESULT } from './chrome/presenter-log.js';
|
|
21
21
|
export class Presenter {
|
|
22
22
|
#paceMs;
|
|
23
23
|
#root;
|
|
@@ -85,6 +85,7 @@ export class Presenter {
|
|
|
85
85
|
onExpand: () => this.#syncAnnotator(),
|
|
86
86
|
onAnnotateToggle: () => this.#syncAnnotator(),
|
|
87
87
|
onCollapse: () => this.#syncAnnotator(),
|
|
88
|
+
onSyncNow: () => this.#onControl?.({ kind: HumanControlKind.SYNC }),
|
|
88
89
|
settings: {
|
|
89
90
|
onBeforeOpen: () => {
|
|
90
91
|
if (this.#shell.isCollapsed())
|
|
@@ -92,6 +93,7 @@ export class Presenter {
|
|
|
92
93
|
},
|
|
93
94
|
onHideUntilRestart: () => this.#applyHideUntilRestart(),
|
|
94
95
|
onSettingsChange: (s) => this.#onSettingsChange(s),
|
|
96
|
+
onHarness: (enabled) => this.#onControl?.({ kind: HumanControlKind.HARNESS, text: enabled ? 'on' : 'off' }),
|
|
95
97
|
},
|
|
96
98
|
});
|
|
97
99
|
}
|
|
@@ -116,8 +118,17 @@ export class Presenter {
|
|
|
116
118
|
return void this.#panel.setFlows(a['flows']);
|
|
117
119
|
if (command.name === ReticleCommand.IMPACT) {
|
|
118
120
|
const snapshot = parseImpactSnapshot(a['snapshot']);
|
|
119
|
-
if (snapshot !== undefined)
|
|
121
|
+
if (snapshot !== undefined) {
|
|
120
122
|
this.#shell.report.setSnapshot(snapshot);
|
|
123
|
+
this.#shell.paintOffer(snapshot.harnessOffer);
|
|
124
|
+
this.#shell.paintHarness(snapshot.harnessConfig);
|
|
125
|
+
this.#shell.paintAccount(snapshot.account, snapshot.dashboardUrl, {
|
|
126
|
+
projectName: snapshot.projectName,
|
|
127
|
+
dashboardUrl: snapshot.dashboardUrl,
|
|
128
|
+
verdicts: snapshot.project.counts.verdicts,
|
|
129
|
+
defects: snapshot.project.counts.failed,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
121
132
|
return;
|
|
122
133
|
}
|
|
123
134
|
const state = a['state'];
|
|
@@ -352,14 +363,17 @@ export class Presenter {
|
|
|
352
363
|
}
|
|
353
364
|
if (idleMs < this.#idleNoticeMs)
|
|
354
365
|
return;
|
|
355
|
-
|
|
356
|
-
|
|
366
|
+
if ('' === this.#lastActionText)
|
|
367
|
+
return;
|
|
368
|
+
const since = ACT_STRIP.SINCE_LAST;
|
|
369
|
+
this.#paintActStrip(`${ACT_STRIP.PLANNING_PREFIX}${humanDuration(idleMs)}${since}`, true);
|
|
357
370
|
if (this.#chatPillTime !== undefined)
|
|
358
371
|
this.#chatPillTime.textContent = humanDuration(idleMs);
|
|
359
372
|
}
|
|
360
373
|
#endIdle(idleMs) {
|
|
361
374
|
this.#endMs = this.#now();
|
|
362
|
-
this.#panel.setState(SessionState.ENDED,
|
|
375
|
+
this.#panel.setState(SessionState.ENDED, `${ACT_STRIP.STOPPED_PREFIX}${humanDuration(idleMs)} quiet`);
|
|
376
|
+
this.#paintActStrip(ACT_STRIP.STOPPED, false);
|
|
363
377
|
if (this.#heartbeatTimer !== undefined) {
|
|
364
378
|
nativeClearTimeout(this.#heartbeatTimer);
|
|
365
379
|
this.#heartbeatTimer = undefined;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { TourAnchor, type TourPrompt } from '@reticlehq/core/tour';
|
|
2
|
+
export declare const HANDOFF_INDEX: number;
|
|
3
|
+
export declare const SLIDE_COUNT: number;
|
|
4
|
+
export declare const TOUR_ATTR = "data-reticle-tour";
|
|
5
|
+
export declare const TOUR_TARGET_ATTR = "data-reticle-tour-target";
|
|
6
|
+
export declare const TOUR_PROMPT_ATTR = "data-reticle-tour-prompt";
|
|
7
|
+
export declare const COPY_LABEL = "Copy";
|
|
8
|
+
export declare const COPIED_LABEL = "Copied";
|
|
9
|
+
export declare const COPY_MANUAL_LABEL = "Selected \u2014 press Ctrl/Cmd+C";
|
|
10
|
+
export declare const COPY_FLASH_MS = 1600;
|
|
11
|
+
export interface TourSlide {
|
|
12
|
+
readonly index: number;
|
|
13
|
+
readonly title: string;
|
|
14
|
+
readonly body: string;
|
|
15
|
+
readonly anchor: TourAnchor;
|
|
16
|
+
readonly tryIt?: string;
|
|
17
|
+
readonly prompts?: readonly TourPrompt[];
|
|
18
|
+
}
|
|
19
|
+
export declare const isInteractive: (slide: TourSlide) => boolean;
|
|
20
|
+
export declare function tourSlides(): readonly TourSlide[];
|
|
21
|
+
export declare function escapeHtml(text: string): string;
|
|
22
|
+
export declare function withInlineCode(text: string): string;
|
|
23
|
+
export declare function nextIndex(current: number, delta: number): number;
|
|
24
|
+
export declare const isLastSlide: (index: number) => boolean;
|
|
25
|
+
export interface TourRect {
|
|
26
|
+
readonly left: number;
|
|
27
|
+
readonly top: number;
|
|
28
|
+
readonly width: number;
|
|
29
|
+
readonly height: number;
|
|
30
|
+
}
|
|
31
|
+
export declare function holeRects(box: TourRect, viewWidth: number, viewHeight: number): readonly TourRect[];
|
|
32
|
+
export declare function slideHtml(slide: TourSlide): string;
|
|
33
|
+
export declare const TOUR_CSS: string;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { TOUR_STEPS, TOUR_HUD_STEPS, TOUR_HANDOFF_PROMPTS, TourAnchor, } from '@reticlehq/core/tour';
|
|
2
|
+
import { Z_TOUR } from '../chrome/layers.js';
|
|
3
|
+
const ORDERED_STEPS = [...TOUR_STEPS.slice(0, 1), ...TOUR_HUD_STEPS];
|
|
4
|
+
export const HANDOFF_INDEX = ORDERED_STEPS.length;
|
|
5
|
+
export const SLIDE_COUNT = ORDERED_STEPS.length + 1;
|
|
6
|
+
export const TOUR_ATTR = 'data-reticle-tour';
|
|
7
|
+
export const TOUR_TARGET_ATTR = 'data-reticle-tour-target';
|
|
8
|
+
export const TOUR_PROMPT_ATTR = 'data-reticle-tour-prompt';
|
|
9
|
+
export const COPY_LABEL = 'Copy';
|
|
10
|
+
export const COPIED_LABEL = 'Copied';
|
|
11
|
+
export const COPY_MANUAL_LABEL = 'Selected — press Ctrl/Cmd+C';
|
|
12
|
+
export const COPY_FLASH_MS = 1600;
|
|
13
|
+
const stepSlide = (step, index) => ({
|
|
14
|
+
index,
|
|
15
|
+
title: step.title,
|
|
16
|
+
body: step.say,
|
|
17
|
+
anchor: step.anchor ?? TourAnchor.NONE,
|
|
18
|
+
...(undefined === step.tryIt ? {} : { tryIt: step.tryIt }),
|
|
19
|
+
});
|
|
20
|
+
export const isInteractive = (slide) => undefined !== slide.tryIt;
|
|
21
|
+
export function tourSlides() {
|
|
22
|
+
return [
|
|
23
|
+
...ORDERED_STEPS.map(stepSlide),
|
|
24
|
+
{
|
|
25
|
+
index: HANDOFF_INDEX,
|
|
26
|
+
title: 'Get Started',
|
|
27
|
+
body: 'Paste one of these into Claude Code, Cursor, Codex, or whatever you use.',
|
|
28
|
+
anchor: TourAnchor.NONE,
|
|
29
|
+
prompts: TOUR_HANDOFF_PROMPTS,
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
}
|
|
33
|
+
export function escapeHtml(text) {
|
|
34
|
+
return text
|
|
35
|
+
.split('&')
|
|
36
|
+
.join('&')
|
|
37
|
+
.split('<')
|
|
38
|
+
.join('<')
|
|
39
|
+
.split('>')
|
|
40
|
+
.join('>')
|
|
41
|
+
.split('"')
|
|
42
|
+
.join('"');
|
|
43
|
+
}
|
|
44
|
+
export function withInlineCode(text) {
|
|
45
|
+
return escapeHtml(text).replace(/`([^`]+)`/g, '<code class="reticle-tour-code">$1</code>');
|
|
46
|
+
}
|
|
47
|
+
export function nextIndex(current, delta) {
|
|
48
|
+
const wanted = current + delta;
|
|
49
|
+
if (wanted < 0)
|
|
50
|
+
return 0;
|
|
51
|
+
if (wanted > SLIDE_COUNT - 1)
|
|
52
|
+
return SLIDE_COUNT - 1;
|
|
53
|
+
return wanted;
|
|
54
|
+
}
|
|
55
|
+
export const isLastSlide = (index) => index === SLIDE_COUNT - 1;
|
|
56
|
+
export function holeRects(box, viewWidth, viewHeight) {
|
|
57
|
+
const left = Math.max(0, box.left);
|
|
58
|
+
const top = Math.max(0, box.top);
|
|
59
|
+
const right = Math.min(viewWidth, box.left + box.width);
|
|
60
|
+
const bottom = Math.min(viewHeight, box.top + box.height);
|
|
61
|
+
const candidates = [
|
|
62
|
+
{ left: 0, top: 0, width: viewWidth, height: top },
|
|
63
|
+
{ left: 0, top: bottom, width: viewWidth, height: viewHeight - bottom },
|
|
64
|
+
{ left: 0, top, width: left, height: bottom - top },
|
|
65
|
+
{ left: right, top, width: viewWidth - right, height: bottom - top },
|
|
66
|
+
];
|
|
67
|
+
return candidates.filter((r) => r.width > 0 && r.height > 0);
|
|
68
|
+
}
|
|
69
|
+
export function slideHtml(slide) {
|
|
70
|
+
const dots = tourSlides()
|
|
71
|
+
.map((s) => `<span class="reticle-tour-dot${s.index === slide.index ? ' is-on' : ''}" aria-hidden="true"></span>`)
|
|
72
|
+
.join('');
|
|
73
|
+
const tryIt = undefined === slide.tryIt
|
|
74
|
+
? ''
|
|
75
|
+
: `<p class="reticle-tour-try" ${TOUR_TARGET_ATTR}="try">${escapeHtml(slide.tryIt)}</p>`;
|
|
76
|
+
const prompt = undefined === slide.prompts
|
|
77
|
+
? ''
|
|
78
|
+
: `<div class="reticle-tour-prompts">` +
|
|
79
|
+
slide.prompts
|
|
80
|
+
.map((p, i) => `<div class="reticle-tour-prompt" ${TOUR_PROMPT_ATTR}="${String(i)}">` +
|
|
81
|
+
`<span class="reticle-tour-prompt-label">${escapeHtml(p.label)}</span>` +
|
|
82
|
+
`<pre class="reticle-tour-prompt-text">${escapeHtml(p.text)}</pre>` +
|
|
83
|
+
`<button type="button" class="reticle-tour-copy" ${TOUR_TARGET_ATTR}="copy" ` +
|
|
84
|
+
`${TOUR_PROMPT_ATTR}="${String(i)}">${COPY_LABEL}</button></div>`)
|
|
85
|
+
.join('') +
|
|
86
|
+
`</div>`;
|
|
87
|
+
const back = 0 === slide.index
|
|
88
|
+
? ''
|
|
89
|
+
: `<button type="button" class="reticle-tour-back" ${TOUR_TARGET_ATTR}="back">Back</button>`;
|
|
90
|
+
const forward = isLastSlide(slide.index)
|
|
91
|
+
? `<button type="button" class="reticle-tour-next" ${TOUR_TARGET_ATTR}="done">Done</button>`
|
|
92
|
+
: `<button type="button" class="reticle-tour-next" ${TOUR_TARGET_ATTR}="next">Next</button>`;
|
|
93
|
+
return (`<div class="reticle-tour-card" role="dialog" aria-modal="false" aria-label="Reticle tour">` +
|
|
94
|
+
`<button type="button" class="reticle-tour-skip" ${TOUR_TARGET_ATTR}="skip" aria-label="Skip the tour">Skip</button>` +
|
|
95
|
+
`<p class="reticle-tour-step">Step ${String(slide.index + 1)} of ${String(SLIDE_COUNT)}</p>` +
|
|
96
|
+
`<h2 class="reticle-tour-title">${escapeHtml(slide.title)}</h2>` +
|
|
97
|
+
`<p class="reticle-tour-body">${withInlineCode(slide.body)}</p>` +
|
|
98
|
+
tryIt +
|
|
99
|
+
prompt +
|
|
100
|
+
`<div class="reticle-tour-foot"><div class="reticle-tour-dots">${dots}</div>` +
|
|
101
|
+
`<div class="reticle-tour-nav">${back}${forward}</div></div>` +
|
|
102
|
+
`</div>`);
|
|
103
|
+
}
|
|
104
|
+
export const TOUR_CSS = `
|
|
105
|
+
[${TOUR_ATTR}]{position:fixed;inset:0;z-index:${String(Z_TOUR)};pointer-events:none;
|
|
106
|
+
font:13px/1.55 ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,sans-serif;}
|
|
107
|
+
[${TOUR_ATTR}] .reticle-tour-scrim{position:absolute;inset:0;background:rgba(6,8,14,.55);
|
|
108
|
+
backdrop-filter:blur(1.5px);pointer-events:auto;z-index:0;}
|
|
109
|
+
/* Blocks the click everywhere the hole is not. Same job as the scrim, minus the one rectangle the
|
|
110
|
+
slide is inviting somebody to press. */
|
|
111
|
+
[${TOUR_ATTR}] .reticle-tour-blocker{position:absolute;pointer-events:auto;z-index:0;}
|
|
112
|
+
/* Ringed slides dim through the ring's own 9999px shadow, which leaves a hole over the thing being
|
|
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
|
+
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;}
|
|
121
|
+
/* Above the ring, and the z-index is the whole point.
|
|
122
|
+
The ring is appended after the card, and both are positioned with an auto z-index, so the ring
|
|
123
|
+
painted on top of it -- and the ring's dimming IS a 9999px shadow, which therefore fell across the
|
|
124
|
+
card as well. On every ringed slide the card was washed at 55% and read as sitting BEHIND the page
|
|
125
|
+
it was drawn over. The ring must stay above the scrim and below the card; saying so explicitly is
|
|
126
|
+
the fix, because DOM order alone cannot express "between these two". */
|
|
127
|
+
[${TOUR_ATTR}] .reticle-tour-card{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
|
|
128
|
+
width:min(430px,calc(100vw - 32px));box-sizing:border-box;pointer-events:auto;z-index:3;
|
|
129
|
+
background:#14161d;color:#e8eaf0;border:1px solid #2b303d;border-radius:14px;padding:20px 20px 16px;
|
|
130
|
+
box-shadow:0 24px 60px rgba(0,0,0,.5);}
|
|
131
|
+
[${TOUR_ATTR}] .reticle-tour-skip{position:absolute;top:10px;right:12px;background:none;border:0;
|
|
132
|
+
color:#8a93a6;font:inherit;font-size:12px;cursor:pointer;padding:4px 6px;border-radius:6px;}
|
|
133
|
+
[${TOUR_ATTR}] .reticle-tour-skip:hover{color:#e8eaf0;background:rgba(255,255,255,.06);}
|
|
134
|
+
[${TOUR_ATTR}] .reticle-tour-step{margin:0 0 6px;font-size:11px;letter-spacing:.06em;
|
|
135
|
+
text-transform:uppercase;color:#7f8798;}
|
|
136
|
+
[${TOUR_ATTR}] .reticle-tour-title{margin:0 0 8px;font-size:18px;line-height:1.25;font-weight:650;
|
|
137
|
+
color:#f2f4f8;}
|
|
138
|
+
[${TOUR_ATTR}] .reticle-tour-body{margin:0 0 10px;color:#c9cedb;}
|
|
139
|
+
/* A call named inside a sentence. Sets its own colour for the reason every other text class does:
|
|
140
|
+
an inherited one loses to any direct element selector in the host app. */
|
|
141
|
+
[${TOUR_ATTR}] .reticle-tour-code{padding:1px 5px;border-radius:5px;background:#0c0e14;
|
|
142
|
+
border:1px solid #232834;color:#9fb6ff;
|
|
143
|
+
font:11.5px/1.45 ui-monospace,SFMono-Regular,Menlo,monospace;overflow-wrap:anywhere;}
|
|
144
|
+
[${TOUR_ATTR}] .reticle-tour-try{margin:0 0 12px;padding:8px 10px;border-radius:8px;
|
|
145
|
+
background:rgba(99,102,241,.1);border:1px solid rgba(99,102,241,.32);color:#c7cbff;
|
|
146
|
+
font-size:12.5px;font-weight:550;}
|
|
147
|
+
[${TOUR_ATTR}] .reticle-tour-prompts{display:flex;flex-direction:column;gap:8px;margin:0 0 12px;}
|
|
148
|
+
[${TOUR_ATTR}] .reticle-tour-prompt{display:grid;gap:6px;padding:9px 10px;border-radius:8px;
|
|
149
|
+
background:#0c0e14;border:1px solid #232834;
|
|
150
|
+
grid-template-columns:1fr auto;grid-template-areas:"label copy" "text text";align-items:center;}
|
|
151
|
+
[${TOUR_ATTR}] .reticle-tour-prompt-label{grid-area:label;color:#8a93a6;font-size:11px;
|
|
152
|
+
letter-spacing:.05em;text-transform:uppercase;}
|
|
153
|
+
[${TOUR_ATTR}] .reticle-tour-prompt-text{grid-area:text;width:100%;box-sizing:border-box;margin:0;
|
|
154
|
+
white-space:pre-wrap;overflow-wrap:anywhere;color:#c9cedb;
|
|
155
|
+
font:12px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace;}
|
|
156
|
+
[${TOUR_ATTR}] .reticle-tour-copy{grid-area:copy;padding:5px 10px;border-radius:6px;
|
|
157
|
+
border:1px solid #2b303d;background:transparent;color:#c9cedb;font:inherit;font-size:11.5px;
|
|
158
|
+
font-weight:600;cursor:pointer;white-space:nowrap;}
|
|
159
|
+
[${TOUR_ATTR}] .reticle-tour-copy:hover{background:rgba(255,255,255,.07);color:#fff;}
|
|
160
|
+
/* The flash that tells somebody the click did something. Colour AND text: colour alone is not an
|
|
161
|
+
answer for the people who cannot see the difference. */
|
|
162
|
+
[${TOUR_ATTR}] .reticle-tour-copy.is-done{background:#16a34a;border-color:#16a34a;color:#fff;}
|
|
163
|
+
[${TOUR_ATTR}] .reticle-tour-copy.is-manual{background:#b45309;border-color:#b45309;color:#fff;}
|
|
164
|
+
[${TOUR_ATTR}] .reticle-tour-foot{display:flex;align-items:center;justify-content:space-between;gap:12px;}
|
|
165
|
+
[${TOUR_ATTR}] .reticle-tour-dots{display:flex;gap:6px;}
|
|
166
|
+
[${TOUR_ATTR}] .reticle-tour-dot{width:6px;height:6px;border-radius:50%;background:#333a49;}
|
|
167
|
+
[${TOUR_ATTR}] .reticle-tour-dot.is-on{background:#6366f1;}
|
|
168
|
+
[${TOUR_ATTR}] .reticle-tour-nav{display:flex;gap:8px;}
|
|
169
|
+
[${TOUR_ATTR}] .reticle-tour-back,[${TOUR_ATTR}] .reticle-tour-next{padding:8px 14px;border-radius:8px;
|
|
170
|
+
font:inherit;font-weight:600;cursor:pointer;border:1px solid #2b303d;background:transparent;color:#c9cedb;}
|
|
171
|
+
[${TOUR_ATTR}] .reticle-tour-next{background:#6366f1;border-color:#6366f1;color:#fff;}
|
|
172
|
+
[${TOUR_ATTR}] .reticle-tour-next:hover{filter:brightness(1.08);}
|
|
173
|
+
[${TOUR_ATTR}] .reticle-tour-back:hover{background:rgba(255,255,255,.06);color:#e8eaf0;}
|
|
174
|
+
[${TOUR_ATTR}] .reticle-tour-ring{position:absolute;border-radius:14px;pointer-events:none;z-index:1;
|
|
175
|
+
box-shadow:0 0 0 3px #6366f1,0 0 0 9999px rgba(6,8,14,.55);transition:all .25s ease;}
|
|
176
|
+
/* A ring somebody is being asked to press, rather than to look at: it pulses, so the invitation in
|
|
177
|
+
the prose has something on the page agreeing with it. */
|
|
178
|
+
[${TOUR_ATTR}] .reticle-tour-ring.is-live{animation:reticle-tour-pulse 1.8s ease-in-out infinite;}
|
|
179
|
+
@keyframes reticle-tour-pulse{
|
|
180
|
+
0%,100%{box-shadow:0 0 0 3px #6366f1,0 0 0 9999px rgba(6,8,14,.55);}
|
|
181
|
+
50%{box-shadow:0 0 0 6px rgba(99,102,241,.55),0 0 0 9999px rgba(6,8,14,.55);}
|
|
182
|
+
}
|
|
183
|
+
/* A REGION, not a target. The spotlight above cuts a hole in the dimming, which is right for
|
|
184
|
+
something small and wrong for the app: the hole becomes the whole viewport, the dimming
|
|
185
|
+
disappears, and the card is left competing with a fully lit page while the ring edges sit off at
|
|
186
|
+
the margins pointing at nothing. An outline says "all of this" without turning the lights on. */
|
|
187
|
+
[${TOUR_ATTR}] .reticle-tour-ring.is-region{box-shadow:none;border:2px dashed #7c83f5;
|
|
188
|
+
background:rgba(99,102,241,.06);}
|
|
189
|
+
@media (prefers-reduced-motion:reduce){
|
|
190
|
+
[${TOUR_ATTR}] .reticle-tour-ring{transition:none;}
|
|
191
|
+
}
|
|
192
|
+
`;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { isLastSlide } from './tour-view.js';
|
|
2
|
+
export declare const TOUR_SEEN_KEY_PREFIX = "reticle.tour.seen.";
|
|
3
|
+
export declare const tourSeenKey: (projectId: string) => string;
|
|
4
|
+
export interface TourStorage {
|
|
5
|
+
getItem: (key: string) => string | null;
|
|
6
|
+
setItem: (key: string, value: string) => void;
|
|
7
|
+
}
|
|
8
|
+
export interface TourDeps {
|
|
9
|
+
readonly document: Document;
|
|
10
|
+
readonly storage: TourStorage | undefined;
|
|
11
|
+
readonly projectId: string;
|
|
12
|
+
readonly isDriving: () => boolean;
|
|
13
|
+
readonly search?: string;
|
|
14
|
+
readonly copy?: (text: string) => Promise<boolean> | boolean;
|
|
15
|
+
readonly select?: (element: Element) => void;
|
|
16
|
+
}
|
|
17
|
+
export interface TourHandle {
|
|
18
|
+
readonly destroy: () => void;
|
|
19
|
+
readonly isOpen: () => boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare function openedByReticle(search: string | undefined): boolean;
|
|
22
|
+
export declare function tourAlreadySeen(storage: TourStorage | undefined, projectId: string): boolean;
|
|
23
|
+
export declare function mountTour(deps: TourDeps): TourHandle | undefined;
|
|
24
|
+
export { isLastSlide };
|
|
25
|
+
export declare function safeLocalStorage(): TourStorage | undefined;
|