@nodetool-ai/game-renderer 0.8.1 → 0.8.3

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 (277) hide show
  1. package/README.md +46 -0
  2. package/dist/.nodetool-build-stamp +1 -1
  3. package/dist/audio/mixer.d.ts +75 -0
  4. package/dist/audio/mixer.d.ts.map +1 -0
  5. package/dist/audio/mixer.js +345 -0
  6. package/dist/audio/mixer.js.map +1 -0
  7. package/dist/audio/player.d.ts +67 -0
  8. package/dist/audio/player.d.ts.map +1 -0
  9. package/dist/audio/player.js +337 -0
  10. package/dist/audio/player.js.map +1 -0
  11. package/dist/audio/spatial.d.ts +50 -0
  12. package/dist/audio/spatial.d.ts.map +1 -0
  13. package/dist/audio/spatial.js +166 -0
  14. package/dist/audio/spatial.js.map +1 -0
  15. package/dist/audio/voices.d.ts +11 -0
  16. package/dist/audio/voices.d.ts.map +1 -0
  17. package/dist/audio/voices.js +22 -0
  18. package/dist/audio/voices.js.map +1 -0
  19. package/dist/audio.d.ts +6 -38
  20. package/dist/audio.d.ts.map +1 -1
  21. package/dist/audio.js +3 -226
  22. package/dist/audio.js.map +1 -1
  23. package/dist/browser.d.ts +2 -1
  24. package/dist/browser.d.ts.map +1 -1
  25. package/dist/browser.js +5 -5
  26. package/dist/browser.js.map +1 -1
  27. package/dist/browser3d.d.ts +2 -0
  28. package/dist/browser3d.d.ts.map +1 -0
  29. package/dist/browser3d.js +2 -0
  30. package/dist/browser3d.js.map +1 -0
  31. package/dist/build-tauri.d.ts +69 -0
  32. package/dist/build-tauri.d.ts.map +1 -0
  33. package/dist/build-tauri.js +406 -0
  34. package/dist/build-tauri.js.map +1 -0
  35. package/dist/build.d.ts.map +1 -1
  36. package/dist/build.js +29 -19
  37. package/dist/build.js.map +1 -1
  38. package/dist/build3d.d.ts +11 -0
  39. package/dist/build3d.d.ts.map +1 -0
  40. package/dist/build3d.js +126 -0
  41. package/dist/build3d.js.map +1 -0
  42. package/dist/canvas2d.d.ts +12 -3
  43. package/dist/canvas2d.d.ts.map +1 -1
  44. package/dist/canvas2d.js +59 -12
  45. package/dist/canvas2d.js.map +1 -1
  46. package/dist/fixed-tick-host.d.ts +10 -0
  47. package/dist/fixed-tick-host.d.ts.map +1 -0
  48. package/dist/fixed-tick-host.js +29 -0
  49. package/dist/fixed-tick-host.js.map +1 -0
  50. package/dist/frame-budget.d.ts +34 -0
  51. package/dist/frame-budget.d.ts.map +1 -0
  52. package/dist/frame-budget.js +52 -0
  53. package/dist/frame-budget.js.map +1 -0
  54. package/dist/frame.d.ts +20 -1
  55. package/dist/frame.d.ts.map +1 -1
  56. package/dist/frame.js +69 -1
  57. package/dist/frame.js.map +1 -1
  58. package/dist/game3d-capture-page.js +5834 -0
  59. package/dist/game3d-player.js +6001 -0
  60. package/dist/imageDiff.d.ts +9 -0
  61. package/dist/imageDiff.d.ts.map +1 -0
  62. package/dist/imageDiff.js +30 -0
  63. package/dist/imageDiff.js.map +1 -0
  64. package/dist/index.d.ts +12 -1
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +9 -0
  67. package/dist/index.js.map +1 -1
  68. package/dist/input-bindings.d.ts +76 -0
  69. package/dist/input-bindings.d.ts.map +1 -0
  70. package/dist/input-bindings.js +377 -0
  71. package/dist/input-bindings.js.map +1 -0
  72. package/dist/input.d.ts +1 -1
  73. package/dist/input.d.ts.map +1 -1
  74. package/dist/input.js +1 -14
  75. package/dist/input.js.map +1 -1
  76. package/dist/input3d.d.ts +3 -0
  77. package/dist/input3d.d.ts.map +1 -0
  78. package/dist/input3d.js +3 -0
  79. package/dist/input3d.js.map +1 -0
  80. package/dist/node.d.ts +5 -0
  81. package/dist/node.d.ts.map +1 -1
  82. package/dist/node.js +28 -11
  83. package/dist/node.js.map +1 -1
  84. package/dist/node3d.d.ts +29 -0
  85. package/dist/node3d.d.ts.map +1 -0
  86. package/dist/node3d.js +160 -0
  87. package/dist/node3d.js.map +1 -0
  88. package/dist/particles/curves.d.ts +16 -0
  89. package/dist/particles/curves.d.ts.map +1 -0
  90. package/dist/particles/curves.js +49 -0
  91. package/dist/particles/curves.js.map +1 -0
  92. package/dist/particles/index.d.ts +6 -0
  93. package/dist/particles/index.d.ts.map +1 -0
  94. package/dist/particles/index.js +6 -0
  95. package/dist/particles/index.js.map +1 -0
  96. package/dist/particles/random.d.ts +10 -0
  97. package/dist/particles/random.d.ts.map +1 -0
  98. package/dist/particles/random.js +26 -0
  99. package/dist/particles/random.js.map +1 -0
  100. package/dist/particles/render2d.d.ts +40 -0
  101. package/dist/particles/render2d.d.ts.map +1 -0
  102. package/dist/particles/render2d.js +70 -0
  103. package/dist/particles/render2d.js.map +1 -0
  104. package/dist/particles/shapes.d.ts +18 -0
  105. package/dist/particles/shapes.d.ts.map +1 -0
  106. package/dist/particles/shapes.js +92 -0
  107. package/dist/particles/shapes.js.map +1 -0
  108. package/dist/particles/simulator.d.ts +88 -0
  109. package/dist/particles/simulator.d.ts.map +1 -0
  110. package/dist/particles/simulator.js +371 -0
  111. package/dist/particles/simulator.js.map +1 -0
  112. package/dist/player-v1.js +2502 -0
  113. package/dist/preparation3d.d.ts +3 -0
  114. package/dist/preparation3d.d.ts.map +1 -0
  115. package/dist/preparation3d.js +3 -0
  116. package/dist/preparation3d.js.map +1 -0
  117. package/dist/renderPipeline.d.ts +12 -0
  118. package/dist/renderPipeline.d.ts.map +1 -0
  119. package/dist/renderPipeline.js +17 -0
  120. package/dist/renderPipeline.js.map +1 -0
  121. package/dist/renderer3d/animation/index.d.ts +11 -0
  122. package/dist/renderer3d/animation/index.d.ts.map +1 -0
  123. package/dist/renderer3d/animation/index.js +212 -0
  124. package/dist/renderer3d/animation/index.js.map +1 -0
  125. package/dist/renderer3d/assets/cache.d.ts +14 -0
  126. package/dist/renderer3d/assets/cache.d.ts.map +1 -0
  127. package/dist/renderer3d/assets/cache.js +74 -0
  128. package/dist/renderer3d/assets/cache.js.map +1 -0
  129. package/dist/renderer3d/assets/dispose.d.ts +4 -0
  130. package/dist/renderer3d/assets/dispose.d.ts.map +1 -0
  131. package/dist/renderer3d/assets/dispose.js +41 -0
  132. package/dist/renderer3d/assets/dispose.js.map +1 -0
  133. package/dist/renderer3d/assets/hdri-contract.d.ts +23 -0
  134. package/dist/renderer3d/assets/hdri-contract.d.ts.map +1 -0
  135. package/dist/renderer3d/assets/hdri-contract.js +2 -0
  136. package/dist/renderer3d/assets/hdri-contract.js.map +1 -0
  137. package/dist/renderer3d/assets/instances.d.ts +5 -0
  138. package/dist/renderer3d/assets/instances.d.ts.map +1 -0
  139. package/dist/renderer3d/assets/instances.js +86 -0
  140. package/dist/renderer3d/assets/instances.js.map +1 -0
  141. package/dist/renderer3d/capture-driver-types.d.ts +42 -0
  142. package/dist/renderer3d/capture-driver-types.d.ts.map +1 -0
  143. package/dist/renderer3d/capture-driver-types.js +2 -0
  144. package/dist/renderer3d/capture-driver-types.js.map +1 -0
  145. package/dist/renderer3d/capture-page.d.ts +36 -0
  146. package/dist/renderer3d/capture-page.d.ts.map +1 -0
  147. package/dist/renderer3d/capture-page.js +68 -0
  148. package/dist/renderer3d/capture-page.js.map +1 -0
  149. package/dist/renderer3d/culling.d.ts +10 -0
  150. package/dist/renderer3d/culling.d.ts.map +1 -0
  151. package/dist/renderer3d/culling.js +27 -0
  152. package/dist/renderer3d/culling.js.map +1 -0
  153. package/dist/renderer3d/environment/index.d.ts +4 -0
  154. package/dist/renderer3d/environment/index.d.ts.map +1 -0
  155. package/dist/renderer3d/environment/index.js +9 -0
  156. package/dist/renderer3d/environment/index.js.map +1 -0
  157. package/dist/renderer3d/environment/sky.d.ts +45 -0
  158. package/dist/renderer3d/environment/sky.d.ts.map +1 -0
  159. package/dist/renderer3d/environment/sky.js +221 -0
  160. package/dist/renderer3d/environment/sky.js.map +1 -0
  161. package/dist/renderer3d/hud/fonts.d.ts +13 -0
  162. package/dist/renderer3d/hud/fonts.d.ts.map +1 -0
  163. package/dist/renderer3d/hud/fonts.js +69 -0
  164. package/dist/renderer3d/hud/fonts.js.map +1 -0
  165. package/dist/renderer3d/hud/paint.d.ts +4 -0
  166. package/dist/renderer3d/hud/paint.d.ts.map +1 -0
  167. package/dist/renderer3d/hud/paint.js +22 -0
  168. package/dist/renderer3d/hud/paint.js.map +1 -0
  169. package/dist/renderer3d/import.d.ts +34 -0
  170. package/dist/renderer3d/import.d.ts.map +1 -0
  171. package/dist/renderer3d/import.js +408 -0
  172. package/dist/renderer3d/import.js.map +1 -0
  173. package/dist/renderer3d/index.d.ts +74 -0
  174. package/dist/renderer3d/index.d.ts.map +1 -0
  175. package/dist/renderer3d/index.js +338 -0
  176. package/dist/renderer3d/index.js.map +1 -0
  177. package/dist/renderer3d/lights/index.d.ts +7 -0
  178. package/dist/renderer3d/lights/index.d.ts.map +1 -0
  179. package/dist/renderer3d/lights/index.js +67 -0
  180. package/dist/renderer3d/lights/index.js.map +1 -0
  181. package/dist/renderer3d/materials/index.d.ts +4 -0
  182. package/dist/renderer3d/materials/index.d.ts.map +1 -0
  183. package/dist/renderer3d/materials/index.js +8 -0
  184. package/dist/renderer3d/materials/index.js.map +1 -0
  185. package/dist/renderer3d/passes/post-processing.d.ts +44 -0
  186. package/dist/renderer3d/passes/post-processing.d.ts.map +1 -0
  187. package/dist/renderer3d/passes/post-processing.js +213 -0
  188. package/dist/renderer3d/passes/post-processing.js.map +1 -0
  189. package/dist/renderer3d/pipeline.d.ts +15 -0
  190. package/dist/renderer3d/pipeline.d.ts.map +1 -0
  191. package/dist/renderer3d/pipeline.js +10 -0
  192. package/dist/renderer3d/pipeline.js.map +1 -0
  193. package/dist/renderer3d/playwright-contract.d.ts +3 -0
  194. package/dist/renderer3d/playwright-contract.d.ts.map +1 -0
  195. package/dist/renderer3d/playwright-contract.js +4 -0
  196. package/dist/renderer3d/playwright-contract.js.map +1 -0
  197. package/dist/renderer3d/preparation.d.ts +79 -0
  198. package/dist/renderer3d/preparation.d.ts.map +1 -0
  199. package/dist/renderer3d/preparation.js +458 -0
  200. package/dist/renderer3d/preparation.js.map +1 -0
  201. package/dist/renderer3d/scene-sync.d.ts +8 -0
  202. package/dist/renderer3d/scene-sync.d.ts.map +1 -0
  203. package/dist/renderer3d/scene-sync.js +96 -0
  204. package/dist/renderer3d/scene-sync.js.map +1 -0
  205. package/dist/renderer3d/shadows/cascades.d.ts +40 -0
  206. package/dist/renderer3d/shadows/cascades.d.ts.map +1 -0
  207. package/dist/renderer3d/shadows/cascades.js +244 -0
  208. package/dist/renderer3d/shadows/cascades.js.map +1 -0
  209. package/dist/renderer3d/shadows/index.d.ts +44 -0
  210. package/dist/renderer3d/shadows/index.d.ts.map +1 -0
  211. package/dist/renderer3d/shadows/index.js +129 -0
  212. package/dist/renderer3d/shadows/index.js.map +1 -0
  213. package/dist/renderer3d/types.d.ts +18 -0
  214. package/dist/renderer3d/types.d.ts.map +1 -0
  215. package/dist/renderer3d/types.js +2 -0
  216. package/dist/renderer3d/types.js.map +1 -0
  217. package/dist/smoke.d.ts +15 -0
  218. package/dist/smoke.d.ts.map +1 -0
  219. package/dist/smoke.js +138 -0
  220. package/dist/smoke.js.map +1 -0
  221. package/dist/standalone-player.d.ts +11 -1
  222. package/dist/standalone-player.d.ts.map +1 -1
  223. package/dist/standalone-player.js +67 -41
  224. package/dist/standalone-player.js.map +1 -1
  225. package/dist/standalone-player3d.d.ts +14 -0
  226. package/dist/standalone-player3d.d.ts.map +1 -0
  227. package/dist/standalone-player3d.js +255 -0
  228. package/dist/standalone-player3d.js.map +1 -0
  229. package/dist/touch-controls.d.ts +22 -8
  230. package/dist/touch-controls.d.ts.map +1 -1
  231. package/dist/touch-controls.js +88 -21
  232. package/dist/touch-controls.js.map +1 -1
  233. package/dist/ui/controller.d.ts +59 -0
  234. package/dist/ui/controller.d.ts.map +1 -0
  235. package/dist/ui/controller.js +201 -0
  236. package/dist/ui/controller.js.map +1 -0
  237. package/dist/ui/hud2d.d.ts +11 -0
  238. package/dist/ui/hud2d.d.ts.map +1 -0
  239. package/dist/ui/hud2d.js +25 -0
  240. package/dist/ui/hud2d.js.map +1 -0
  241. package/dist/ui/layout.d.ts +42 -0
  242. package/dist/ui/layout.d.ts.map +1 -0
  243. package/dist/ui/layout.js +182 -0
  244. package/dist/ui/layout.js.map +1 -0
  245. package/dist/ui/paint.d.ts +45 -0
  246. package/dist/ui/paint.d.ts.map +1 -0
  247. package/dist/ui/paint.js +121 -0
  248. package/dist/ui/paint.js.map +1 -0
  249. package/dist/webgpu/effects.d.ts +22 -0
  250. package/dist/webgpu/effects.d.ts.map +1 -0
  251. package/dist/webgpu/effects.js +69 -0
  252. package/dist/webgpu/effects.js.map +1 -0
  253. package/dist/webgpu/hud.d.ts +4 -0
  254. package/dist/webgpu/hud.d.ts.map +1 -0
  255. package/dist/webgpu/hud.js +27 -0
  256. package/dist/webgpu/hud.js.map +1 -0
  257. package/dist/webgpu/lighting.d.ts +4 -0
  258. package/dist/webgpu/lighting.d.ts.map +1 -0
  259. package/dist/webgpu/lighting.js +47 -0
  260. package/dist/webgpu/lighting.js.map +1 -0
  261. package/dist/webgpu/pipeline.d.ts +11 -0
  262. package/dist/webgpu/pipeline.d.ts.map +1 -0
  263. package/dist/webgpu/pipeline.js +11 -0
  264. package/dist/webgpu/pipeline.js.map +1 -0
  265. package/dist/webgpu/sprites.d.ts +6 -0
  266. package/dist/webgpu/sprites.d.ts.map +1 -0
  267. package/dist/webgpu/sprites.js +74 -0
  268. package/dist/webgpu/sprites.js.map +1 -0
  269. package/dist/webgpu/types.d.ts +14 -0
  270. package/dist/webgpu/types.d.ts.map +1 -0
  271. package/dist/webgpu/types.js +2 -0
  272. package/dist/webgpu/types.js.map +1 -0
  273. package/dist/webgpu.d.ts +4 -3
  274. package/dist/webgpu.d.ts.map +1 -1
  275. package/dist/webgpu.js +119 -279
  276. package/dist/webgpu.js.map +1 -1
  277. package/package.json +52 -10
