@joydle/ui 0.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 (161) hide show
  1. package/dist/controls/ActionButton.d.ts +18 -0
  2. package/dist/controls/ActionButton.d.ts.map +1 -0
  3. package/dist/controls/ActionButton.js +108 -0
  4. package/dist/controls/ActionButton.js.map +1 -0
  5. package/dist/controls/ActionButtonGroup.d.ts +15 -0
  6. package/dist/controls/ActionButtonGroup.d.ts.map +1 -0
  7. package/dist/controls/ActionButtonGroup.js +55 -0
  8. package/dist/controls/ActionButtonGroup.js.map +1 -0
  9. package/dist/controls/Joystick.d.ts +9 -0
  10. package/dist/controls/Joystick.d.ts.map +1 -0
  11. package/dist/controls/Joystick.js +168 -0
  12. package/dist/controls/Joystick.js.map +1 -0
  13. package/dist/controls/SwipeZone.d.ts +10 -0
  14. package/dist/controls/SwipeZone.d.ts.map +1 -0
  15. package/dist/controls/SwipeZone.js +84 -0
  16. package/dist/controls/SwipeZone.js.map +1 -0
  17. package/dist/controls/TouchDPad.d.ts +9 -0
  18. package/dist/controls/TouchDPad.d.ts.map +1 -0
  19. package/dist/controls/TouchDPad.js +90 -0
  20. package/dist/controls/TouchDPad.js.map +1 -0
  21. package/dist/controls/index.d.ts +6 -0
  22. package/dist/controls/index.d.ts.map +1 -0
  23. package/dist/controls/index.js +9 -0
  24. package/dist/controls/index.js.map +1 -0
  25. package/dist/core/GameShell.d.ts +43 -0
  26. package/dist/core/GameShell.d.ts.map +1 -0
  27. package/dist/core/GameShell.js +91 -0
  28. package/dist/core/GameShell.js.map +1 -0
  29. package/dist/core/GameState.d.ts +35 -0
  30. package/dist/core/GameState.d.ts.map +1 -0
  31. package/dist/core/GameState.js +74 -0
  32. package/dist/core/GameState.js.map +1 -0
  33. package/dist/core/InputManager.d.ts +39 -0
  34. package/dist/core/InputManager.d.ts.map +1 -0
  35. package/dist/core/InputManager.js +149 -0
  36. package/dist/core/InputManager.js.map +1 -0
  37. package/dist/core/ScaleManager.d.ts +38 -0
  38. package/dist/core/ScaleManager.d.ts.map +1 -0
  39. package/dist/core/ScaleManager.js +106 -0
  40. package/dist/core/ScaleManager.js.map +1 -0
  41. package/dist/core/index.d.ts +9 -0
  42. package/dist/core/index.d.ts.map +1 -0
  43. package/dist/core/index.js +14 -0
  44. package/dist/core/index.js.map +1 -0
  45. package/dist/core/types.d.ts +113 -0
  46. package/dist/core/types.d.ts.map +1 -0
  47. package/dist/core/types.js +5 -0
  48. package/dist/core/types.js.map +1 -0
  49. package/dist/core/useGame.d.ts +22 -0
  50. package/dist/core/useGame.d.ts.map +1 -0
  51. package/dist/core/useGame.js +40 -0
  52. package/dist/core/useGame.js.map +1 -0
  53. package/dist/core/useGameLoop.d.ts +16 -0
  54. package/dist/core/useGameLoop.d.ts.map +1 -0
  55. package/dist/core/useGameLoop.js +35 -0
  56. package/dist/core/useGameLoop.js.map +1 -0
  57. package/dist/core/useGameSetup.d.ts +16 -0
  58. package/dist/core/useGameSetup.d.ts.map +1 -0
  59. package/dist/core/useGameSetup.js +27 -0
  60. package/dist/core/useGameSetup.js.map +1 -0
  61. package/dist/hud/BottomHint.d.ts +17 -0
  62. package/dist/hud/BottomHint.d.ts.map +1 -0
  63. package/dist/hud/BottomHint.js +68 -0
  64. package/dist/hud/BottomHint.js.map +1 -0
  65. package/dist/hud/ComboLabel.d.ts +18 -0
  66. package/dist/hud/ComboLabel.d.ts.map +1 -0
  67. package/dist/hud/ComboLabel.js +125 -0
  68. package/dist/hud/ComboLabel.js.map +1 -0
  69. package/dist/hud/HUD.d.ts +18 -0
  70. package/dist/hud/HUD.d.ts.map +1 -0
  71. package/dist/hud/HUD.js +72 -0
  72. package/dist/hud/HUD.js.map +1 -0
  73. package/dist/hud/Lives.d.ts +21 -0
  74. package/dist/hud/Lives.d.ts.map +1 -0
  75. package/dist/hud/Lives.js +92 -0
  76. package/dist/hud/Lives.js.map +1 -0
  77. package/dist/hud/Meter.d.ts +24 -0
  78. package/dist/hud/Meter.d.ts.map +1 -0
  79. package/dist/hud/Meter.js +133 -0
  80. package/dist/hud/Meter.js.map +1 -0
  81. package/dist/hud/MiniMap.d.ts +41 -0
  82. package/dist/hud/MiniMap.d.ts.map +1 -0
  83. package/dist/hud/MiniMap.js +103 -0
  84. package/dist/hud/MiniMap.js.map +1 -0
  85. package/dist/hud/Score.d.ts +15 -0
  86. package/dist/hud/Score.d.ts.map +1 -0
  87. package/dist/hud/Score.js +74 -0
  88. package/dist/hud/Score.js.map +1 -0
  89. package/dist/hud/Timer.d.ts +21 -0
  90. package/dist/hud/Timer.d.ts.map +1 -0
  91. package/dist/hud/Timer.js +111 -0
  92. package/dist/hud/Timer.js.map +1 -0
  93. package/dist/hud/WaveLabel.d.ts +15 -0
  94. package/dist/hud/WaveLabel.d.ts.map +1 -0
  95. package/dist/hud/WaveLabel.js +70 -0
  96. package/dist/hud/WaveLabel.js.map +1 -0
  97. package/dist/hud/index.d.ts +10 -0
  98. package/dist/hud/index.d.ts.map +1 -0
  99. package/dist/hud/index.js +13 -0
  100. package/dist/hud/index.js.map +1 -0
  101. package/dist/index.d.ts +8 -0
  102. package/dist/index.d.ts.map +1 -0
  103. package/dist/index.js +13 -0
  104. package/dist/index.js.map +1 -0
  105. package/dist/screens/GameOverScreen.d.ts +36 -0
  106. package/dist/screens/GameOverScreen.d.ts.map +1 -0
  107. package/dist/screens/GameOverScreen.js +255 -0
  108. package/dist/screens/GameOverScreen.js.map +1 -0
  109. package/dist/screens/LoadingScreen.d.ts +21 -0
  110. package/dist/screens/LoadingScreen.d.ts.map +1 -0
  111. package/dist/screens/LoadingScreen.js +129 -0
  112. package/dist/screens/LoadingScreen.js.map +1 -0
  113. package/dist/screens/LobbyScreen.d.ts +39 -0
  114. package/dist/screens/LobbyScreen.d.ts.map +1 -0
  115. package/dist/screens/LobbyScreen.js +266 -0
  116. package/dist/screens/LobbyScreen.js.map +1 -0
  117. package/dist/screens/PauseOverlay.d.ts +29 -0
  118. package/dist/screens/PauseOverlay.d.ts.map +1 -0
  119. package/dist/screens/PauseOverlay.js +158 -0
  120. package/dist/screens/PauseOverlay.js.map +1 -0
  121. package/dist/screens/ScreenManager.d.ts +26 -0
  122. package/dist/screens/ScreenManager.d.ts.map +1 -0
  123. package/dist/screens/ScreenManager.js +61 -0
  124. package/dist/screens/ScreenManager.js.map +1 -0
  125. package/dist/screens/TitleScreen.d.ts +30 -0
  126. package/dist/screens/TitleScreen.d.ts.map +1 -0
  127. package/dist/screens/TitleScreen.js +263 -0
  128. package/dist/screens/TitleScreen.js.map +1 -0
  129. package/dist/screens/index.d.ts +7 -0
  130. package/dist/screens/index.d.ts.map +1 -0
  131. package/dist/screens/index.js +10 -0
  132. package/dist/screens/index.js.map +1 -0
  133. package/dist/theme/ThemeProvider.d.ts +25 -0
  134. package/dist/theme/ThemeProvider.d.ts.map +1 -0
  135. package/dist/theme/ThemeProvider.js +51 -0
  136. package/dist/theme/ThemeProvider.js.map +1 -0
  137. package/dist/theme/index.d.ts +4 -0
  138. package/dist/theme/index.d.ts.map +1 -0
  139. package/dist/theme/index.js +7 -0
  140. package/dist/theme/index.js.map +1 -0
  141. package/dist/theme/palettes.d.ts +10 -0
  142. package/dist/theme/palettes.d.ts.map +1 -0
  143. package/dist/theme/palettes.js +173 -0
  144. package/dist/theme/palettes.js.map +1 -0
  145. package/dist/theme/theme.d.ts +24 -0
  146. package/dist/theme/theme.d.ts.map +1 -0
  147. package/dist/theme/theme.js +29 -0
  148. package/dist/theme/theme.js.map +1 -0
  149. package/dist/transitions/ScreenTransition.d.ts +19 -0
  150. package/dist/transitions/ScreenTransition.d.ts.map +1 -0
  151. package/dist/transitions/ScreenTransition.js +105 -0
  152. package/dist/transitions/ScreenTransition.js.map +1 -0
  153. package/dist/transitions/index.d.ts +3 -0
  154. package/dist/transitions/index.d.ts.map +1 -0
  155. package/dist/transitions/index.js +6 -0
  156. package/dist/transitions/index.js.map +1 -0
  157. package/dist/transitions/presets.d.ts +21 -0
  158. package/dist/transitions/presets.d.ts.map +1 -0
  159. package/dist/transitions/presets.js +37 -0
  160. package/dist/transitions/presets.js.map +1 -0
  161. package/package.json +62 -0
