@rydr/game-sdk 7.1.0 → 8.0.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 (39) hide show
  1. package/README.md +28 -4
  2. package/dist/client/PlatformClient.d.ts +14 -7
  3. package/dist/client/PlatformClient.d.ts.map +1 -1
  4. package/dist/client/PlatformClient.js.map +1 -1
  5. package/dist/difficulty/index.d.ts +141 -0
  6. package/dist/difficulty/index.d.ts.map +1 -0
  7. package/dist/difficulty/index.js +300 -0
  8. package/dist/difficulty/index.js.map +1 -0
  9. package/dist/nav/README.md +1 -1
  10. package/dist/protocol/version.d.ts +1 -1
  11. package/dist/protocol/version.js +1 -1
  12. package/dist/ui/README.md +28 -15
  13. package/dist/ui/action-diamond.js +1 -1
  14. package/dist/ui/action-diamond.js.map +1 -1
  15. package/dist/ui/controller/input-chip.d.ts +2 -1
  16. package/dist/ui/controller/input-chip.d.ts.map +1 -1
  17. package/dist/ui/controller/input-chip.js +9 -8
  18. package/dist/ui/controller/input-chip.js.map +1 -1
  19. package/dist/ui/index.d.ts +1 -1
  20. package/dist/ui/index.d.ts.map +1 -1
  21. package/dist/ui/index.js +1 -1
  22. package/dist/ui/index.js.map +1 -1
  23. package/dist/ui/keycap.d.ts +51 -20
  24. package/dist/ui/keycap.d.ts.map +1 -1
  25. package/dist/ui/keycap.js +94 -28
  26. package/dist/ui/keycap.js.map +1 -1
  27. package/dist/ui/{pause-menu.d.ts → option-menu.d.ts} +34 -24
  28. package/dist/ui/option-menu.d.ts.map +1 -0
  29. package/dist/ui/{pause-menu.js → option-menu.js} +134 -90
  30. package/dist/ui/option-menu.js.map +1 -0
  31. package/dist/ui/showcase/index.js +9 -9
  32. package/dist/ui/showcase/index.js.map +1 -1
  33. package/dist/ui/solo-labeled-diamond.js +1 -1
  34. package/dist/ui/solo-labeled-diamond.js.map +1 -1
  35. package/dist/ui/styles.js +14 -9
  36. package/dist/ui/styles.js.map +1 -1
  37. package/package.json +5 -1
  38. package/dist/ui/pause-menu.d.ts.map +0 -1
  39. package/dist/ui/pause-menu.js.map +0 -1
@@ -1,14 +1,14 @@
1
1
  /**
2
- * PAUSE MENU — the in-game menu every RYDR game shares, opened by the game's own `OPTIONS` button.
2
+ * OPTION MENU — the in-game menu every RYDR game shares, opened by the game's own `OPTIONS` button.
3
3
  *
4
4
  * This is the GAME's menu, deliberately distinct from the PLATFORM's overlay (which the shell opens on
5
5
  * `MENU` / `M` / phone MENU and which never reaches a game — see `ButtonName`). The platform overlay
6
6
  * carries the rider's concerns: activity, hardware, volume, exit. This one carries the *game's*:
7
7
  * resume, restart, back to your own menu. It deliberately quotes the platform overlay's visual language
8
- * — dim + blurred backdrop, frosted rows, the staggered "warp" entrance — so pause reads as a RYDR
8
+ * — dim + blurred backdrop, frosted rows, the staggered "warp" entrance — so it reads as a RYDR
9
9
  * surface one level shallower than the platform's, rather than as a second, unrelated menu.
10
10
  *
11
- * const pause = mountPauseMenu(document.body, {
11
+ * const menu = mountOptionMenu(document.body, {
12
12
  * session,
13
13
  * title: "Split Racing",
14
14
  * canOpen: () => phase === "playing", // the game decides WHEN it may open
@@ -23,28 +23,40 @@
23
23
  * `Resume` is prepended for free, so the shortest useful menu is no items at all.
24
24
  *
25
25
  * **Everything the rider reads is in the GAME's font.** The title and every row inherit the host's
26
- * font-family (override with the `font` option or the `--rydr-pause-font` custom property), because
26
+ * font-family (override with the `font` option or the `--rydr-option-font` custom property), because
27
27
  * this menu belongs to the game, not to the shell — the one exception being the keycaps, which keep
28
28
  * their standard RYDR type because they are pictures of the rider's physical controller.
29
29
  *
30
+ * Inheritance being the mechanism, the host page has to actually DECLARE a font: with nothing setting
31
+ * `font-family` up the tree, this renders in the browser default (Times). Every real game sets a body
32
+ * font, so it costs nothing — but a menu that looks like a legal document is that, not a bug here.
33
+ *
30
34
  * **What it does NOT do: stop your game.** No library can — your loop, timers and physics are yours.
31
35
  * The menu tells you when it opens and closes; freeze there. What it *does* guarantee is that a game
32
36
  * which keeps running cannot be *driven* while the rider is in the menu: it takes the controller over
33
37
  * for the duration (`session.grabInput`), so your `onButton` handlers go quiet, `isDown`/`stick` read
34
- * resting, and anything held is released first. It also declares `"menu"` to the shell while it's up,
35
- * so trainer resistance eases like on any other non-racing screen, and restores `"playing"` on resume.
38
+ * resting, and anything held is released first.
39
+ *
40
+ * **It also does NOT touch the trainer.** Opening this menu leaves the activity state exactly as the
41
+ * game left it — a rider who opens it mid-run keeps the resistance they were riding. That is
42
+ * deliberate: this menu is a transient overlay over LIVE gameplay, not a screen the rider navigated
43
+ * to, and they are still on the bike with their legs turning. Dropping to the eased menu resistance
44
+ * and snapping back on `Resume` would change the difficulty of the very run they are deciding
45
+ * whether to restart. (Contrast the PLATFORM overlay, which is the shell's own and does ease.)
46
+ * A game that genuinely wants easing here can still do it itself in `onOpen`/`onClose` —
47
+ * `session.setActivity("menu")` / `("playing")` — which is more flexible than an option anyway.
36
48
  *
37
49
  * A row may advertise a `shortcut` — the button the rider can press *in-game* to do the same thing
38
50
  * (`Restart · Shortcut [Y]`). Binding that shortcut during play stays the game's own job; the menu
39
51
  * only teaches it, and honours it while the menu itself is open (where it owns input anyway).
40
52
  */