@@ -0,0 +1,255 @@
1
+ import { createGameSession3D, decodePreparedGameCollider3D } from "@nodetool-ai/game-runtime";
2
+ import { z } from "zod";
3
+ import { gameDocument3D, resolveGameInputBindings } from "@nodetool-ai/protocol";
4
+ import { createGameRenderer3D } from "./browser3d.js";
5
+ import { GameInput3D } from "./input3d.js";
6
+ import { browserGamepads } from "./input-bindings.js";
7
+ import { mountTouchControls, touchLayout } from "./touch-controls.js";
8
+ import { GameAudioPlayer, gameAudioSpatialView3D } from "./audio.js";
9
+ import { FixedTickClock } from "./fixed-tick-host.js";
10
+ import { GameFrameBudgetMonitor, gameAudioVoiceCount } from "./frame-budget.js";
11
+ function status(message) {
12
+ const element = document.getElementById("status");
13
+ if (element) {
14
+ element.textContent = message;
15
+ }
16
+ }
17
+ async function start() {
18
+ const controller = new AbortController();
19
+ const response = await fetch("./game.json", { signal: controller.signal });
20
+ if (!response.ok) {
21
+ throw new Error("Game document could not load");
22
+ }
23
+ const game = gameDocument3D.parse(await response.json());
24
+ const manifestResponse = await fetch("./manifest.json", { signal: controller.signal });
25
+ if (!manifestResponse.ok) {
26
+ throw new Error("Export manifest could not load");
27
+ }
28
+ const manifest = z.object({ assets: z.record(z.string(), z.string().regex(/^\.\/assets\/[0-9a-f]{64}\.[a-z0-9]+$/)) }).parse(await manifestResponse.json());
29
+ const canvas = document.getElementById("game");
30
+ if (!(canvas instanceof HTMLCanvasElement)) {
31
+ throw new Error("Game viewport is missing");
32
+ }
33
+ const localAssets = new Map();
34
+ for (const [logicalId, binding] of Object.entries(game.assets)) {
35
+ const path = manifest.assets[logicalId];
36
+ if (!path || !path.startsWith(`./assets/${binding.digest}.`)) {
37
+ throw new Error(`Export asset ${logicalId} has no verified local path`);
38
+ }
39
+ const asset = await fetch(path, { signal: controller.signal });
40
+ if (!asset.ok) {
41
+ throw new Error(`Export asset ${logicalId} could not load`);
42
+ }
43
+ const bytes = new Uint8Array(await asset.arrayBuffer());
44
+ const hash = await crypto.subtle.digest("SHA-256", bytes);
45
+ const digest = Array.from(new Uint8Array(hash), (byte) => byte.toString(16).padStart(2, "0")).join("");
46
+ if (digest !== binding.digest) {
47
+ throw new Error(`Export asset ${logicalId} failed its digest check`);
48
+ }
49
+ localAssets.set(logicalId, bytes);
50
+ }
51
+ const open = () => createGameSession3D(game, 1, undefined, { signal: controller.signal,
52
+ resolveCollider: async (binding) => {
53
+ const bytes = localAssets.get(Object.entries(game.assets).find(([, entry]) => entry.assetId === binding.assetId)?.[0] ?? "");
54
+ if (!bytes) {
55
+ throw new Error("Export collider is missing");
56
+ }
57
+ return decodePreparedGameCollider3D(bytes, binding);
58
+ } });
59
+ let session = await open();
60
+ let paused = false;
61
+ let contextPaused = false;
62
+ const renderer = await createGameRenderer3D({ canvas, signal: controller.signal,
63
+ resolveFont: async (logicalId) => { const bytes = localAssets.get(logicalId); return bytes ? { bytes, digest: game.assets[logicalId]?.digest } : null; },
64
+ resolveModel: async (logicalId) => {
65
+ const bytes = localAssets.get(logicalId);
66
+ return bytes ? { bytes, digest: game.assets[logicalId]?.digest } : null;
67
+ },
68
+ onContextState: (state) => {
69
+ contextPaused = state === "lost";
70
+ status(state === "lost" ? "Graphics context lost. Play is paused until recovery." : "Graphics recovered");
71
+ } });
72
+ renderer.resize(game.presentation.hudWidth, game.presentation.hudWidth / game.presentation.aspectRatio);
73
+ let frame = session.frame();
74
+ const audio = new GameAudioPlayer({ assets: game.assets, tickRate: game.tickRate,
75
+ resolveAsset: async (binding) => manifest.assets[Object.entries(game.assets).find(([, entry]) => entry.assetId === binding.assetId && entry.digest === binding.digest)?.[0] ?? ""] ?? null,
76
+ status, mixer: game.audio?.mixer });
77
+ audio.preload();
78
+ audio.sync(session.snapshot());
79
+ const budget = new GameFrameBudgetMonitor(game.performance?.budgets);
80
+ for (const event of ["pointerdown", "keydown"]) {
81
+ window.addEventListener(event, () => { void audio.unlock(); }, { signal: controller.signal });
82
+ }
83
+ const step = (nextInput) => {
84
+ const result = session.step(nextInput);
85
+ frame = result.frame;
86
+ for (const event of result.events) {
87
+ audio.handle(event);
88
+ }
89
+ audio.sync(session.snapshot());
90
+ };
91
+ const input = new GameInput3D();
92
+ let dragging = false;
93
+ const release = () => { input.release(); dragging = false; };
94
+ window.addEventListener("keydown", (event) => {
95
+ input.keyDown(event.code, event.key);
96
+ if (input.handlesKey(game, event.code, event.key)) {
97
+ event.preventDefault();
98
+ }
99
+ }, { signal: controller.signal });
100
+ window.addEventListener("keyup", (event) => { input.keyUp(event.code); }, { signal: controller.signal });
101
+ const mouseFire = game.inputActions.includes("fire");
102
+ const bindings = resolveGameInputBindings(game);
103
+ const mouseLook = bindings.look.some((binding) => binding.kind === "mouse");
104
+ let unmountTouch;
105
+ // The touch layer is styled by the staged style.css, because the export CSP blocks inline styles.
106
+ const enableTouch = () => {
107
+ if (unmountTouch) {
108
+ return;
109
+ }
110
+ const layer = document.createElement("div");
111
+ layer.className = "touch-layer";
112
+ document.body.append(layer);
113
+ const unmount = mountTouchControls(layer, { layout: touchLayout(bindings), onChange: (state) => input.setTouch(state), onLook: (x, y) => input.touchLook(x, y) });
114
+ unmountTouch = () => { unmount(); layer.remove(); };
115
+ };
116
+ if (window.matchMedia("(pointer: coarse)").matches) {
117
+ enableTouch();
118
+ }
119
+ window.addEventListener("touchstart", enableTouch, { passive: true, signal: controller.signal });
120
+ // A touch that reaches the canvas, outside the touch zones or before they appear, fires on tap and turns the camera on drag.
121
+ canvas.addEventListener("pointerdown", (event) => {
122
+ dragging = true;
123
+ canvas.setPointerCapture(event.pointerId);
124
+ input.mouseDown(event.button);
125
+ if (event.pointerType !== "touch" && (mouseFire || mouseLook) && document.pointerLockElement !== canvas) {
126
+ void canvas.requestPointerLock().catch(() => { status(mouseFire ? "Mouse capture unavailable. Hold and drag to aim, or press F to fire." : "Mouse capture unavailable. Drag to turn the camera."); });
127
+ }
128
+ }, { signal: controller.signal });
129
+ window.addEventListener("pointerup", (event) => { dragging = false; input.mouseUp(event.button); }, { signal: controller.signal });
130
+ canvas.addEventListener("pointercancel", release, { signal: controller.signal });
131
+ document.addEventListener("pointerlockchange", () => { if (document.pointerLockElement !== canvas) {
132
+ release();
133
+ } }, { signal: controller.signal });
134
+ canvas.addEventListener("pointermove", (event) => { if (dragging || document.pointerLockElement === canvas) {
135
+ input.look(event.movementX, event.movementY);
136
+ } }, { signal: controller.signal });
137
+ window.addEventListener("blur", release, { signal: controller.signal });
138
+ const clock = new FixedTickClock(game.tickRate);
139
+ let animationId = 0;
140
+ let rendering = false;
141
+ let disposed = false;
142
+ const render = async (interpolation) => {
143
+ if (rendering || disposed) {
144
+ return;
145
+ }
146
+ rendering = true;
147
+ try {
148
+ audio.updateSpatial(gameAudioSpatialView3D(frame, interpolation));
149
+ const stats = await renderer.render(frame, interpolation);
150
+ budget.observe({ drawCalls: stats.drawCalls, triangles: stats.triangles, voices: gameAudioVoiceCount(audio.mixerState()) });
151
+ }
152
+ catch (error) {
153
+ if (renderer.capabilities.deviceStatus !== "lost") {
154
+ paused = true;
155
+ }
156
+ status(error instanceof Error ? error.message : "Rendering failed");
157
+ }
158
+ finally {
159
+ rendering = false;
160
+ }
161
+ };
162
+ document.addEventListener("visibilitychange", () => { release(); clock.reset(); }, { signal: controller.signal });
163
+ const tick = (now) => {
164
+ if (disposed) {
165
+ return;
166
+ }
167
+ if (!paused && !contextPaused && !document.hidden) {
168
+ try {
169
+ const interpolation = clock.advance(now, () => { input.pollGamepads(browserGamepads()); step(input.sample(game)); });
170
+ void render(interpolation);
171
+ }
172
+ catch (error) {
173
+ paused = true;
174
+ clock.reset();
175
+ status(error instanceof Error ? error.message : "Game script failed");
176
+ }
177
+ }
178
+ else if (contextPaused && renderer.capabilities.deviceStatus === "ready") {
179
+ void render(1);
180
+ }
181
+ if (paused || contextPaused || document.hidden) {
182
+ clock.reset();
183
+ }
184
+ animationId = requestAnimationFrame(tick);
185
+ };
186
+ document.getElementById("pause")?.addEventListener("click", (event) => {
187
+ paused = !paused;
188
+ // Input pressed while paused is dropped rather than delivered on resume (F23).
189
+ input.setEnabled(!paused);
190
+ if (event.currentTarget instanceof HTMLButtonElement) {
191
+ event.currentTarget.textContent = paused ? "Resume" : "Pause";
192
+ }
193
+ release();
194
+ clock.reset();
195
+ if (paused) {
196
+ audio.pause();
197
+ }
198
+ else {
199
+ audio.resume();
200
+ }
201
+ }, { signal: controller.signal });
202
+ document.getElementById("reset")?.addEventListener("click", () => {
203
+ paused = true;
204
+ void open().then((replacement) => {
205
+ session.dispose();
206
+ session = replacement;
207
+ audio.reset(session.snapshot());
208
+ frame = session.frame();
209
+ clock.reset();
210
+ release();
211
+ // Reset resumes play, so input dropped by an earlier pause is accepted again.
212
+ paused = false;
213
+ input.setEnabled(true);
214
+ const pause = document.getElementById("pause");
215
+ if (pause) {
216
+ pause.textContent = "Pause";
217
+ }
218
+ void render(1);
219
+ })
220
+ .catch((error) => status(error instanceof Error ? error.message : "Reset failed"));
221
+ }, { signal: controller.signal });
222
+ window.addEventListener("beforeunload", () => {
223
+ disposed = true;
224
+ cancelAnimationFrame(animationId);
225
+ controller.abort();
226
+ unmountTouch?.();
227
+ session.dispose();
228
+ renderer.dispose();
229
+ audio.dispose();
230
+ }, { once: true });
231
+ window.nativeGame3DPlayer = Object.freeze({
232
+ pause: () => { paused = true; release(); clock.reset(); },
233
+ step: async (nextInput) => { paused = true; step(nextInput); await render(1); },
234
+ reset: async () => { paused = true; const replacement = await open(); session.dispose(); session = replacement; audio.reset(session.snapshot()); frame = session.frame(); release(); clock.reset(); await render(1); },
235
+ advance: async (inputs) => {
236
+ if (inputs.length > 10_000) {
237
+ throw new Error("Recorded route exceeds 10000 ticks");
238
+ }
239
+ paused = true;
240
+ for (const nextInput of inputs) {
241
+ step(nextInput);
242
+ }
243
+ await render(1);
244
+ },
245
+ snapshot: () => session.snapshot(),
246
+ inspect: () => session.inspect()
247
+ });
248
+ // A static HTTP host is required for browser modules and WASM.
249
+ status(mouseFire ? "Ready. Click to capture the mouse and fire. WASD moves, F fires, Space jumps, Escape releases the mouse."
250
+ : "Ready. Move with WASD, arrows or a gamepad, jump with Space. Click to capture the mouse for looking, Escape releases it.");
251
+ await render(1);
252
+ animationId = requestAnimationFrame(tick);
253
+ }
254
+ void start().catch((error) => status(error instanceof Error ? error.message : "Game could not start"));
255
+ //# sourceMappingURL=standalone-player3d.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standalone-player3d.js","sourceRoot":"","sources":["../src/standalone-player3d.ts"],"names":[],"mappings":"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"}
@@ -1,15 +1,29 @@
1
+ import type { ResolvedGameInputBindings } from "@nodetool-ai/protocol";
2
+ import type { TouchInputState } from "./input-bindings.js";
1
3
  /** Maps a stick offset in screen pixels (y grows downward) to the direction actions it presses. */