@@ -0,0 +1,6 @@
1
+ export { TouchDPad, type TouchDPadProps } from './TouchDPad.js';
2
+ export { Joystick, type JoystickProps } from './Joystick.js';
3
+ export { ActionButton, type ActionButtonProps, type ActionButtonSize, } from './ActionButton.js';
4
+ export { ActionButtonGroup, type ActionButtonGroupProps, type ActionButtonConfig, } from './ActionButtonGroup.js';
5
+ export { SwipeZone, type SwipeZoneProps, type SwipeDirection, } from './SwipeZone.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/controls/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EACL,YAAY,EACZ,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,iBAAiB,EACjB,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,GACxB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,SAAS,EACT,KAAK,cAAc,EACnB,KAAK,cAAc,GACpB,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,9 @@
1
+ // ---------------------------------------------------------------------------
2
+ // @joydle/ui/controls — barrel export
3
+ // ---------------------------------------------------------------------------
4
+ export { TouchDPad } from './TouchDPad.js';
5
+ export { Joystick } from './Joystick.js';
6
+ export { ActionButton, } from './ActionButton.js';
7
+ export { ActionButtonGroup, } from './ActionButtonGroup.js';
8
+ export { SwipeZone, } from './SwipeZone.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/controls/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,uCAAuC;AACvC,8EAA8E;AAE9E,OAAO,EAAE,SAAS,EAAuB,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAsB,MAAM,eAAe,CAAC;AAC7D,OAAO,EACL,YAAY,GAGb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,iBAAiB,GAGlB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,SAAS,GAGV,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,43 @@
1
+ import type { ComponentChildren } from 'preact';
2
+ import { InputManager } from './InputManager';
3
+ import { ScaleManager, type ScaleMode } from './ScaleManager';
4
+ import { GameState } from './GameState';
5
+ import type { BridgeInstance, ThemeConfig, GameStateShape } from './types';
6
+ export interface GameContextValue {
7
+ inputManager: InputManager;
8
+ scaleManager: ScaleManager;
9
+ gameState: GameState<GameStateShape>;
10
+ bridge: BridgeInstance;
11
+ }
12
+ /**
13
+ * Context shared with all descendant components.
14
+ * Access via `useContext(GameContext)` or the `useGame()` hook.
15
+ */
16
+ export declare const GameContext: import("preact").Context<GameContextValue | null>;
17
+ export interface GameShellProps {
18
+ /** Logical game width in pixels. */
19
+ width: number;
20
+ /** Logical game height in pixels. */
21
+ height: number;
22
+ /** The renderer bridge instance. */
23
+ renderer: BridgeInstance;
24
+ /** Optional theme configuration. */
25
+ theme?: ThemeConfig;
26
+ /** Canvas scale mode (default: `"letterbox"`). */
27
+ scaleMode?: ScaleMode;
28
+ /** Overlay UI rendered on top of the canvas. */
29
+ children?: ComponentChildren;
30
+ }
31
+ /**
32
+ * Root shell that wires up a renderer bridge, input manager, and scale
33
+ * manager. Renders an overlay `<div>` on top of the canvas for HUD /
34
+ * screen components.
35
+ *
36
+ * ```tsx
37
+ * <GameShell width={800} height={600} renderer={bridge}>
38
+ * <HUD />
39
+ * </GameShell>
40
+ * ```
41
+ */
42
+ export declare function GameShell({ width, height, renderer, theme, scaleMode, children, }: GameShellProps): import("preact").JSX.Element;
43
+ //# sourceMappingURL=GameShell.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GameShell.d.ts","sourceRoot":"","sources":["../../src/core/GameShell.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAI3E,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,YAAY,CAAC;IAC3B,YAAY,EAAE,YAAY,CAAC;IAC3B,SAAS,EAAE,SAAS,CAAC,cAAc,CAAC,CAAC;IACrC,MAAM,EAAE,cAAc,CAAC;CACxB;AAED;;;GAGG;AACH,eAAO,MAAM,WAAW,mDAA+C,CAAC;AAIxE,MAAM,WAAW,cAAc;IAC7B,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,QAAQ,EAAE,cAAc,CAAC;IACzB,oCAAoC;IACpC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,kDAAkD;IAClD,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,gDAAgD;IAChD,QAAQ,CAAC,EAAE,iBAAiB,CAAC;CAC9B;AAsBD;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,MAAM,EACN,QAAQ,EACR,KAAK,EACL,SAAuB,EACvB,QAAQ,GACT,EAAE,cAAc,gCA+EhB"}
@@ -0,0 +1,91 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
+ // ---------------------------------------------------------------------------
3
+ // @joydle/ui — Root container component (Preact)
4
+ // ---------------------------------------------------------------------------
5
+ import { createContext } from 'preact';
6
+ import { useEffect, useRef, useMemo } from 'preact/hooks';
7
+ import { InputManager } from './InputManager';
8
+ import { ScaleManager } from './ScaleManager';
9
+ import { GameState } from './GameState';
10
+ /**
11
+ * Context shared with all descendant components.
12
+ * Access via `useContext(GameContext)` or the `useGame()` hook.
13
+ */
14
+ export const GameContext = createContext(null);
15
+ // ---- Styles ---------------------------------------------------------------
16
+ const SHELL_STYLE = {
17
+ position: 'relative',
18
+ overflow: 'hidden',
19
+ width: '100%',
20
+ height: '100%',
21
+ };
22
+ const OVERLAY_STYLE = {
23
+ position: 'absolute',
24
+ top: '0',
25
+ left: '0',
26
+ width: '100%',
27
+ height: '100%',
28
+ pointerEvents: 'none',
29
+ };
30
+ // ---- Component ------------------------------------------------------------
31
+ /**
32
+ * Root shell that wires up a renderer bridge, input manager, and scale
33
+ * manager. Renders an overlay `<div>` on top of the canvas for HUD /
34
+ * screen components.
35
+ *
36
+ * ```tsx
37
+ * <GameShell width={800} height={600} renderer={bridge}>
38
+ * <HUD />
39
+ * </GameShell>
40
+ * ```
41
+ */
42
+ export function GameShell({ width, height, renderer, theme, scaleMode = 'letterbox', children, }) {
43
+ const containerRef = useRef(null);
44
+ // Stable managers created once per mount.
45
+ const inputManager = useMemo(() => new InputManager(), []);
46
+ const scaleManager = useMemo(() => new ScaleManager(renderer.canvas, width, height, scaleMode),
47
+ // Intentionally only depends on renderer identity.
48
+ // eslint-disable-next-line react-hooks/exhaustive-deps
49
+ [renderer]);
50
+ const gameState = useMemo(() => new GameState({ screen: '' }), []);
51
+ // Mount the renderer canvas into our container.
52
+ useEffect(() => {
53
+ const container = containerRef.current;
54
+ if (!container)
55
+ return;
56
+ renderer.mount(container);
57
+ // Attach the renderer's input adapter to our InputManager.
58
+ const adapter = renderer.getInputAdapter();
59
+ adapter.attach(inputManager, renderer.canvas);
60
+ // Notify the renderer of the logical size.
61
+ renderer.resize(width, height);
62
+ return () => {
63
+ adapter.detach();
64
+ renderer.dispose();
65
+ };
66
+ // eslint-disable-next-line react-hooks/exhaustive-deps
67
+ }, [renderer]);
68
+ // Tear down managers on unmount.
69
+ useEffect(() => {
70
+ return () => {
71
+ inputManager.destroy();
72
+ scaleManager.destroy();
73
+ };
74
+ // eslint-disable-next-line react-hooks/exhaustive-deps
75
+ }, []);
76
+ // Apply theme CSS custom properties to the shell element.
77
+ // When a full ThemeConfig is provided the ThemeProvider is the canonical
78
+ // source of CSS vars, so we only set a minimal subset here as a fallback.
79
+ const shellStyle = useMemo(() => {
80
+ const base = { ...SHELL_STYLE };
81
+ if (theme) {
82
+ base['--joydle-primary'] = theme.primary;
83
+ base['--joydle-bg'] = theme.bg;
84
+ base['--joydle-font'] = theme.font;
85
+ }
86
+ return base;
87
+ }, [theme]);
88
+ const ctxValue = useMemo(() => ({ inputManager, scaleManager, gameState, bridge: renderer }), [inputManager, scaleManager, gameState, renderer]);
89
+ return (_jsx(GameContext.Provider, { value: ctxValue, children: _jsxs("div", { class: "joydle-shell", style: shellStyle, children: [_jsx("div", { ref: containerRef, style: { width: '100%', height: '100%' } }), _jsx("div", { class: "joydle-overlay", style: OVERLAY_STYLE, children: children })] }) }));
90
+ }
91
+ //# sourceMappingURL=GameShell.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GameShell.js","sourceRoot":"","sources":["../../src/core/GameShell.tsx"],"names":[],"mappings":";AAAA,8EAA8E;AAC9E,kDAAkD;AAClD,8EAA8E;AAE9E,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE1D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAkB,MAAM,gBAAgB,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAYxC;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,aAAa,CAA0B,IAAI,CAAC,CAAC;AAmBxE,8EAA8E;AAE9E,MAAM,WAAW,GAA2B;IAC1C,QAAQ,EAAE,UAAU;IACpB,QAAQ,EAAE,QAAQ;IAClB,KAAK,EAAE,MAAM;IACb,MAAM,EAAE,MAAM;CACf,CAAC;AAEF,MAAM,aAAa,GAA2B;IAC5C,QAAQ,EAAE,UAAU;IACpB,GAAG,EAAE,GAAG;IACR,IAAI,EAAE,GAAG;IACT,KAAK,EAAE,MAAM;IACb,MAAM,EAAE,MAAM;IACd,aAAa,EAAE,MAAM;CACtB,CAAC;AAEF,8EAA8E;AAE9E;;;;;;;;;;GAUG;AACH,MAAM,UAAU,SAAS,CAAC,EACxB,KAAK,EACL,MAAM,EACN,QAAQ,EACR,KAAK,EACL,SAAS,GAAG,WAAW,EACvB,QAAQ,GACO;IACf,MAAM,YAAY,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IAElD,0CAA0C;IAC1C,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,YAAY,EAAE,EAAE,EAAE,CAAC,CAAC;IAE3D,MAAM,YAAY,GAAG,OAAO,CAC1B,GAAG,EAAE,CAAC,IAAI,YAAY,CAAC,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC;IACjE,mDAAmD;IACnD,uDAAuD;IACvD,CAAC,QAAQ,CAAC,CACX,CAAC;IAEF,MAAM,SAAS,GAAG,OAAO,CACvB,GAAG,EAAE,CAAC,IAAI,SAAS,CAAiB,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,EACnD,EAAE,CACH,CAAC;IAEF,gDAAgD;IAChD,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC;QACvC,IAAI,CAAC,SAAS;YAAE,OAAO;QAEvB,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAE1B,2DAA2D;QAC3D,MAAM,OAAO,GAAG,QAAQ,CAAC,eAAe,EAAE,CAAC;QAC3C,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;QAE9C,2CAA2C;QAC3C,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAE/B,OAAO,GAAG,EAAE;YACV,OAAO,CAAC,MAAM,EAAE,CAAC;YACjB,QAAQ,CAAC,OAAO,EAAE,CAAC;QACrB,CAAC,CAAC;QACF,uDAAuD;IACzD,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEf,iCAAiC;IACjC,SAAS,CAAC,GAAG,EAAE;QACb,OAAO,GAAG,EAAE;YACV,YAAY,CAAC,OAAO,EAAE,CAAC;YACvB,YAAY,CAAC,OAAO,EAAE,CAAC;QACzB,CAAC,CAAC;QACF,uDAAuD;IACzD,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,0DAA0D;IAC1D,yEAAyE;IACzE,0EAA0E;IAC1E,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,EAAE;QAC9B,MAAM,IAAI,GAA2B,EAAE,GAAG,WAAW,EAAE,CAAC;QACxD,IAAI,KAAK,EAAE,CAAC;YACV,IAAI,CAAC,kBAAkB,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC;YACzC,IAAI,CAAC,aAAa,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC;YAC/B,IAAI,CAAC,eAAe,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC;QACrC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAEZ,MAAM,QAAQ,GAAG,OAAO,CACtB,GAAG,EAAE,CAAC,CAAC,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EACnE,CAAC,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,QAAQ,CAAC,CAClD,CAAC;IAEF,OAAO,CACL,KAAC,WAAW,CAAC,QAAQ,IAAC,KAAK,EAAE,QAAQ,YACnC,eAAK,KAAK,EAAC,cAAc,EAAC,KAAK,EAAE,UAAU,aAEzC,cAAK,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAI,EAGpE,cAAK,KAAK,EAAC,gBAAgB,EAAC,KAAK,EAAE,aAAa,YAC7C,QAAQ,GACL,IACF,GACe,CACxB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,35 @@
1
+ import type { GameStateShape } from './types';
2
+ /**
3
+ * Minimal reactive state container for game UI.
4
+ *
5
+ * - `set()` merges partial updates into the state object.
6
+ * - Multiple `set()` calls within the same microtask are coalesced into a
7
+ * single subscriber notification (via `queueMicrotask`).
8
+ * - Built-in `screen` management for menu / gameplay / pause flow.
9
+ */
10
+ export declare class GameState<T extends GameStateShape> {
11
+ private state;
12
+ private listeners;
13
+ private notifyScheduled;
14
+ constructor(initial: T);
15
+ /** Read a single key from the state. */
16
+ get<K extends keyof T>(key: K): T[K];
17
+ /** Return the entire state as a readonly shallow clone. */
18
+ getAll(): Readonly<T>;
19
+ /**
20
+ * Merge `partial` into the state. Multiple calls within the same
21
+ * microtask are batched — subscribers fire at most once per microtask.
22
+ */
23
+ set(partial: Partial<T>): void;
24
+ /** Convenience getter for the `screen` key (defaults to `""`). */
25
+ get screen(): string;
26
+ /** Convenience setter — equivalent to `set({ screen: name })`. */
27
+ setScreen(name: string): void;
28
+ /**
29
+ * Register a listener that fires whenever state changes.
30
+ * Returns an unsubscribe function.
31
+ */
32
+ subscribe(listener: (state: T) => void): () => void;
33
+ private scheduleNotify;
34
+ }
35
+ //# sourceMappingURL=GameState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GameState.d.ts","sourceRoot":"","sources":["../../src/core/GameState.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C;;;;;;;GAOG;AACH,qBAAa,SAAS,CAAC,CAAC,SAAS,cAAc;IAC7C,OAAO,CAAC,KAAK,CAAI;IACjB,OAAO,CAAC,SAAS,CAAsC;IACvD,OAAO,CAAC,eAAe,CAAS;gBAEpB,OAAO,EAAE,CAAC;IAOtB,wCAAwC;IACxC,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAIpC,2DAA2D;IAC3D,MAAM,IAAI,QAAQ,CAAC,CAAC,CAAC;IAMrB;;;OAGG;IACH,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI;IAS9B,kEAAkE;IAClE,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,kEAAkE;IAClE,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAM7B;;;OAGG;IACH,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAAG,MAAM,IAAI;IASnD,OAAO,CAAC,cAAc;CAWvB"}
@@ -0,0 +1,74 @@
1
+ // ---------------------------------------------------------------------------
2
+ // @joydle/ui — Pub/sub state store with batched notifications
3
+ // ---------------------------------------------------------------------------
4
+ /**
5
+ * Minimal reactive state container for game UI.
6
+ *
7
+ * - `set()` merges partial updates into the state object.
8
+ * - Multiple `set()` calls within the same microtask are coalesced into a
9
+ * single subscriber notification (via `queueMicrotask`).
10
+ * - Built-in `screen` management for menu / gameplay / pause flow.
11
+ */
12
+ export class GameState {
13
+ state;
14
+ listeners = new Set();
15
+ notifyScheduled = false;
16
+ constructor(initial) {
17
+ // Shallow-clone so we own the object.
18
+ this.state = { ...initial };
19
+ }
20
+ // -- Read -----------------------------------------------------------------
21
+ /** Read a single key from the state. */
22
+ get(key) {
23
+ return this.state[key];
24
+ }
25
+ /** Return the entire state as a readonly shallow clone. */
26
+ getAll() {
27
+ return { ...this.state };
28
+ }
29
+ // -- Write ----------------------------------------------------------------
30
+ /**
31
+ * Merge `partial` into the state. Multiple calls within the same
32
+ * microtask are batched — subscribers fire at most once per microtask.
33
+ */
34
+ set(partial) {
35
+ // Apply the update immediately so subsequent `get()` calls within the
36
+ // same synchronous block see the new value.
37
+ Object.assign(this.state, partial);
38
+ this.scheduleNotify();
39
+ }
40
+ // -- Screen shorthand -----------------------------------------------------
41
+ /** Convenience getter for the `screen` key (defaults to `""`). */
42
+ get screen() {
43
+ return this.state['screen'] ?? '';
44
+ }
45
+ /** Convenience setter — equivalent to `set({ screen: name })`. */
46
+ setScreen(name) {
47
+ this.set({ screen: name });
48
+ }
49
+ // -- Subscriptions --------------------------------------------------------
50
+ /**
51
+ * Register a listener that fires whenever state changes.
52
+ * Returns an unsubscribe function.
53
+ */
54
+ subscribe(listener) {
55
+ this.listeners.add(listener);
56
+ return () => {
57
+ this.listeners.delete(listener);
58
+ };
59
+ }
60
+ // -- Internal -------------------------------------------------------------
61
+ scheduleNotify() {
62
+ if (this.notifyScheduled)
63
+ return;
64
+ this.notifyScheduled = true;
65
+ queueMicrotask(() => {
66
+ this.notifyScheduled = false;
67
+ const snapshot = { ...this.state };
68
+ for (const fn of this.listeners) {
69
+ fn(snapshot);
70
+ }
71
+ });
72
+ }
73
+ }
74
+ //# sourceMappingURL=GameState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GameState.js","sourceRoot":"","sources":["../../src/core/GameState.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,+DAA+D;AAC/D,8EAA8E;AAI9E;;;;;;;GAOG;AACH,MAAM,OAAO,SAAS;IACZ,KAAK,CAAI;IACT,SAAS,GAA4B,IAAI,GAAG,EAAE,CAAC;IAC/C,eAAe,GAAG,KAAK,CAAC;IAEhC,YAAY,OAAU;QACpB,sCAAsC;QACtC,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC;IAC9B,CAAC;IAED,4EAA4E;IAE5E,wCAAwC;IACxC,GAAG,CAAoB,GAAM;QAC3B,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAED,2DAA2D;IAC3D,MAAM;QACJ,OAAO,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;IAC3B,CAAC;IAED,4EAA4E;IAE5E;;;OAGG;IACH,GAAG,CAAC,OAAmB;QACrB,sEAAsE;QACtE,4CAA4C;QAC5C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACnC,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IAED,4EAA4E;IAE5E,kEAAkE;IAClE,IAAI,MAAM;QACR,OAAQ,IAAI,CAAC,KAAiC,CAAC,QAAQ,CAAW,IAAI,EAAE,CAAC;IAC3E,CAAC;IAED,kEAAkE;IAClE,SAAS,CAAC,IAAY;QACpB,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,EAA2B,CAAC,CAAC;IACtD,CAAC;IAED,4EAA4E;IAE5E;;;OAGG;IACH,SAAS,CAAC,QAA4B;QACpC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC7B,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAClC,CAAC,CAAC;IACJ,CAAC;IAED,4EAA4E;IAEpE,cAAc;QACpB,IAAI,IAAI,CAAC,eAAe;YAAE,OAAO;QACjC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC5B,cAAc,CAAC,GAAG,EAAE;YAClB,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;YAC7B,MAAM,QAAQ,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;YACnC,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;gBAChC,EAAE,CAAC,QAAQ,CAAC,CAAC;YACf,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,39 @@
1
+ import type { InputState } from './types';
2
+ /**
3
+ * Maintains a frame-stable snapshot of all input axes and buttons.
4
+ *
5
+ * - Keyboard events are captured on `document`.
6
+ * - Pointer state and action overrides can be injected by external adapters
7
+ * (touch controls, bridge input adapters) via `setPointer` / `setAction`.
8
+ * - `consume(action)` implements one-shot detection: returns `true` on the
9
+ * first call after the action goes down, then `false` until released and
10
+ * re-pressed.
11
+ */
12
+ export declare class InputManager {
13
+ private held;
14
+ private consumed;
15
+ private _pointerX;
16
+ private _pointerY;
17
+ private _pointerDown;
18
+ private _pointerActive;
19
+ private onKeyDown;
20
+ private onKeyUp;
21
+ constructor();
22
+ /** Return a readonly snapshot of the current input state. */
23
+ getState(): Readonly<InputState>;
24
+ /**
25
+ * Returns `true` exactly once per press of the given action.
26
+ * Subsequent calls return `false` until the action is released and
27
+ * re-pressed.
28
+ */
29
+ consume(action: string): boolean;
30
+ /** Inject pointer state (e.g. from touch controls or bridge adapter). */
31
+ setPointer(x: number, y: number, down: boolean): void;
32
+ /** Inject an action value (e.g. from on-screen touch buttons). */
33
+ setAction(name: string, value: boolean): void;
34
+ /** Remove all event listeners. Call when the game shell unmounts. */
35
+ destroy(): void;
36
+ private handleKeyDown;
37
+ private handleKeyUp;
38
+ }
39
+ //# sourceMappingURL=InputManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InputManager.d.ts","sourceRoot":"","sources":["../../src/core/InputManager.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAoC1C;;;;;;;;;GASG;AACH,qBAAa,YAAY;IAEvB,OAAO,CAAC,IAAI,CAA+B;IAG3C,OAAO,CAAC,QAAQ,CAA0B;IAG1C,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,cAAc,CAAS;IAG/B,OAAO,CAAC,SAAS,CAA6B;IAC9C,OAAO,CAAC,OAAO,CAA6B;;IAY5C,6DAA6D;IAC7D,QAAQ,IAAI,QAAQ,CAAC,UAAU,CAAC;IA6BhC;;;;OAIG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAShC,yEAAyE;IACzE,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI;IAOrD,kEAAkE;IAClE,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAa7C,qEAAqE;IACrE,OAAO,IAAI,IAAI;IAOf,OAAO,CAAC,aAAa;IAkBrB,OAAO,CAAC,WAAW;CAOpB"}
@@ -0,0 +1,149 @@
1
+ // ---------------------------------------------------------------------------
2
+ // @joydle/ui — Unified keyboard + pointer input manager
3
+ // ---------------------------------------------------------------------------
4
+ // ---- Key mappings ---------------------------------------------------------
5
+ /** Sets of physical keys that map to each logical action. */
6
+ const KEY_MAP = {
7
+ left: ['ArrowLeft', 'a', 'A'],
8
+ right: ['ArrowRight', 'd', 'D'],
9
+ up: ['ArrowUp', 'w', 'W'],
10
+ down: ['ArrowDown', 's', 'S'],
11
+ action: [' ', 'Enter'], // Space = ' '
12
+ action2: ['z', 'Z', 'j', 'J'],
13
+ action3: ['x', 'X', 'k', 'K'],
14
+ pause: ['Escape'],
15
+ };
16
+ /**
17
+ * Reverse lookup: physical key -> logical action name.
18
+ * Built once at module load time.
19
+ */
20
+ const KEY_TO_ACTION = new Map();
21
+ for (const [action, keys] of Object.entries(KEY_MAP)) {
22
+ for (const k of keys) {
23
+ KEY_TO_ACTION.set(k, action);
24
+ }
25
+ }
26
+ // ---- Action names that support `consume()` --------------------------------
27
+ const CONSUMABLE = new Set([
28
+ 'action', 'action2', 'action3', 'pause',
29
+ 'left', 'right', 'up', 'down',
30
+ ]);
31
+ // ---- InputManager ---------------------------------------------------------
32
+ /**
33
+ * Maintains a frame-stable snapshot of all input axes and buttons.
34
+ *
35
+ * - Keyboard events are captured on `document`.
36
+ * - Pointer state and action overrides can be injected by external adapters
37
+ * (touch controls, bridge input adapters) via `setPointer` / `setAction`.
38
+ * - `consume(action)` implements one-shot detection: returns `true` on the
39
+ * first call after the action goes down, then `false` until released and
40
+ * re-pressed.
41
+ */
42
+ export class InputManager {
43
+ // Raw held-down state for keyboard-driven actions.
44
+ held = {};
45
+ // Tracks which actions have already been consumed this press.
46
+ consumed = new Set();
47
+ // Pointer state (written by setPointer or bridge adapter).
48
+ _pointerX = 0;
49
+ _pointerY = 0;
50
+ _pointerDown = false;
51
+ _pointerActive = false;
52
+ // Bound handlers kept for removeEventListener.
53
+ onKeyDown;
54
+ onKeyUp;
55
+ constructor() {
56
+ this.onKeyDown = this.handleKeyDown.bind(this);
57
+ this.onKeyUp = this.handleKeyUp.bind(this);
58
+ document.addEventListener('keydown', this.onKeyDown);
59
+ document.addEventListener('keyup', this.onKeyUp);
60
+ }
61
+ // -- Public: read state ---------------------------------------------------
62
+ /** Return a readonly snapshot of the current input state. */
63
+ getState() {
64
+ const left = !!this.held['left'];
65
+ const right = !!this.held['right'];
66
+ const up = !!this.held['up'];
67
+ const down = !!this.held['down'];
68
+ const dirX = left && !right ? -1 : right && !left ? 1 : 0;
69
+ const dirY = up && !down ? -1 : down && !up ? 1 : 0;
70
+ return {
71
+ left,
72
+ right,
73
+ up,
74
+ down,
75
+ dirX,
76
+ dirY,
77
+ action: !!this.held['action'],
78
+ action2: !!this.held['action2'],
79
+ action3: !!this.held['action3'],
80
+ pause: !!this.held['pause'],
81
+ pointerX: this._pointerX,
82
+ pointerY: this._pointerY,
83
+ pointerDown: this._pointerDown,
84
+ pointerActive: this._pointerActive,
85
+ };
86
+ }
87
+ // -- Public: consume (one-shot) -------------------------------------------
88
+ /**
89
+ * Returns `true` exactly once per press of the given action.
90
+ * Subsequent calls return `false` until the action is released and
91
+ * re-pressed.
92
+ */
93
+ consume(action) {
94
+ if (!this.held[action])
95
+ return false;
96
+ if (this.consumed.has(action))
97
+ return false;
98
+ this.consumed.add(action);
99
+ return true;
100
+ }
101
+ // -- Public: external writers ---------------------------------------------
102
+ /** Inject pointer state (e.g. from touch controls or bridge adapter). */
103
+ setPointer(x, y, down) {
104
+ this._pointerX = x;
105
+ this._pointerY = y;
106
+ this._pointerDown = down;
107
+ this._pointerActive = true;
108
+ }
109
+ /** Inject an action value (e.g. from on-screen touch buttons). */
110
+ setAction(name, value) {
111
+ const wasDown = !!this.held[name];
112
+ this.held[name] = value;
113
+ // Reset consumed flag when the action is released so the next press
114
+ // can be consumed again.
115
+ if (wasDown && !value) {
116
+ this.consumed.delete(name);
117
+ }
118
+ }
119
+ // -- Lifecycle ------------------------------------------------------------
120
+ /** Remove all event listeners. Call when the game shell unmounts. */
121
+ destroy() {
122
+ document.removeEventListener('keydown', this.onKeyDown);
123
+ document.removeEventListener('keyup', this.onKeyUp);
124
+ }
125
+ // -- Internal: keyboard ---------------------------------------------------
126
+ handleKeyDown(e) {
127
+ const action = KEY_TO_ACTION.get(e.key);
128
+ if (!action)
129
+ return;
130
+ // Prevent browser defaults for mapped keys (e.g. scrolling with arrows).
131
+ if (CONSUMABLE.has(action)) {
132
+ e.preventDefault();
133
+ }
134
+ const wasDown = !!this.held[action];
135
+ this.held[action] = true;
136
+ // Only clear consumed when transitioning from up -> down.
137
+ if (!wasDown) {
138
+ this.consumed.delete(action);
139
+ }
140
+ }
141
+ handleKeyUp(e) {
142
+ const action = KEY_TO_ACTION.get(e.key);
143
+ if (!action)
144
+ return;
145
+ this.held[action] = false;
146
+ this.consumed.delete(action);
147
+ }
148
+ }
149
+ //# sourceMappingURL=InputManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InputManager.js","sourceRoot":"","sources":["../../src/core/InputManager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ export type ScaleMode = 'letterbox' | 'fill' | 'stretch';
2
+ /**
3
+ * Keeps the renderer canvas sized correctly within its container.
4
+ *
5
+ * Supports three modes:
6
+ * - **letterbox** — uniform scale, centred, with bars on the short axis.
7
+ * - **fill** — uniform scale, cropped so no bars appear.
8
+ * - **stretch** — non-uniform, stretches to fill container exactly.
9
+ *
10
+ * The manager listens to `window.resize` and `orientationchange` and
11
+ * automatically recalculates.
12
+ */
13
+ export declare class ScaleManager {
14
+ private canvas;
15
+ private gameWidth;
16
+ private gameHeight;
17
+ private mode;
18
+ private currentScale;
19
+ private offsetX;
20
+ private offsetY;
21
+ private onResize;
22
+ constructor(canvas: HTMLCanvasElement, gameWidth: number, gameHeight: number, mode?: ScaleMode);
23
+ /** Recalculate scale and update the canvas CSS dimensions. */
24
+ resize(): void;
25
+ /**
26
+ * Convert a DOM client coordinate (e.g. from a mouse event) into
27
+ * game-space coordinates (0..gameWidth, 0..gameHeight).
28
+ */
29
+ toGameCoords(clientX: number, clientY: number): {
30
+ x: number;
31
+ y: number;
32
+ };
33
+ /** Return the current uniform scale factor. */
34
+ getScale(): number;
35
+ /** Remove event listeners. */
36
+ destroy(): void;
37
+ }
38
+ //# sourceMappingURL=ScaleManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScaleManager.d.ts","sourceRoot":"","sources":["../../src/core/ScaleManager.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG,MAAM,GAAG,SAAS,CAAC;AAEzD;;;;;;;;;;GAUG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,IAAI,CAAY;IAExB,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,OAAO,CAAK;IAEpB,OAAO,CAAC,QAAQ,CAAa;gBAG3B,MAAM,EAAE,iBAAiB,EACzB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,IAAI,GAAE,SAAuB;IAiB/B,8DAA8D;IAC9D,MAAM,IAAI,IAAI;IAiDd;;;OAGG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE;IAexE,+CAA+C;IAC/C,QAAQ,IAAI,MAAM;IAIlB,8BAA8B;IAC9B,OAAO,IAAI,IAAI;CAIhB"}
@@ -0,0 +1,106 @@
1
+ // ---------------------------------------------------------------------------
2
+ // @joydle/ui — Responsive canvas scaling
3
+ // ---------------------------------------------------------------------------
4
+ /**
5
+ * Keeps the renderer canvas sized correctly within its container.
6
+ *
7
+ * Supports three modes:
8
+ * - **letterbox** — uniform scale, centred, with bars on the short axis.
9
+ * - **fill** — uniform scale, cropped so no bars appear.
10
+ * - **stretch** — non-uniform, stretches to fill container exactly.
11
+ *
12
+ * The manager listens to `window.resize` and `orientationchange` and
13
+ * automatically recalculates.
14
+ */
15
+ export class ScaleManager {
16
+ canvas;
17
+ gameWidth;
18
+ gameHeight;
19
+ mode;
20
+ currentScale = 1;
21
+ offsetX = 0;
22
+ offsetY = 0;
23
+ onResize;
24
+ constructor(canvas, gameWidth, gameHeight, mode = 'letterbox') {
25
+ this.canvas = canvas;
26
+ this.gameWidth = gameWidth;
27
+ this.gameHeight = gameHeight;
28
+ this.mode = mode;
29
+ this.onResize = this.resize.bind(this);
30
+ window.addEventListener('resize', this.onResize);
31
+ window.addEventListener('orientationchange', this.onResize);
32
+ // Initial sizing.
33
+ this.resize();
34
+ }
35
+ // -- Public ---------------------------------------------------------------
36
+ /** Recalculate scale and update the canvas CSS dimensions. */
37
+ resize() {
38
+ const parent = this.canvas.parentElement;
39
+ if (!parent)
40
+ return;
41
+ const containerW = parent.clientWidth;
42
+ const containerH = parent.clientHeight;
43
+ if (containerW === 0 || containerH === 0)
44
+ return;
45
+ const ratioX = containerW / this.gameWidth;
46
+ const ratioY = containerH / this.gameHeight;
47
+ let cssW;
48
+ let cssH;
49
+ switch (this.mode) {
50
+ case 'letterbox': {
51
+ const scale = Math.min(ratioX, ratioY);
52
+ this.currentScale = scale;
53
+ cssW = this.gameWidth * scale;
54
+ cssH = this.gameHeight * scale;
55
+ break;
56
+ }
57
+ case 'fill': {
58
+ const scale = Math.max(ratioX, ratioY);
59
+ this.currentScale = scale;
60
+ cssW = this.gameWidth * scale;
61
+ cssH = this.gameHeight * scale;
62
+ break;
63
+ }
64
+ case 'stretch': {
65
+ this.currentScale = ratioX; // arbitrary — stretch means non-uniform
66
+ cssW = containerW;
67
+ cssH = containerH;
68
+ break;
69
+ }
70
+ }
71
+ // Centre the canvas within the container.
72
+ this.offsetX = (containerW - cssW) / 2;
73
+ this.offsetY = (containerH - cssH) / 2;
74
+ this.canvas.style.width = `${cssW}px`;
75
+ this.canvas.style.height = `${cssH}px`;
76
+ this.canvas.style.position = 'absolute';
77
+ this.canvas.style.left = `${this.offsetX}px`;
78
+ this.canvas.style.top = `${this.offsetY}px`;
79
+ }
80
+ /**
81
+ * Convert a DOM client coordinate (e.g. from a mouse event) into
82
+ * game-space coordinates (0..gameWidth, 0..gameHeight).
83
+ */
84
+ toGameCoords(clientX, clientY) {
85
+ const rect = this.canvas.getBoundingClientRect();
86
+ const cssW = rect.width;
87
+ const cssH = rect.height;
88
+ // Normalise to 0..1 within the canvas element.
89
+ const normX = (clientX - rect.left) / cssW;
90
+ const normY = (clientY - rect.top) / cssH;
91
+ return {
92
+ x: normX * this.gameWidth,
93
+ y: normY * this.gameHeight,
94
+ };
95
+ }
96
+ /** Return the current uniform scale factor. */
97
+ getScale() {
98
+ return this.currentScale;
99
+ }
100
+ /** Remove event listeners. */
101
+ destroy() {
102
+ window.removeEventListener('resize', this.onResize);
103
+ window.removeEventListener('orientationchange', this.onResize);
104
+ }
105
+ }
106
+ //# sourceMappingURL=ScaleManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScaleManager.js","sourceRoot":"","sources":["../../src/core/ScaleManager.ts"],"names":[],"mappings":"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"}