41
- import { DEFAULT_GLYPH_SET } from "../protocol/glyphs.js";
53
+ import { DEFAULT_GLYPH_SET, isDiamondButton } from "../protocol/glyphs.js";
42
54
  import { createSpatialNav } from "../nav/spatial-nav.js";
43
- import { createKeycap, createLabelKeycap } from "./keycap.js";
55
+ import { createKeycap, createDpadKeycap, createButtonKeycap, } from "./keycap.js";
44
56
  import { injectCss } from "./styles.js";
45
57
  /**
46
58
  * The rider's controller lettering: an explicit `glyphSet` wins, else the one a real `PlatformSession`
47
- * carries, else Xbox. Probed structurally rather than declared on {@link PauseMenuSession} — same
59
+ * carries, else Xbox. Probed structurally rather than declared on {@link OptionMenuSession} — same
48
60
  * reasoning as `createKeycap`'s own probe: that type is the *minimal* slice the menu needs, and
49
61
  * demanding a hardware snapshot would break every hand-rolled stub for a value that's optional anyway.
50
62
  */
@@ -54,31 +66,54 @@ function resolveGlyphSet(opts) {
54
66
  const probe = opts.session;
55
67
  return probe?.hardware?.current?.glyphSet ?? DEFAULT_GLYPH_SET;
56
68
  }
69
+ /** The d-pad directions, for {@link shortcutKeycap}'s shape choice. */
70
+ const DIRECTIONS = ["UP", "DOWN", "LEFT", "RIGHT"];
71
+ /** Shortcut caps ride at 65% — secondary to the full-size confirm cap. See {@link shortcutKeycap}. */
72
+ const SHORTCUT_CAP_SCALE = 0.65;
73
+ /**
74
+ * A keycap for a row's shortcut, drawn as the REAL button: a face-diamond button gets its lettered pip
75
+ * (`Y` / `△` / `X`), a direction gets its chevron pip, and everything else — the options button, the
76
+ * triggers, the stick clicks — goes to `createButtonKeycap`, which picks the pad's icon pip or a text
77
+ * pill as appropriate. Routing here rather than sending everything to one factory is what keeps a
78
+ * `Restart [Y]` row printing the same key the rest of RYDR prints for that button.
79
+ */
80
+ function shortcutKeycap(button, press, glyphSet) {
81
+ // Mono (the keycap default): a white face with a dark letter, as everywhere else in RYDR. Never the
82
+ // per-button hues — a coloured pip here would be the one loud key on the screen. And SMALL: this is a
83
+ // hint the rider may take, sitting beside the label, so it must not compete with the confirm cap that
84
+ // says what to press right now (`scale`, not a transform — see KeycapOptions.scale).
85
+ const opts = { press, glyphSet, animate: false, variant: "solo", scale: SHORTCUT_CAP_SCALE };
86
+ if (isDiamondButton(button))
87
+ return createKeycap(button, opts);
88
+ if (DIRECTIONS.includes(button))
89
+ return createDpadKeycap(button, opts);
90
+ return createButtonKeycap(button, opts);
91
+ }
57
92
  /** Entrance/exit duration + per-row stagger, mirrored in the CSS below. */
58
93
  const ANIM_MS = 500;
59
94
  const STAGGER_MS = 26;
60
95
  /** Rows warp outward this fraction of their distance from the list's centre. */