2
4
  export declare function stickActions(dx: number, dy: number, deadZone: number): string[];
3
- /** Splits a game's input actions into the stick's directions and one button per other action. */
4
- export declare function touchLayout(inputActions: readonly string[]): {
5
- stick: string[];
6
- buttons: string[];
7
- };
5
+ export interface TouchLayout {
6
+ /** Whether any action or axis reads the floating stick. */
7
+ readonly stick: boolean;
8
+ /** One button per action with a touch button binding, in action order. */
9
+ readonly buttons: readonly {
10
+ readonly action: string;
11
+ readonly label: string;
12
+ }[];
13
+ /** Whether a drag on the right half turns the camera. */
14
+ readonly look: boolean;
15
+ }
16
+ /** Derives the on-screen controls from a document's resolved input bindings. */
17
+ export declare function touchLayout(bindings: ResolvedGameInputBindings): TouchLayout;
8
18
  export interface TouchControlsOptions {
9
- readonly inputActions: readonly string[];
10
- /** Called with the full set of actions touch currently holds. */
11
- readonly onChange: (pressed: ReadonlySet<string>) => void;
19
+ readonly layout: TouchLayout;
20
+ /** Called with everything touch currently holds. */
21
+ readonly onChange: (state: TouchInputState) => void;
22
+ /** Called with drag movement in pixels on the look zone. */
23
+ readonly onLook?: (x: number, y: number) => void;
12
24
  }
