@rydr/game-sdk 3.0.0 → 3.1.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 (57) hide show
  1. package/README.md +72 -0
  2. package/dist/client/PlatformClient.d.ts +10 -1
  3. package/dist/client/PlatformClient.d.ts.map +1 -1
  4. package/dist/client/PlatformClient.js +76 -5
  5. package/dist/client/PlatformClient.js.map +1 -1
  6. package/dist/protocol/buttons.d.ts +30 -0
  7. package/dist/protocol/buttons.d.ts.map +1 -1
  8. package/dist/protocol/buttons.js +33 -1
  9. package/dist/protocol/buttons.js.map +1 -1
  10. package/dist/protocol/messages.d.ts.map +1 -1
  11. package/dist/protocol/version.d.ts +1 -1
  12. package/dist/protocol/version.js +1 -1
  13. package/dist/ui/action-card.d.ts +39 -0
  14. package/dist/ui/action-card.d.ts.map +1 -0
  15. package/dist/ui/action-card.js +38 -0
  16. package/dist/ui/action-card.js.map +1 -0
  17. package/dist/ui/action-diamond.d.ts +41 -0
  18. package/dist/ui/action-diamond.d.ts.map +1 -0
  19. package/dist/ui/action-diamond.js +35 -0
  20. package/dist/ui/action-diamond.js.map +1 -0
  21. package/dist/ui/card.d.ts +45 -0
  22. package/dist/ui/card.d.ts.map +1 -0
  23. package/dist/ui/card.js +73 -0
  24. package/dist/ui/card.js.map +1 -0
  25. package/dist/ui/choice-card.d.ts +73 -0
  26. package/dist/ui/choice-card.d.ts.map +1 -0
  27. package/dist/ui/choice-card.js +141 -0
  28. package/dist/ui/choice-card.js.map +1 -0
  29. package/dist/ui/dialogue-card.d.ts +54 -0
  30. package/dist/ui/dialogue-card.d.ts.map +1 -0
  31. package/dist/ui/dialogue-card.js +107 -0
  32. package/dist/ui/dialogue-card.js.map +1 -0
  33. package/dist/ui/index.d.ts +31 -0
  34. package/dist/ui/index.d.ts.map +1 -0
  35. package/dist/ui/index.js +31 -0
  36. package/dist/ui/index.js.map +1 -0
  37. package/dist/ui/keycap.d.ts +77 -0
  38. package/dist/ui/keycap.d.ts.map +1 -0
  39. package/dist/ui/keycap.js +160 -0
  40. package/dist/ui/keycap.js.map +1 -0
  41. package/dist/ui/labeled-diamond.d.ts +32 -0
  42. package/dist/ui/labeled-diamond.d.ts.map +1 -0
  43. package/dist/ui/labeled-diamond.js +86 -0
  44. package/dist/ui/labeled-diamond.js.map +1 -0
  45. package/dist/ui/showcase/gallery.d.ts +56 -0
  46. package/dist/ui/showcase/gallery.d.ts.map +1 -0
  47. package/dist/ui/showcase/gallery.js +85 -0
  48. package/dist/ui/showcase/gallery.js.map +1 -0
  49. package/dist/ui/showcase/index.d.ts +29 -0
  50. package/dist/ui/showcase/index.d.ts.map +1 -0
  51. package/dist/ui/showcase/index.js +255 -0
  52. package/dist/ui/showcase/index.js.map +1 -0
  53. package/dist/ui/styles.d.ts +12 -0
  54. package/dist/ui/styles.d.ts.map +1 -0
  55. package/dist/ui/styles.js +183 -0
  56. package/dist/ui/styles.js.map +1 -0
  57. package/package.json +13 -2
