@rydr/game-sdk 8.0.0 → 8.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.
Files changed (75) hide show
  1. package/dist/client/PlatformClient.d.ts.map +1 -1
  2. package/dist/client/PlatformClient.js +3 -5
  3. package/dist/client/PlatformClient.js.map +1 -1
  4. package/dist/nav/README.md +13 -2
  5. package/dist/nav/motion.d.ts +10 -0
  6. package/dist/nav/motion.d.ts.map +1 -0
  7. package/dist/nav/motion.js +16 -0
  8. package/dist/nav/motion.js.map +1 -0
  9. package/dist/nav/scroll-nav.d.ts.map +1 -1
  10. package/dist/nav/scroll-nav.js +2 -6
  11. package/dist/nav/scroll-nav.js.map +1 -1
  12. package/dist/nav/spatial-nav.d.ts +12 -1
  13. package/dist/nav/spatial-nav.d.ts.map +1 -1
  14. package/dist/nav/spatial-nav.js +19 -5
  15. package/dist/nav/spatial-nav.js.map +1 -1
  16. package/dist/protocol/buttons.d.ts +17 -0
  17. package/dist/protocol/buttons.d.ts.map +1 -1
  18. package/dist/protocol/buttons.js +24 -0
  19. package/dist/protocol/buttons.js.map +1 -1
  20. package/dist/protocol/version.d.ts +1 -1
  21. package/dist/protocol/version.js +1 -1
  22. package/dist/ui/README.md +59 -4
  23. package/dist/ui/controller/combo-demo.d.ts +13 -4
  24. package/dist/ui/controller/combo-demo.d.ts.map +1 -1
  25. package/dist/ui/controller/combo-demo.js +55 -21
  26. package/dist/ui/controller/combo-demo.js.map +1 -1
  27. package/dist/ui/controller/control-spotlight.d.ts +188 -0
  28. package/dist/ui/controller/control-spotlight.d.ts.map +1 -0
  29. package/dist/ui/controller/control-spotlight.js +563 -0
  30. package/dist/ui/controller/control-spotlight.js.map +1 -0
  31. package/dist/ui/controller/control-trainer.d.ts.map +1 -1
  32. package/dist/ui/controller/control-trainer.js +6 -22
  33. package/dist/ui/controller/control-trainer.js.map +1 -1
  34. package/dist/ui/controller/controller-map.d.ts +20 -0
  35. package/dist/ui/controller/controller-map.d.ts.map +1 -1
  36. package/dist/ui/controller/controller-map.js +115 -56
  37. package/dist/ui/controller/controller-map.js.map +1 -1
  38. package/dist/ui/controller/controller-pad.d.ts +9 -3
  39. package/dist/ui/controller/controller-pad.d.ts.map +1 -1
  40. package/dist/ui/controller/controller-pad.js +7 -7
  41. package/dist/ui/controller/controller-pad.js.map +1 -1
  42. package/dist/ui/controller/index.d.ts +2 -0
  43. package/dist/ui/controller/index.d.ts.map +1 -1
  44. package/dist/ui/controller/index.js +2 -0
  45. package/dist/ui/controller/index.js.map +1 -1
  46. package/dist/ui/controller/live-presses.d.ts +68 -0
  47. package/dist/ui/controller/live-presses.d.ts.map +1 -0
  48. package/dist/ui/controller/live-presses.js +71 -0
  49. package/dist/ui/controller/live-presses.js.map +1 -0
  50. package/dist/ui/controller/pad-svg.d.ts +23 -3
  51. package/dist/ui/controller/pad-svg.d.ts.map +1 -1
  52. package/dist/ui/controller/pad-svg.js +28 -4
  53. package/dist/ui/controller/pad-svg.js.map +1 -1
  54. package/dist/ui/controller/parts-map.d.ts +51 -3
  55. package/dist/ui/controller/parts-map.d.ts.map +1 -1
  56. package/dist/ui/controller/parts-map.js +76 -4
  57. package/dist/ui/controller/parts-map.js.map +1 -1
  58. package/dist/ui/controller/styles.js +113 -8
  59. package/dist/ui/controller/styles.js.map +1 -1
  60. package/dist/ui/keycap.d.ts +7 -1
  61. package/dist/ui/keycap.d.ts.map +1 -1
  62. package/dist/ui/keycap.js.map +1 -1
  63. package/dist/ui/option-menu.d.ts +3 -2
  64. package/dist/ui/option-menu.d.ts.map +1 -1
  65. package/dist/ui/option-menu.js +5 -0
  66. package/dist/ui/option-menu.js.map +1 -1
  67. package/dist/ui/showcase/gallery.d.ts +12 -3
  68. package/dist/ui/showcase/gallery.d.ts.map +1 -1
  69. package/dist/ui/showcase/gallery.js +16 -7
  70. package/dist/ui/showcase/gallery.js.map +1 -1
  71. package/dist/ui/showcase/index.d.ts +4 -3
  72. package/dist/ui/showcase/index.d.ts.map +1 -1
  73. package/dist/ui/showcase/index.js +175 -13
  74. package/dist/ui/showcase/index.js.map +1 -1
  75. package/package.json +1 -1