25
+ /** Styles for the touch layer, for players whose page does not already define them. */
26
+ export declare const TOUCH_CONTROLS_CSS: string;
13
27
  /**
14
28
  * A floating stick on the left half of the screen and action buttons on the right.
15
29
  * The stick appears where the thumb lands, so it works for any hand position.
@@ -1 +1 @@
1
- {"version":3,"file":"touch-controls.d.ts","sourceRoot":"","sources":["../src/touch-controls.ts"],"names":[],"mappings":"AAIA,mGAAmG;AACnG,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAa/E;AAED,iGAAiG;AACjG,wBAAgB,WAAW,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,GAAG;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAGnG;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;CAC3D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,oBAAoB,GAAG,MAAM,IAAI,CAqG/F"}
1
+ {"version":3,"file":"touch-controls.d.ts","sourceRoot":"","sources":["../src/touch-controls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAK3D,mGAAmG;AACnG,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAa/E;AAED,MAAM,WAAW,WAAW;IAC1B,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,EAAE,SAAS;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACjF,yDAAyD;IACzD,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACxB;AAED,gFAAgF;AAChF,wBAAgB,WAAW,CAAC,QAAQ,EAAE,yBAAyB,GAAG,WAAW,CAY5E;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,oDAAoD;IACpD,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;IACpD,4DAA4D;IAC5D,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAClD;AAED,uFAAuF;AACvF,eAAO,MAAM,kBAAkB,QAU4B,CAAC;AAE5D;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,oBAAoB,GAAG,MAAM,IAAI,CA4I/F"}
@@ -1,4 +1,3 @@
1
- const DIRECTIONS = ["left", "right", "up", "down"];
2
1
  // sin(22.5°): an axis turns on once the stick leans past the edge of its 45° sector, which gives 8-way input.
3
2
  const AXIS_THRESHOLD = 0.38;
4
3
  /** Maps a stick offset in screen pixels (y grows downward) to the direction actions it presses. */