@@ -0,0 +1,31 @@
1
+ /**
2
+ * `@rydr/game-sdk/ui` — the reusable controller-button UI: a DOM/CSS keycap of the A/B/Y/Z face
3
+ * buttons and the UP/DOWN/LEFT/RIGHT d-pad, plus higher-level pieces built from it (a dark glass card
4
+ * with a content slot, an action card, the full A/B/Y/Z move diamond, an NPC dialogue card, a player
5
+ * choice menu, and a labeled diamond). Games use it to show "which button to press" in one consistent
6
+ * visual language across the library.
7
+ *
8
+ * Self-contained, like `@rydr/game-sdk/three`'s perf-overlay: it injects its own scoped `.rydr-ui-*`
9
+ * CSS once into `<head>`, depends on no global styles or CSS variables, and pulls in no three.js — so
10
+ * a game gets the look just by constructing a component. Wire any keycap to real input by passing the
11
+ * `session` (it satisfies the structural {@link ButtonSource}); the keycap then reflects real presses.
12
+ *
13
+ * ──────────────────────────────────────────────────────────────────────────────────────────────
14
+ * **Do NOT restyle the components — their type sizes and dimensions are deliberate.** Every RYDR
15
+ * game is played on a **trainer screen viewed from a few feet away while pedaling**, so the keycaps,
16
+ * card labels, dialogue/choice type, and diamond spacing are tuned to stay legible at that distance.
17
+ * Shrinking a label "to fit", overriding the `.rydr-ui-*` font sizes / paddings / widths, or wrapping
18
+ * a component in a `transform: scale()` breaks that contract and makes the UI unreadable on a real
19
+ * setup. Fill the **content slot** (`mountCard`/`mountActionCard` take any DOM, the diamond takes a
20
+ * label or custom node) and use the provided state methods — but treat the frame's sizing as fixed.
21
+ * If a label doesn't fit, shorten the text, don't shrink the type.
22
+ * ──────────────────────────────────────────────────────────────────────────────────────────────
23
+ */
24
+ export * from "./keycap.js";
25
+ export * from "./card.js";
26
+ export * from "./action-card.js";
27
+ export * from "./action-diamond.js";
28
+ export * from "./dialogue-card.js";
29
+ export * from "./choice-card.js";
30
+ export * from "./labeled-diamond.js";
31
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * `@rydr/game-sdk/ui` — the reusable controller-button UI: a DOM/CSS keycap of the A/B/Y/Z face
3
+ * buttons and the UP/DOWN/LEFT/RIGHT d-pad, plus higher-level pieces built from it (a dark glass card
4
+ * with a content slot, an action card, the full A/B/Y/Z move diamond, an NPC dialogue card, a player
5
+ * choice menu, and a labeled diamond). Games use it to show "which button to press" in one consistent
6
+ * visual language across the library.
7
+ *
8
+ * Self-contained, like `@rydr/game-sdk/three`'s perf-overlay: it injects its own scoped `.rydr-ui-*`
9
+ * CSS once into `<head>`, depends on no global styles or CSS variables, and pulls in no three.js — so
10
+ * a game gets the look just by constructing a component. Wire any keycap to real input by passing the
11
+ * `session` (it satisfies the structural {@link ButtonSource}); the keycap then reflects real presses.
12
+ *
13
+ * ──────────────────────────────────────────────────────────────────────────────────────────────
14
+ * **Do NOT restyle the components — their type sizes and dimensions are deliberate.** Every RYDR
15
+ * game is played on a **trainer screen viewed from a few feet away while pedaling**, so the keycaps,
16
+ * card labels, dialogue/choice type, and diamond spacing are tuned to stay legible at that distance.
17
+ * Shrinking a label "to fit", overriding the `.rydr-ui-*` font sizes / paddings / widths, or wrapping
18
+ * a component in a `transform: scale()` breaks that contract and makes the UI unreadable on a real
19
+ * setup. Fill the **content slot** (`mountCard`/`mountActionCard` take any DOM, the diamond takes a
20
+ * label or custom node) and use the provided state methods — but treat the frame's sizing as fixed.
21
+ * If a label doesn't fit, shorten the text, don't shrink the type.
22
+ * ──────────────────────────────────────────────────────────────────────────────────────────────
23
+ */
24
+ export * from "./keycap.js";
25
+ export * from "./card.js";
26
+ export * from "./action-card.js";
27
+ export * from "./action-diamond.js";
28
+ export * from "./dialogue-card.js";
29
+ export * from "./choice-card.js";
30
+ export * from "./labeled-diamond.js";
31
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * KEYCAP — a DOM/CSS keycap of the controller face buttons (zero dependencies), so a game can show
3
+ * "which button to press" in a consistent visual language. Two shapes:
4
+ *
5
+ * • "full" — the classic diamond: the active button enlarged + coloured, the other three small + grey.
6
+ * • "solo" — light variation: JUST the active button (the idle siblings removed).
7
+ *
8
+ * The keycap is a small STATE MACHINE: it can pulse (the bouncing attract animation), show a pressed
9
+ * sink, run a depleting COOLDOWN ring + countdown, render colored or neutral ("mono"), and hide.
10
+ *
11
+ * Two button families share that machine: `createKeycap` for the A/B/Y/Z face buttons (a letter on
12
+ * the active pip) and `createDpadKeycap` for the UP/DOWN/LEFT/RIGHT directional buttons (a white
13
+ * chevron on a steel-grey pip, rotated per direction). They differ ONLY in the face drawn and the
14
+ * button name the press-wiring listens for; everything else is identical.
15
+ */
16
+ import type { ButtonName, ButtonEdge } from "../protocol/index.js";
17
+ /** The four face buttons (a subset of the canonical {@link ButtonName} vocabulary). */
18
+ export type ButtonLetter = "A" | "B" | "Y" | "Z";
19
+ /** The four directional buttons (the canonical d-pad {@link ButtonName}s). */
20
+ export type DirButton = "UP" | "DOWN" | "LEFT" | "RIGHT";
21
+ /** "full" = the 4-button diamond; "solo" = just the active button. */
22
+ export type KeycapVariant = "full" | "solo";
23
+ /** @deprecated alias kept for back-compat. */
24
+ export type DiamondVariant = KeycapVariant;
25
+ /**
26
+ * A controller button-event source — the minimal slice of the platform `session` the keycap needs to
27
+ * auto-reflect real presses. A `PlatformSession` satisfies it structurally (you just pass `session`).
28
+ */
29
+ export interface ButtonSource {
30
+ onButton(cb: (e: {
31
+ name: ButtonName;
32
+ edge: ButtonEdge;
33
+ }) => void): () => void;
34
+ }
35
+ export interface KeycapOptions {
36
+ /** "solo" (default) shows only the active button; "full" shows the 4-button diamond. */
37
+ variant?: KeycapVariant;
38
+ /** Per-button hue (default true). `false` → a neutral palette (full colour can be too loud). */
39
+ colored?: boolean;
40
+ /** Run the bouncing pulse on the active button (default true). */
41
+ animate?: boolean;
42
+ /**
43
+ * Wire the keycap to real controller input: pass `session` (or anything with `onButton`) and the
44
+ * keycap shows its pressed sink while ITS button is held (down → pressed, up → released).
45
+ */
46
+ press?: ButtonSource;
47
+ }
48
+ /** A bare keycap with its state setters. */
49
+ export interface Keycap {
50
+ /** The root element to place wherever you want. */
51
+ el: HTMLElement;
52
+ /** Pressed-in sink (default false). */
53
+ setPressed(on: boolean): void;
54
+ /** Bouncing pulse on the active button. */
55
+ setAnimated(on: boolean): void;
56
+ /**
57
+ * Cooldown ring on the active button. `frac` 0..1 = portion REMAINING (depletes clockwise from 12
58
+ * o'clock); pass `label` to show a centred countdown (e.g. the remaining seconds). `null` clears it.
59
+ */
60
+ setCooldown(frac: number | null, label?: string | number): void;
61
+ /** Per-button hue on/off. */
62
+ setColored(on: boolean): void;
63
+ /** Disabled = the action is unavailable: flat grey, inert (no pulse/press), letter dimmed. */
64
+ setDisabled(on: boolean): void;
65
+ /** Hide/show the keycap. */
66
+ setVisible(on: boolean): void;
67
+ dispose(): void;
68
+ }
69
+ /** Build a bare A/B/Y/Z keycap with state setters. */
70
+ export declare function createKeycap(button: ButtonLetter, opts?: KeycapOptions): Keycap;
71
+ /**
72
+ * Build a bare directional (UP/DOWN/LEFT/RIGHT) keycap. Identical state machine to {@link createKeycap}
73
+ * — solo/full, pulse, pressed, cooldown ring — only the active button shows a WHITE chevron (rotated
74
+ * per direction) on a steel-grey pip, not the loud A/B/Y/Z hues.
75
+ */
76
+ export declare function createDpadKeycap(dir: DirButton, opts?: KeycapOptions): Keycap;
77
+ //# sourceMappingURL=keycap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keycap.d.ts","sourceRoot":"","sources":["../../src/ui/keycap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGnE,uFAAuF;AACvF,MAAM,MAAM,YAAY,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AACjD,8EAA8E;AAC9E,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AACzD,sEAAsE;AACtE,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,CAAC;AAC5C,8CAA8C;AAC9C,MAAM,MAAM,cAAc,GAAG,aAAa,CAAC;AAE3C;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAE,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CAC/E;AAED,MAAM,WAAW,aAAa;IAC5B,wFAAwF;IACxF,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,gGAAgG;IAChG,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kEAAkE;IAClE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAED,4CAA4C;AAC5C,MAAM,WAAW,MAAM;IACrB,mDAAmD;IACnD,EAAE,EAAE,WAAW,CAAC;IAChB,uCAAuC;IACvC,UAAU,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9B,2CAA2C;IAC3C,WAAW,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/B;;;OAGG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAChE,6BAA6B;IAC7B,UAAU,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9B,8FAA8F;IAC9F,WAAW,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/B,4BAA4B;IAC5B,UAAU,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9B,OAAO,IAAI,IAAI,CAAC;CACjB;AA8ID,sDAAsD;AACtD,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,GAAE,aAAkB,GAAG,MAAM,CAEnF;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,GAAE,aAAkB,GAAG,MAAM,CAEjF"}
@@ -0,0 +1,160 @@
1
+ /**
2
+ * KEYCAP — a DOM/CSS keycap of the controller face buttons (zero dependencies), so a game can show
3
+ * "which button to press" in a consistent visual language. Two shapes:
4
+ *
5
+ * • "full" — the classic diamond: the active button enlarged + coloured, the other three small + grey.
6
+ * • "solo" — light variation: JUST the active button (the idle siblings removed).
7
+ *
8
+ * The keycap is a small STATE MACHINE: it can pulse (the bouncing attract animation), show a pressed
9
+ * sink, run a depleting COOLDOWN ring + countdown, render colored or neutral ("mono"), and hide.
10
+ *
11
+ * Two button families share that machine: `createKeycap` for the A/B/Y/Z face buttons (a letter on
12
+ * the active pip) and `createDpadKeycap` for the UP/DOWN/LEFT/RIGHT directional buttons (a white
13
+ * chevron on a steel-grey pip, rotated per direction). They differ ONLY in the face drawn and the
14
+ * button name the press-wiring listens for; everything else is identical.
15
+ */
16
+ import { injectCss } from "./styles.js";
17
+ const LETTER = { top: "Y", rgt: "A", bot: "B", lft: "Z" };
18
+ const COLOR = { top: "#4DD5FF", rgt: "#00C968", bot: "#FF79FF", lft: "#FF7A0C" };
19
+ const DIR = { top: [0, -1], bot: [0, 1], lft: [-1, 0], rgt: [1, 0] };
20
+ const POS_OF = { Y: "top", A: "rgt", B: "bot", Z: "lft" };
21
+ const POS_OF_DIR = { UP: "top", DOWN: "bot", LEFT: "lft", RIGHT: "rgt" };
22
+ /** Rotation (deg) applied to the base-points-up chevron so it points the right way per position. */
23
+ const ROT = { top: 0, rgt: 90, bot: 180, lft: 270 };
24
+ /** The chevron glyph, drawn in a 24×24 box pointing UP (rotated per position); white on the pip. */
25
+ const CHEVRON = `<polyline points="5,15 12,8 19,15" fill="none" stroke="currentColor" stroke-width="3.2" stroke-linecap="round" stroke-linejoin="round"/>`;
26
+ /** Background tone of a directional keycap's active pip ("acier" / steel-grey); the glyph stays white. */
27
+ const DPAD_TONE = "#566273";
28
+ const PIP = 20, PIP_ACTIVE = 38, LETTER_FONT = 23;
29
+ const NEUTRAL_ACTIVE = "#ffffff"; // mono active button: white face (the letter stays dark)
30
+ const INACTIVE = "#3a424e"; // the idle siblings in a full diamond
31
+ /** Letter face — the A/B/Y/Z glyph (sized up on the active button, hidden font otherwise). */
32
+ const letterFace = (pos, active) => {
33
+ const lt = document.createElement("span");
34
+ lt.className = "rydr-ui-pip-letter";
35
+ if (active)
36
+ lt.style.fontSize = `${LETTER_FONT}px`;
37
+ lt.textContent = LETTER[pos];
38
+ return lt;
39
+ };
40
+ /** Directional face — a white chevron (only on the active button; siblings stay blank). */
41
+ const chevronFace = (pos, active, size) => {
42
+ if (!active)
43
+ return null;
44
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
45
+ svg.setAttribute("viewBox", "0 0 24 24");
46
+ svg.setAttribute("width", String(Math.round(size * 0.62)));
47
+ svg.setAttribute("height", String(Math.round(size * 0.62)));
48
+ svg.setAttribute("class", "rydr-ui-pip-glyph");
49
+ svg.style.transform = `rotate(${ROT[pos]}deg)`;
50
+ svg.style.color = "#ffffff"; // white icon on the steel pip
51
+ svg.innerHTML = CHEVRON;
52
+ return svg;
53
+ };
54
+ /** Make one keycap pip (a circular button). Active pips also carry cooldown scaffolding. */
55
+ function makePip(active, color, pos, size, animate, face) {
56
+ const pip = document.createElement("div");
57
+ pip.className = "rydr-ui-pip" + (active ? " active" : "") + (active && animate ? " anim" : "");
58
+ pip.style.setProperty("--c", active ? color : INACTIVE);
59
+ pip.style.width = pip.style.height = `${size}px`;
60
+ const content = face(pos, active, size);
61
+ if (content)
62
+ pip.appendChild(content);
63
+ if (active) {
64
+ const ring = document.createElement("div"); // cooldown — a circular loader inside the rim
65
+ ring.className = "rydr-ui-ring";
66
+ pip.appendChild(ring);
67
+ const num = document.createElement("span"); // cooldown — centred countdown number
68
+ num.className = "rydr-ui-cd-num";
69
+ pip.appendChild(num);
70
+ }
71
+ return pip;
72
+ }
73
+ /** Build the keycap for `activePos`; returns the element + a reference to the active pip (for state). */
74
+ function buildPips(activePos, variant, colored, animate, face, accent) {
75
+ const diamond = document.createElement("div");
76
+ diamond.className = "rydr-ui-diamond";
77
+ // `accent` (used by the directional keycap) overrides the per-button hue with a fixed tone.
78
+ const activeColor = accent ?? (colored ? COLOR[activePos] : NEUTRAL_ACTIVE);
79
+ if (variant === "solo") {
80
+ diamond.style.width = diamond.style.height = `${PIP_ACTIVE}px`;
81
+ const pip = makePip(true, activeColor, activePos, PIP_ACTIVE, animate, face);
82
+ pip.style.left = pip.style.top = "0px";
83
+ diamond.appendChild(pip);
84
+ return { el: diamond, activePip: pip };
85
+ }
86
+ const ri = PIP / 2, Ra = PIP_ACTIVE / 2, r0 = Ra, D = r0 + ri;
87
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
88
+ const pts = {};
89
+ for (const k of ["top", "lft", "rgt", "bot"]) {
90
+ const active = k === activePos, r = active ? Ra : ri, dist = active ? D : r0;
91
+ const cx = DIR[k][0] * dist, cy = DIR[k][1] * dist;
92
+ pts[k] = { cx, cy, r, active };
93
+ minX = Math.min(minX, cx - r);
94
+ maxX = Math.max(maxX, cx + r);
95
+ minY = Math.min(minY, cy - r);
96
+ maxY = Math.max(maxY, cy + r);
97
+ }
98
+ diamond.style.width = `${maxX - minX}px`;
99
+ diamond.style.height = `${maxY - minY}px`;
100
+ let activePip;
101
+ for (const k of ["top", "lft", "rgt", "bot"]) {
102
+ const pt = pts[k];
103
+ const pip = makePip(pt.active, pt.active ? activeColor : (colored ? COLOR[k] : NEUTRAL_ACTIVE), k, pt.r * 2, animate, face);
104
+ pip.style.left = `${pt.cx - pt.r - minX}px`;
105
+ pip.style.top = `${pt.cy - pt.r - minY}px`;
106
+ diamond.appendChild(pip);
107
+ if (pt.active)
108
+ activePip = pip;
109
+ }
110
+ return { el: diamond, activePip };
111
+ }
112
+ /**
113
+ * Build the keycap + its state setters. Shared core for both the A/B/Y/Z (`createKeycap`) and the
114
+ * directional (`createDpadKeycap`) factories — they differ only in the `face` drawn and the button
115
+ * name (`pressName`) the press-wiring listens for.
116
+ */
117
+ function build(activePos, pressName, face, opts, accent) {
118
+ injectCss();
119
+ const variant = opts.variant ?? "solo";
120
+ const animate = opts.animate ?? true;
121
+ const colored = opts.colored ?? true;
122
+ const { el, activePip } = buildPips(activePos, variant, colored, animate, face, accent);
123
+ const num = activePip.querySelector(".rydr-ui-cd-num");
124
+ const setPressed = (on) => activePip.classList.toggle("pressed", on);
125
+ // wire to real controller input: pressed while THIS button is held (down → pressed, up → released)
126
+ const off = opts.press?.onButton((e) => { if (e.name === pressName)
127
+ setPressed(e.edge === "down"); });
128
+ return {
129
+ el,
130
+ setPressed,
131
+ setAnimated: (on) => activePip.classList.toggle("anim", on),
132
+ setColored: (on) => activePip.style.setProperty("--c", accent ?? (on ? COLOR[activePos] : NEUTRAL_ACTIVE)),
133
+ setDisabled: (on) => activePip.classList.toggle("disabled", on),
134
+ setVisible: (on) => { el.style.visibility = on ? "" : "hidden"; },
135
+ setCooldown: (frac, label) => {
136
+ if (frac == null) {
137
+ activePip.classList.remove("cooling");
138
+ return;
139
+ }
140
+ activePip.classList.add("cooling");
141
+ activePip.style.setProperty("--cd", String(Math.max(0, Math.min(1, frac))));
142
+ if (num)
143
+ num.textContent = label == null ? "" : String(label);
144
+ },
145
+ dispose: () => { off?.(); el.remove(); },
146
+ };
147
+ }
148
+ /** Build a bare A/B/Y/Z keycap with state setters. */
149
+ export function createKeycap(button, opts = {}) {
150
+ return build(POS_OF[button], button, letterFace, opts);
151
+ }
152
+ /**
153
+ * Build a bare directional (UP/DOWN/LEFT/RIGHT) keycap. Identical state machine to {@link createKeycap}
154
+ * — solo/full, pulse, pressed, cooldown ring — only the active button shows a WHITE chevron (rotated
155
+ * per direction) on a steel-grey pip, not the loud A/B/Y/Z hues.
156
+ */
157
+ export function createDpadKeycap(dir, opts = {}) {
158
+ return build(POS_OF_DIR[dir], dir, chevronFace, opts, DPAD_TONE);
159
+ }
160
+ //# sourceMappingURL=keycap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keycap.js","sourceRoot":"","sources":["../../src/ui/keycap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAwDxC,MAAM,MAAM,GAAwB,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAC/E,MAAM,KAAK,GAAwB,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC;AACtG,MAAM,GAAG,GAAkC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;AACpG,MAAM,MAAM,GAA8B,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC;AACrF,MAAM,UAAU,GAA2B,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AACjG,oGAAoG;AACpG,MAAM,GAAG,GAAwB,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AACzE,oGAAoG;AACpG,MAAM,OAAO,GAAG,0IAA0I,CAAC;AAC3J,0GAA0G;AAC1G,MAAM,SAAS,GAAG,SAAS,CAAC;AAE5B,MAAM,GAAG,GAAG,EAAE,EAAE,UAAU,GAAG,EAAE,EAAE,WAAW,GAAG,EAAE,CAAC;AAClD,MAAM,cAAc,GAAG,SAAS,CAAC,CAAC,yDAAyD;AAC3F,MAAM,QAAQ,GAAG,SAAS,CAAC,CAAC,sCAAsC;AAKlE,8FAA8F;AAC9F,MAAM,UAAU,GAAS,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE;IACvC,MAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAC1C,EAAE,CAAC,SAAS,GAAG,oBAAoB,CAAC;IACpC,IAAI,MAAM;QAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,GAAG,GAAG,WAAW,IAAI,CAAC;IACnD,EAAE,CAAC,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7B,OAAO,EAAE,CAAC;AACZ,CAAC,CAAC;AAEF,2FAA2F;AAC3F,MAAM,WAAW,GAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE;IAC9C,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,GAAG,GAAG,QAAQ,CAAC,eAAe,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;IAC1E,GAAG,CAAC,YAAY,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IACzC,GAAG,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IAC3D,GAAG,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5D,GAAG,CAAC,YAAY,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;IAC/C,GAAG,CAAC,KAAK,CAAC,SAAS,GAAG,UAAU,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC;IAC/C,GAAG,CAAC,KAAK,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC,8BAA8B;IAC3D,GAAG,CAAC,SAAS,GAAG,OAAO,CAAC;IACxB,OAAO,GAAG,CAAC;AACb,CAAC,CAAC;AAEF,4FAA4F;AAC5F,SAAS,OAAO,CAAC,MAAe,EAAE,KAAa,EAAE,GAAQ,EAAE,IAAY,EAAE,OAAgB,EAAE,IAAU;IACnG,MAAM,GAAG,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAC1C,GAAG,CAAC,SAAS,GAAG,aAAa,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC/F,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IACxD,GAAG,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC;IAEjD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IACxC,IAAI,OAAO;QAAE,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IAEtC,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,8CAA8C;QAC1F,IAAI,CAAC,SAAS,GAAG,cAAc,CAAC;QAChC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACtB,MAAM,GAAG,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,sCAAsC;QAClF,GAAG,CAAC,SAAS,GAAG,gBAAgB,CAAC;QACjC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACvB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,yGAAyG;AACzG,SAAS,SAAS,CAAC,SAAc,EAAE,OAAsB,EAAE,OAAgB,EAAE,OAAgB,EAAE,IAAU,EAAE,MAAe;IACxH,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAC9C,OAAO,CAAC,SAAS,GAAG,iBAAiB,CAAC;IACtC,4FAA4F;IAC5F,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;IAE5E,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACvB,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,UAAU,IAAI,CAAC;QAC/D,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QAC7E,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,GAAG,KAAK,CAAC;QACvC,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QACzB,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC;IACzC,CAAC;IAED,MAAM,EAAE,GAAG,GAAG,GAAG,CAAC,EAAE,EAAE,GAAG,UAAU,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC;IAC9D,IAAI,IAAI,GAAG,QAAQ,EAAE,IAAI,GAAG,QAAQ,EAAE,IAAI,GAAG,CAAC,QAAQ,EAAE,IAAI,GAAG,CAAC,QAAQ,CAAC;IACzE,MAAM,GAAG,GAAwE,EAAW,CAAC;IAC7F,KAAK,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAU,EAAE,CAAC;QACtD,MAAM,MAAM,GAAG,CAAC,KAAK,SAAS,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7E,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,EAAE,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;QACnD,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;QAC/B,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;QAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;QAC7D,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;QAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,IAAI,GAAG,IAAI,IAAI,CAAC;IACzC,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,IAAI,GAAG,IAAI,IAAI,CAAC;IAC1C,IAAI,SAAuB,CAAC;IAC5B,KAAK,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAU,EAAE,CAAC;QACtD,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QAClB,MAAM,GAAG,GAAG,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QAC5H,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC;QAC5C,GAAG,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC;QAC3C,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QACzB,IAAI,EAAE,CAAC,MAAM;YAAE,SAAS,GAAG,GAAG,CAAC;IACjC,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AACpC,CAAC;AAED;;;;GAIG;AACH,SAAS,KAAK,CAAC,SAAc,EAAE,SAAqB,EAAE,IAAU,EAAE,IAAmB,EAAE,MAAe;IACpG,SAAS,EAAE,CAAC;IACZ,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,MAAM,CAAC;IACvC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC;IACrC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC;IACrC,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,GAAG,SAAS,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IACxF,MAAM,GAAG,GAAG,SAAS,CAAC,aAAa,CAAC,iBAAiB,CAAuB,CAAC;IAC7E,MAAM,UAAU,GAAG,CAAC,EAAW,EAAE,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IAE9E,mGAAmG;IACnG,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS;QAAE,UAAU,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEtG,OAAO;QACL,EAAE;QACF,UAAU;QACV,WAAW,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;QAC3D,UAAU,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,WAAW,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;QAC1G,WAAW,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,CAAC;QAC/D,UAAU,EAAE,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,UAAU,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;QACjE,WAAW,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;YAC3B,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;gBACjB,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBACtC,OAAO;YACT,CAAC;YACD,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YACnC,SAAS,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5E,IAAI,GAAG;gBAAE,GAAG,CAAC,WAAW,GAAG,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAChE,CAAC;QACD,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;KACzC,CAAC;AACJ,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,YAAY,CAAC,MAAoB,EAAE,OAAsB,EAAE;IACzE,OAAO,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;AACzD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAc,EAAE,OAAsB,EAAE;IACvE,OAAO,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;AACnE,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * LABELED DIAMOND — a central A/B/Y/Z pip cluster where ANY number of buttons can be active at once,
3
+ * each active button carrying a label that radiates outward from its position (Y above · B below ·
4
+ * Z to the left, right-aligned · A to the right, left-aligned). Where the {@link mountActionDiamond}
5
+ * is a HUD of four big move cards, this is the compact "here are the actions on offer" affordance —
6
+ * e.g. an inspect target showing `Y Inspecter · A Équiper · B Inventaire` around the button glyphs.
7
+ *
8
+ * Each label is seated in the shared dark glass {@link Card} so it stays legible on any background.
9
+ * Fixed-overlay by default (call `setScreenPos` to track a projected world point), or `inline:true`
10
+ * to lay it in normal flow.
11
+ */
12
+ import { type ButtonLetter } from "./keycap.js";
13
+ export interface LabeledDiamondOptions {
14
+ /** Lay the diamond inline (normal flow, full width) instead of as a fixed overlay. */
15
+ inline?: boolean;
16
+ /** Initial active labels — a label string activates that button; omit a button to leave it greyed. */
17
+ actions?: Partial<Record<ButtonLetter, string>>;
18
+ }
19
+ export interface LabeledDiamond {
20
+ root: HTMLElement;
21
+ /** Activate a button with a radiating label, or pass `null`/`""` to deactivate it (pip greys, label hides). */
22
+ setLabel(button: ButtonLetter, text: string | null): void;
23
+ /** Disable an active action: its pip goes inert grey and its label fades back. */
24
+ setDisabled(button: ButtonLetter, on: boolean): void;
25
+ setVisible(v: boolean): void;
26
+ /** Centre the diamond at a viewport pixel (e.g. a target projected to screen). */
27
+ setScreenPos(x: number, y: number): void;
28
+ dispose(): void;
29
+ }
30
+ /** Mount a labeled A/B/Y/Z diamond: a pip cluster with a card-seated label radiating from each active button. */
31
+ export declare function mountLabeledDiamond(host: HTMLElement, opts?: LabeledDiamondOptions): LabeledDiamond;
32
+ //# sourceMappingURL=labeled-diamond.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"labeled-diamond.d.ts","sourceRoot":"","sources":["../../src/ui/labeled-diamond.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAUhD,MAAM,WAAW,qBAAqB;IACpC,sFAAsF;IACtF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,sGAAsG;IACtG,OAAO,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC;CACjD;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,WAAW,CAAC;IAClB,+GAA+G;IAC/G,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1D,kFAAkF;IAClF,WAAW,CAAC,MAAM,EAAE,YAAY,EAAE,EAAE,EAAE,OAAO,GAAG,IAAI,CAAC;IACrD,UAAU,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7B,kFAAkF;IAClF,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,iHAAiH;AACjH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,GAAE,qBAA0B,GAAG,cAAc,CAsEvG"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * LABELED DIAMOND — a central A/B/Y/Z pip cluster where ANY number of buttons can be active at once,
3
+ * each active button carrying a label that radiates outward from its position (Y above · B below ·
4
+ * Z to the left, right-aligned · A to the right, left-aligned). Where the {@link mountActionDiamond}
5
+ * is a HUD of four big move cards, this is the compact "here are the actions on offer" affordance —
6
+ * e.g. an inspect target showing `Y Inspecter · A Équiper · B Inventaire` around the button glyphs.
7
+ *
8
+ * Each label is seated in the shared dark glass {@link Card} so it stays legible on any background.
9
+ * Fixed-overlay by default (call `setScreenPos` to track a projected world point), or `inline:true`
10
+ * to lay it in normal flow.
11
+ */
12
+ import { mountCard } from "./card.js";
13
+ import {} from "./keycap.js";
14
+ import { injectCss } from "./styles.js";
15
+ const POS = { Y: "top", A: "rgt", B: "bot", Z: "lft" };
16
+ /** Per-position hue — mirrors the keycap palette (Y cyan · A green · B magenta · Z orange). */
17
+ const COLOR = { top: "#4DD5FF", rgt: "#00C968", bot: "#FF79FF", lft: "#FF7A0C" };
18
+ const INACTIVE = "#3a424e"; // a button that isn't part of this menu (greyed, sunk)
19
+ const FACES = ["Y", "A", "B", "Z"];
20
+ /** Mount a labeled A/B/Y/Z diamond: a pip cluster with a card-seated label radiating from each active button. */
21
+ export function mountLabeledDiamond(host, opts = {}) {
22
+ injectCss();
23
+ const root = document.createElement("div");
24
+ root.className = "rydr-ui-ld-host" + (opts.inline ? " inline" : "");
25
+ const wrap = document.createElement("div");
26
+ wrap.className = "rydr-ui-ld-wrap";
27
+ root.appendChild(wrap);
28
+ // the central 4-pip cluster — every face button, active ones lit + raised, the rest greyed + sunk
29
+ const cluster = document.createElement("div");
30
+ cluster.className = "rydr-ui-ld-cluster";
31
+ wrap.appendChild(cluster);
32
+ const pips = {};
33
+ const slots = {};
34
+ const cards = {};
35
+ for (const b of FACES) {
36
+ const pos = POS[b];
37
+ const pip = document.createElement("div");
38
+ pip.className = `rydr-ui-pip pos-${pos}`;
39
+ pip.style.setProperty("--c", INACTIVE);
40
+ pip.style.opacity = "0.5";
41
+ const lt = document.createElement("span");
42
+ lt.className = "rydr-ui-pip-letter";
43
+ lt.style.fontSize = "18px";
44
+ lt.textContent = b;
45
+ pip.appendChild(lt);
46
+ cluster.appendChild(pip);
47
+ pips[b] = pip;
48
+ // one outer label cell per button (pre-created, shown only while the button is active), each
49
+ // seated in the dark glass card so it reads on any background
50
+ const slot = document.createElement("div");
51
+ slot.className = `rydr-ui-ld-lbl rydr-ui-ld-lbl-${pos}`;
52
+ slot.style.display = "none";
53
+ cards[b] = mountCard(slot, { content: "", inline: true });
54
+ wrap.appendChild(slot);
55
+ slots[b] = slot;
56
+ }
57
+ host.appendChild(root);
58
+ const setLabel = (button, text) => {
59
+ const on = text != null && text !== "";
60
+ pips[button].classList.toggle("active", on);
61
+ if (!on)
62
+ pips[button].classList.remove("disabled"); // a deactivated button can't stay "disabled"
63
+ pips[button].style.setProperty("--c", on ? COLOR[POS[button]] : INACTIVE);
64
+ pips[button].style.opacity = on ? "1" : "0.5";
65
+ slots[button].style.display = on ? "" : "none";
66
+ if (on)
67
+ cards[button].setLabel(text);
68
+ };
69
+ for (const b of FACES)
70
+ setLabel(b, opts.actions?.[b] ?? null);
71
+ return {
72
+ root,
73
+ setLabel,
74
+ setDisabled: (button, on) => {
75
+ pips[button].classList.toggle("disabled", on);
76
+ slots[button].classList.toggle("rydr-ui-ld-lbl-off", on);
77
+ },
78
+ setVisible: (v) => { root.style.visibility = v ? "" : "hidden"; },
79
+ setScreenPos(x, y) {
80
+ root.style.left = `${x}px`;
81
+ root.style.top = `${y}px`;
82
+ },
83
+ dispose: () => root.remove(),
84
+ };
85
+ }
86
+ //# sourceMappingURL=labeled-diamond.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"labeled-diamond.js","sourceRoot":"","sources":["../../src/ui/labeled-diamond.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Minimal scrollable-gallery harness for {@link mountUiShowcase} — a header with an optional live
3
+ * watts readout, a column of captioned sections, and a `requestAnimationFrame` clock that drives the
4
+ * live demos (cooldown rings, the typewriter dialogue). Self-contained: it injects its own `gw-*`
5
+ * chrome CSS. Not exported from the package — internal to the showcase entry.
6
+ */
7
+ import type { ButtonName, ButtonEdge } from "../../protocol/index.js";
8
+ /**
9
+ * The thin slice of a `PlatformSession` the showcase consumes — so a host can pass its real session
10
+ * (live watts + real button reflection) and a `PlatformSession` satisfies this structurally, while a
11
+ * standalone dev page can pass a tiny keyboard-driven stand-in. Omit it and the catalog renders static.
12
+ */
13
+ export interface ShowcaseSession {
14
+ onButton(cb: (e: {
15
+ name: ButtonName;
16
+ edge: ButtonEdge;
17
+ }) => void): () => void;
18
+ hardware: {
19
+ current: {
20
+ power: number;
21
+ };
22
+ };
23
+ identity: {
24
+ ftp: number;
25
+ };
26
+ }
27
+ /** Append a classed element (optionally with innerHTML) to `parent` and return it. */
28
+ export declare function el(parent: HTMLElement, tag: string, className: string, html?: string): HTMLElement;
29
+ /** A dpr-aware smooth 2D canvas; draw at logical px. */
30
+ export declare function makeCanvas(logicalW: number, logicalH: number): {
31
+ canvas: HTMLCanvasElement;
32
+ c: CanvasRenderingContext2D;
33
+ };
34
+ export interface GalleryRunOpts {
35
+ /** A real (or stand-in) session — drives the live watts readout and wires keycaps to real presses. */
36
+ session?: ShowcaseSession;
37
+ /** Per-frame work (e.g. ticking the cooldown rings + the demo dialogue), `dt` in seconds. */
38
+ perFrame?: (dt: number) => void;
39
+ }
40
+ /** A scrollable gallery: a chrome + captioned sections, then `run` the RAF loop. */
41
+ export declare class Gallery {
42
+ readonly list: HTMLElement;
43
+ private style;
44
+ private powerB;
45
+ private raf;
46
+ private last;
47
+ constructor(host: HTMLElement, opts: {
48
+ title: string;
49
+ sub: string;
50
+ css?: string;
51
+ });
52
+ section(title: string, sub: string): HTMLElement;
53
+ run(opts?: GalleryRunOpts): void;
54
+ dispose(): void;
55
+ }
56
+ //# sourceMappingURL=gallery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gallery.d.ts","sourceRoot":"","sources":["../../../src/ui/showcase/gallery.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAEtE;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAE,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAC9E,QAAQ,EAAE;QAAE,OAAO,EAAE;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;IACzC,QAAQ,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3B;AAED,sFAAsF;AACtF,wBAAgB,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,CAMlG;AAED,wDAAwD;AACxD,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,iBAAiB,CAAC;IAAC,CAAC,EAAE,wBAAwB,CAAA;CAAE,CAYzH;AAED,MAAM,WAAW,cAAc;IAC7B,sGAAsG;IACtG,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,6FAA6F;IAC7F,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAED,oFAAoF;AACpF,qBAAa,OAAO;IAClB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,OAAO,CAAC,KAAK,CAAmB;IAChC,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,IAAI,CAAK;gBAEL,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE;IAmBjF,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,WAAW;IAIhD,GAAG,CAAC,IAAI,GAAE,cAAmB,GAAG,IAAI;IAYpC,OAAO,IAAI,IAAI;CAKhB"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Minimal scrollable-gallery harness for {@link mountUiShowcase} — a header with an optional live
3
+ * watts readout, a column of captioned sections, and a `requestAnimationFrame` clock that drives the
4
+ * live demos (cooldown rings, the typewriter dialogue). Self-contained: it injects its own `gw-*`
5
+ * chrome CSS. Not exported from the package — internal to the showcase entry.
6
+ */
7
+ /** Append a classed element (optionally with innerHTML) to `parent` and return it. */
8
+ export function el(parent, tag, className, html) {
9
+ const e = document.createElement(tag);
10
+ e.className = className;
11
+ if (html != null)
12
+ e.innerHTML = html;
13
+ parent.appendChild(e);
14
+ return e;
15
+ }
16
+ /** A dpr-aware smooth 2D canvas; draw at logical px. */
17
+ export function makeCanvas(logicalW, logicalH) {
18
+ const dpr = Math.min(window.devicePixelRatio || 1, 2);
19
+ const canvas = document.createElement("canvas");
20
+ canvas.width = Math.round(logicalW * dpr);
21
+ canvas.height = Math.round(logicalH * dpr);
22
+ canvas.style.width = logicalW + "px";
23
+ canvas.style.height = logicalH + "px";
24
+ const c = canvas.getContext("2d");
25
+ if (!c)
26
+ throw new Error("2D canvas context unavailable");
27
+ c.scale(dpr, dpr);
28
+ c.imageSmoothingEnabled = true;
29
+ return { canvas, c };
30
+ }
31
+ /** A scrollable gallery: a chrome + captioned sections, then `run` the RAF loop. */
32
+ export class Gallery {
33
+ list;
34
+ style;
35
+ powerB;
36
+ raf = 0;
37
+ last = 0;
38
+ constructor(host, opts) {
39
+ this.style = document.createElement("style");
40
+ this.style.textContent = BASE_CSS + (opts.css ?? "");
41
+ host.appendChild(this.style);
42
+ this.list = document.createElement("div");
43
+ this.list.className = "gw-list";
44
+ host.appendChild(this.list);
45
+ el(this.list, "div", "gw-head", `<div><div class="gw-title">${opts.title}</div><div class="gw-sub">${opts.sub}</div></div>` +
46
+ `<div class="gw-power"><b>—</b> W</div>`);
47
+ this.powerB = this.list.querySelector(".gw-power b");
48
+ }
49
+ section(title, sub) {
50
+ return el(this.list, "div", "gw-section", `<div class="gw-section-title">${title}</div><div class="gw-section-sub">${sub}</div>`);
51
+ }
52
+ run(opts = {}) {
53
+ const { session } = opts;
54
+ const loop = (ts) => {
55
+ this.raf = requestAnimationFrame(loop);
56
+ const dt = this.last ? Math.min(0.05, (ts - this.last) / 1000) : 0;
57
+ this.last = ts;
58
+ opts.perFrame?.(dt);
59
+ if (session)
60
+ this.powerB.textContent = String(Math.round(session.hardware.current.power));
61
+ };
62
+ this.raf = requestAnimationFrame(loop);
63
+ }
64
+ dispose() {
65
+ cancelAnimationFrame(this.raf);
66
+ this.list.remove();
67
+ this.style.remove();
68
+ }
69
+ }
70
+ /** Shared chrome styles (`gw-` prefix). */
71
+ const BASE_CSS = `
72
+ .gw-list { position: fixed; inset: 0; overflow-y: auto; overflow-x: hidden; z-index: 1;
73
+ padding: clamp(1rem, 4vw, 3rem) clamp(1rem, 4vw, 3rem) 4rem; color: #eef4ff;
74
+ font-family: system-ui, sans-serif;
75
+ display: flex; flex-direction: column; gap: 1rem; }
76
+ .gw-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
77
+ .gw-title { font-size: clamp(1.3rem, 4vw, 2rem); font-weight: 800; letter-spacing: -0.01em; }
78
+ .gw-sub { opacity: 0.55; font-size: 0.85rem; }
79
+ .gw-power { font-variant-numeric: tabular-nums; opacity: 0.75; font-size: 0.9rem; }
80
+ .gw-power b { font-size: 1.3rem; font-weight: 800; }
81
+ .gw-section { margin-top: 1rem; }
82
+ .gw-section-title { font-size: 1.1rem; font-weight: 700; }
83
+ .gw-section-sub { opacity: 0.5; font-size: 0.82rem; margin-top: 0.15rem; }
84
+ `;
85
+ //# sourceMappingURL=gallery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gallery.js","sourceRoot":"","sources":["../../../src/ui/showcase/gallery.ts"],"names":[],"mappings":"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"}