@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.
- package/README.md +28 -4
- package/dist/client/PlatformClient.d.ts +14 -7
- package/dist/client/PlatformClient.d.ts.map +1 -1
- package/dist/client/PlatformClient.js.map +1 -1
- package/dist/difficulty/index.d.ts +141 -0
- package/dist/difficulty/index.d.ts.map +1 -0
- package/dist/difficulty/index.js +300 -0
- package/dist/difficulty/index.js.map +1 -0
- package/dist/nav/README.md +1 -1
- package/dist/protocol/version.d.ts +1 -1
- package/dist/protocol/version.js +1 -1
- package/dist/ui/README.md +28 -15
- package/dist/ui/action-diamond.js +1 -1
- package/dist/ui/action-diamond.js.map +1 -1
- package/dist/ui/controller/input-chip.d.ts +2 -1
- package/dist/ui/controller/input-chip.d.ts.map +1 -1
- package/dist/ui/controller/input-chip.js +9 -8
- package/dist/ui/controller/input-chip.js.map +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +1 -1
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/keycap.d.ts +51 -20
- package/dist/ui/keycap.d.ts.map +1 -1
- package/dist/ui/keycap.js +94 -28
- package/dist/ui/keycap.js.map +1 -1
- package/dist/ui/{pause-menu.d.ts → option-menu.d.ts} +34 -24
- package/dist/ui/option-menu.d.ts.map +1 -0
- package/dist/ui/{pause-menu.js → option-menu.js} +134 -90
- package/dist/ui/option-menu.js.map +1 -0
- package/dist/ui/showcase/index.js +9 -9
- package/dist/ui/showcase/index.js.map +1 -1
- package/dist/ui/solo-labeled-diamond.js +1 -1
- package/dist/ui/solo-labeled-diamond.js.map +1 -1
- package/dist/ui/styles.js +14 -9
- package/dist/ui/styles.js.map +1 -1
- package/package.json +5 -1
- package/dist/ui/pause-menu.d.ts.map +0 -1
- package/dist/ui/pause-menu.js.map +0 -1
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
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
|
|
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
|
|
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-
|
|
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.
|
|
35
|
-
*
|
|
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,
|
|
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
|
|
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
|
|
63
|
-
function
|
|
64
|
-
if (
|
|
65
|
-
|
|
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-
|
|
104
|
+
style.id = "rydr-option-css";
|
|
70
105
|
style.textContent = CSS;
|
|
71
106
|
document.head.appendChild(style);
|
|
72
|
-
|
|
107
|
+
optionCssInjected = true;
|
|
73
108
|
}
|
|
74
109
|
/**
|
|
75
|
-
* Mount 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
|
|
113
|
+
export function mountOptionMenu(host, opts) {
|
|
79
114
|
injectCss(); // the keycaps' own .rydr-ui-* styles
|
|
80
|
-
|
|
81
|
-
const { session, openButton = "OPTIONS", resumeLabel = "Resume",
|
|
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-
|
|
132
|
+
root.className = "rydr-option";
|
|
98
133
|
root.tabIndex = -1;
|
|
99
134
|
if (opts.font)
|
|
100
|
-
root.style.setProperty("--rydr-
|
|
135
|
+
root.style.setProperty("--rydr-option-font", opts.font);
|
|
101
136
|
const inner = document.createElement("div");
|
|
102
|
-
inner.className = "rydr-
|
|
137
|
+
inner.className = "rydr-option-inner";
|
|
103
138
|
const titleEl = document.createElement("div");
|
|
104
|
-
titleEl.className = "rydr-
|
|
139
|
+
titleEl.className = "rydr-option-title";
|
|
105
140
|
titleEl.textContent = opts.title;
|
|
106
141
|
const listEl = document.createElement("div");
|
|
107
|
-
listEl.className = "rydr-
|
|
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-
|
|
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-
|
|
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-
|
|
192
|
+
text.className = "rydr-option-text";
|
|
159
193
|
const label = document.createElement("span");
|
|
160
|
-
label.className = "rydr-
|
|
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-
|
|
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
|
-
|
|
171
|
-
|
|
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-
|
|
212
|
+
shortcut.className = "rydr-option-shortcut";
|
|
175
213
|
const tag = document.createElement("span");
|
|
176
|
-
tag.className = "rydr-
|
|
214
|
+
tag.className = "rydr-option-shortcut-tag";
|
|
177
215
|
tag.textContent = "Shortcut";
|
|
178
|
-
const cap =
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
295
|
-
|
|
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-
|
|
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
|
-
//
|
|
376
|
-
//
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
412
|
-
.rydr-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
458
|
+
.rydr-option-list { position: relative; display: flex; flex-direction: column; gap: 12px; }
|
|
423
459
|
|
|
424
|
-
/* a row: frosted glass
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
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-
|
|
436
|
-
.rydr-
|
|
437
|
-
.rydr-
|
|
438
|
-
.rydr-
|
|
439
|
-
|
|
440
|
-
.rydr-
|
|
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-
|
|
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:
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
469
|
-
.rydr-
|
|
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-
|
|
472
|
-
@keyframes rydr-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
494
|
-
.rydr-
|
|
495
|
-
.rydr-
|
|
496
|
-
.rydr-
|
|
497
|
-
@keyframes rydr-
|
|
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=
|
|
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":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAGH,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAgC,MAAM,uBAAuB,CAAC;AACvF,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,GAKnB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAexC;;;;;GAKG;AACH,SAAS,eAAe,CAAC,IAAuB;IAC9C,IAAI,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC,QAAQ,CAAC;IACxC,MAAM,KAAK,GAAG,IAAI,CAAC,OAA2E,CAAC;IAC/F,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,IAAI,iBAAiB,CAAC;AACjE,CAAC;AAiFD,uEAAuE;AACvE,MAAM,UAAU,GAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AAC1E,sGAAsG;AACtG,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAEhC;;;;;;GAMG;AACH,SAAS,cAAc,CAAC,MAAkB,EAAE,KAAmB,EAAE,QAAkB;IACjF,oGAAoG;IACpG,sGAAsG;IACtG,sGAAsG;IACtG,qFAAqF;IACrF,MAAM,IAAI,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAe,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC;IACtG,IAAI,eAAe,CAAC,MAAM,CAAC;QAAE,OAAO,YAAY,CAAC,MAAuB,EAAE,IAAI,CAAC,CAAC;IAChF,IAAI,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,gBAAgB,CAAC,MAAmB,EAAE,IAAI,CAAC,CAAC;IACpF,OAAO,kBAAkB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AAC1C,CAAC;AAED,2EAA2E;AAC3E,MAAM,OAAO,GAAG,GAAG,CAAC;AACpB,MAAM,UAAU,GAAG,EAAE,CAAC;AACtB,gFAAgF;AAChF,MAAM,SAAS,GAAG,GAAG,CAAC;AAEtB,IAAI,iBAAiB,GAAG,KAAK,CAAC;AAE9B,SAAS,eAAe;IACtB,IAAI,iBAAiB,IAAI,QAAQ,CAAC,cAAc,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACpE,iBAAiB,GAAG,IAAI,CAAC;QACzB,OAAO;IACT,CAAC;IACD,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAC9C,KAAK,CAAC,EAAE,GAAG,iBAAiB,CAAC;IAC7B,KAAK,CAAC,WAAW,GAAG,GAAG,CAAC;IACxB,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IACjC,iBAAiB,GAAG,IAAI,CAAC;AAC3B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,IAAiB,EAAE,IAAuB;IACxE,SAAS,EAAE,CAAC,CAAC,qCAAqC;IAClD,eAAe,EAAE,CAAC;IAElB,MAAM,EACJ,OAAO,EACP,UAAU,GAAG,SAAS,EACtB,WAAW,GAAG,QAAQ,EACtB,YAAY,GAAG,IAAI,EACnB,GAAG,GACJ,GAAG,IAAI,CAAC;IACT,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IAEvC,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9C,IAAI,IAAI,GAAG,KAAK,CAAC;IACjB,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,UAAU,GAAyC,IAAI,CAAC;IAC5D,IAAI,YAAY,GAAwB,IAAI,CAAC;IAC7C,IAAI,GAAG,GAAsB,IAAI,CAAC;IAClC,0EAA0E;IAC1E,IAAI,IAAI,GAAkB,EAAE,CAAC;IAC7B,mEAAmE;IACnE,IAAI,QAAQ,GAAqB,EAAE,CAAC;IACpC,sEAAsE;IACtE,IAAI,OAAO,GAAa,EAAE,CAAC;IAE3B,kGAAkG;IAClG,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAC3C,IAAI,CAAC,SAAS,GAAG,aAAa,CAAC;IAC/B,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;IACnB,IAAI,IAAI,CAAC,IAAI;QAAE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,oBAAoB,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACvE,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAC5C,KAAK,CAAC,SAAS,GAAG,mBAAmB,CAAC;IACtC,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAC9C,OAAO,CAAC,SAAS,GAAG,mBAAmB,CAAC;IACxC,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC;IACjC,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAC7C,MAAM,CAAC,SAAS,GAAG,kBAAkB,CAAC;IACtC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC9B,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IACxB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAEvB,mFAAmF;IACnF,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;QACnC,IAAI,CAAC,CAAC,MAAM,KAAK,IAAI;YAAE,KAAK,EAAE,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,kGAAkG;IAClG,kGAAkG;IAClG,mGAAmG;IACnG,2EAA2E;IAC3E,MAAM,YAAY,GAAG,IAAI,GAAG,EAAuD,CAAC;IACpF,MAAM,SAAS,GAAiB;QAC9B,QAAQ,EAAE,CAAC,EAAE,EAAE,EAAE;YACf,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACrB,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACvC,CAAC;KACF,CAAC;IAEF;;;;;;OAMG;IACH,MAAM,UAAU,GAAG,YAAY,CAAC,cAAc,EAAE;QAC9C,OAAO,EAAE,MAAM;QACf,OAAO,EAAE,IAAI;QACb,KAAK,EAAE,SAAS;QAChB,QAAQ;KACT,CAAC,CAAC;IACH,UAAU,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAE/C,kGAAkG;IAClG,SAAS,SAAS;QAChB,KAAK,MAAM,GAAG,IAAI,OAAO;YAAE,GAAG,CAAC,OAAO,EAAE,CAAC;QACzC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,WAAW,GAAG,EAAE,CAAC;QACxB,QAAQ,GAAG,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,GAAG,KAAK,CAAC,CAAC;QACxF,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;YAC9B,MAAM,GAAG,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;YAC7C,GAAG,CAAC,IAAI,GAAG,QAAQ,CAAC;YACpB,GAAG,CAAC,SAAS,GAAG,iBAAiB,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACnE,IAAI,IAAI,CAAC,QAAQ;gBAAE,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC;YACvC,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YACxC,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAE/D,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;YAC5C,IAAI,CAAC,SAAS,GAAG,kBAAkB,CAAC;YACpC,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;YAC7C,KAAK,CAAC,SAAS,GAAG,mBAAmB,CAAC;YACtC,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC;YAC/B,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YACxB,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;gBAC5C,IAAI,CAAC,SAAS,GAAG,kBAAkB,CAAC;gBACpC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC;gBAC7B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YACzB,CAAC;YACD,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YAEtB,8FAA8F;YAC9F,6FAA6F;YAC7F,iGAAiG;YACjG,+FAA+F;YAC/F,8FAA8F;YAC9F,wEAAwE;YACxE,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAClB,MAAM,QAAQ,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;gBAChD,QAAQ,CAAC,SAAS,GAAG,sBAAsB,CAAC;gBAC5C,MAAM,GAAG,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;gBAC3C,GAAG,CAAC,SAAS,GAAG,0BAA0B,CAAC;gBAC3C,GAAG,CAAC,WAAW,GAAG,UAAU,CAAC;gBAC7B,MAAM,GAAG,GAAG,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;gBAC/D,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAClB,IAAI,IAAI,CAAC,QAAQ;oBAAE,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;gBACzC,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;gBAC7B,GAAG,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;YAC5B,CAAC;YAED,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;YAC7C,KAAK,CAAC,SAAS,GAAG,mBAAmB,CAAC;YACtC,+FAA+F;YAC/F,uEAAuE;YACvE,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;YAC5C,IAAI,CAAC,SAAS,GAAG,qBAAqB,CAAC;YACvC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YACxB,GAAG,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YAEvB,GAAG,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YAC/C,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;YACxB,OAAO,GAAG,CAAC;QACb,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACH,SAAS,cAAc;QACrB,MAAM,EAAE,GAAG,MAAM,CAAC,YAAY,GAAG,CAAC,CAAC;QACnC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,EAAE,GAAG,GAAG,CAAC,SAAS,GAAG,GAAG,CAAC,YAAY,GAAG,CAAC,CAAC;YAChD,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;IAED,oGAAoG;IACpG,SAAS,QAAQ;QACf,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QACjD,MAAM,IAAI,GAAG,GAAG,EAAE,aAAa,CAAC,sBAAsB,CAAC,CAAC;QACxD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC;YACvB,OAAO;QACT,CAAC;QACD,IAAI,UAAU,CAAC,EAAE,CAAC,UAAU,KAAK,IAAI;YAAE,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IACzE,CAAC;IAED,kGAAkG;IAClG;;;OAGG;IACH,SAAS,MAAM,CAAC,CAAS;QACvB,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QACzB,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QACnC,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC5B,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;QACpD,GAAG,EAAE,CAAC,aAAa,CAAC,CAAC;QACrB,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,IAAI,CAAC,QAAQ,EAAE,CAAC;YAChB,OAAO;QACT,CAAC;QACD,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,QAAQ,EAAE,CAAC;IAClB,CAAC;IAED,kGAAkG;IAClG,mEAAmE;IACnE,SAAS,YAAY,CAAC,IAAgB;QACpC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,IAAI,IAAI,KAAK,UAAU;gBAAE,OAAO,KAAK,CAAC;YACtC,OAAO,QAAQ,EAAE,CAAC;QACpB,CAAC;QACD,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;YACxB,KAAK,EAAE,CAAC;YACR,OAAO,IAAI,CAAC;QACd,CAAC;QACD,kGAAkG;QAClG,mDAAmD;QACnD,MAAM,aAAa,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,QAAQ,KAAK,IAAI,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC;QACvF,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;YACtB,MAAM,CAAC,aAAa,CAAC,CAAC;YACtB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,qDAAqD;QACxE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,gGAAgG;IAChG,SAAS,MAAM,CAAC,CAAyC;QACvD,KAAK,MAAM,EAAE,IAAI,YAAY;YAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QACrC,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM;YAAE,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC9C,CAAC;IAED,qGAAqG;IACrG,qGAAqG;IACrG,gFAAgF;IAChF,MAAM,UAAU,GAAG,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE;QACzC,IAAI,YAAY;YAAE,OAAO,CAAC,kCAAkC;QAC5D,MAAM,CAAC,CAAC,CAAC,CAAC;IACZ,CAAC,CAAC,CAAC;IAEH,kGAAkG;IAClG,SAAS,QAAQ;QACf,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;QACtB,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;YAAE,OAAO,KAAK,CAAC;QAClD,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;YACxB,YAAY,CAAC,UAAU,CAAC,CAAC;YACzB,UAAU,GAAG,IAAI,CAAC;QACpB,CAAC;QACD,IAAI,GAAG,IAAI,CAAC;QACZ,OAAO,GAAG,KAAK,CAAC;QAChB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACjC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC3B,SAAS,EAAE,CAAC;QACZ,cAAc,EAAE,CAAC;QAEjB,IAAI,YAAY,IAAI,OAAO,EAAE,SAAS;YAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACjF,+FAA+F;QAC/F,kEAAkE;QAElE,4FAA4F;QAC5F,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;QAEf,GAAG,GAAG,gBAAgB,CAAC;YACrB,IAAI,EAAE,MAAM;YACZ,QAAQ,EAAE,kCAAkC;YAC5C,IAAI,EAAE,KAAK,EAAE,2BAA2B;YACxC,IAAI,EAAE,IAAI,EAAE,8DAA8D;YAC1E,OAAO,EAAE,IAAI,EAAE,6CAA6C;YAC5D,GAAG;YACH,UAAU,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC5C,MAAM,EAAE,KAAK;YACb,aAAa,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE;SAChC,CAAC,CAAC;QACH,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,oCAAoC;QACtD,QAAQ,EAAE,CAAC;QACX,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;QAChB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,SAAS,KAAK;QACZ,IAAI,CAAC,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QAC9B,IAAI,UAAU,KAAK,IAAI;YAAE,OAAO,CAAC,kBAAkB;QACnD,IAAI,GAAG,KAAK,CAAC;QACb,OAAO,GAAG,IAAI,CAAC;QACf,+FAA+F;QAC/F,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC9B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAE9B,YAAY,EAAE,EAAE,CAAC;QACjB,YAAY,GAAG,IAAI,CAAC;QACpB,GAAG,EAAE,OAAO,EAAE,CAAC;QACf,GAAG,GAAG,IAAI,CAAC;QACX,kFAAkF;QAClF,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC;QACvB,GAAG,EAAE,CAAC,UAAU,CAAC,CAAC;QAClB,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;QAEjB,UAAU,GAAG,UAAU,CAAC,GAAG,EAAE;YAC3B,UAAU,GAAG,IAAI,CAAC;YAClB,OAAO,GAAG,KAAK,CAAC;YAChB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACjC,MAAM,CAAC,WAAW,GAAG,EAAE,CAAC;YACxB,KAAK,MAAM,GAAG,IAAI,OAAO;gBAAE,GAAG,CAAC,OAAO,EAAE,CAAC;YACzC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,EAAE,CAAC;QACZ,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC,MAAM,GAAG,UAAU,GAAG,GAAG,CAAC,CAAC;IAC/C,CAAC;IAED,OAAO;QACL,IAAI;QACJ,IAAI,EAAE,QAAQ;QACd,KAAK;QACL,MAAM,EAAE,GAAG,EAAE;YACX,IAAI,IAAI,EAAE,CAAC;gBACT,KAAK,EAAE,CAAC;gBACR,OAAO,KAAK,CAAC;YACf,CAAC;YACD,OAAO,QAAQ,EAAE,CAAC;QACpB,CAAC;QACD,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI;QAClB,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE;YACjB,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;YAClB,IAAI,IAAI,EAAE,CAAC;gBACT,SAAS,EAAE,CAAC;gBACZ,cAAc,EAAE,CAAC;gBACjB,GAAG,EAAE,UAAU,EAAE,CAAC;gBAClB,GAAG,EAAE,UAAU,EAAE,CAAC;gBAClB,QAAQ,EAAE,CAAC;YACb,CAAC;QACH,CAAC;QACD,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE;YAClB,OAAO,CAAC,WAAW,GAAG,KAAK,CAAC;QAC9B,CAAC;QACD,YAAY;QACZ,OAAO,EAAE,GAAG,EAAE;YACZ,UAAU,EAAE,EAAE,CAAC;YACf,YAAY,EAAE,EAAE,CAAC;YACjB,YAAY,GAAG,IAAI,CAAC;YACpB,8FAA8F;YAC9F,yDAAyD;YACzD,IAAI,GAAG,KAAK,CAAC;YACb,IAAI,UAAU,KAAK,IAAI;gBAAE,YAAY,CAAC,UAAU,CAAC,CAAC;YAClD,GAAG,EAAE,OAAO,EAAE,CAAC;YACf,GAAG,GAAG,IAAI,CAAC;YACX,KAAK,MAAM,GAAG,IAAI,OAAO;gBAAE,GAAG,CAAC,OAAO,EAAE,CAAC;YACzC,OAAO,GAAG,EAAE,CAAC;YACb,UAAU,CAAC,OAAO,EAAE,CAAC;YACrB,YAAY,CAAC,KAAK,EAAE,CAAC;YACrB,IAAI,CAAC,MAAM,EAAE,CAAC;QAChB,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,GAAG,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sEAwE0D,OAAO;iDAC5B,UAAU;4EACiB,OAAO;0EACT,OAAO;gDACjC,UAAU;gFACsB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BtF,CAAC"}
|
|
@@ -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,
|
|
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
|
|
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
|
|
437
|
-
g.section("
|
|
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
|
-
|
|
443
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
655
|
+
optionMenu?.dispose();
|
|
656
656
|
for (const c of rarityCards)
|
|
657
657
|
c.dispose();
|
|
658
658
|
for (const p of pads)
|