@rydr/game-sdk 7.0.0 → 7.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.
- package/README.md +19 -0
- package/dist/protocol/glyphs.d.ts +7 -0
- package/dist/protocol/glyphs.d.ts.map +1 -1
- package/dist/protocol/glyphs.js +26 -0
- package/dist/protocol/glyphs.js.map +1 -1
- package/dist/protocol/version.d.ts +1 -1
- package/dist/protocol/version.js +1 -1
- package/dist/three/perf-overlay.d.ts +52 -2
- package/dist/three/perf-overlay.d.ts.map +1 -1
- package/dist/three/perf-overlay.js +188 -3
- package/dist/three/perf-overlay.js.map +1 -1
- package/dist/ui/README.md +119 -0
- package/dist/ui/controller/combo-demo.d.ts +74 -0
- package/dist/ui/controller/combo-demo.d.ts.map +1 -0
- package/dist/ui/controller/combo-demo.js +295 -0
- package/dist/ui/controller/combo-demo.js.map +1 -0
- package/dist/ui/controller/combo-strip.d.ts +36 -0
- package/dist/ui/controller/combo-strip.d.ts.map +1 -0
- package/dist/ui/controller/combo-strip.js +66 -0
- package/dist/ui/controller/combo-strip.js.map +1 -0
- package/dist/ui/controller/control-trainer.d.ts +132 -0
- package/dist/ui/controller/control-trainer.d.ts.map +1 -0
- package/dist/ui/controller/control-trainer.js +570 -0
- package/dist/ui/controller/control-trainer.js.map +1 -0
- package/dist/ui/controller/controller-map.d.ts +82 -0
- package/dist/ui/controller/controller-map.d.ts.map +1 -0
- package/dist/ui/controller/controller-map.js +291 -0
- package/dist/ui/controller/controller-map.js.map +1 -0
- package/dist/ui/controller/controller-pad.d.ts +122 -0
- package/dist/ui/controller/controller-pad.d.ts.map +1 -0
- package/dist/ui/controller/controller-pad.js +200 -0
- package/dist/ui/controller/controller-pad.js.map +1 -0
- package/dist/ui/controller/index.d.ts +26 -0
- package/dist/ui/controller/index.d.ts.map +1 -0
- package/dist/ui/controller/index.js +26 -0
- package/dist/ui/controller/index.js.map +1 -0
- package/dist/ui/controller/input-chip.d.ts +100 -0
- package/dist/ui/controller/input-chip.d.ts.map +1 -0
- package/dist/ui/controller/input-chip.js +322 -0
- package/dist/ui/controller/input-chip.js.map +1 -0
- package/dist/ui/controller/layouts/dualsense.d.ts +12 -0
- package/dist/ui/controller/layouts/dualsense.d.ts.map +1 -0
- package/dist/ui/controller/layouts/dualsense.js +64 -0
- package/dist/ui/controller/layouts/dualsense.js.map +1 -0
- package/dist/ui/controller/layouts/index.d.ts +20 -0
- package/dist/ui/controller/layouts/index.d.ts.map +1 -0
- package/dist/ui/controller/layouts/index.js +34 -0
- package/dist/ui/controller/layouts/index.js.map +1 -0
- package/dist/ui/controller/layouts/joycon.d.ts +21 -0
- package/dist/ui/controller/layouts/joycon.d.ts.map +1 -0
- package/dist/ui/controller/layouts/joycon.js +76 -0
- package/dist/ui/controller/layouts/joycon.js.map +1 -0
- package/dist/ui/controller/layouts/switch-pro.d.ts +12 -0
- package/dist/ui/controller/layouts/switch-pro.d.ts.map +1 -0
- package/dist/ui/controller/layouts/switch-pro.js +60 -0
- package/dist/ui/controller/layouts/switch-pro.js.map +1 -0
- package/dist/ui/controller/layouts/xbox.d.ts +11 -0
- package/dist/ui/controller/layouts/xbox.d.ts.map +1 -0
- package/dist/ui/controller/layouts/xbox.js +59 -0
- package/dist/ui/controller/layouts/xbox.js.map +1 -0
- package/dist/ui/controller/layouts/zwift.d.ts +27 -0
- package/dist/ui/controller/layouts/zwift.d.ts.map +1 -0
- package/dist/ui/controller/layouts/zwift.js +62 -0
- package/dist/ui/controller/layouts/zwift.js.map +1 -0
- package/dist/ui/controller/pad-svg.d.ts +105 -0
- package/dist/ui/controller/pad-svg.d.ts.map +1 -0
- package/dist/ui/controller/pad-svg.js +289 -0
- package/dist/ui/controller/pad-svg.js.map +1 -0
- package/dist/ui/controller/parts-map.d.ts +96 -0
- package/dist/ui/controller/parts-map.d.ts.map +1 -0
- package/dist/ui/controller/parts-map.js +187 -0
- package/dist/ui/controller/parts-map.js.map +1 -0
- package/dist/ui/controller/styles.d.ts +19 -0
- package/dist/ui/controller/styles.d.ts.map +1 -0
- package/dist/ui/controller/styles.js +364 -0
- package/dist/ui/controller/styles.js.map +1 -0
- package/dist/ui/controller/tokens.d.ts +106 -0
- package/dist/ui/controller/tokens.d.ts.map +1 -0
- package/dist/ui/controller/tokens.js +50 -0
- package/dist/ui/controller/tokens.js.map +1 -0
- package/dist/ui/controller/types.d.ts +157 -0
- package/dist/ui/controller/types.d.ts.map +1 -0
- package/dist/ui/controller/types.js +61 -0
- package/dist/ui/controller/types.js.map +1 -0
- package/dist/ui/index.d.ts +8 -0
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +8 -0
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/keycap.d.ts +14 -0
- package/dist/ui/keycap.d.ts.map +1 -1
- package/dist/ui/keycap.js +5 -0
- package/dist/ui/keycap.js.map +1 -1
- package/dist/ui/showcase/gallery.d.ts +13 -1
- package/dist/ui/showcase/gallery.d.ts.map +1 -1
- package/dist/ui/showcase/gallery.js.map +1 -1
- package/dist/ui/showcase/index.d.ts.map +1 -1
- package/dist/ui/showcase/index.js +257 -1
- package/dist/ui/showcase/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CONTROLLER MAP — the static "here are your controls" view: the live pad, flanked by callout cards, each
|
|
3
|
+
* joined by a line to the button it belongs to.
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* const map = mountControllerMap(host, {
|
|
7
|
+
* press: session,
|
|
8
|
+
* actions: [
|
|
9
|
+
* { label: "Se déplacer", combo: ["LSTICK"] },
|
|
10
|
+
* { label: "Freiner", combo: ["LT"] },
|
|
11
|
+
* { label: "Tir chargé", combo: ["RT", "DIAMOND_DOWN"] },
|
|
12
|
+
* { label: "Dash", combo: ["LT", "LSTICK:left"] },
|
|
13
|
+
* ],
|
|
14
|
+
* });
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* **It is an inline widget, not a screen.** It renders a self-sized block into whatever host you give it
|
|
18
|
+
* — no fixed positioning, no backdrop, no close button, no back-button handling. Whether that host is a
|
|
19
|
+
* pause overlay, a pre-race panel or a settings tab is the game's call, same as every other component in
|
|
20
|
+
* the kit. For a pause overlay, own the host yourself:
|
|
21
|
+
*
|
|
22
|
+
* ```ts
|
|
23
|
+
* const overlay = document.createElement("div");
|
|
24
|
+
* overlay.style.cssText = "position:fixed; inset:0; display:grid; place-items:center; background:rgba(4,8,14,.72)";
|
|
25
|
+
* document.body.appendChild(overlay);
|
|
26
|
+
* mountControllerMap(overlay, { press: session, actions });
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* The one genuinely new mechanic here is the **connector layer**. The lines join DOM cards to SVG nodes,
|
|
30
|
+
* so they can't live in the pad's own viewBox — they're drawn in page-pixel space from measured
|
|
31
|
+
* rectangles. Re-routing is therefore layout-dependent and is triggered by a `ResizeObserver` on the root
|
|
32
|
+
* *and on every card* (a label wrapping to two lines moves every card below it), by `document.fonts.ready`
|
|
33
|
+
* (a web font swapping in reflows labels after first paint), and by a pad re-letter. Every trigger is
|
|
34
|
+
* coalesced into a single `requestAnimationFrame` — this never runs per frame.
|
|
35
|
+
*/
|
|
36
|
+
import type { ButtonName } from "../../protocol/buttons.js";
|
|
37
|
+
import type { GlyphSet } from "../../protocol/glyphs.js";
|
|
38
|
+
import type { StickPictogramStyle } from "./input-chip.js";
|
|
39
|
+
import { type ControllerPad, type PadInputSource } from "./controller-pad.js";
|
|
40
|
+
import type { ControlAction } from "./tokens.js";
|
|
41
|
+
import type { PadBrandKey } from "./types.js";
|
|
42
|
+
export interface ControllerMapOptions {
|
|
43
|
+
/** What the rider can do, and what they press to do it. */
|
|
44
|
+
actions: readonly ControlAction[];
|
|
45
|
+
/**
|
|
46
|
+
* The rider's input source. Used for the LETTERING (and to re-letter on a pad swap) even when
|
|
47
|
+
* {@link live} is false — a controls screen must print the letters on the pad actually in their hands.
|
|
48
|
+
*/
|
|
49
|
+
press?: PadInputSource;
|
|
50
|
+
/**
|
|
51
|
+
* Reflect real presses: light the pad and sink the matching chips as the rider presses. **Default
|
|
52
|
+
* false** — this is a reference diagram, and it stays legible precisely by not moving. A rider
|
|
53
|
+
* resting a thumb on a stick would otherwise light half the drawing while they are trying to read it.
|
|
54
|
+
* Pass `true` where a live "watch it respond" affordance is the point.
|
|
55
|
+
*/
|
|
56
|
+
live?: boolean;
|
|
57
|
+
brand?: PadBrandKey;
|
|
58
|
+
glyphSet?: GlyphSet;
|
|
59
|
+
/** Draw the pad's own button letters. Default true. */
|
|
60
|
+
labels?: boolean;
|
|
61
|
+
/** Per-role hue on face keycaps in the chips (confirm green, back amber). Default true. */
|
|
62
|
+
colored?: boolean;
|
|
63
|
+
/** Drawing convention for the joystick pictograms in the chips. See {@link StickPictogramStyle}. */
|
|
64
|
+
stickStyle?: StickPictogramStyle;
|
|
65
|
+
/** Pad size preset. Default `"lg"` — this view is meant to be read. */
|
|
66
|
+
size?: "sm" | "md" | "lg";
|
|
67
|
+
accent?: string;
|
|
68
|
+
/** Optional heading above the pad. */
|
|
69
|
+
title?: string;
|
|
70
|
+
/** Buttons the rider's device actually has; the rest are drawn faint. See `ControllerPadOptions`. */
|
|
71
|
+
available?: readonly ButtonName[];
|
|
72
|
+
}
|
|
73
|
+
export interface ControllerMap {
|
|
74
|
+
readonly root: HTMLDivElement;
|
|
75
|
+
readonly pad: ControllerPad;
|
|
76
|
+
/** Replace the action list. Rebuilds the callouts and re-routes the lines. */
|
|
77
|
+
setActions(actions: readonly ControlAction[]): void;
|
|
78
|
+
setVisible(on: boolean): void;
|
|
79
|
+
dispose(): void;
|
|
80
|
+
}
|
|
81
|
+
export declare function mountControllerMap(host: HTMLElement, opts: ControllerMapOptions): ControllerMap;
|
|
82
|
+
//# sourceMappingURL=controller-map.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller-map.d.ts","sourceRoot":"","sources":["../../../src/ui/controller/controller-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAGzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAsB,KAAK,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGlG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAW,MAAM,YAAY,CAAC;AAEvD,MAAM,WAAW,oBAAoB;IACnC,2DAA2D;IAC3D,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;IAClC;;;OAGG;IACH,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,uDAAuD;IACvD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,2FAA2F;IAC3F,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oGAAoG;IACpG,UAAU,CAAC,EAAE,mBAAmB,CAAC;IACjC,uEAAuE;IACvE,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qGAAqG;IACrG,SAAS,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAC;IAC5B,8EAA8E;IAC9E,UAAU,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,GAAG,IAAI,CAAC;IACpD,UAAU,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9B,OAAO,IAAI,IAAI,CAAC;CACjB;AA4BD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,oBAAoB,GAAG,aAAa,CAmQ/F"}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CONTROLLER MAP — the static "here are your controls" view: the live pad, flanked by callout cards, each
|
|
3
|
+
* joined by a line to the button it belongs to.
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* const map = mountControllerMap(host, {
|
|
7
|
+
* press: session,
|
|
8
|
+
* actions: [
|
|
9
|
+
* { label: "Se déplacer", combo: ["LSTICK"] },
|
|
10
|
+
* { label: "Freiner", combo: ["LT"] },
|
|
11
|
+
* { label: "Tir chargé", combo: ["RT", "DIAMOND_DOWN"] },
|
|
12
|
+
* { label: "Dash", combo: ["LT", "LSTICK:left"] },
|
|
13
|
+
* ],
|
|
14
|
+
* });
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* **It is an inline widget, not a screen.** It renders a self-sized block into whatever host you give it
|
|
18
|
+
* — no fixed positioning, no backdrop, no close button, no back-button handling. Whether that host is a
|
|
19
|
+
* pause overlay, a pre-race panel or a settings tab is the game's call, same as every other component in
|
|
20
|
+
* the kit. For a pause overlay, own the host yourself:
|
|
21
|
+
*
|
|
22
|
+
* ```ts
|
|
23
|
+
* const overlay = document.createElement("div");
|
|
24
|
+
* overlay.style.cssText = "position:fixed; inset:0; display:grid; place-items:center; background:rgba(4,8,14,.72)";
|
|
25
|
+
* document.body.appendChild(overlay);
|
|
26
|
+
* mountControllerMap(overlay, { press: session, actions });
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* The one genuinely new mechanic here is the **connector layer**. The lines join DOM cards to SVG nodes,
|
|
30
|
+
* so they can't live in the pad's own viewBox — they're drawn in page-pixel space from measured
|
|
31
|
+
* rectangles. Re-routing is therefore layout-dependent and is triggered by a `ResizeObserver` on the root
|
|
32
|
+
* *and on every card* (a label wrapping to two lines moves every card below it), by `document.fonts.ready`
|
|
33
|
+
* (a web font swapping in reflows labels after first paint), and by a pad re-letter. Every trigger is
|
|
34
|
+
* coalesced into a single `requestAnimationFrame` — this never runs per frame.
|
|
35
|
+
*/
|
|
36
|
+
import { buildCard } from "../card.js";
|
|
37
|
+
import { createComboStrip } from "./combo-strip.js";
|
|
38
|
+
import { mountControllerPad } from "./controller-pad.js";
|
|
39
|
+
import { anchorPartOfToken, anchorTokenOf, partCentreOf, sideOfPart } from "./parts-map.js";
|
|
40
|
+
import { injectPadCss } from "./styles.js";
|
|
41
|
+
const NS = "http://www.w3.org/2000/svg";
|
|
42
|
+
/**
|
|
43
|
+
* Beyond this many callouts per side the lines get dense and the cards squeeze. The component still
|
|
44
|
+
* renders — it warns rather than truncating, because silently dropping a control from a controls screen
|
|
45
|
+
* is worse than a crowded one.
|
|
46
|
+
*/
|
|
47
|
+
const SOFT_CAP_PER_SIDE = 5;
|
|
48
|
+
/**
|
|
49
|
+
* The pad column's width per size preset. Upper bounds match the `.rydr-pad.sm/.md/.lg` max-widths; the
|
|
50
|
+
* `clamp` lets the pad shrink on a narrow host instead of squeezing the callout columns to nothing.
|
|
51
|
+
*/
|
|
52
|
+
const PAD_COL_WIDTH = {
|
|
53
|
+
sm: "clamp(150px, 20vw, 220px)",
|
|
54
|
+
md: "clamp(190px, 28vw, 340px)",
|
|
55
|
+
lg: "clamp(220px, 34vw, 460px)",
|
|
56
|
+
};
|
|
57
|
+
export function mountControllerMap(host, opts) {
|
|
58
|
+
injectPadCss();
|
|
59
|
+
const root = document.createElement("div");
|
|
60
|
+
root.className = "rydr-padmap";
|
|
61
|
+
// The shared card padding is asymmetric by default to seat a straddling keycap; these callouts have
|
|
62
|
+
// none, so even padding reads better. Set on the ancestor, which is the documented way to retune it.
|
|
63
|
+
root.style.setProperty("--rydr-ui-card-pad", "12px 16px 14px");
|
|
64
|
+
let titleEl;
|
|
65
|
+
if (opts.title) {
|
|
66
|
+
titleEl = document.createElement("h3");
|
|
67
|
+
titleEl.className = "rydr-padmap-title";
|
|
68
|
+
titleEl.textContent = opts.title;
|
|
69
|
+
root.appendChild(titleEl);
|
|
70
|
+
}
|
|
71
|
+
// The pad column needs a DEFINITE width (see styles.ts) — derive it from the size preset rather than
|
|
72
|
+
// letting an `auto` track and a `width:100%` SVG try to size each other.
|
|
73
|
+
const size = opts.size ?? "lg";
|
|
74
|
+
root.style.setProperty("--rydr-padmap-pad-w", PAD_COL_WIDTH[size]);
|
|
75
|
+
const leftCol = document.createElement("ul");
|
|
76
|
+
leftCol.className = "rydr-padmap-col left";
|
|
77
|
+
const padCell = document.createElement("div");
|
|
78
|
+
padCell.className = "rydr-padmap-padcell";
|
|
79
|
+
const rightCol = document.createElement("ul");
|
|
80
|
+
rightCol.className = "rydr-padmap-col right";
|
|
81
|
+
root.appendChild(leftCol);
|
|
82
|
+
root.appendChild(padCell);
|
|
83
|
+
root.appendChild(rightCol);
|
|
84
|
+
const lines = document.createElementNS(NS, "svg");
|
|
85
|
+
lines.setAttribute("class", "rydr-padmap-lines");
|
|
86
|
+
lines.setAttribute("aria-hidden", "true");
|
|
87
|
+
root.appendChild(lines);
|
|
88
|
+
host.appendChild(root);
|
|
89
|
+
// Glyph-only unless `live`: the pad still learns the rider's lettering and follows a pad swap, but its
|
|
90
|
+
// inert `onButton` means nothing reflects presses.
|
|
91
|
+
const padPress = opts.press && (opts.live ? opts.press : { onButton: () => () => { }, hardware: opts.press.hardware });
|
|
92
|
+
const pad = mountControllerPad(padCell, {
|
|
93
|
+
press: padPress,
|
|
94
|
+
brand: opts.brand,
|
|
95
|
+
glyphSet: opts.glyphSet,
|
|
96
|
+
size: opts.size ?? "lg",
|
|
97
|
+
labels: opts.labels,
|
|
98
|
+
accent: opts.accent,
|
|
99
|
+
available: opts.available,
|
|
100
|
+
});
|
|
101
|
+
let callouts = [];
|
|
102
|
+
let warnedCap = false;
|
|
103
|
+
// ── routing ─────────────────────────────────────────────────────────────────────────────────────
|
|
104
|
+
let routeQueued = 0;
|
|
105
|
+
function queueRoute() {
|
|
106
|
+
if (routeQueued)
|
|
107
|
+
return;
|
|
108
|
+
routeQueued = requestAnimationFrame(() => {
|
|
109
|
+
routeQueued = 0;
|
|
110
|
+
route();
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
function route() {
|
|
114
|
+
const rootRect = root.getBoundingClientRect();
|
|
115
|
+
if (rootRect.width < 1 || rootRect.height < 1)
|
|
116
|
+
return; // not laid out yet (hidden host, etc.)
|
|
117
|
+
// The narrow-host media query stacks the columns and hides this layer; don't compute geometry for
|
|
118
|
+
// something nobody can see.
|
|
119
|
+
if (getComputedStyle(lines).display === "none")
|
|
120
|
+
return;
|
|
121
|
+
lines.setAttribute("viewBox", `0 0 ${rootRect.width} ${rootRect.height}`);
|
|
122
|
+
lines.setAttribute("width", String(rootRect.width));
|
|
123
|
+
lines.setAttribute("height", String(rootRect.height));
|
|
124
|
+
lines.textContent = "";
|
|
125
|
+
const parts = pad.svg().parts;
|
|
126
|
+
// Stagger the elbow per card WITHIN its column so parallel runs don't overlap into one thick line.
|
|
127
|
+
const seen = { left: 0, right: 0 };
|
|
128
|
+
for (const c of callouts) {
|
|
129
|
+
const partNode = c.anchorPart ? parts.get(c.anchorPart) : undefined;
|
|
130
|
+
if (!partNode)
|
|
131
|
+
continue; // this layout has no such part (a Zwift pod has no sticks)
|
|
132
|
+
const pr = partNode.getBoundingClientRect();
|
|
133
|
+
const cr = c.li.getBoundingClientRect();
|
|
134
|
+
if (pr.width < 1 || cr.width < 1)
|
|
135
|
+
continue;
|
|
136
|
+
const i = seen[c.side]++;
|
|
137
|
+
const leftward = c.side === "left";
|
|
138
|
+
const from = {
|
|
139
|
+
x: (leftward ? cr.right : cr.left) - rootRect.left,
|
|
140
|
+
y: cr.top - rootRect.top + cr.height / 2,
|
|
141
|
+
};
|
|
142
|
+
// Stop at the button's NEAR EDGE, not its centre, so the terminating dot marks the control
|
|
143
|
+
// without sitting on top of its letter.
|
|
144
|
+
const to = {
|
|
145
|
+
x: (leftward ? pr.left - 4 : pr.right + 4) - rootRect.left,
|
|
146
|
+
y: pr.top - rootRect.top + pr.height / 2,
|
|
147
|
+
};
|
|
148
|
+
// The elbow MUST land between the two endpoints. Clamping it against the TARGET alone (which is
|
|
149
|
+
// what this did) can put it behind `from`, and the polyline then juts backwards out of the card
|
|
150
|
+
// before doubling back on itself — a visibly broken line. Clamping to the span can't do that.
|
|
151
|
+
const lo = Math.min(from.x, to.x) + 2;
|
|
152
|
+
const hi = Math.max(from.x, to.x) - 2;
|
|
153
|
+
const spur = leftward ? from.x + (10 + i * 8) : from.x - (10 + i * 8);
|
|
154
|
+
const elbowX = hi <= lo ? (from.x + to.x) / 2 : Math.min(hi, Math.max(lo, spur));
|
|
155
|
+
// A card sitting level with its button needs a straight line, not a 4-point path with a
|
|
156
|
+
// zero-length riser — which is what produced the kinked stub at the join.
|
|
157
|
+
const level = Math.abs(from.y - to.y) < 2;
|
|
158
|
+
const p = (x, y) => `${x.toFixed(1)},${y.toFixed(1)}`;
|
|
159
|
+
const poly = document.createElementNS(NS, "polyline");
|
|
160
|
+
poly.setAttribute("class", "rydr-padmap-line" + (c.action.disabled ? " dim" : ""));
|
|
161
|
+
poly.setAttribute("points", level
|
|
162
|
+
? `${p(from.x, from.y)} ${p(to.x, to.y)}`
|
|
163
|
+
: `${p(from.x, from.y)} ${p(elbowX, from.y)} ${p(elbowX, to.y)} ${p(to.x, to.y)}`);
|
|
164
|
+
lines.appendChild(poly);
|
|
165
|
+
// A DOT, not an arrowhead. See `.rydr-padmap-dot` in styles.ts for why.
|
|
166
|
+
const dot = document.createElementNS(NS, "circle");
|
|
167
|
+
dot.setAttribute("class", "rydr-padmap-dot" + (c.action.disabled ? " dim" : ""));
|
|
168
|
+
dot.setAttribute("cx", to.x.toFixed(1));
|
|
169
|
+
dot.setAttribute("cy", to.y.toFixed(1));
|
|
170
|
+
dot.setAttribute("r", "2.9");
|
|
171
|
+
lines.appendChild(dot);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// ── callouts ────────────────────────────────────────────────────────────────────────────────────
|
|
175
|
+
const cardObserver = new ResizeObserver(() => queueRoute());
|
|
176
|
+
function clearCallouts() {
|
|
177
|
+
cardObserver.disconnect();
|
|
178
|
+
for (const c of callouts) {
|
|
179
|
+
c.strip.dispose();
|
|
180
|
+
c.disposeCard();
|
|
181
|
+
c.li.remove();
|
|
182
|
+
}
|
|
183
|
+
callouts = [];
|
|
184
|
+
}
|
|
185
|
+
function buildCallouts(actions) {
|
|
186
|
+
clearCallouts();
|
|
187
|
+
const layout = pad.layout();
|
|
188
|
+
const prepared = actions.map((action) => {
|
|
189
|
+
const token = anchorTokenOf(action);
|
|
190
|
+
const anchorPart = token ? anchorPartOfToken(token) : undefined;
|
|
191
|
+
const side = action.side ?? (anchorPart ? sideOfPart(layout, anchorPart) : "left");
|
|
192
|
+
// Order down the column by the anchor's height on the pad, so lines run roughly parallel and
|
|
193
|
+
// don't cross. Anchorless actions sink to the bottom.
|
|
194
|
+
const order = (anchorPart ? partCentreOf(layout, anchorPart)?.y : undefined) ?? Number.MAX_SAFE_INTEGER;
|
|
195
|
+
return { action, anchorPart, side, order };
|
|
196
|
+
});
|
|
197
|
+
prepared.sort((a, b) => a.order - b.order);
|
|
198
|
+
for (const p of prepared) {
|
|
199
|
+
const li = document.createElement("li");
|
|
200
|
+
li.className = "rydr-padmap-item" + (p.action.disabled ? " disabled" : "");
|
|
201
|
+
const box = document.createElement("div");
|
|
202
|
+
box.className = "rydr-padmap-callout";
|
|
203
|
+
const label = document.createElement("div");
|
|
204
|
+
label.className = "rydr-padmap-label";
|
|
205
|
+
label.textContent = p.action.label;
|
|
206
|
+
box.appendChild(label);
|
|
207
|
+
if (p.action.hint) {
|
|
208
|
+
const hint = document.createElement("div");
|
|
209
|
+
hint.className = "rydr-padmap-hint";
|
|
210
|
+
hint.textContent = p.action.hint;
|
|
211
|
+
box.appendChild(hint);
|
|
212
|
+
}
|
|
213
|
+
const strip = createComboStrip(p.action, {
|
|
214
|
+
glyphSet: opts.glyphSet ?? pad.glyphSet(),
|
|
215
|
+
press: opts.live ? opts.press : undefined,
|
|
216
|
+
colored: opts.colored,
|
|
217
|
+
stickStyle: opts.stickStyle,
|
|
218
|
+
});
|
|
219
|
+
box.appendChild(strip.el);
|
|
220
|
+
const card = buildCard({ inline: true, content: box });
|
|
221
|
+
if (p.action.disabled)
|
|
222
|
+
card.setDisabled(true);
|
|
223
|
+
li.appendChild(card.root);
|
|
224
|
+
(p.side === "left" ? leftCol : rightCol).appendChild(li);
|
|
225
|
+
cardObserver.observe(li);
|
|
226
|
+
callouts.push({
|
|
227
|
+
action: p.action,
|
|
228
|
+
li,
|
|
229
|
+
strip,
|
|
230
|
+
disposeCard: card.dispose,
|
|
231
|
+
anchorPart: p.anchorPart,
|
|
232
|
+
side: p.side,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
// Un-dim any control an action actually names. `SHELL_OWNED_PARTS` draws the bumpers / View / Guide
|
|
236
|
+
// faint because they are not a GAME's to use — but when a callout points at one it is the subject of
|
|
237
|
+
// the screen, and drawing the subject at 40% reads as "disabled". The shell's own resistance map is
|
|
238
|
+
// exactly that case.
|
|
239
|
+
const referenced = new Set();
|
|
240
|
+
for (const c of callouts) {
|
|
241
|
+
for (const token of c.action.combo) {
|
|
242
|
+
const part = anchorPartOfToken(token);
|
|
243
|
+
if (part)
|
|
244
|
+
referenced.add(part);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
for (const [name, node] of pad.svg().parts)
|
|
248
|
+
node.classList.toggle("referenced", referenced.has(name));
|
|
249
|
+
const perSide = { left: 0, right: 0 };
|
|
250
|
+
for (const c of callouts)
|
|
251
|
+
perSide[c.side]++;
|
|
252
|
+
if (!warnedCap && (perSide.left > SOFT_CAP_PER_SIDE || perSide.right > SOFT_CAP_PER_SIDE)) {
|
|
253
|
+
warnedCap = true;
|
|
254
|
+
console.warn(`[rydr/ui] mountControllerMap: ${perSide.left} left / ${perSide.right} right callouts — the ` +
|
|
255
|
+
`layout is tuned for up to ${SOFT_CAP_PER_SIDE} per side. Nothing is dropped, but consider ` +
|
|
256
|
+
`splitting the list across screens or setting \`side\` to rebalance.`);
|
|
257
|
+
}
|
|
258
|
+
queueRoute();
|
|
259
|
+
}
|
|
260
|
+
buildCallouts(opts.actions);
|
|
261
|
+
const rootObserver = new ResizeObserver(() => queueRoute());
|
|
262
|
+
rootObserver.observe(root);
|
|
263
|
+
// A web font swapping in after first paint re-wraps every label, which moves every card.
|
|
264
|
+
void document.fonts?.ready.then(() => queueRoute()).catch(() => { });
|
|
265
|
+
// A pad swap rebuilds the drawing, so the cached part nodes we measure are new ones.
|
|
266
|
+
const offRebuild = pad.onRebuild(() => {
|
|
267
|
+
buildCallouts(callouts.map((c) => c.action));
|
|
268
|
+
});
|
|
269
|
+
return {
|
|
270
|
+
root,
|
|
271
|
+
pad,
|
|
272
|
+
setActions(actions) {
|
|
273
|
+
buildCallouts(actions);
|
|
274
|
+
},
|
|
275
|
+
setVisible(on) {
|
|
276
|
+
root.style.display = on ? "" : "none";
|
|
277
|
+
if (on)
|
|
278
|
+
queueRoute();
|
|
279
|
+
},
|
|
280
|
+
dispose() {
|
|
281
|
+
if (routeQueued)
|
|
282
|
+
cancelAnimationFrame(routeQueued);
|
|
283
|
+
rootObserver.disconnect();
|
|
284
|
+
offRebuild();
|
|
285
|
+
clearCallouts();
|
|
286
|
+
pad.dispose();
|
|
287
|
+
root.remove();
|
|
288
|
+
},
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
//# sourceMappingURL=controller-map.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller-map.js","sourceRoot":"","sources":["../../../src/ui/controller/controller-map.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CONTROLLER PAD — the live drawing of the controller in the rider's hands.
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* const pad = mountControllerPad(host, { press: session, size: "lg" });
|
|
6
|
+
* // …buttons light as they're pressed, stick caps follow the real axes. Nothing to call per frame.
|
|
7
|
+
* pad.dispose();
|
|
8
|
+
* ```
|
|
9
|
+
*
|
|
10
|
+
* This is the game-side counterpart of the shell's own controller diagram, and it is deliberately fed by
|
|
11
|
+
* **canonical input, not raw hardware**: a game never sees gamepad indices, per-device bindings or analog
|
|
12
|
+
* trigger travel, so this module translates `ButtonName` → drawn position (`parts-map.ts`) and polls
|
|
13
|
+
* `session.axis()` for the sticks. Three consequences worth knowing:
|
|
14
|
+
*
|
|
15
|
+
* 1. **Axes are polled, buttons are not.** `onButton` is an edge stream, so presses cost nothing between
|
|
16
|
+
* events; axes have no event, so when the source exposes `axis()` the pad runs its own
|
|
17
|
+
* `requestAnimationFrame` loop. It stops on `dispose()` and while the tab is hidden, and the
|
|
18
|
+
* `PadDriver` dirty-guard means an idle stick performs zero DOM writes per frame.
|
|
19
|
+
*
|
|
20
|
+
* 2. **The d-pad is shared with the left stick.** `UP`/`DOWN`/`LEFT`/`RIGHT` are emitted by the d-pad
|
|
21
|
+
* *and* by the left stick past its deadzone (see `protocol/buttons.ts`), and nothing in the canonical
|
|
22
|
+
* stream says which. When `axis()` is available we suppress the d-pad highlight for a direction the
|
|
23
|
+
* stick already explains — so pushing the stick moves the cap instead of falsely lighting the d-pad.
|
|
24
|
+
* Without `axis()` the d-pad lights for both, which is the honest best we can do.
|
|
25
|
+
*
|
|
26
|
+
* 3. **Triggers are clicks.** `LT`/`RT` have no analog travel in this protocol, so their travel meters
|
|
27
|
+
* are driven 0/1 from button state.
|
|
28
|
+
*
|
|
29
|
+
* It also **re-letters itself.** A keycap resolves its glyph set once, on the reasoning that keycaps are
|
|
30
|
+
* cheap to recreate; a controls screen is long-lived and `/hardware` is exactly where a rider swaps pads.
|
|
31
|
+
* So if the source exposes `hardware.subscribe`, the pad rebuilds its drawing in place when the resolved
|
|
32
|
+
* set changes, restoring anything currently held. Callers keep their handle and need to know nothing.
|
|
33
|
+
*/
|
|
34
|
+
import type { AxisName, ButtonEdge, ButtonName } from "../../protocol/buttons.js";
|
|
35
|
+
import { type GlyphSet } from "../../protocol/glyphs.js";
|
|
36
|
+
import { type PadDriver, type PadSvg } from "./pad-svg.js";
|
|
37
|
+
import type { ControlName } from "./tokens.js";
|
|
38
|
+
import type { PadBrandKey, PadLayout } from "./types.js";
|
|
39
|
+
/**
|
|
40
|
+
* The slice of a `PlatformSession` the controller views consume. A session satisfies it structurally —
|
|
41
|
+
* just pass `session`. Everything but `onButton` is optional so a hand-rolled stub or a showcase double
|
|
42
|
+
* works too; the pad simply does less with less.
|
|
43
|
+
*/
|
|
44
|
+
export interface PadInputSource {
|
|
45
|
+
/**
|
|
46
|
+
* Typed on {@link ControlName} — a superset of `ButtonName` that also covers the shell-owned controls.
|
|
47
|
+
*
|
|
48
|
+
* A `PlatformSession` only ever emits the `ButtonName` subset and still satisfies this, while a
|
|
49
|
+
* SHELL-side source can hand the same object to both this and `mountControlTrainer`. They used to
|
|
50
|
+
* take different shapes, which meant a caller with one source could feed the lesson but not the map.
|
|
51
|
+
*/
|
|
52
|
+
onButton(cb: (e: {
|
|
53
|
+
name: ControlName;
|
|
54
|
+
edge: ButtonEdge;
|
|
55
|
+
}) => void): () => void;
|
|
56
|
+
/** Analog stick positions, −1..1 (right = +1, up = +1). Present → the sticks and cap glow come alive. */
|
|
57
|
+
axis?(name: AxisName): number;
|
|
58
|
+
/** Currently-held buttons, for showing the right state immediately on mount instead of on next press. */
|
|
59
|
+
buttonsDown?(): ButtonName[];
|
|
60
|
+
/** The hardware store — read for the rider's glyph set, subscribed to for pad swaps. */
|
|
61
|
+
hardware?: {
|
|
62
|
+
current: {
|
|
63
|
+
glyphSet?: GlyphSet;
|
|
64
|
+
};
|
|
65
|
+
subscribe?(cb: (snapshot: {
|
|
66
|
+
glyphSet?: GlyphSet;
|
|
67
|
+
}) => void): () => void;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export interface ControllerPadOptions {
|
|
71
|
+
/** Wire to real input. Omit for static art. */
|
|
72
|
+
press?: PadInputSource;
|
|
73
|
+
/** Force a silhouette. Default: derived from the glyph set (`"joycon"` is only reachable this way). */
|
|
74
|
+
brand?: PadBrandKey;
|
|
75
|
+
/**
|
|
76
|
+
* Force the lettering. Default: the set reported by `press`, else `"xbox"`. Setting this explicitly
|
|
77
|
+
* also PINS it — the pad stops following pad swaps, which is what a settings preview wants.
|
|
78
|
+
*/
|
|
79
|
+
glyphSet?: GlyphSet;
|
|
80
|
+
/** Max width preset. Default `"md"` (340px). */
|
|
81
|
+
size?: "sm" | "md" | "lg";
|
|
82
|
+
/** Draw the face/trigger/bumper letters on the art. Default true. */
|
|
83
|
+
labels?: boolean;
|
|
84
|
+
/** Dim the whole drawing and skip the input subscription — a placeholder or a backdrop. */
|
|
85
|
+
ghost?: boolean;
|
|
86
|
+
/** Accent colour for pressed parts. Themed through this option, never by overriding the CSS. */
|
|
87
|
+
accent?: string;
|
|
88
|
+
/**
|
|
89
|
+
* Buttons the rider's device actually has; everything else is drawn faint.
|
|
90
|
+
*
|
|
91
|
+
* Opt-in because the protocol cannot tell us: `HardwareSnapshot` reports `controllerConnected` and
|
|
92
|
+
* `glyphSet` but not a button inventory, while `protocol/buttons.ts` warns that a phone exposes only
|
|
93
|
+
* the `DIAMOND_DOWN`/`DIAMOND_RIGHT` pair. Pass it when your game knows better.
|
|
94
|
+
*/
|
|
95
|
+
available?: readonly ButtonName[];
|
|
96
|
+
/**
|
|
97
|
+
* Let the drawing light the shell-owned controls (the resistance bumpers, View, Guide).
|
|
98
|
+
*
|
|
99
|
+
* Off by default and normally correct that way: a game never receives them, so lighting them would
|
|
100
|
+
* tell the rider something untrue. The legitimate caller is a lesson that TEACHES those controls —
|
|
101
|
+
* `mountControlTrainer` sets it when a step names one.
|
|
102
|
+
*/
|
|
103
|
+
allowShellOwned?: boolean;
|
|
104
|
+
}
|
|
105
|
+
export interface ControllerPad {
|
|
106
|
+
readonly root: HTMLDivElement;
|
|
107
|
+
/** The current drawing's node cache. Replaced on a re-letter — don't hold onto it across one. */
|
|
108
|
+
svg(): PadSvg;
|
|
109
|
+
driver(): PadDriver;
|
|
110
|
+
layout(): PadLayout;
|
|
111
|
+
/**
|
|
112
|
+
* The lettering currently drawn. Read it rather than re-deriving: anything built alongside the pad
|
|
113
|
+
* (the map's chips) must use the SAME set, or a pad swap leaves the two disagreeing.
|
|
114
|
+
*/
|
|
115
|
+
glyphSet(): GlyphSet;
|
|
116
|
+
/** Fires after the drawing is rebuilt (a pad swap). The map view uses it to re-route its lines. */
|
|
117
|
+
onRebuild(cb: () => void): () => void;
|
|
118
|
+
setVisible(on: boolean): void;
|
|
119
|
+
dispose(): void;
|
|
120
|
+
}
|
|
121
|
+
export declare function mountControllerPad(host: HTMLElement, opts?: ControllerPadOptions): ControllerPad;
|
|
122
|
+
//# sourceMappingURL=controller-pad.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller-pad.d.ts","sourceRoot":"","sources":["../../../src/ui/controller/controller-pad.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAClF,OAAO,EAAqB,KAAK,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAG5E,OAAO,EAAoD,KAAK,SAAS,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAE7G,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAW,MAAM,YAAY,CAAC;AAElE;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAE,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAC/E,yGAAyG;IACzG,IAAI,CAAC,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC9B,yGAAyG;IACzG,WAAW,CAAC,IAAI,UAAU,EAAE,CAAC;IAC7B,wFAAwF;IACxF,QAAQ,CAAC,EAAE;QACT,OAAO,EAAE;YAAE,QAAQ,CAAC,EAAE,QAAQ,CAAA;SAAE,CAAC;QACjC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,EAAE;YAAE,QAAQ,CAAC,EAAE,QAAQ,CAAA;SAAE,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;KACzE,CAAC;CACH;AAED,MAAM,WAAW,oBAAoB;IACnC,+CAA+C;IAC/C,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,uGAAuG;IACvG,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,gDAAgD;IAChD,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,qEAAqE;IACrE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,2FAA2F;IAC3F,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,gGAAgG;IAChG,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IAClC;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,iGAAiG;IACjG,GAAG,IAAI,MAAM,CAAC;IACd,MAAM,IAAI,SAAS,CAAC;IACpB,MAAM,IAAI,SAAS,CAAC;IACpB;;;OAGG;IACH,QAAQ,IAAI,QAAQ,CAAC;IACrB,mGAAmG;IACnG,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;IACtC,UAAU,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9B,OAAO,IAAI,IAAI,CAAC;CACjB;AAUD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,GAAE,oBAAyB,GAAG,aAAa,CAuJpG"}
|