@@ -0,0 +1,563 @@
1
+ /**
2
+ * CONTROL SPOTLIGHT — "this button, and here's what it does", written on the button itself.
3
+ *
4
+ * ```ts
5
+ * // A tour: each control in turn, captioned, on a clock.
6
+ * const spot = mountControlSpotlight(host, {
7
+ * press: shellInput,
8
+ * watch: true,
9
+ * steps: [
10
+ * { label: "Descendre de vitesse", combo: ["LB"] },
11
+ * { label: "Monter de vitesse", combo: ["RB"] },
12
+ * ],
13
+ * });
14
+ *
15
+ * // Or no clock at all: one control, lit and captioned, indefinitely.
16
+ * mountControlSpotlight(hud, { press: session, mode: "still", steps: [{ label: "Move", combo: ["LSTICK"] }] });
17
+ * ```
18
+ *
19
+ * The caption sits **on the control**, not in a list beside the pad — which is the whole reason this
20
+ * view exists. A rider on a bike reading a trainer screen from a few feet away cannot be asked to trace
21
+ * a line from a legend back to a button, and the control genuinely cannot be named in text: the same
22
+ * button reads `LB` on an Xbox pad, `L1` on a DualSense and a bare `L` on a Switch. Pointing at it and
23
+ * saying what it does is the only honest way to teach it.
24
+ *
25
+ * ## Which of the four controller views is this
26
+ *
27
+ * {@link mountControllerPad} the drawing alone, reflecting real input
28
+ * {@link mountControllerMap} STATIC — every control at once, callouts in columns joined by lines
29
+ * {@link mountComboDemo} DYNAMIC — one combination, animated on a loop
30
+ * {@link mountControlSpotlight} N controls, ONE at a time, captioned ON the button, on its own clock
31
+ * {@link mountControlTrainer} a lesson that WATCHES and gates on the rider performing it
32
+ *
33
+ * The map is what you want for "here is the control scheme". This is what you want when the caption has
34
+ * to land on the button, when each control needs its own dwell, or when there is no clock at all and you
35
+ * simply want one control lit with a label on it.
36
+ *
37
+ * ## Two modes, one render
38
+ *
39
+ * `"tour"` walks the steps: each lights for its `holdMs`, then a dark `gapMs`, then the next. The gap is
40
+ * load-bearing — two steps shown back to back read as one flickering picture, and the dark beat between
41
+ * them is what makes them two statements rather than one.
42
+ *
43
+ * `"still"` has no clock: every step lights and every caption shows, and stays. With a single step that
44
+ * is "highlight this key, with this text, for as long as I'm on screen", which needs no timeline to
45
+ * express. `prefers-reduced-motion` simply forces this mode — so the reduced-motion still is not a
46
+ * second code path to keep in step with the first, it IS the first.
47
+ *
48
+ * ## The rider outranks the lesson (`watch`)
49
+ *
50
+ * With `watch`, a control the rider is actually holding paints **green** (`done`) instead of the accent,
51
+ * and the timeline **stands down**: it stops, every unheld control goes dark, and it restarts from the
52
+ * top `resumeMs` after they let go. A loop that keeps cycling underneath a rider trying it out is two
53
+ * hands on the same pad — they cannot tell which of the lights is theirs, which is the entire feedback
54
+ * the lesson exists to give.
55
+ *
56
+ * Green is not limited to the controls the steps name: with `watch` the drawing paints EVERY control the
57
+ * rider is holding, including ones no step teaches. A rider trying the pad out while the lesson runs is
58
+ * asking "which one is this?", and a drawing that answers only for the two buttons it happens to be
59
+ * teaching reads as a dead pad rather than as an answer. Only what the rider holds turns green, so the
60
+ * lesson's own controls stay the only BLUE thing on it.
61
+ *
62
+ * Blue says *this is the press*; green says *and that one was yours*. Neither is the whitish `hint` the
63
+ * trainer uses, which is built for "do this, I'll wait" — here the instruction is a moving picture of a
64
+ * press, and a press drawn in not-a-press colours doesn't look like the thing it's asking for.
65
+ *
66
+ * ## This component is the sole writer of the drawing
67
+ *
68
+ * The pad is mounted **glyph-only** (it gets the hardware store for its lettering, and an `onButton` that
69
+ * does nothing), exactly as {@link mountControlTrainer} does. Wiring the pad to input as well would give
70
+ * two writers for the same classes every frame, each undoing the other. We take the rider's presses
71
+ * ourselves and fold them into the paint, so there is one writer and no race.
72
+ *
73
+ * ⚠️ **A caption is ONE line: the action's name.** No `hint` subtitle — `ControlSpotlightStep` types it
74
+ * out — because this bubble floats over the rider's controller for about two seconds while they pedal,
75
+ * and a second quieter line under the first means neither gets read. Qualify the label if you must.
76
+ *
77
+ * ⚠️ **Captions are text-only for now.** A step whose combo names several controls lights all of them
78
+ * and pins its caption per the usual anchor rule, but the bubble does not draw chips — the multi-control
79
+ * case is getting its own treatment later. Don't grow chips in here by drift; `mountComboDemo` and
80
+ * `mountControllerMap` are where a combination is spelled out today.
81
+ */
82
+ import { mountControllerPad } from "./controller-pad.js";
83
+ import { anchorPartOfToken, anchorTokenOf, channelsOf, litPartOfControl, partCentreOf, partGeomOf, triggerSideOfPart, } from "./parts-map.js";
84
+ import { createLivePresses } from "./live-presses.js";
85
+ import { injectPadCss } from "./styles.js";
86
+ import { isShellControl, parseToken } from "./tokens.js";
87
+ import { partCentre } from "./types.js";
88
+ /** How long a step holds lit, unless it says otherwise. */
89
+ export const SPOTLIGHT_HOLD_MS = 2000;
90
+ /**
91
+ * The dark beat after a step. Not decoration — see the note on the gap above; two lit controls with no
92
+ * dark between them read as one picture flickering rather than as two separate statements.
93
+ */
94
+ export const SPOTLIGHT_GAP_MS = 1000;
95
+ /** How long after the rider lets go of everything before the timeline takes the pad back. */
96
+ export const SPOTLIGHT_RESUME_MS = 1000;
97
+ /** One full revolution of an undirected stick sweep, ms. Matches `combo-demo`'s feel. */
98
+ const SWEEP_MS = 1200;
99
+ /** Past this deflection an undirected sweep counts as engaged, for the caption's sake. */
100
+ const SWEEP_RAMP = 1;
101
+ export function mountControlSpotlight(host, opts) {
102
+ injectPadCss();
103
+ const reduceMotion = typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
104
+ const mode = reduceMotion ? "still" : (opts.mode ?? "tour");
105
+ const watch = opts.watch === true && opts.press != null;
106
+ const resumeMs = opts.resumeMs ?? SPOTLIGHT_RESUME_MS;
107
+ const speed = opts.speed ?? 1;
108
+ const loop = opts.loop !== false;
109
+ const root = document.createElement("div");
110
+ root.className = "rydr-padspot" + (opts.glow === false ? " no-glow" : "");
111
+ // The captions are absolutely positioned over the drawing, so they need a positioned box of their own.
112
+ // `host` belongs to the caller and may be laid out however that screen wants.
113
+ const stage = document.createElement("div");
114
+ stage.className = "rydr-padspot-stage";
115
+ root.appendChild(stage);
116
+ host.appendChild(root);
117
+ /**
118
+ * Does any step name a shell-owned control? Then the drawing must be allowed to light it — that IS the
119
+ * lesson. Derived rather than asked for, so a caller can't forget it and get a dead highlight. (Same
120
+ * reasoning, same shape, as `control-trainer`.)
121
+ */
122
+ const teachesShellOwned = (steps) => steps.some((s) => s.combo.some((t) => {
123
+ const p = parseToken(t);
124
+ return p.kind === "button" && isShellControl(p.name);
125
+ }));
126
+ // Glyph-only: this component owns the drawing's state, so real presses must not write it too. We route
127
+ // them ourselves, below, and fold them into the same paint the timeline uses.
128
+ /**
129
+ * Whether the drawing may light the shell-owned shoulders. Held in a variable because the live-press
130
+ * paint below has to respect the same answer the pad was mounted with: lighting `LB` on a drawing that
131
+ * draws it faint (because nothing here teaches it) would be a highlight nobody can read.
132
+ */
133
+ const shellOwnedLit = teachesShellOwned(opts.steps);
134
+ const pad = mountControllerPad(stage, {
135
+ press: opts.press && { onButton: () => () => { }, hardware: opts.press.hardware },
136
+ brand: opts.brand,
137
+ glyphSet: opts.glyphSet,
138
+ size: opts.size ?? "lg",
139
+ labels: opts.labels,
140
+ accent: opts.accent,
141
+ allowShellOwned: shellOwnedLit,
142
+ });
143
+ let spots = [];
144
+ let cycleMs = 0;
145
+ let done = [];
146
+ /** What we last painted per part — the dirty guard that keeps this a few DOM writes a second. */
147
+ const painted = new Map();
148
+ /** What tone each caption last had, for the same reason. */
149
+ const toned = new Map();
150
+ /** Controls the rider is holding right now. These paint green whatever the timeline is doing. */
151
+ const held = new Set();
152
+ // ── build ───────────────────────────────────────────────────────────────────────────────────────
153
+ function buildSpots(steps) {
154
+ for (const s of spots)
155
+ s.tag.remove();
156
+ painted.clear();
157
+ toned.clear();
158
+ let at = 0;
159
+ spots = steps.map((step) => {
160
+ const holdMs = step.holdMs ?? SPOTLIGHT_HOLD_MS;
161
+ const from = at;
162
+ at += holdMs + (step.gapMs ?? SPOTLIGHT_GAP_MS);
163
+ const anchorToken = anchorTokenOf(step);
164
+ const spot = {
165
+ step,
166
+ channels: channelsOf(step),
167
+ // Only BUTTONS can be observed as held: `onButton` is the only stream we have, and a stick's
168
+ // deflection isn't a press. A stick-only step therefore never counts as performed, which is
169
+ // honest — we'd need axes to know, and the caption is doing the teaching either way.
170
+ required: step.combo.map((t) => parseToken(t)).flatMap((p) => (p.kind === "button" ? [p.name] : [])),
171
+ anchor: anchorToken ? anchorPartOfToken(anchorToken) : undefined,
172
+ tag: buildTag(step.label),
173
+ from,
174
+ holdMs,
175
+ };
176
+ stage.appendChild(spot.tag);
177
+ return spot;
178
+ });
179
+ cycleMs = Math.max(1, at);
180
+ done = spots.map(() => false);
181
+ placeAll();
182
+ }
183
+ /** The bubble: one line, and the caret that ties it to its control. Nothing else — see `hint`. */
184
+ function buildTag(label) {
185
+ const tag = document.createElement("div");
186
+ tag.className = "rydr-padspot-tag";
187
+ const text = document.createElement("span");
188
+ text.className = "rydr-padspot-label";
189
+ text.textContent = label;
190
+ tag.appendChild(text);
191
+ // The caret ties the bubble to the control directly beneath (or above) it. A separate element rather
192
+ // than a border trick on the bubble, because it has to be recoloured along with it.
193
+ const caret = document.createElement("span");
194
+ caret.className = "rydr-padspot-caret";
195
+ tag.appendChild(caret);
196
+ return tag;
197
+ }
198
+ /**
199
+ * Which side of its control a caption sits on.
200
+ *
201
+ * Top half → above, bottom half → below, so the bubble lands in the free space beyond the drawing
202
+ * rather than across the middle of the control cluster. Derived from the layout, never hardcoded per
203
+ * brand, for the same reason `sideOfPart` is: it has to stay right across five very different control
204
+ * arrangements.
205
+ *
206
+ * ⚠️ For a control in the MIDDLE of the body there is no side that covers nothing — the controls
207
+ * occupy the middle, and a caption near one of them will cross another. A left-stick caption crosses
208
+ * the shoulder row; a d-pad caption crosses the grips. That is the same trade the shell's original
209
+ * shift demo made deliberately (a caption sitting ON its control is what makes it unambiguous, and a
210
+ * control hidden for two seconds costs less than a caption the rider has to trace back to its button).
211
+ * `captionSide` is the escape when a particular screen disagrees.
212
+ */
213
+ function sideFor(part) {
214
+ if (opts.captionSide && opts.captionSide !== "auto")
215
+ return opts.captionSide;
216
+ const layout = pad.layout();
217
+ const centre = part ? partCentreOf(layout, part) : undefined;
218
+ const midY = layout.viewBox[1] + layout.viewBox[3] / 2;
219
+ return (centre?.y ?? midY) <= midY ? "above" : "below";
220
+ }
221
+ /**
222
+ * Where a control sits, in page pixels relative to the stage.
223
+ *
224
+ * Projected from the layout's own GEOMETRY through the SVG's screen matrix, rather than measured off
225
+ * the part's `getBoundingClientRect()`. That distinction is load-bearing for the sticks: the node
226
+ * registered as `lstickPress` lives *inside* the translated group that deflects the cap, so measuring
227
+ * it pins the caption to the moving thumb-cap and the bubble drifts around as the stick sweeps. The
228
+ * geometry is the stick's RESTING centre — the well — which is what the caption should aim at and the
229
+ * one point on a stick that doesn't move.
230
+ *
231
+ * It also means nothing here depends on a transform anywhere in the drawing, so a caption can be
232
+ * placed once and stay correct however the control animates underneath it.
233
+ */
234
+ function projectPart(part) {
235
+ const svgEl = pad.svg().svg;
236
+ const geom = partGeomOf(pad.layout(), part);
237
+ const ctm = svgEl.getScreenCTM();
238
+ if (!geom || !ctm)
239
+ return null;
240
+ let x;
241
+ let yTop;
242
+ let yBottom;
243
+ if (geom.kind === "circle") {
244
+ x = geom.cx;
245
+ yTop = geom.cy - geom.r;
246
+ yBottom = geom.cy + geom.r;
247
+ }
248
+ else if (geom.kind === "rect") {
249
+ x = geom.x + geom.w / 2;
250
+ yTop = geom.y;
251
+ yBottom = geom.y + geom.h;
252
+ }
253
+ else {
254
+ const c = partCentre(geom);
255
+ x = c.x;
256
+ yTop = c.y;
257
+ yBottom = c.y;
258
+ }
259
+ const box = stage.getBoundingClientRect();
260
+ const pt = svgEl.createSVGPoint();
261
+ pt.x = x;
262
+ pt.y = yTop;
263
+ const a = pt.matrixTransform(ctm);
264
+ pt.y = yBottom;
265
+ const b = pt.matrixTransform(ctm);
266
+ return { cx: a.x - box.left, top: a.y - box.top, bottom: b.y - box.top };
267
+ }
268
+ /**
269
+ * Park a caption over (or under) the control it names.
270
+ *
271
+ * Pinned in PAGE PIXELS, because the bubble is DOM and the control lives in an SVG viewBox — the same
272
+ * constraint the map view's connector layer has. Anything that changes that mapping has to re-run
273
+ * this: a pad swap rebuilds the SVG outright, and a resize rescales it underneath.
274
+ */
275
+ function place(spot) {
276
+ const side = sideFor(spot.anchor);
277
+ spot.tag.classList.toggle("above", side === "above");
278
+ spot.tag.classList.toggle("below", side === "below");
279
+ const at = spot.anchor ? projectPart(spot.anchor) : null;
280
+ // A layout may genuinely lack the part (a Zwift pod has no sticks or triggers). Centre the caption
281
+ // on the drawing rather than dropping it: the words are still the lesson.
282
+ if (!at) {
283
+ spot.tag.style.left = "50%";
284
+ spot.tag.style.top = "50%";
285
+ return;
286
+ }
287
+ spot.tag.style.left = `${at.cx}px`;
288
+ spot.tag.style.top = side === "above" ? `${at.top - 6}px` : `${at.bottom + 6}px`;
289
+ }
290
+ const placeAll = () => spots.forEach((spot) => place(spot));
291
+ // ── paint ───────────────────────────────────────────────────────────────────────────────────────
292
+ const paint = (part, state) => {
293
+ if (painted.get(part) === state)
294
+ return;
295
+ painted.set(part, state);
296
+ pad.driver().setPartState(part, state);
297
+ };
298
+ /**
299
+ * Paint one part, and move the meter if that part is a trigger — the pair that always travels together
300
+ * (see `triggerSideOfPart`). Both writers below go through here so neither can light a shoulder without
301
+ * pulling it.
302
+ */
303
+ function paintPart(part, state, engaged) {
304
+ paint(part, state);
305
+ const meter = triggerSideOfPart(part);
306
+ if (meter)
307
+ pad.driver().setMeter(meter, engaged ? 1 : 0);
308
+ }
309
+ /** Is the rider performing this step right now? Every button it names has to be down. */
310
+ const riderDoing = (spot) => watch && spot.required.length > 0 && spot.required.every((n) => held.has(n));
311
+ /** Is the rider holding anything this spotlight teaches? That's what makes the timeline stand down. */
312
+ const lessonHeld = () => spots.some((s) => s.required.some((n) => held.has(n)));
313
+ function showTag(spot, index, on, mine) {
314
+ if (on && !spot.tag.classList.contains("show"))
315
+ place(spot);
316
+ spot.tag.classList.toggle("show", on);
317
+ // Only ever recoloured while it's UP: a caption that turned white on its way out would flash through
318
+ // the fade, which reads as a second, different caption leaving rather than the same one.
319
+ if (!on || toned.get(index) === mine)
320
+ return;
321
+ toned.set(index, mine);
322
+ spot.tag.classList.toggle("mine", mine);
323
+ }
324
+ /**
325
+ * Apply one step's state to the drawing and its caption.
326
+ *
327
+ * `since` is ms into this step's own hold, which only an undirected stick needs — it sweeps its cap
328
+ * around the circle so the drawing says "any direction" rather than parking it somewhere arbitrary.
329
+ */
330
+ function paintSpot(spot, index, lit, since) {
331
+ const mine = riderDoing(spot);
332
+ const driver = pad.driver();
333
+ const engaged = lit || mine;
334
+ const state = mine ? "done" : lit ? "on" : "off";
335
+ for (const ch of spot.channels) {
336
+ if (ch.stick) {
337
+ let x = 0;
338
+ let y = 0;
339
+ if (engaged) {
340
+ if (ch.stick.vec) {
341
+ x = ch.stick.vec[0];
342
+ y = ch.stick.vec[1];
343
+ }
344
+ else {
345
+ const angle = (Math.max(0, since) / SWEEP_MS) * Math.PI * 2;
346
+ x = Math.cos(angle) * SWEEP_RAMP;
347
+ y = Math.sin(angle) * SWEEP_RAMP;
348
+ }
349
+ }
350
+ driver.setStick(ch.stick.side, x, y, engaged ? "on" : "off");
351
+ continue;
352
+ }
353
+ if (!ch.part)
354
+ continue;
355
+ paintPart(ch.part, state, engaged);
356
+ }
357
+ showTag(spot, index, engaged, mine);
358
+ if (mine && !done[index]) {
359
+ done[index] = true;
360
+ opts.onPerformed?.(spot.step, index);
361
+ }
362
+ }
363
+ /** Does any step draw this part? Then it, not the live pass, decides what the part looks like unheld. */
364
+ const ownedByAStep = (part) => spots.some((s) => s.channels.some((ch) => ch.part === part));
365
+ /**
366
+ * Paint every control the rider is holding GREEN, on top of whatever the timeline just drew — the shared
367
+ * live-press pass (`live-presses.ts`), which every view in this family runs.
368
+ *
369
+ * It goes LAST, so a held control wins the part it shares with a step: the rider's own hand is the one
370
+ * thing on the drawing they can verify.
371
+ */
372
+ const paintLivePresses = createLivePresses({
373
+ state: "done",
374
+ axis: opts.press?.axis?.bind(opts.press),
375
+ shellOwned: shellOwnedLit,
376
+ light: (part, state) => paintPart(part, state, true),
377
+ // The rider's own stick outranks a step's demonstration on that side, for the same reason their press
378
+ // outranks a lit part: one of the two is a picture of their actual hand.
379
+ setStick: (side, x, y, state) => pad.driver().setStick(side, x, y, state === "done" ? "done" : "on"),
380
+ release: (part) => {
381
+ // A part a step draws is HANDED BACK, not blanked: dropping the cached value is enough, since that
382
+ // step repaints it (meter included) on the next frame. Blanking it here would flash one dark frame
383
+ // through a control the lesson is currently lighting.
384
+ if (ownedByAStep(part))
385
+ painted.delete(part);
386
+ else
387
+ paintPart(part, "off", false);
388
+ },
389
+ });
390
+ /**
391
+ * Every step engaged at once, captions up — the `"still"` render.
392
+ *
393
+ * `"still"` means there is no TIMELINE: nothing steps from one control to the next, nothing goes dark
394
+ * between them. It does **not** mean the artwork is a frozen frame. A control whose engaged state is
395
+ * inherently a motion still has to move, or the drawing stops saying what it is: an undirected stick
396
+ * parked at one arbitrary angle reads as "push it that way", when the whole point of a bare `LSTICK`
397
+ * is *any* direction. So the sweep keeps running and `t` is passed straight through.
398
+ *
399
+ * The exception is reduced motion, which asks for no animation at all — there we pass a fixed `t`.
400
+ */
401
+ function renderStill(t) {
402
+ spots.forEach((spot, i) => paintSpot(spot, i, true, t));
403
+ }
404
+ // ── the loop ────────────────────────────────────────────────────────────────────────────────────
405
+ let raf = 0;
406
+ /** Timeline origin. Cleared whenever the rider takes over, so the tour RESTARTS rather than resumes. */
407
+ let start = 0;
408
+ /** Nothing may light before this. Pushed forward every frame the rider is holding something. */
409
+ let resumeAt = 0;
410
+ /** Which step was last reported to `onStepShown`, so it fires on the change and not every frame. */
411
+ let shown = -1;
412
+ /** Which cycle we were in last frame, so `onCycle` fires once per pass. */
413
+ let cycles = 0;
414
+ /**
415
+ * One frame, for both modes — so the rAF plumbing, the hidden-tab guard and "what does engaged look
416
+ * like" each exist exactly once. `"still"` simply has no timeline to advance.
417
+ */
418
+ function frame(t) {
419
+ raf = requestAnimationFrame(frame);
420
+ if (document.hidden)
421
+ return;
422
+ if (mode === "still")
423
+ renderStill(reduceMotion ? 0 : t);
424
+ else
425
+ renderTour(t);
426
+ if (watch)
427
+ paintLivePresses(held);
428
+ }
429
+ function renderTour(t) {
430
+ // The rider driving outranks the lesson. `"still"` is exempt: there's no animation to stand down
431
+ // from, just a labelled diagram, and blanking it would take the lesson away mid-press.
432
+ if (lessonHeld()) {
433
+ start = 0;
434
+ resumeAt = t + resumeMs;
435
+ }
436
+ // Held pushes `resumeAt` forward every frame, so one test covers both the press and the quiet beat.
437
+ const waiting = resumeAt > t;
438
+ if (!waiting && !start)
439
+ start = t;
440
+ const elapsed = waiting ? -1 : (t - start) * speed;
441
+ if (!loop && elapsed >= cycleMs) {
442
+ spots.forEach((spot, i) => paintSpot(spot, i, false, 0));
443
+ stopFrames();
444
+ return;
445
+ }
446
+ if (elapsed >= 0) {
447
+ const pass = Math.floor(elapsed / cycleMs);
448
+ if (pass > cycles) {
449
+ cycles = pass;
450
+ opts.onCycle?.();
451
+ }
452
+ }
453
+ // Out of every step's range, so a waiting frame paints the pad dark and leaves the rider's own press
454
+ // as the only thing on it.
455
+ const at = waiting ? -1 : elapsed % cycleMs;
456
+ let lighting = -1;
457
+ spots.forEach((spot, i) => {
458
+ const lit = at >= spot.from && at < spot.from + spot.holdMs;
459
+ if (lit)
460
+ lighting = i;
461
+ paintSpot(spot, i, lit, at - spot.from);
462
+ });
463
+ if (lighting !== shown) {
464
+ shown = lighting;
465
+ if (lighting >= 0)
466
+ opts.onStepShown?.(spots[lighting].step, lighting);
467
+ }
468
+ }
469
+ function startFrames() {
470
+ if (!raf)
471
+ raf = requestAnimationFrame(frame);
472
+ }
473
+ function stopFrames() {
474
+ if (raf)
475
+ cancelAnimationFrame(raf);
476
+ raf = 0;
477
+ }
478
+ /**
479
+ * Is anything in `"still"` mode inherently in motion? Only an UNDIRECTED stick: a bare `LSTICK` means
480
+ * "any direction", which is a sweep, not a position. A directed token (`LSTICK:left`) is a place the
481
+ * cap sits, and buttons are just lit — neither needs a frame after the first.
482
+ */
483
+ const stillAnimates = () => !reduceMotion && spots.some((s) => s.channels.some((c) => c.stick && !c.stick.vec));
484
+ /**
485
+ * `"still"` only takes frames when it has a reason to: an animating control, or `watch` (where a press
486
+ * still has to turn green). A permanent spotlight on a plain button costs nothing per frame, which is
487
+ * part of why you'd ask for it.
488
+ */
489
+ function begin() {
490
+ renderStill(0);
491
+ if (mode === "tour" || watch || stillAnimates())
492
+ startFrames();
493
+ }
494
+ // ── input ───────────────────────────────────────────────────────────────────────────────────────
495
+ const offButton = watch
496
+ ? opts.press?.onButton(({ name, edge }) => {
497
+ if (edge === "down")
498
+ held.add(name);
499
+ else
500
+ held.delete(name);
501
+ // A part whose state is decided by `held` has to be repainted next frame; drop the cached value
502
+ // so the dirty guard doesn't hold the stale one.
503
+ for (const spot of spots) {
504
+ if (!spot.required.includes(name))
505
+ continue;
506
+ for (const ch of spot.channels)
507
+ if (ch.part)
508
+ painted.delete(ch.part);
509
+ }
510
+ // …and the pressed control's own part, which turns green whether or not a step names it.
511
+ const own = litPartOfControl(name);
512
+ if (own)
513
+ painted.delete(own);
514
+ // In "still" + watch the loop is already running and will repaint on the next frame with a live
515
+ // clock; painting here would write a stale one and stutter an in-flight sweep.
516
+ })
517
+ : undefined;
518
+ // ── relayout ────────────────────────────────────────────────────────────────────────────────────
519
+ // A pad swap rebuilds the SVG outright, so every measured caption position is stale.
520
+ const offRebuild = pad.onRebuild(() => {
521
+ painted.clear();
522
+ placeAll();
523
+ // A swap with no loop running (a still, non-watching spotlight) has nobody to repaint it.
524
+ if (mode === "still" && !raf)
525
+ renderStill(0);
526
+ });
527
+ const resize = new ResizeObserver(() => placeAll());
528
+ resize.observe(stage);
529
+ buildSpots(opts.steps);
530
+ begin();
531
+ return {
532
+ root,
533
+ pad,
534
+ setSteps(steps) {
535
+ buildSpots(steps);
536
+ start = 0;
537
+ shown = -1;
538
+ cycles = 0;
539
+ pad.driver().clearAll();
540
+ if (mode === "still") {
541
+ renderStill(0);
542
+ if (watch || stillAnimates())
543
+ startFrames();
544
+ }
545
+ else
546
+ startFrames();
547
+ },
548
+ play: startFrames,
549
+ pause: stopFrames,
550
+ performed: () => done.slice(),
551
+ dispose() {
552
+ stopFrames();
553
+ resize.disconnect();
554
+ offRebuild();
555
+ offButton?.();
556
+ // Release everything before detaching: a control lit at teardown must not survive a re-mount.
557
+ pad.driver().clearAll();
558
+ pad.dispose();
559
+ root.remove();
560
+ },
561
+ };
562
+ }
563
+ //# sourceMappingURL=control-spotlight.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control-spotlight.js","sourceRoot":"","sources":["../../../src/ui/controller/control-spotlight.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"control-trainer.d.ts","sourceRoot":"","sources":["../../../src/ui/controller/control-trainer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEzD,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,KAAK,EAAkB,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAI3E,OAAO,EAAgD,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAC9G,OAAO,KAAK,EAAE,WAAW,EAAW,MAAM,YAAY,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAE,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAC/E,IAAI,CAAC,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC9B,QAAQ,CAAC,EAAE;QACT,OAAO,EAAE;YAAE,QAAQ,CAAC,EAAE,QAAQ,CAAA;SAAE,CAAC;QACjC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,EAAE;YAAE,QAAQ,CAAC,EAAE,QAAQ,CAAA;SAAE,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;KACzE,CAAC;CACH;AAED,MAAM,WAAW,WAAW;IAC1B,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,mFAAmF;IACnF,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IAC7B,kEAAkE;IAClE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sCAAsC;IACtC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,qBAAqB;IACpC,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IAC9B,mGAAmG;IACnG,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,mBAAmB,CAAC;IACjC,uGAAuG;IACvG,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACxD,2FAA2F;IAC3F,UAAU,CAAC,IAAI,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAC;IAC5B,sCAAsC;IACtC,SAAS,IAAI,SAAS,OAAO,EAAE,CAAC;IAChC,sEAAsE;IACtE,KAAK,IAAI,IAAI,CAAC;IACd,OAAO,IAAI,IAAI,CAAC;CACjB;AA0DD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,qBAAqB,GAAG,cAAc,CAuelG"}
1
+ {"version":3,"file":"control-trainer.d.ts","sourceRoot":"","sources":["../../../src/ui/controller/control-trainer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEzD,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,KAAK,EAAkB,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAK3E,OAAO,EAAgD,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAC9G,OAAO,KAAK,EAAE,WAAW,EAAW,MAAM,YAAY,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAE,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAC/E,IAAI,CAAC,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC9B,QAAQ,CAAC,EAAE;QACT,OAAO,EAAE;YAAE,QAAQ,CAAC,EAAE,QAAQ,CAAA;SAAE,CAAC;QACjC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,EAAE;YAAE,QAAQ,CAAC,EAAE,QAAQ,CAAA;SAAE,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;KACzE,CAAC;CACH;AAED,MAAM,WAAW,WAAW;IAC1B,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,mFAAmF;IACnF,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IAC7B,kEAAkE;IAClE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sCAAsC;IACtC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,qBAAqB;IACpC,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IAC9B,mGAAmG;IACnG,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,mBAAmB,CAAC;IACjC,uGAAuG;IACvG,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACxD,2FAA2F;IAC3F,UAAU,CAAC,IAAI,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAC;IAC5B,sCAAsC;IACtC,SAAS,IAAI,SAAS,OAAO,EAAE,CAAC;IAChC,sEAAsE;IACtE,KAAK,IAAI,IAAI,CAAC;IACd,OAAO,IAAI,IAAI,CAAC;CACjB;AAsDD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,qBAAqB,GAAG,cAAc,CA4dlG"}
@@ -55,15 +55,12 @@
55
55
  */
56
56
  import { createComboStrip } from "./combo-strip.js";
57
57
  import { mountControllerPad } from "./controller-pad.js";
58
- import { DPAD_DIRECTIONS, litPartOfControl } from "./parts-map.js";
58
+ import { heldParts } from "./live-presses.js";
59
+ import { litPartOfControl } from "./parts-map.js";
59
60
  import { STICK_LIT_DEADZONE } from "./pad-svg.js";
60
61
  import { injectPadCss } from "./styles.js";
61
62
  import { isShellControl, parseToken, STICK_DIR_VECTOR } from "./tokens.js";
62
63
  const CHECK = "✓";
63
- /** Is this one of the four directions the d-pad SHARES with the left stick? */
64
- function isDpadName(name) {
65
- return name === "UP" || name === "DOWN" || name === "LEFT" || name === "RIGHT";
66
- }
67
64
  /**
68
65
  * Demonstration timings, in ms.
69
66
  *
@@ -369,23 +366,10 @@ export function mountControlTrainer(host, opts) {
369
366
  // it belongs to has been completed.
370
367
  // Pass 2 — the hardware. EVERY held control goes blue, whether or not the lesson mentions it: the
371
368
  // pad is a picture of the controller, and a press it ignores looks like a fault.
372
- const axis = opts.press?.axis;
373
- const lx = axis?.("LX") ?? 0;
374
- const ly = axis?.("LY") ?? 0;
375
- for (const name of held) {
376
- // UP/DOWN/LEFT/RIGHT come from the d-pad AND from the left stick past its deadzone (see
377
- // protocol/buttons.ts) — nothing in the canonical stream says which. So when the axes already
378
- // explain a direction, DON'T light the d-pad: the stick's own cap is the truthful picture, and
379
- // lighting both makes it look like the rider is pressing a d-pad they never touched.
380
- // `mountControllerPad` has always done this; the "reflect everything" pass added here skipped it.
381
- if (axis && isDpadName(name)) {
382
- const [vx, vy] = DPAD_DIRECTIONS[name];
383
- if (lx * vx + ly * vy > STICK_LIT_DEADZONE)
384
- continue;
385
- }
386
- const part = litPartOfControl(name);
387
- if (part)
388
- wanted.set(part, "on");
369
+ const axis = opts.press?.axis?.bind(opts.press);
370
+ // Blue, not green: in a lesson green is reserved for "this step is finished".
371
+ for (const part of heldParts({ held: held, axis, shellOwned: teachesShellOwned })) {
372
+ wanted.set(part, "on");
389
373
  }
390
374
  // Both sticks, live, for the same reason — and this overrides any demonstration on that side.
391
375
  if (axis) {