@@ -20,30 +19,94 @@ export function stickActions(dx, dy, deadZone) {
20
19
  actions.push("down");
21
20
  return actions;
22
21
  }
23
- /** Splits a game's input actions into the stick's directions and one button per other action. */
24
- export function touchLayout(inputActions) {
25
- const stick = DIRECTIONS.filter((action) => inputActions.includes(action));
26
- return { stick, buttons: inputActions.filter((action) => !stick.includes(action)) };
22
+ /** Derives the on-screen controls from a document's resolved input bindings. */
23
+ export function touchLayout(bindings) {
24
+ const buttons = [];
25
+ for (const { action, bindings: list } of bindings.actions) {
26
+ const button = list.find((binding) => binding.kind === "touchButton");
27
+ if (button) {
28
+ buttons.push({ action, label: button.label ?? action.toUpperCase() });
29
+ }
30
+ }
31
+ return {
32
+ stick: bindings.actions.some(({ bindings: list }) => list.some((binding) => binding.kind === "touchStick")) ||
33
+ bindings.axes.some(({ bindings: list }) => list.some((binding) => binding.kind === "touchStick")),
34
+ buttons,
35
+ look: bindings.look.some((binding) => binding.kind === "touchDrag")
36
+ };
27
37
  }
38
+ /** Styles for the touch layer, for players whose page does not already define them. */
39
+ export const TOUCH_CONTROLS_CSS = ".touch-layer{position:fixed;inset:0;z-index:2;pointer-events:none}" +
40
+ ".touch-stick-zone{position:absolute;left:0;top:0;bottom:0;width:50%;pointer-events:auto;touch-action:none}" +
41
+ ".touch-look-zone{position:absolute;right:0;top:0;bottom:0;width:50%;pointer-events:auto;touch-action:none}" +
42
+ ".touch-stick{position:absolute;left:25%;top:70%;width:7.5rem;height:7.5rem;margin:-3.75rem 0 0 -3.75rem;border-radius:50%;border:2px solid #fff5;background:#fff1;opacity:.35;transition:opacity .15s}" +
43
+ ".touch-stick.active{opacity:.9}" +
44
+ ".touch-knob{position:absolute;left:50%;top:50%;width:3.25rem;height:3.25rem;margin:-1.625rem 0 0 -1.625rem;border-radius:50%;background:#fff8;box-shadow:0 0 1rem #fff6}" +
45
+ ".touch-buttons{position:absolute;right:max(1.25rem,env(safe-area-inset-right));bottom:max(1.25rem,env(safe-area-inset-bottom));display:flex;flex-direction:column-reverse;gap:1rem;pointer-events:auto}" +
46
+ ".touch-button{width:5.25rem;height:5.25rem;border-radius:50%;border:2px solid #fff6;background:#ffffff1f;color:#fff;font:600 .8rem system-ui,sans-serif;letter-spacing:.05em;touch-action:none;box-shadow:0 0 1.25rem #0008}" +
47
+ ".touch-button.active{background:#ffffff59;transform:scale(.94)}" +
48
+ // After mouse movement the layer lets clicks through, so a mouse on a touchscreen laptop reaches the game.
49
+ ".touch-layer[data-pointer=mouse] *{pointer-events:none}";
28
50
  /**
29
51
  * A floating stick on the left half of the screen and action buttons on the right.
30
52
  * The stick appears where the thumb lands, so it works for any hand position.
31
53
  */
32
54
  export function mountTouchControls(root, options) {
33
- const layout = touchLayout(options.inputActions);
34
- const held = new Map();
35
- const emit = () => {
36
- const pressed = new Set();
37
- for (const actions of held.values())
38
- actions.forEach((action) => pressed.add(action));
39
- options.onChange(pressed);
40
- };
55
+ const { layout } = options;
56
+ let stick;
57
+ const held = new Set();
58
+ const emit = () => { options.onChange({ stick, buttons: new Set(held) }); };
41
59
  const cleanups = [];
60
+ // A mouse that moves over the page switches the layer to pass-through. The next touch or pen switches it back.
61
+ const pointerMode = (event) => {
62
+ const mode = event.pointerType === "mouse" ? "mouse" : "touch";
63
+ if (root.dataset.pointer !== mode) {
64
+ root.dataset.pointer = mode;
65
+ }
66
+ };
67
+ window.addEventListener("pointermove", pointerMode, { capture: true, passive: true });
68
+ window.addEventListener("pointerdown", pointerMode, { capture: true, passive: true });
69
+ cleanups.push(() => {
70
+ window.removeEventListener("pointermove", pointerMode, { capture: true });
71
+ window.removeEventListener("pointerdown", pointerMode, { capture: true });
72
+ delete root.dataset.pointer;
73
+ });
42
74
  const listen = (target, type, handler) => {
43
75
  target.addEventListener(type, handler, { passive: false });
44
76
  cleanups.push(() => target.removeEventListener(type, handler));
45
77
  };
46
- if (layout.stick.length > 0) {
78
+ if (layout.look && options.onLook) {
79
+ const onLook = options.onLook;
80
+ const zone = document.createElement("div");
81
+ zone.className = "touch-look-zone";
82
+ root.append(zone);
83
+ let pointerId;
84
+ let lastX = 0;
85
+ let lastY = 0;
86
+ const end = (event) => { if (event.pointerId === pointerId)
87
+ pointerId = undefined; };
88
+ listen(zone, "pointerdown", (event) => {
89
+ if (event.pointerType === "mouse")
90
+ return;
91
+ event.preventDefault();
92
+ if (pointerId !== undefined)
93
+ return;
94
+ pointerId = event.pointerId;
95
+ zone.setPointerCapture(event.pointerId);
96
+ lastX = event.clientX;
97
+ lastY = event.clientY;
98
+ });
99
+ listen(zone, "pointermove", (event) => {
100
+ if (event.pointerId !== pointerId)
101
+ return;
102
+ onLook(event.clientX - lastX, event.clientY - lastY);
103
+ lastX = event.clientX;
104
+ lastY = event.clientY;
105
+ });
106
+ listen(zone, "pointerup", end);
107
+ listen(zone, "pointercancel", end);
108
+ }
109
+ if (layout.stick) {
47
110
  const zone = document.createElement("div");
48
111
  zone.className = "touch-stick-zone";
49
112
  const base = document.createElement("div");
@@ -67,7 +130,7 @@ export function mountTouchControls(root, options) {
67
130
  dy = (dy / length) * limit;
68
131
  }
69
132
  knob.style.transform = `translate(${dx}px, ${dy}px)`;
70
- held.set("stick", new Set(stickActions(dx, dy, limit * 0.25).filter((action) => layout.stick.includes(action))));
133
+ stick = { x: dx / limit, y: dy / limit };
71
134
  emit();
72
135
  };
73
136
  const release = (event) => {
@@ -76,10 +139,12 @@ export function mountTouchControls(root, options) {
76
139
  pointerId = undefined;
77
140
  base.classList.remove("active");
78
141
  knob.style.transform = "";
79
- held.delete("stick");
142
+ stick = undefined;
80
143
  emit();
81
144
  };
82
145
  listen(zone, "pointerdown", (event) => {
146
+ if (event.pointerType === "mouse")
147
+ return;
83
148
  event.preventDefault();
84
149
  if (pointerId !== undefined)
85
150
  return;
@@ -102,23 +167,24 @@ export function mountTouchControls(root, options) {
102
167
  }
103
168
  const buttons = document.createElement("div");
104
169
  buttons.className = "touch-buttons";
105
- for (const action of layout.buttons) {
170
+ for (const { action, label } of layout.buttons) {
106
171
  const button = document.createElement("button");
107
172
  button.type = "button";
108
173
  button.className = "touch-button";
109
- button.textContent = action.toUpperCase();
174
+ button.textContent = label;
110
175
  button.setAttribute("aria-label", action);
111
- const key = `button:${action}`;
112
176
  const up = () => {
113
177
  button.classList.remove("active");
114
- held.delete(key);
178
+ held.delete(action);
115
179
  emit();
116
180
  };
117
181
  listen(button, "pointerdown", (event) => {
182
+ if (event.pointerType === "mouse")
183
+ return;
118
184
  event.preventDefault();
119
185
  button.setPointerCapture(event.pointerId);
120
186
  button.classList.add("active");
121
- held.set(key, new Set([action]));
187
+ held.add(action);
122
188
  emit();
123
189
  });
124
190
  listen(button, "pointerup", up);
@@ -131,6 +197,7 @@ export function mountTouchControls(root, options) {
131
197
  cleanups.forEach((cleanup) => cleanup());
132
198
  root.replaceChildren();
133
199
  held.clear();
200
+ stick = undefined;
134
201
  };
135
202
  }
136
203
  //# sourceMappingURL=touch-controls.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"touch-controls.js","sourceRoot":"","sources":["../src/touch-controls.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"touch-controls.js","sourceRoot":"","sources":["../src/touch-controls.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ import type { GameUiFrame } from "@nodetool-ai/protocol";
2
+ import type { GamepadLike } from "../input-bindings.js";
3
+ import { type GameUiInsets, type GameUiMeasureText, type GameUiViewport } from "./layout.js";
4
+ /** Where HUD button presses go: `GameInput` holds the button's action like a bound control. */
5
+ export interface GameUiActionSink {
6
+ pressUiAction(action: string): void;
7
+ releaseUiAction(action: string): void;
8
+ }
9
+ /**
10
+ * Turns pointer, touch and gamepad input on the HUD into presses of the buttons' input actions.
11
+ * A pressed button holds its action until the pointer lifts or the confirm button is released, so a button
12
+ * reaches the simulation as ordinary input and replays exactly. Focus is presentation and never enters a snapshot.
13
+ */
14
+ export declare class GameUiController {
15
+ private readonly sink;
16
+ private readonly measure;
17
+ private boxes;
18
+ private ui;
19
+ private focusId;
20
+ private readonly held;
21
+ private readonly padButtons;
22
+ private insets;
23
+ constructor(sink: GameUiActionSink, measure: GameUiMeasureText);
24
+ /** Lays out the frame's HUD tree in the HUD rectangle, for hit tests and focus moves until the next frame. */
25
+ update(ui: GameUiFrame | undefined, viewport: GameUiViewport, insets?: GameUiInsets): void;
26
+ /** The frame's HUD tree with the focused button and safe-area insets, for the renderer. */
27
+ annotate<F extends {
28
+ ui?: GameUiFrame;
29
+ }>(frame: F): F;
30
+ get focusedId(): string | undefined;
31
+ /** A pointer went down at a HUD point. Returns true when it landed on a button, which then holds its action. */
32
+ pointerDown(pointerId: number, x: number, y: number): boolean;
33
+ pointerUp(pointerId: number): void;
34
+ /** Whether the D-pad and confirm button drive the HUD this frame. */
35
+ get navigating(): boolean;
36
+ /**
37
+ * Moves focus with the D-pad and presses the focused button with the confirm button, then returns the pads
38
+ * with those buttons released, so the game's own bindings do not also see them. Without focus navigation the
39
+ * pads pass through unchanged.
40
+ */
41
+ pollGamepads(pads: readonly (GamepadLike | null)[] | null | undefined): readonly (GamepadLike | null)[];
42
+ /** Releases everything the HUD holds, as when play pauses or stops. */
43
+ release(): void;
44
+ private hold;
45
+ private releaseKey;
46
+ }
47
+ /**
48
+ * Forwards pointer events on `element` that land on HUD buttons to the controller, in HUD pixels.
49
+ * Events that hit a button stop there, so they do not also reach touch look or canvas focus handlers.
50
+ */
51
+ export declare function attachGameUiPointer(element: HTMLElement, controller: GameUiController, viewport: () => GameUiViewport | undefined, active?: () => boolean): () => void;
52
+ /**
53
+ * The device safe area where it overlaps `element`, in HUD pixels. Reads the CSS `env(safe-area-inset-*)`
54
+ * values through a probe element, so it is zero on devices without a notch or rounded corners.
55
+ */
56
+ export declare function browserGameUiInsets(element: HTMLElement, viewport: GameUiViewport): GameUiInsets;
57
+ /** Measures HUD text with an offscreen browser canvas. Without a 2D context, text measures zero wide. */
58
+ export declare function browserGameUiMeasure(gameId: string | undefined): GameUiMeasureText;
59
+ //# sourceMappingURL=controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/ui/controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,EAAwF,KAAK,YAAY,EAAE,KAAK,iBAAiB,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAEnL,+FAA+F;AAC/F,MAAM,WAAW,gBAAgB;IAC/B,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;AAOD;;;;GAIG;AACH,qBAAa,gBAAgB;IAQf,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAoB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAP7E,OAAO,CAAC,KAAK,CAAmB;IAChC,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAsC;IAC3D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;IAChD,OAAO,CAAC,MAAM,CAA2B;IAEzC,YAA6B,IAAI,EAAE,gBAAgB,EAAmB,OAAO,EAAE,iBAAiB,EAAI;IAEpG,8GAA8G;IAC9G,MAAM,CAAC,EAAE,EAAE,WAAW,GAAG,SAAS,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,YAAY,GAAG,IAAI,CAOzF;IAED,2FAA2F;IAC3F,QAAQ,CAAC,CAAC,SAAS;QAAE,EAAE,CAAC,EAAE,WAAW,CAAA;KAAE,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAMpD;IAED,IAAI,SAAS,IAAI,MAAM,GAAG,SAAS,CAAyB;IAE5D,gHAAgH;IAChH,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAK5D;IAED,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAEjC;IAED,qEAAqE;IACrE,IAAI,UAAU,IAAI,OAAO,CAExB;IAED;;;;OAIG;IACH,YAAY,CAAC,IAAI,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI,GAAG,SAAS,GAAG,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,EAAE,CA4BtG;IAED,uEAAuE;IACvE,OAAO,IAAI,IAAI,CAGd;IAED,OAAO,CAAC,IAAI;IAMZ,OAAO,CAAC,UAAU;CAMnB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,cAAc,GAAG,SAAS,EAChI,MAAM,GAAE,MAAM,OAAoB,GAAG,MAAM,IAAI,CAsBhD;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,cAAc,GAAG,YAAY,CAmBhG;AAED,yGAAyG;AACzG,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,iBAAiB,CAOlF"}