@steppy/player 0.0.0-stage → 0.4.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/LICENSE +202 -0
- package/NOTICE +9 -0
- package/README.md +190 -2
- package/THIRD_PARTY_LICENSES.txt +158 -0
- package/dist/steppy-cursor-legacy.js +2 -0
- package/dist/steppy-cursor-legacy.js.map +1 -0
- package/dist/steppy-player.js +5419 -0
- package/dist/steppy-player.js.map +1 -0
- package/dist/steppy-tour.js +2164 -0
- package/dist/steppy-tour.js.map +1 -0
- package/dist/steppy-video.js +472 -0
- package/dist/steppy-video.js.map +1 -0
- package/dist/types/cursor/animator.d.ts +135 -0
- package/dist/types/cursor/types.d.ts +61 -0
- package/dist/types/i18n/ui-strings.d.ts +48 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/interaction/positioning.d.ts +22 -0
- package/dist/types/manifest/content.d.ts +24 -0
- package/dist/types/manifest/index.d.ts +3 -0
- package/dist/types/manifest/parse.d.ts +23 -0
- package/dist/types/manifest/schema.d.ts +2895 -0
- package/dist/types/steppy-guide.d.ts +195 -0
- package/dist/types/templates/base-styles.d.ts +15 -0
- package/dist/types/templates/controls.d.ts +17 -0
- package/dist/types/templates/index.d.ts +23 -0
- package/dist/types/templates/inline.d.ts +10 -0
- package/dist/types/templates/overlay.d.ts +10 -0
- package/dist/types/templates/sidebar.d.ts +19 -0
- package/dist/types/templates/types.d.ts +76 -0
- package/dist/types/tour/api.d.ts +54 -0
- package/dist/types/tour/contract.d.ts +54 -0
- package/dist/types/tour/dom.d.ts +43 -0
- package/dist/types/tour/engine.d.ts +157 -0
- package/dist/types/tour/events.d.ts +42 -0
- package/dist/types/tour/host.d.ts +153 -0
- package/dist/types/tour/index.d.ts +15 -0
- package/dist/types/tour/layer.d.ts +16 -0
- package/dist/types/tour/loader.d.ts +70 -0
- package/dist/types/tour/resolver.d.ts +30 -0
- package/dist/types/tour/routes.d.ts +40 -0
- package/dist/types/tour/signature.d.ts +16 -0
- package/dist/types/tour/storage.d.ts +28 -0
- package/dist/types/tour/strings.d.ts +42 -0
- package/dist/types/tour/styles.d.ts +13 -0
- package/dist/types/tour/templates.d.ts +47 -0
- package/dist/types/tour/tracker.d.ts +45 -0
- package/dist/types/tour/triggers.d.ts +52 -0
- package/dist/types/tour/validate.d.ts +82 -0
- package/dist/types/ui/markdown.d.ts +58 -0
- package/dist/types/ui/names.d.ts +18 -0
- package/dist/types/ui/placement.d.ts +59 -0
- package/dist/types/ui/strings.d.ts +31 -0
- package/dist/types/ui/tokens.d.ts +57 -0
- package/package.json +70 -4
- package/schema/README.md +19 -0
- package/schema/guide.schema.json +427 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ``<steppy-guide>`` — Lit-based interactive guide player.
|
|
3
|
+
*
|
|
4
|
+
* Public API is HTML attributes + CustomEvents. See the Stage 1 design doc for
|
|
5
|
+
* the full contract. Implementation notes:
|
|
6
|
+
*
|
|
7
|
+
* - Manifest is fetched lazily when ``src`` is set; cached in ``manifest``
|
|
8
|
+
* state. ``parseManifest`` does both JSON parsing and Zod validation.
|
|
9
|
+
* - Coordinates flow capture-space → display-space at one point only:
|
|
10
|
+
* ``computeHotspotRect`` (which the cursor animator also uses via
|
|
11
|
+
* ``getDisplayScale``). Everything downstream — hotspot button, tooltip,
|
|
12
|
+
* cursor target — derives from the same scale, so resizes can never
|
|
13
|
+
* produce drift between cursor and hotspot.
|
|
14
|
+
* - The cursor / ripple / kbd elements live at a fixed position in the
|
|
15
|
+
* template so Lit's incremental render doesn't churn them; the animator
|
|
16
|
+
* refreshes its DOM refs once on connect.
|
|
17
|
+
* - Step progression is centralized in ``_advance`` and ``_playCurrentPosition``;
|
|
18
|
+
* the rest of the component is rendering.
|
|
19
|
+
*/
|
|
20
|
+
import { LitElement, type PropertyValues } from 'lit';
|
|
21
|
+
import { type AnimatorOptions } from './cursor/animator.js';
|
|
22
|
+
import type { AnimatorFrame } from './cursor/types.js';
|
|
23
|
+
import { DEFAULT_UI_STRINGS, type UiStrings } from './i18n/ui-strings.js';
|
|
24
|
+
import type { CursorMode, TemplateMode } from './manifest/schema.js';
|
|
25
|
+
/** Animator-shaped subset the player actually calls. Tests can plug in a
|
|
26
|
+
* stub that skips real timing without owning the full CursorAnimator API. */
|
|
27
|
+
export interface AnimatorLike {
|
|
28
|
+
setTimings(t: Partial<import('./cursor/types.js').CursorTimings>): void;
|
|
29
|
+
setStage(stage: HTMLElement): void;
|
|
30
|
+
refreshDom(): void;
|
|
31
|
+
hide(): void;
|
|
32
|
+
abort(): void;
|
|
33
|
+
travelToTarget(frame: AnimatorFrame, targetIndex: number): Promise<void>;
|
|
34
|
+
playTargetAction(frame: AnimatorFrame, targetIndex: number): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
export declare class SteppyGuide extends LitElement {
|
|
37
|
+
/** Factory hook — overridden in tests to swap in a fast/no-op animator. */
|
|
38
|
+
static createAnimator: (opts: AnimatorOptions) => AnimatorLike;
|
|
39
|
+
static styles: import("lit").CSSResult[];
|
|
40
|
+
src: string;
|
|
41
|
+
locale: string | null;
|
|
42
|
+
variant: string | null;
|
|
43
|
+
autoplay: boolean;
|
|
44
|
+
startIndex: number;
|
|
45
|
+
/**
|
|
46
|
+
* Cursor presentation:
|
|
47
|
+
* - 'arrow' (default) animate the arrow + ripple/kbd; hotspot also pulses
|
|
48
|
+
* - 'hotspot' skip arrow/ripple/kbd; hotspot pulses and user still clicks
|
|
49
|
+
* - 'off' skip arrow + hotspot; auto-advance every step after waitAfter
|
|
50
|
+
* Unset (null) ⇒ fall back to manifest's `cursor.mode`, then 'arrow'.
|
|
51
|
+
*/
|
|
52
|
+
cursor: CursorMode | null;
|
|
53
|
+
/**
|
|
54
|
+
* Presentation template (runtime-switchable viewer setting):
|
|
55
|
+
* - 'overlay' (default) floating callout pinned to the target
|
|
56
|
+
* - 'sidebar' left rail with a step timeline + description
|
|
57
|
+
* - 'inline' slim bottom caption bar
|
|
58
|
+
* Resolution: this attribute → persisted localStorage → manifest
|
|
59
|
+
* ``defaultTemplate`` → 'overlay'. Unknown strings fall through.
|
|
60
|
+
*/
|
|
61
|
+
template: TemplateMode | null;
|
|
62
|
+
/** When set, suppress the built-in layout (variant) switcher so a host can
|
|
63
|
+
* own the control (the generated Angular toolbar / static page nav drives
|
|
64
|
+
* `variant` directly). The locale switcher is unaffected. */
|
|
65
|
+
hideLayoutSwitcher: boolean;
|
|
66
|
+
/** Sparse per-locale UI string overrides. Property-only (no attribute). */
|
|
67
|
+
uiStrings: Partial<Record<string, Partial<UiStrings>>> | undefined;
|
|
68
|
+
private manifest;
|
|
69
|
+
private loadError;
|
|
70
|
+
private currentStepIndex;
|
|
71
|
+
private currentFrameIndex;
|
|
72
|
+
private currentTargetIndex;
|
|
73
|
+
private waitingForInput;
|
|
74
|
+
private completed;
|
|
75
|
+
private missCount;
|
|
76
|
+
private hotspotRect;
|
|
77
|
+
private tooltipPlacement;
|
|
78
|
+
private stageSize;
|
|
79
|
+
/** Resolved presentation template the current render uses. */
|
|
80
|
+
private _activeTemplate;
|
|
81
|
+
/** The stage element the ResizeObserver/animator are currently bound to.
|
|
82
|
+
* Tracked so a template switch (which relocates the stage) re-attaches. */
|
|
83
|
+
private _observedStage;
|
|
84
|
+
/** Furthest step the user has reached. Used to gate ``goTo`` / ``prev``. */
|
|
85
|
+
private maxReachedStepIndex;
|
|
86
|
+
private animator;
|
|
87
|
+
private stageRef;
|
|
88
|
+
private imgRef;
|
|
89
|
+
private cursorRef;
|
|
90
|
+
private rippleRef;
|
|
91
|
+
private kbdRef;
|
|
92
|
+
private tooltipRef;
|
|
93
|
+
private resizeObserver;
|
|
94
|
+
private autoAdvanceTimer;
|
|
95
|
+
/** Token bumped on every ``_playCurrentPosition`` call so stale awaits drop. */
|
|
96
|
+
private playToken;
|
|
97
|
+
/** Resolved on the user's correct click; the running play awaits this. */
|
|
98
|
+
private waitForClick;
|
|
99
|
+
private resolveClick;
|
|
100
|
+
connectedCallback(): void;
|
|
101
|
+
disconnectedCallback(): void;
|
|
102
|
+
willUpdate(changed: PropertyValues): void;
|
|
103
|
+
updated(changed: PropertyValues): void;
|
|
104
|
+
/** Keep the current timeline row visible. The sidebar rail is height-capped
|
|
105
|
+
* and scrolls its timeline internally, so on a long guide ``next()`` would
|
|
106
|
+
* otherwise advance to a row below the fold. ``block: 'nearest'`` scrolls the
|
|
107
|
+
* minimum needed, which also means it does nothing when the row is already
|
|
108
|
+
* visible — including in the templates that have no timeline at all. */
|
|
109
|
+
private _scrollActiveRowIntoView;
|
|
110
|
+
/** Re-bind the ResizeObserver + animator to a relocated stage element (e.g.
|
|
111
|
+
* after a template switch) and reposition the hotspot/tooltip. Does not
|
|
112
|
+
* touch step state, so the in-flight play loop continues uninterrupted. */
|
|
113
|
+
private _reattachStage;
|
|
114
|
+
next(): void;
|
|
115
|
+
prev(): void;
|
|
116
|
+
restart(): void;
|
|
117
|
+
goTo(index: number): void;
|
|
118
|
+
private _loadManifest;
|
|
119
|
+
private _fallbackLayoutKey;
|
|
120
|
+
private _resolveVariant;
|
|
121
|
+
private _restartFromStart;
|
|
122
|
+
private _setupResizeObserver;
|
|
123
|
+
private _currentStep;
|
|
124
|
+
/**
|
|
125
|
+
* The `Capture` the active render reads from — screenshots AND caption.
|
|
126
|
+
*
|
|
127
|
+
* Both go through this one accessor on purpose: a step's text can be
|
|
128
|
+
* overridden per layout (the twin-branch pattern), so reading the frame from
|
|
129
|
+
* one layout and the wording from another would put "click the row of the
|
|
130
|
+
* table" above a screenshot of a card list.
|
|
131
|
+
*/
|
|
132
|
+
private _captureFor;
|
|
133
|
+
private _contentFor;
|
|
134
|
+
private _currentFrame;
|
|
135
|
+
private _currentTargetBbox;
|
|
136
|
+
private _recomputeHotspot;
|
|
137
|
+
private _placeTooltip;
|
|
138
|
+
private _goToStep;
|
|
139
|
+
/** Resolve the effective cursor mode. Host attribute wins; manifest is the
|
|
140
|
+
* fallback; 'arrow' is the final default. Unknown attribute strings fall
|
|
141
|
+
* through to the manifest so a typo can't strand the player. */
|
|
142
|
+
private _effectiveCursorMode;
|
|
143
|
+
private _playCurrentPosition;
|
|
144
|
+
private _advance;
|
|
145
|
+
/** Idempotent: emits ``steppy:hotspot`` with target=correct and resolves the
|
|
146
|
+
* current wait. A second call is a no-op (the wait already advanced). */
|
|
147
|
+
private _confirmHotspotClick;
|
|
148
|
+
private _onStageClick;
|
|
149
|
+
private _onHotspotClick;
|
|
150
|
+
private _handleKeyDown;
|
|
151
|
+
private _onLocaleChange;
|
|
152
|
+
private _onVariantChange;
|
|
153
|
+
private _onImgLoad;
|
|
154
|
+
private _emit;
|
|
155
|
+
render(): import("lit-html").TemplateResult;
|
|
156
|
+
/** The live stage subtree (screenshot + cursor/ripple/kbd + hotspot +
|
|
157
|
+
* miss-toast), shared verbatim by every template via the ``renderStage``
|
|
158
|
+
* thunk so the refs + handlers stay bound to the component.
|
|
159
|
+
*
|
|
160
|
+
* A template *switch* swaps the whole top-level template, so Lit recreates
|
|
161
|
+
* the stage element. That is handled by ``updated`` → ``_reattachStage``
|
|
162
|
+
* (re-point the animator + ResizeObserver, recompute placement) WITHOUT
|
|
163
|
+
* resetting step state, so the in-flight animation survives the move. The
|
|
164
|
+
* floating tooltip is included only in overlay mode (sidebar and inline
|
|
165
|
+
* surface the description in their own chrome). */
|
|
166
|
+
private _renderStage;
|
|
167
|
+
private _renderHotspot;
|
|
168
|
+
private _renderTooltip;
|
|
169
|
+
private _renderMissToast;
|
|
170
|
+
/** Project the ordered manifest steps into presentation view models. Pure
|
|
171
|
+
* projection of engine state — no behavior, no duplicated gates. */
|
|
172
|
+
private _buildStepViews;
|
|
173
|
+
/** Display-only duration estimate (~8s/step). Never feeds the play loop. */
|
|
174
|
+
private _durationLabel;
|
|
175
|
+
private _buildTemplateContext;
|
|
176
|
+
/** The built-in template switcher routes through the reflected property so
|
|
177
|
+
* ``willUpdate`` re-resolves + persists (and hosts observe the attribute). */
|
|
178
|
+
private _onTemplateChange;
|
|
179
|
+
private static readonly TEMPLATE_STORAGE_KEY;
|
|
180
|
+
private _readPersistedTemplate;
|
|
181
|
+
private _persistTemplate;
|
|
182
|
+
/** Resolve the effective template: explicit ``template`` attribute →
|
|
183
|
+
* persisted localStorage → manifest ``defaultTemplate`` → 'overlay'.
|
|
184
|
+
* Unknown attribute strings fall through so a typo can't strand the player. */
|
|
185
|
+
private _resolveTemplate;
|
|
186
|
+
private _renderLoading;
|
|
187
|
+
private _renderError;
|
|
188
|
+
}
|
|
189
|
+
declare global {
|
|
190
|
+
interface HTMLElementTagNameMap {
|
|
191
|
+
'steppy-guide': SteppyGuide;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
export { DEFAULT_UI_STRINGS };
|
|
195
|
+
export type { UiStrings };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared player styles: design tokens, the screenshot stage + cursor/ripple/
|
|
3
|
+
* kbd/hotspot/tooltip, status/error blocks, and the shared chrome control
|
|
4
|
+
* primitives (buttons, selects, segmented control) every template composes.
|
|
5
|
+
*
|
|
6
|
+
* Design language: warm cream surfaces, near-black text, rust-orange accent,
|
|
7
|
+
* green for completed steps. Serif titles, sans body, mono meta/counters. All
|
|
8
|
+
* theming flows through CSS custom properties on ``:host`` so the player stays
|
|
9
|
+
* overridable; fonts use system stacks only (offline, no CDN).
|
|
10
|
+
*
|
|
11
|
+
* The historical engine variable names (``--steppy-hotspot-color`` etc.) are
|
|
12
|
+
* preserved and merely re-pointed at the new palette so existing host overrides
|
|
13
|
+
* keep working.
|
|
14
|
+
*/
|
|
15
|
+
export declare const baseStyles: import("lit").CSSResult;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared chrome fragments composed by the three templates. Pure functions over
|
|
3
|
+
* {@link TemplateContext} — no state, no engine logic. Each template arranges
|
|
4
|
+
* these into its own layout.
|
|
5
|
+
*/
|
|
6
|
+
import { nothing, type TemplateResult } from 'lit';
|
|
7
|
+
import type { TemplateContext } from './types.js';
|
|
8
|
+
export declare function renderProgress(ctx: TemplateContext): TemplateResult;
|
|
9
|
+
/** Compact "i/n" counter (inline + sidebar footers). */
|
|
10
|
+
export declare function renderCounter(ctx: TemplateContext): TemplateResult;
|
|
11
|
+
export declare function renderPrev(ctx: TemplateContext): TemplateResult;
|
|
12
|
+
export declare function renderNext(ctx: TemplateContext): TemplateResult;
|
|
13
|
+
export declare function renderRestart(ctx: TemplateContext): TemplateResult;
|
|
14
|
+
export declare function renderLocaleSelect(ctx: TemplateContext): TemplateResult | typeof nothing;
|
|
15
|
+
export declare function renderLayoutSelect(ctx: TemplateContext): TemplateResult | typeof nothing;
|
|
16
|
+
/** Segmented [Sidebar | Overlay | Inline] control (active = dark fill). */
|
|
17
|
+
export declare function renderTemplateSwitcher(ctx: TemplateContext): TemplateResult;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template registry. The component dispatches ``render()`` to one of these by
|
|
3
|
+
* the resolved ``template`` id; ``static styles`` aggregates every template's
|
|
4
|
+
* CSS (unrendered templates simply have no matching DOM).
|
|
5
|
+
*/
|
|
6
|
+
import type { TemplateId, TemplateModule } from './types.js';
|
|
7
|
+
/**
|
|
8
|
+
* The registry is the single source of truth: registering a template here is
|
|
9
|
+
* the only edit needed inside this file. `ALL_TEMPLATES`, `isTemplateId` and
|
|
10
|
+
* `templateStyles` are all derived from it below, so a new template can never
|
|
11
|
+
* be renderable-but-unstyled or renderable-but-unlistable.
|
|
12
|
+
*/
|
|
13
|
+
export declare const TEMPLATES: Record<TemplateId, TemplateModule>;
|
|
14
|
+
/**
|
|
15
|
+
* Display/selection order for the segmented switcher — deliberately not the
|
|
16
|
+
* registry's declaration order, which is why this one list stays explicit.
|
|
17
|
+
* `templates.test.ts` asserts it covers every registered template.
|
|
18
|
+
*/
|
|
19
|
+
export declare const ALL_TEMPLATES: readonly TemplateId[];
|
|
20
|
+
export declare function isTemplateId(value: unknown): value is TemplateId;
|
|
21
|
+
/** Aggregated styles for the component's ``static styles``. */
|
|
22
|
+
export declare const templateStyles: import("lit").CSSResult[];
|
|
23
|
+
export type { TemplateContext, TemplateId, StepView, TemplateModule } from './types.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline template — full-bleed preview with a slim full-width caption bar: the
|
|
3
|
+
* step counter, the current localized description, a mono sub-label, and
|
|
4
|
+
* prev/next. The switchers ride along compactly so the player stays
|
|
5
|
+
* self-contained, but the bar is the only chrome over the preview.
|
|
6
|
+
*/
|
|
7
|
+
import { type TemplateResult } from 'lit';
|
|
8
|
+
import type { TemplateContext } from './types.js';
|
|
9
|
+
export declare function render(ctx: TemplateContext): TemplateResult;
|
|
10
|
+
export declare const styles: import("lit").CSSResult;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Overlay template — the original/default player presentation: a full-bleed
|
|
3
|
+
* screenshot stage with a floating callout pinned to the highlighted target,
|
|
4
|
+
* and a bottom chrome bar. Lifted from the component verbatim (only the new
|
|
5
|
+
* template switcher is added); engine logic is untouched.
|
|
6
|
+
*/
|
|
7
|
+
import { type TemplateResult } from 'lit';
|
|
8
|
+
import type { TemplateContext } from './types.js';
|
|
9
|
+
export declare function render(ctx: TemplateContext): TemplateResult;
|
|
10
|
+
export declare const styles: import("lit").CSSResult;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sidebar template — a fixed left rail (guide title, meta line, vertical step
|
|
3
|
+
* timeline, current-step description, prev/next + counter) beside the preview.
|
|
4
|
+
* The current step's full localized description is surfaced in the rail; the
|
|
5
|
+
* in-stage floating tooltip is suppressed (the stage handles that based on the
|
|
6
|
+
* active template). Collapses to a single column on narrow containers.
|
|
7
|
+
*
|
|
8
|
+
* The rail is height-capped and the timeline scrolls inside it, so a 19-step
|
|
9
|
+
* guide occupies the same vertical space as a 3-step one. The component pairs
|
|
10
|
+
* this with ``_scrollActiveRowIntoView`` so advancing never leaves the current
|
|
11
|
+
* step off-screen. Every layout dimension the cap touches — rail width, rail
|
|
12
|
+
* height, sticky offset — is a ``--steppy-*`` custom property, because a host
|
|
13
|
+
* that embeds the player under its own chrome (the Angular target) cannot use
|
|
14
|
+
* the ``100vh`` default.
|
|
15
|
+
*/
|
|
16
|
+
import { type TemplateResult } from 'lit';
|
|
17
|
+
import type { TemplateContext } from './types.js';
|
|
18
|
+
export declare function render(ctx: TemplateContext): TemplateResult;
|
|
19
|
+
export declare const styles: import("lit").CSSResult;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Engine → template contract.
|
|
3
|
+
*
|
|
4
|
+
* The engine (the ``<steppy-guide>`` component) owns ALL state and behavior:
|
|
5
|
+
* manifest, step indices, cursor animation, hotspot/tooltip placement,
|
|
6
|
+
* navigation, i18n, persistence. A *template* is a pure render function over an
|
|
7
|
+
* immutable {@link TemplateContext} — it decides only where the chrome lives and
|
|
8
|
+
* how it looks. Templates never hold step state and never re-implement engine
|
|
9
|
+
* gates; everything they need (including derived values like step ``status`` and
|
|
10
|
+
* disabled flags) is precomputed on the context.
|
|
11
|
+
*
|
|
12
|
+
* Anything that needs a live DOM ref — the screenshot stage with its
|
|
13
|
+
* cursor/ripple/kbd/hotspot, and the overlay's floating callout — is supplied as
|
|
14
|
+
* the {@link TemplateContext.renderStage} thunk, which the component owns so the
|
|
15
|
+
* ``ref()`` directives and event handlers stay bound to it across template
|
|
16
|
+
* switches.
|
|
17
|
+
*/
|
|
18
|
+
import type { CSSResult, TemplateResult } from 'lit';
|
|
19
|
+
import type { UiStrings } from '../i18n/ui-strings.js';
|
|
20
|
+
import type { TemplateMode } from '../manifest/schema.js';
|
|
21
|
+
export type TemplateId = TemplateMode;
|
|
22
|
+
/** A single step projected for presentation. Pure projection of engine state —
|
|
23
|
+
* no behavior, no duplicated gates. */
|
|
24
|
+
export interface StepView {
|
|
25
|
+
/** 1-based step index (matches the manifest ``step.index``). */
|
|
26
|
+
index: number;
|
|
27
|
+
/** Short heading (sidebar timeline). Falls back to the description. */
|
|
28
|
+
title: string;
|
|
29
|
+
/** Full localized instruction body (the manifest ``text``). */
|
|
30
|
+
description: string;
|
|
31
|
+
/** Mono meta sub-label (the manifest ``subtitle``). May be empty. */
|
|
32
|
+
subLabel: string;
|
|
33
|
+
/** Position relative to the current step. */
|
|
34
|
+
status: 'completed' | 'current' | 'upcoming';
|
|
35
|
+
/** True when the step is reachable via ``goTo`` (index ≤ furthest reached). */
|
|
36
|
+
reachable: boolean;
|
|
37
|
+
}
|
|
38
|
+
export interface TemplateContext {
|
|
39
|
+
steps: readonly StepView[];
|
|
40
|
+
/** 1-based index of the current step. */
|
|
41
|
+
currentIndex: number;
|
|
42
|
+
total: number;
|
|
43
|
+
/** Localized, display-only duration estimate (e.g. "~3 min"). */
|
|
44
|
+
durationLabel: string;
|
|
45
|
+
/** Localized guide title for the current locale. */
|
|
46
|
+
guideTitle: string;
|
|
47
|
+
locale: string;
|
|
48
|
+
ui: UiStrings;
|
|
49
|
+
waitingForInput: boolean;
|
|
50
|
+
completed: boolean;
|
|
51
|
+
onNext: () => void;
|
|
52
|
+
onPrev: () => void;
|
|
53
|
+
onRestart: () => void;
|
|
54
|
+
/** Jump to a 1-based step index (engine gates to reachable steps). */
|
|
55
|
+
onGoTo: (index1: number) => void;
|
|
56
|
+
prevDisabled: boolean;
|
|
57
|
+
nextDisabled: boolean;
|
|
58
|
+
locales: readonly string[];
|
|
59
|
+
layouts: readonly string[];
|
|
60
|
+
templates: readonly TemplateId[];
|
|
61
|
+
activeTemplate: TemplateId;
|
|
62
|
+
activeLocale: string;
|
|
63
|
+
activeLayout: string | null;
|
|
64
|
+
hideLayoutSwitcher: boolean;
|
|
65
|
+
onLocaleChange: (ev: Event) => void;
|
|
66
|
+
onLayoutChange: (ev: Event) => void;
|
|
67
|
+
onTemplateChange: (t: TemplateId) => void;
|
|
68
|
+
/** The ``.steppy-stage`` subtree (img + cursor + ripple + kbd + hotspot +
|
|
69
|
+
* miss-toast, and the floating tooltip in overlay mode). Carries the live
|
|
70
|
+
* refs + handlers; identical element shape in every template. */
|
|
71
|
+
renderStage: () => TemplateResult;
|
|
72
|
+
}
|
|
73
|
+
export interface TemplateModule {
|
|
74
|
+
render(ctx: TemplateContext): TemplateResult;
|
|
75
|
+
styles: CSSResult;
|
|
76
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The public surface of ``<steppy-tour-host>`` — what an app (or a
|
|
3
|
+
* framework adapter) programs against.
|
|
4
|
+
*/
|
|
5
|
+
import type { TourSource } from './loader.js';
|
|
6
|
+
import type { TourStringOverrides } from './strings.js';
|
|
7
|
+
export type { TourSource } from './loader.js';
|
|
8
|
+
export type { TourStringOverrides } from './strings.js';
|
|
9
|
+
export interface TourStartOptions {
|
|
10
|
+
/** Route parameters for the tour's entry (``/invoices/:invoiceId``). */
|
|
11
|
+
params?: Record<string, string | number>;
|
|
12
|
+
/** Start at this step id instead of the first. */
|
|
13
|
+
step?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface TourPosition {
|
|
16
|
+
tourId: string;
|
|
17
|
+
stepId: string;
|
|
18
|
+
/** 0-based among the steps that apply to the current layout. */
|
|
19
|
+
index: number;
|
|
20
|
+
total: number;
|
|
21
|
+
}
|
|
22
|
+
export interface TourNavigateContext {
|
|
23
|
+
/** The same path relative to ``<base href>`` — what a client-side router wants. */
|
|
24
|
+
appPath: string;
|
|
25
|
+
tourId: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Navigate the app to a tour's entry (or a route linked from step text).
|
|
29
|
+
* ``path`` is the document path, base included. Default: ``location.assign``.
|
|
30
|
+
* A returned promise is awaited (its value is ignored); a rejection fails the
|
|
31
|
+
* start with ``entry-params``. Router calls fit as they are:
|
|
32
|
+
* ``(_, { appPath }) => router.navigateByUrl(appPath)``.
|
|
33
|
+
*/
|
|
34
|
+
export type TourNavigate = (path: string, ctx: TourNavigateContext) => void | PromiseLike<unknown>;
|
|
35
|
+
export interface TourHostApi {
|
|
36
|
+
manifest: string;
|
|
37
|
+
fallbackManifest: string;
|
|
38
|
+
buildId: string;
|
|
39
|
+
locale: string;
|
|
40
|
+
guideOrigin: string;
|
|
41
|
+
baseZIndex: number;
|
|
42
|
+
targetTimeout: number;
|
|
43
|
+
disabled: boolean;
|
|
44
|
+
source: TourSource | null;
|
|
45
|
+
navigate: TourNavigate | null;
|
|
46
|
+
strings: TourStringOverrides | undefined;
|
|
47
|
+
/** Start a tour. Resolves true once it started; never rejects. */
|
|
48
|
+
start(tourId: string, options?: TourStartOptions): Promise<boolean>;
|
|
49
|
+
stop(reason?: string): void;
|
|
50
|
+
/** Advance the current step, whatever its kind. */
|
|
51
|
+
next(): void;
|
|
52
|
+
readonly isActive: boolean;
|
|
53
|
+
readonly current: TourPosition | null;
|
|
54
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-app tour data contract v1, the TypeScript twin of ``src/steppy/tour_spec.py``.
|
|
3
|
+
*
|
|
4
|
+
* The overlay validates every manifest and tour file against these values
|
|
5
|
+
* (``validate.ts``); the compiler never emits anything they reject.
|
|
6
|
+
* ``tests/test_tour_contract_sync.py`` keeps both sides equal, so every value
|
|
7
|
+
* stays a plain literal: string patterns carry no backslashes, and arrays are
|
|
8
|
+
* written on one line.
|
|
9
|
+
*/
|
|
10
|
+
export declare const TOUR_SCHEMA_VERSION = 1;
|
|
11
|
+
export declare const ADVANCE_KINDS: readonly ["click", "input", "change", "visible", "manual"];
|
|
12
|
+
export declare const TARGETED_ADVANCE_KINDS: readonly ["click", "input", "change", "visible"];
|
|
13
|
+
export declare const PLACEMENTS: readonly ["top", "bottom", "left", "right", "auto"];
|
|
14
|
+
export declare const TOUR_EVENTS: readonly ["tour-start", "tour-step", "tour-complete", "tour-dismiss", "tour-offpath", "tour-error"];
|
|
15
|
+
export declare const TOUR_ERROR_REASONS: readonly ["target-timeout", "entry-params", "load-failed", "schema-unsupported", "invalid-data"];
|
|
16
|
+
export declare const TOUR_PARTS: readonly ["layer", "spotlight", "bubble", "title", "body", "progress", "button", "primary", "secondary", "next", "close", "skip", "end", "resume", "pill", "guide-link"];
|
|
17
|
+
export declare const ATTR_TOUR_ID = "data-tour-id";
|
|
18
|
+
export declare const ATTR_STEP_ID = "data-step-id";
|
|
19
|
+
export declare const ATTR_STATE = "data-state";
|
|
20
|
+
export declare const ATTR_ADVANCE = "data-advance";
|
|
21
|
+
export declare const BUBBLE_STATES: readonly ["shown", "fallback"];
|
|
22
|
+
/** Languages ``strings.ts`` ships chrome strings for; others need the host's ``strings``. */
|
|
23
|
+
export declare const CHROME_STRING_LOCALES: readonly ["en", "fr", "nl", "de"];
|
|
24
|
+
export declare const TEST_ID_PATTERN = "^[A-Za-z0-9_.:/-]{1,128}$";
|
|
25
|
+
export declare const STEP_ID_PATTERN = "^[A-Za-z0-9_.:/-]{1,128}$";
|
|
26
|
+
export declare const TEST_ID_ATTRIBUTE_PATTERN = "^data-[a-z0-9-]+$";
|
|
27
|
+
export declare const TOUR_ID_PATTERN = "^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$";
|
|
28
|
+
export declare const LAYOUT_NAME_PATTERN = "^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$";
|
|
29
|
+
export declare const ROLE_PATTERN = "^[A-Za-z0-9_-]{1,64}$";
|
|
30
|
+
export declare const LOCALE_PATTERN = "^[A-Za-z]{2,3}(?:[-_][A-Za-z0-9]{1,8}){0,5}$";
|
|
31
|
+
export declare const SRI_PATTERN = "^sha384-[A-Za-z0-9+/]{64}$";
|
|
32
|
+
export declare const TOUR_FILE_NAME_PATTERN = "^[A-Za-z0-9][A-Za-z0-9_-]{0,63}[.][A-Za-z0-9_-]{1,64}[.]json$";
|
|
33
|
+
export declare const ROUTE_PATTERN = "^/$|^(?:/(?::[A-Za-z_][A-Za-z0-9_]{0,63}|[A-Za-z0-9._~%!$&()*+,;=@-]{1,128})){1,32}$";
|
|
34
|
+
export declare const MEDIA_QUERY_PATTERN = "^[A-Za-z0-9 :().,<>=/_-]{1,256}$";
|
|
35
|
+
export declare const BUILD_ID_PATTERN = "^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,62}[A-Za-z0-9])?$";
|
|
36
|
+
export declare const MAX_TOURS = 500;
|
|
37
|
+
export declare const MAX_STEPS = 100;
|
|
38
|
+
export declare const MAX_LOCALES = 32;
|
|
39
|
+
export declare const MAX_LAYOUTS = 16;
|
|
40
|
+
export declare const MAX_TITLE_CHARS = 200;
|
|
41
|
+
export declare const MAX_TEXT_CHARS = 2000;
|
|
42
|
+
export declare const MAX_ROUTE_CHARS = 512;
|
|
43
|
+
export declare const MAX_URL_CHARS = 2048;
|
|
44
|
+
export declare const MAX_MEDIA_QUERY_CHARS = 256;
|
|
45
|
+
export declare const MAX_TOUR_FILE_BYTES = 262144;
|
|
46
|
+
export declare const MAX_MANIFEST_BYTES = 1048576;
|
|
47
|
+
/** Player-only defaults (no Python twin). */
|
|
48
|
+
export declare const DEFAULT_TARGET_TIMEOUT_MS = 8000;
|
|
49
|
+
export declare const DEFAULT_BASE_Z_INDEX = 2147483000;
|
|
50
|
+
export type AdvanceKind = (typeof ADVANCE_KINDS)[number];
|
|
51
|
+
export type Placement = (typeof PLACEMENTS)[number];
|
|
52
|
+
export type TourEventName = (typeof TOUR_EVENTS)[number];
|
|
53
|
+
export type TourErrorReason = (typeof TOUR_ERROR_REASONS)[number];
|
|
54
|
+
export type TourPart = (typeof TOUR_PARTS)[number];
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Everything the overlay asks the browser about geometry and timing, behind
|
|
3
|
+
* one interface. ``defaultDom`` is the real thing; tests swap
|
|
4
|
+
* ``SteppyTourHost.dom`` for a stub with settable rects and a manual
|
|
5
|
+
* scheduler (happy-dom has no layout, and its rAF escapes fake timers).
|
|
6
|
+
*/
|
|
7
|
+
import { type Rect } from '../ui/placement.js';
|
|
8
|
+
export interface Scheduler {
|
|
9
|
+
now(): number;
|
|
10
|
+
raf(cb: () => void): number;
|
|
11
|
+
cancelRaf(id: number): void;
|
|
12
|
+
setTimeout(cb: () => void, ms: number): number;
|
|
13
|
+
clearTimeout(id: number): void;
|
|
14
|
+
setInterval(cb: () => void, ms: number): number;
|
|
15
|
+
clearInterval(id: number): void;
|
|
16
|
+
}
|
|
17
|
+
export interface Viewport {
|
|
18
|
+
x: number;
|
|
19
|
+
y: number;
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
}
|
|
23
|
+
export interface DomAdapter {
|
|
24
|
+
scheduler: Scheduler;
|
|
25
|
+
/** The element's effective rect (see ``effectiveRect``), or null when it has none. */
|
|
26
|
+
rect(el: Element): Rect | null;
|
|
27
|
+
isVisible(el: Element): boolean;
|
|
28
|
+
/** The visual viewport, in layout-viewport coordinates. */
|
|
29
|
+
viewport(): Viewport;
|
|
30
|
+
matchMedia(query: string): boolean;
|
|
31
|
+
reducedMotion(): boolean;
|
|
32
|
+
scrollIntoView(el: Element, instant: boolean): void;
|
|
33
|
+
supportsPopover(): boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Playwright's notion of a visible box, extended for wrapper hosts: an element
|
|
37
|
+
* with ``display: contents`` measures as the union of its children, and an
|
|
38
|
+
* empty 0x0 host (a custom element wrapping a native control) as its first
|
|
39
|
+
* visible focusable descendant.
|
|
40
|
+
*/
|
|
41
|
+
export declare function effectiveRect(el: Element, depth?: number): Rect | null;
|
|
42
|
+
export declare const defaultScheduler: Scheduler;
|
|
43
|
+
export declare const defaultDom: DomAdapter;
|