61
96
  const WARP_PUSH = 0.9;
62
- let pauseCssInjected = false;
63
- function injectPauseCss() {
64
- if (pauseCssInjected || document.getElementById("rydr-pause-css")) {
65
- pauseCssInjected = true;
97
+ let optionCssInjected = false;
98
+ function injectOptionCss() {
99
+ if (optionCssInjected || document.getElementById("rydr-option-css")) {
100
+ optionCssInjected = true;
66
101
  return;
67
102
  }
68
103
  const style = document.createElement("style");
69
- style.id = "rydr-pause-css";
104
+ style.id = "rydr-option-css";
70
105
  style.textContent = CSS;
71
106
  document.head.appendChild(style);
72
- pauseCssInjected = true;
107
+ optionCssInjected = true;
73
108
  }
74
109
  /**
75
- * Mount the pause menu (hidden until opened). Appended to `host` — normally `document.body`, so the
110
+ * Mount the option menu (hidden until opened). Appended to `host` — normally `document.body`, so the
76
111
  * overlay covers the game whatever the DOM under it looks like.
77
112
  */
78
- export function mountPauseMenu(host, opts) {
113
+ export function mountOptionMenu(host, opts) {
79
114
  injectCss(); // the keycaps' own .rydr-ui-* styles
80
- injectPauseCss();
81
- const { session, openButton = "OPTIONS", resumeLabel = "Resume", activity = true, captureInput = true, cue, } = opts;
115
+ injectOptionCss();
116
+ const { session, openButton = "OPTIONS", resumeLabel = "Resume", captureInput = true, cue, } = opts;
82
117
  const glyphSet = resolveGlyphSet(opts);
83
118
  let items = opts.items ? [...opts.items] : [];
84
119
  let open = false;
@@ -94,17 +129,17 @@ export function mountPauseMenu(host, opts) {
94
129
  let rowCaps = [];
95
130
  // ── DOM ────────────────────────────────────────────────────────────────────────────────────────
96
131
  const root = document.createElement("div");
97
- root.className = "rydr-pause";
132
+ root.className = "rydr-option";
98
133
  root.tabIndex = -1;
99
134
  if (opts.font)
100
- root.style.setProperty("--rydr-pause-font", opts.font);
135
+ root.style.setProperty("--rydr-option-font", opts.font);
101
136
  const inner = document.createElement("div");
102
- inner.className = "rydr-pause-inner";
137
+ inner.className = "rydr-option-inner";
103
138
  const titleEl = document.createElement("div");
104
- titleEl.className = "rydr-pause-title";
139
+ titleEl.className = "rydr-option-title";
105
140
  titleEl.textContent = opts.title;
106
141
  const listEl = document.createElement("div");
107
- listEl.className = "rydr-pause-list";
142
+ listEl.className = "rydr-option-list";
108
143
  inner.append(titleEl, listEl);
109
144
  root.appendChild(inner);
110
145
  host.appendChild(root);
@@ -133,12 +168,11 @@ export function mountPauseMenu(host, opts) {
133
168
  */
134
169
  const confirmCap = createKeycap("DIAMOND_DOWN", {
135
170
  variant: "solo",
136
- colored: false,
137
171
  animate: true,
138
172
  press: capSource,
139
173
  glyphSet,
140
174
  });
141
- confirmCap.el.classList.add("rydr-pause-cap");
175
+ confirmCap.el.classList.add("rydr-option-cap");
142
176
  // ── Rows ───────────────────────────────────────────────────────────────────────────────────────
143
177
  function buildRows() {
144
178
  for (const cap of rowCaps)
@@ -149,43 +183,49 @@ export function mountPauseMenu(host, opts) {
149
183
  rows = rowItems.map((item, i) => {
150
184
  const row = document.createElement("button");
151
185
  row.type = "button";
152
- row.className = "rydr-pause-row" + (item.danger ? " danger" : "");
186
+ row.className = "rydr-option-row" + (item.danger ? " danger" : "");
153
187
  if (item.disabled)
154
188
  row.disabled = true;
155
189
  row.style.setProperty("--i", String(i));
156
190
  row.style.setProperty("--ri", String(rowItems.length - 1 - i));
157
191
  const text = document.createElement("span");
158
- text.className = "rydr-pause-text";
192
+ text.className = "rydr-option-text";
159
193
  const label = document.createElement("span");
160
- label.className = "rydr-pause-label";
194
+ label.className = "rydr-option-label";
161
195
  label.textContent = item.label;
162
196
  text.appendChild(label);
163
197
  if (item.hint) {
164
198
  const hint = document.createElement("span");
165
- hint.className = "rydr-pause-hint";
199
+ hint.className = "rydr-option-hint";
166
200
  hint.textContent = item.hint;
167
201
  text.appendChild(hint);
168
202
  }
169
203
  row.appendChild(text);
170
- const right = document.createElement("span");
171
- right.className = "rydr-pause-right";
204
+ // The shortcut sits INLINE, immediately right of the text — a sibling of it in the row, not a
205
+ // line of its own under the label. Two things fall out of that: a row is one line tall again
206
+ // (the list reads as a list rather than a stack of two-line blocks), and the chip is anchored to
207
+ // the label it belongs to rather than to the row's right edge, where a column of keys detached
208
+ // from their words reads as unrelated hardware. It follows the label's WIDTH, so the chips do
209
+ // not line up down the list — that's the cost of belonging to the text.
172
210
  if (item.shortcut) {
173
211
  const shortcut = document.createElement("span");
174
- shortcut.className = "rydr-pause-shortcut";
212
+ shortcut.className = "rydr-option-shortcut";
175
213
  const tag = document.createElement("span");
176
- tag.className = "rydr-pause-shortcut-tag";
214
+ tag.className = "rydr-option-shortcut-tag";
177
215
  tag.textContent = "Shortcut";
178
- const cap = createLabelKeycap(item.shortcut, { press: capSource, glyphSet });
216
+ const cap = shortcutKeycap(item.shortcut, capSource, glyphSet);
179
217
  rowCaps.push(cap);
180
218
  if (item.disabled)
181
219
  cap.setDisabled(true);
182
220
  shortcut.append(tag, cap.el);
183
- right.appendChild(shortcut);
221
+ row.appendChild(shortcut);
184
222
  }
223
+ const right = document.createElement("span");
224
+ right.className = "rydr-option-right";
185
225
  // The slot the confirm keycap moves into when this row takes focus. Always present (and always
186
226
  // the same width) so focus moving between rows never reflows the list.
187
227
  const slot = document.createElement("span");
188
- slot.className = "rydr-pause-confirm";
228
+ slot.className = "rydr-option-confirm";
189
229
  right.appendChild(slot);
190
230
  row.appendChild(right);
191
231
  row.addEventListener("click", () => select(i));
@@ -209,7 +249,7 @@ export function mountPauseMenu(host, opts) {
209
249
  /** Move the confirm keycap into the focused row's slot (out of the DOM when nothing is focused). */
210
250
  function placeCap() {
211
251
  const row = open ? nav?.current() ?? null : null;
212
- const slot = row?.querySelector(".rydr-pause-confirm");
252
+ const slot = row?.querySelector(".rydr-option-confirm");
213
253
  if (!slot) {
214
254
  confirmCap.el.remove();
215
255
  return;
@@ -291,13 +331,13 @@ export function mountPauseMenu(host, opts) {
291
331
  computeOffsets();
292
332
  if (captureInput && session?.grabInput)
293
333
  releaseInput = session.grabInput(onEdge);
294
- if (activity)
295
- session?.setActivity?.("menu");
334
+ // Note what is NOT here: an activity change. The rider keeps the resistance they were riding —
335
+ // see the header. Input is taken over; the trainer is left alone.
296
336
  // Pull keyboard focus onto the overlay so a game that also handles DOM focus can't keep it.
297
337
  root.focus?.();
298
338
  nav = createSpatialNav({
299
339
  root: listEl,
300
- selector: ".rydr-pause-row:not([disabled])",
340
+ selector: ".rydr-option-row:not([disabled])",
301
341
  ring: false, // the ring is styled below
302
342
  wrap: true, // a single-axis list: DOWN past the last returns to the first
303
343
  pointer: true, // hover and controller share one focus state
@@ -323,8 +363,6 @@ export function mountPauseMenu(host, opts) {
323
363
  root.classList.add("closing");
324
364
  releaseInput?.();
325
365
  releaseInput = null;
326
- if (activity)
327
- session?.setActivity?.("playing");
328
366
  nav?.dispose();
329
367
  nav = null;
330
368
  // The cap leaves with the focus ring, but the rows stay mounted for the warp-out.
@@ -372,10 +410,8 @@ export function mountPauseMenu(host, opts) {
372
410
  offSession?.();
373
411
  releaseInput?.();
374
412
  releaseInput = null;
375
- // Torn down mid-open (a game unmounting its screen): hand the trainer back to gameplay, or the
376
- // rider would keep riding the menu's eased resistance with no menu on screen.
377
- if (open && activity)
378
- session?.setActivity?.("playing");
413
+ // Nothing to restore on the trainer: opening never changed it. Releasing the input grab above
414
+ // is the whole of the cleanup a mid-open teardown needs.
379
415
  open = false;
380
416
  if (closeTimer !== null)
381
417
  clearTimeout(closeTimer);
@@ -391,110 +427,118 @@ export function mountPauseMenu(host, opts) {
391
427
  };
392
428
  }
393
429
  /**
394
- * The overlay's own `.rydr-pause-*` CSS — self-contained, injected once, no global styles or CSS
430
+ * The overlay's own `.rydr-option-*` CSS — self-contained, injected once, no global styles or CSS
395
431
  * variables required. Deliberately quotes the platform menu's numbers (the same dim, blur, radii and
396
432
  * warp curve) so the two surfaces read as one family.
397
433
  *
398
- * `font-family: var(--rydr-pause-font, inherit)` on every text rule is load-bearing: the menu must
434
+ * `font-family: var(--rydr-option-font, inherit)` on every text rule is load-bearing: the menu must
399
435
  * render in the GAME's font, which it gets by inheriting from the host. The keycaps are the exception —
400
436
  * they carry their own type from `styles.ts`, because they depict the rider's physical controller.
401
437
  */
402
438
  const CSS = `
403
- .rydr-pause { position: fixed; inset: 0; z-index: 1400; display: flex; justify-content: center;
439
+ .rydr-option { position: fixed; inset: 0; z-index: 1400; display: flex; justify-content: center;
404
440
  overflow-x: hidden; overflow-y: auto; padding: clamp(40px, 7vh, 88px) 24px 96px;
405
- font-family: var(--rydr-pause-font, inherit);
441
+ font-family: var(--rydr-option-font, inherit);
406
442
  background: rgba(2, 6, 23, calc(0.82 * var(--bd, 0)));
407
443
  -webkit-backdrop-filter: blur(calc(14px * var(--bd, 0)));
408
444
  backdrop-filter: blur(calc(14px * var(--bd, 0)));
409
445
  opacity: 0; pointer-events: none; visibility: hidden;
410
446
  transition: background 0.45s ease, backdrop-filter 0.45s ease, opacity 0.35s ease; }
411
- .rydr-pause:focus { outline: none; }
412
- .rydr-pause.open { opacity: 1; pointer-events: auto; visibility: visible; --bd: 1; }
447
+ .rydr-option:focus { outline: none; }
448
+ .rydr-option.open { opacity: 1; pointer-events: auto; visibility: visible; --bd: 1; }
413
449
  /* closing: stay visible so the rows can warp back out — only the backdrop fades */
414
- .rydr-pause.closing { opacity: 1; visibility: visible; pointer-events: none; --bd: 0;
450
+ .rydr-option.closing { opacity: 1; visibility: visible; pointer-events: none; --bd: 0;
415
451
  transition: background 0.45s ease, backdrop-filter 0.45s ease; }
416
452
 
417
- .rydr-pause-inner { width: 100%; max-width: 620px; margin: auto 0; display: flex; flex-direction: column; gap: 26px; }
453
+ .rydr-option-inner { width: 100%; max-width: 620px; margin: auto 0; display: flex; flex-direction: column; gap: 26px; }
418
454
  /* the game's name, in the game's font — the biggest thing on the screen */
419
- .rydr-pause-title { font-family: var(--rydr-pause-font, inherit); text-align: center;
455
+ .rydr-option-title { font-family: var(--rydr-option-font, inherit); text-align: center;
420
456
  font-size: clamp(2.4rem, 8vw, 3.4rem); font-weight: 800; letter-spacing: -0.03em; line-height: 1;
421
457
  color: #f2f5fb; text-shadow: 0 6px 30px rgba(0, 0, 0, 0.6); }
422
- .rydr-pause-list { position: relative; display: flex; flex-direction: column; gap: 12px; }
458
+ .rydr-option-list { position: relative; display: flex; flex-direction: column; gap: 12px; }
423
459
 
424
- /* a row: frosted glass, label left, shortcut right */
425
- .rydr-pause-row { appearance: none; cursor: pointer; width: 100%; box-sizing: border-box;
426
- display: flex; align-items: center; justify-content: space-between; gap: 18px;
427
- min-height: 74px; padding: 0.9rem 1.1rem 0.9rem 1.4rem; border-radius: 18px;
428
- font-family: var(--rydr-pause-font, inherit); color: #f2f5fb; text-align: left;
460
+ /* a row: frosted glass; the label (with the hint under it) then the shortcut chip beside it, and the
461
+ confirm cap's slot pinned to the right.
462
+ Packed from the LEFT (not space-between) so the shortcut lands against the text it belongs to; the
463
+ slot is pushed to the far edge by its own auto margin instead. */
464
+ .rydr-option-row { appearance: none; cursor: pointer; width: 100%; box-sizing: border-box;
465
+ display: flex; align-items: center; justify-content: flex-start; gap: 18px;
466
+ min-height: 86px; padding: 0.9rem 1.1rem 0.9rem 1.4rem; border-radius: 18px;
467
+ font-family: var(--rydr-option-font, inherit); color: #f2f5fb; text-align: left;
429
468
  background: rgba(255, 255, 255, 0.06); border: 1px solid rgba(255, 255, 255, 0.14);
430
469
  -webkit-backdrop-filter: blur(20px) saturate(120%);
431
470
  backdrop-filter: blur(20px) saturate(120%);
432
471
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.28), inset 0 1px 0 rgba(255, 255, 255, 0.08);
433
472
  transition: transform 0.14s ease, border-color 0.14s ease, box-shadow 0.14s ease, background 0.14s ease;
434
473
  will-change: transform, opacity, filter; }
435
- .rydr-pause-row:hover:not([disabled]) { background: rgba(255, 255, 255, 0.1); border-color: rgba(255, 255, 255, 0.3); }
436
- .rydr-pause-row:focus-visible { outline: none; }
437
- .rydr-pause-row[disabled] { cursor: default; opacity: 0.45; }
438
- .rydr-pause-row.danger .rydr-pause-label { color: #ff8b8b; }
439
- .rydr-pause-text { display: flex; flex-direction: column; gap: 0.2rem; min-width: 0; }
440
- .rydr-pause-label { font-family: var(--rydr-pause-font, inherit); font-weight: 700; font-size: 1.6rem;
474
+ .rydr-option-row:hover:not([disabled]) { background: rgba(255, 255, 255, 0.1); border-color: rgba(255, 255, 255, 0.3); }
475
+ .rydr-option-row:focus-visible { outline: none; }
476
+ .rydr-option-row[disabled] { cursor: default; opacity: 0.45; }
477
+ .rydr-option-row.danger .rydr-option-label { color: #ff8b8b; }
478
+ /* the left column: the label, with the hint under it (the shortcut is a sibling of this, inline) */
479
+ .rydr-option-text { display: flex; flex-direction: column; align-items: flex-start; gap: 0.35rem; min-width: 0; }
480
+ .rydr-option-label { font-family: var(--rydr-option-font, inherit); font-weight: 700; font-size: 1.6rem;
441
481
  letter-spacing: 0.01em; line-height: 1.1; overflow-wrap: anywhere; }
442
- .rydr-pause-hint { font-family: var(--rydr-pause-font, inherit); font-size: 0.95rem; font-weight: 500;
482
+ .rydr-option-hint { font-family: var(--rydr-option-font, inherit); font-size: 0.95rem; font-weight: 500;
443
483
  color: rgba(238, 244, 255, 0.62); line-height: 1.25; }
444
- /* right side: the "Shortcut" tag + its keycap, then the slot the confirm cap rides into */
445
- .rydr-pause-right { display: flex; align-items: center; gap: 18px; flex: 0 0 auto; }
446
- .rydr-pause-shortcut { display: flex; align-items: center; gap: 9px; flex: 0 0 auto; }
447
- .rydr-pause-shortcut-tag { font-family: var(--rydr-pause-font, inherit); font-size: 0.7rem; font-weight: 700;
484
+ /* right side: only the slot the confirm cap rides into. The auto left margin is what pins it to the far
485
+ edge now that the row packs from the left it eats all the free space, so nothing before it moves.
486
+ (No backticks in here: this comment sits inside a template literal, where one would end the literal
487
+ and break every rule after it.) */
488
+ .rydr-option-right { display: flex; align-items: center; flex: 0 0 auto; margin-left: auto; }
489
+ /* the shortcut chip, inline beside the text: the "SHORTCUT" tag + that button's own keycap */
490
+ .rydr-option-shortcut { display: flex; align-items: center; gap: 9px; flex: 0 0 auto; }
491
+ .rydr-option-shortcut-tag { font-family: var(--rydr-option-font, inherit); font-size: 0.7rem; font-weight: 700;
448
492
  letter-spacing: 0.14em; text-transform: uppercase; color: rgba(238, 244, 255, 0.5); }
449
493
  /* the slot is always there, at a FIXED width (one solo keycap), so focus moving row to row can never
450
494
  reflow the list — only the cap inside it appears and disappears */
451
- .rydr-pause-confirm { flex: 0 0 38px; height: 38px; display: flex; align-items: center; justify-content: center; }
495
+ .rydr-option-confirm { flex: 0 0 38px; height: 38px; display: flex; align-items: center; justify-content: center; }
452
496
 
453
497
  /* roving controller focus: white ring + halo, as on the platform overlay */
454
- .rydr-pause-row[nav-focused] { outline: none; transform: scale(1.02);
498
+ .rydr-option-row[nav-focused] { outline: none; transform: scale(1.02);
455
499
  border-color: rgba(255, 255, 255, 0.9);
456
500
  box-shadow: 0 0 0 3px #ffffff, 0 0 22px 0 rgba(255, 255, 255, 0.16), 0 22px 50px rgba(0, 0, 0, 0.55);
457
501
  z-index: 2; }
458
502
 
459
503
  /* the confirm keycap, sitting in the focused row's slot */
460
- .rydr-pause-cap { pointer-events: none;
504
+ .rydr-option-cap { pointer-events: none;
461
505
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.7)) drop-shadow(0 8px 16px rgba(0, 0, 0, 0.5)); }
462
506
 
463
507
  /* ── entrance / exit: rows warp in from their own side of the list, staggered ──
464
508
  fill: backwards (not both) so the resting frame hands control back to plain CSS and the
465
509
  [nav-focused] scale can take over without a flash. */
466
- .rydr-pause.open .rydr-pause-row { animation: rydr-pause-warp-in ${ANIM_MS}ms cubic-bezier(0.1, 0.8, 0.4, 1) backwards;
510
+ .rydr-option.open .rydr-option-row { animation: rydr-option-warp-in ${ANIM_MS}ms cubic-bezier(0.1, 0.8, 0.4, 1) backwards;
467
511
  animation-delay: calc(var(--ri, 0) * ${STAGGER_MS}ms); }
468
- .rydr-pause.open .rydr-pause-title { animation: rydr-pause-warp-in-top ${ANIM_MS}ms cubic-bezier(0.1, 0.8, 0.4, 1) backwards; }
469
- .rydr-pause.closing .rydr-pause-row { animation: rydr-pause-warp-out ${ANIM_MS}ms cubic-bezier(0.6, 0, 0.9, 0.2) both;
512
+ .rydr-option.open .rydr-option-title { animation: rydr-option-warp-in-top ${ANIM_MS}ms cubic-bezier(0.1, 0.8, 0.4, 1) backwards; }
513
+ .rydr-option.closing .rydr-option-row { animation: rydr-option-warp-out ${ANIM_MS}ms cubic-bezier(0.6, 0, 0.9, 0.2) both;
470
514
  animation-delay: calc(var(--i, 0) * ${STAGGER_MS}ms); }
471
- .rydr-pause.closing .rydr-pause-title { animation: rydr-pause-warp-out-top ${ANIM_MS}ms cubic-bezier(0.6, 0, 0.9, 0.2) both; }
472
- @keyframes rydr-pause-warp-in {
515
+ .rydr-option.closing .rydr-option-title { animation: rydr-option-warp-out-top ${ANIM_MS}ms cubic-bezier(0.6, 0, 0.9, 0.2) both; }
516
+ @keyframes rydr-option-warp-in {
473
517
  0% { opacity: 0; transform: translateY(calc(var(--dy, 0px) * 1.5)) scale(2.4); filter: blur(6px); }
474
518
  55% { opacity: 1; filter: blur(0); }
475
519
  100% { opacity: 1; transform: translateY(0) scale(1); filter: blur(0); }
476
520
  }
477
- @keyframes rydr-pause-warp-out {
521
+ @keyframes rydr-option-warp-out {
478
522
  0% { opacity: 1; transform: translateY(0) scale(1); filter: blur(0); }
479
523
  45% { opacity: 1; filter: blur(0); }
480
524
  100% { opacity: 0; transform: translateY(calc(var(--dy, 0px) * 1.5)) scale(2.4); filter: blur(6px); }
481
525
  }
482
- @keyframes rydr-pause-warp-in-top {
526
+ @keyframes rydr-option-warp-in-top {
483
527
  0% { opacity: 0; transform: translateY(-70px) scale(1.9); filter: blur(6px); }
484
528
  55% { opacity: 1; filter: blur(0); }
485
529
  100% { opacity: 1; transform: translateY(0) scale(1); filter: blur(0); }
486
530
  }
487
- @keyframes rydr-pause-warp-out-top {
531
+ @keyframes rydr-option-warp-out-top {
488
532
  0% { opacity: 1; transform: translateY(0) scale(1); filter: blur(0); }
489
533
  45% { opacity: 1; filter: blur(0); }
490
534
  100% { opacity: 0; transform: translateY(-70px) scale(1.9); filter: blur(6px); }
491
535
  }
492
536
  @media (prefers-reduced-motion: reduce) {
493
- .rydr-pause.open .rydr-pause-row,
494
- .rydr-pause.open .rydr-pause-title { animation: rydr-pause-fade 0.3s ease both !important; }
495
- .rydr-pause.closing .rydr-pause-row,
496
- .rydr-pause.closing .rydr-pause-title { animation: rydr-pause-fade 0.2s ease reverse both !important; }
497
- @keyframes rydr-pause-fade { from { opacity: 0; } to { opacity: 1; } }
537
+ .rydr-option.open .rydr-option-row,
538
+ .rydr-option.open .rydr-option-title { animation: rydr-option-fade 0.3s ease both !important; }
539
+ .rydr-option.closing .rydr-option-row,
540
+ .rydr-option.closing .rydr-option-title { animation: rydr-option-fade 0.2s ease reverse both !important; }
541
+ @keyframes rydr-option-fade { from { opacity: 0; } to { opacity: 1; } }
498
542
  }
499
543
  `;
500
- //# sourceMappingURL=pause-menu.js.map
544
+ //# sourceMappingURL=option-menu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"option-menu.js","sourceRoot":"","sources":["../../src/ui/option-menu.ts"],"names":[],"mappings":"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"}
@@ -12,7 +12,7 @@
12
12
  * components never pulls the catalog into its bundle. Pass a real `session` for live watts + real
13
13
  * button reflection; omit it and the catalog renders static.
14
14
  */
15
- import { createKeycap, createDpadKeycap, mountCard, mountActionCard, mountActionDiamond, mountDialogueCard, mountChoiceCard, mountLabeledDiamond, mountSoloLabeledDiamond, mountRarityCard, mountPauseMenu, createPowerRace, mountControllerPad, mountControllerMap, mountComboDemo, mountControlTrainer, createInputChip, RARITY, } from "../index.js";
15
+ import { createKeycap, createDpadKeycap, mountCard, mountActionCard, mountActionDiamond, mountDialogueCard, mountChoiceCard, mountLabeledDiamond, mountSoloLabeledDiamond, mountRarityCard, mountOptionMenu, createPowerRace, mountControllerPad, mountControllerMap, mountComboDemo, mountControlTrainer, createInputChip, RARITY, } from "../index.js";
16
16
  import { Gallery, el, makeCanvas } from "./gallery.js";
17
17
  const FACES = ["DIAMOND_DOWN", "DIAMOND_LEFT", "DIAMOND_UP", "DIAMOND_RIGHT"];
18
18
  const DIRS = ["UP", "RIGHT", "DOWN", "LEFT"];
@@ -88,7 +88,7 @@ export function mountUiShowcase(host, opts = {}) {
88
88
  const ftp = session?.identity.ftp ?? 250;
89
89
  let race;
90
90
  let raceHideT = 0;
91
- let pause;
91
+ let optionMenu;
92
92
  const pads = [];
93
93
  const stickChips = [];
94
94
  let map;
@@ -433,14 +433,14 @@ export function mountUiShowcase(host, opts = {}) {
433
433
  text: { heading: "RÉSONANCE INSTABLE", prompt: "PÉDALE POUR CHARGER", deadlineLabel: "INSTABILITÉ", chargeLabel: "CHARGE", win: "CHARGÉE", lose: "TROP TARD" },
434
434
  });
435
435
  }
436
- // --- the in-game pause menu -------------------------------------------
437
- g.section("Pause menu (overlay)", "The GAME's own menu, opened in-game by OPTIONS (keyboard O, phone OPT, the pad's Menu/Options/+ button) — Resume is free, the game adds the rest. ▲▼ move, A picks, OPTIONS or B resumes, and a row can advertise the shortcut that does the same thing during play. Every word is in the GAME's font (inherited from the page the second button forces one instead). With a real session it takes the controller over while it's up: the button monitor above goes quiet until you resume.");
436
+ // --- the in-game option menu -------------------------------------------
437
+ g.section("Option menu (overlay)", "The GAME's own menu, opened in-game by OPTIONS (keyboard O, phone OPT, the pad's Menu/Options/+ button) — Resume is free, the game adds the rest. ▲▼ move, A picks, OPTIONS or B resumes, and a row can advertise the shortcut that does the same thing during play, shown inline beside its label. Every word is in the HOST's font inherited, so in a game it's the game's own face and here it's this page's; the second button forces one instead. With a real session it takes the controller over while it's up: the button monitor above goes quiet until you resume.");
438
438
  {
439
439
  const row = el(g.list, "div", "bs-btnrow");
440
440
  const status = el(g.list, "div", "bs-cap", " ");
441
441
  const launch = (title, items, font) => {
442
- pause?.dispose();
443
- pause = mountPauseMenu(document.body, {
442
+ optionMenu?.dispose();
443
+ optionMenu = mountOptionMenu(document.body, {
444
444
  session,
445
445
  title,
446
446
  items,
@@ -448,14 +448,14 @@ export function mountUiShowcase(host, opts = {}) {
448
448
  onOpen: () => { status.textContent = "Paused — the game would freeze its loop here."; },
449
449
  onClose: () => { status.textContent = "Resumed."; },
450
450
  });
451
- pause.open();
451
+ optionMenu.open();
452
452
  };
453
453
  const pick = (what) => () => { status.textContent = `Picked: ${what}`; };
454
454
  const mk = (label, run) => {
455
455
  const b = el(row, "button", "bs-btn", label);
456
456
  b.addEventListener("click", run);
457
457
  };
458
- mk("Open pause menu", () => launch("Split Racing", [
458
+ mk("Open option menu", () => launch("Split Racing", [
459
459
  { label: "Restart", shortcut: "DIAMOND_UP", onSelect: pick("Restart") },
460
460
  { label: "Back to menu", onSelect: pick("Back to menu") },
461
461
  ]));
@@ -652,7 +652,7 @@ export function mountUiShowcase(host, opts = {}) {
652
652
  choice?.dispose();
653
653
  dialogue?.dispose();
654
654
  race?.dispose();
655
- pause?.dispose();
655
+ optionMenu?.dispose();
656
656
  for (const c of rarityCards)
657
657
  c.dispose();
658
658
  for (const p of pads)