@thunderclap-engine/engine 0.0.0-stage → 1.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 (566) hide show
  1. package/CHANGELOG.md +390 -0
  2. package/LICENSE +21 -0
  3. package/README.md +333 -2
  4. package/dist/analytics/analytics.d.ts +80 -0
  5. package/dist/analytics/analytics.js +129 -0
  6. package/dist/analytics/index.d.ts +1 -0
  7. package/dist/analytics/index.js +1 -0
  8. package/dist/anim/aim.d.ts +43 -0
  9. package/dist/anim/aim.js +157 -0
  10. package/dist/anim/clip.d.ts +98 -0
  11. package/dist/anim/clip.js +282 -0
  12. package/dist/anim/compress.d.ts +65 -0
  13. package/dist/anim/compress.js +339 -0
  14. package/dist/anim/foot-ik.d.ts +74 -0
  15. package/dist/anim/foot-ik.js +241 -0
  16. package/dist/anim/graph.d.ts +153 -0
  17. package/dist/anim/graph.js +532 -0
  18. package/dist/anim/ik.d.ts +105 -0
  19. package/dist/anim/ik.js +435 -0
  20. package/dist/anim/index.d.ts +36 -0
  21. package/dist/anim/index.js +21 -0
  22. package/dist/anim/match.d.ts +66 -0
  23. package/dist/anim/match.js +153 -0
  24. package/dist/anim/mixer.d.ts +113 -0
  25. package/dist/anim/mixer.js +314 -0
  26. package/dist/anim/morph.d.ts +41 -0
  27. package/dist/anim/morph.js +94 -0
  28. package/dist/anim/pose.d.ts +74 -0
  29. package/dist/anim/pose.js +254 -0
  30. package/dist/anim/quat.d.ts +44 -0
  31. package/dist/anim/quat.js +187 -0
  32. package/dist/anim/retarget.d.ts +97 -0
  33. package/dist/anim/retarget.js +366 -0
  34. package/dist/anim/root-motion.d.ts +44 -0
  35. package/dist/anim/root-motion.js +224 -0
  36. package/dist/anim/skin-pose.d.ts +39 -0
  37. package/dist/anim/skin-pose.js +53 -0
  38. package/dist/anim/socket.d.ts +47 -0
  39. package/dist/anim/socket.js +84 -0
  40. package/dist/anim/spring-chain.d.ts +73 -0
  41. package/dist/anim/spring-chain.js +294 -0
  42. package/dist/assets/atlas.d.ts +90 -0
  43. package/dist/assets/atlas.js +212 -0
  44. package/dist/assets/baked.d.ts +33 -0
  45. package/dist/assets/baked.js +38 -0
  46. package/dist/assets/bundle.d.ts +88 -0
  47. package/dist/assets/bundle.js +464 -0
  48. package/dist/assets/cache.d.ts +56 -0
  49. package/dist/assets/cache.js +123 -0
  50. package/dist/assets/gltf-load.d.ts +252 -0
  51. package/dist/assets/gltf-load.js +661 -0
  52. package/dist/assets/gltf-skin.d.ts +36 -0
  53. package/dist/assets/gltf-skin.js +109 -0
  54. package/dist/assets/gltf.d.ts +706 -0
  55. package/dist/assets/gltf.js +1376 -0
  56. package/dist/assets/index.d.ts +20 -0
  57. package/dist/assets/index.js +10 -0
  58. package/dist/assets/ktx2.d.ts +71 -0
  59. package/dist/assets/ktx2.js +213 -0
  60. package/dist/assets/manifest.d.ts +79 -0
  61. package/dist/assets/manifest.js +7 -0
  62. package/dist/assets/meshopt.d.ts +23 -0
  63. package/dist/assets/meshopt.js +403 -0
  64. package/dist/audio/audio.d.ts +733 -0
  65. package/dist/audio/audio.js +2013 -0
  66. package/dist/audio/dsp.d.ts +61 -0
  67. package/dist/audio/dsp.js +159 -0
  68. package/dist/audio/duck.d.ts +44 -0
  69. package/dist/audio/duck.js +101 -0
  70. package/dist/audio/envelope.d.ts +53 -0
  71. package/dist/audio/envelope.js +106 -0
  72. package/dist/audio/index.d.ts +13 -0
  73. package/dist/audio/index.js +14 -0
  74. package/dist/audio/music.d.ts +113 -0
  75. package/dist/audio/music.js +548 -0
  76. package/dist/audio/occlusion.d.ts +55 -0
  77. package/dist/audio/occlusion.js +61 -0
  78. package/dist/audio/persist.d.ts +23 -0
  79. package/dist/audio/persist.js +27 -0
  80. package/dist/audio/recipes.d.ts +55 -0
  81. package/dist/audio/recipes.js +227 -0
  82. package/dist/audio/send.d.ts +41 -0
  83. package/dist/audio/send.js +118 -0
  84. package/dist/audio/sprites.d.ts +59 -0
  85. package/dist/audio/sprites.js +43 -0
  86. package/dist/audio/synth.d.ts +103 -0
  87. package/dist/audio/synth.js +197 -0
  88. package/dist/audio/variations.d.ts +25 -0
  89. package/dist/audio/variations.js +55 -0
  90. package/dist/audio/zones.d.ts +100 -0
  91. package/dist/audio/zones.js +167 -0
  92. package/dist/core/detached-surface.d.ts +44 -0
  93. package/dist/core/detached-surface.js +104 -0
  94. package/dist/core/events.d.ts +24 -0
  95. package/dist/core/events.js +43 -0
  96. package/dist/core/game.d.ts +409 -0
  97. package/dist/core/game.js +530 -0
  98. package/dist/core/hash.d.ts +24 -0
  99. package/dist/core/hash.js +49 -0
  100. package/dist/core/index.d.ts +17 -0
  101. package/dist/core/index.js +9 -0
  102. package/dist/core/loop.d.ts +113 -0
  103. package/dist/core/loop.js +185 -0
  104. package/dist/core/math.d.ts +58 -0
  105. package/dist/core/math.js +210 -0
  106. package/dist/core/noise.d.ts +40 -0
  107. package/dist/core/noise.js +247 -0
  108. package/dist/core/pool.d.ts +42 -0
  109. package/dist/core/pool.js +85 -0
  110. package/dist/core/random.d.ts +61 -0
  111. package/dist/core/random.js +199 -0
  112. package/dist/core/scene.d.ts +79 -0
  113. package/dist/core/scene.js +195 -0
  114. package/dist/core/timer.d.ts +66 -0
  115. package/dist/core/timer.js +286 -0
  116. package/dist/fx/billboard-wgsl.d.ts +29 -0
  117. package/dist/fx/billboard-wgsl.js +158 -0
  118. package/dist/fx/billboard.d.ts +249 -0
  119. package/dist/fx/billboard.js +702 -0
  120. package/dist/fx/debug-draw.d.ts +127 -0
  121. package/dist/fx/debug-draw.js +523 -0
  122. package/dist/fx/debug-wgsl.d.ts +7 -0
  123. package/dist/fx/debug-wgsl.js +32 -0
  124. package/dist/fx/decals-wgsl.d.ts +15 -0
  125. package/dist/fx/decals-wgsl.js +173 -0
  126. package/dist/fx/decals.d.ts +216 -0
  127. package/dist/fx/decals.js +607 -0
  128. package/dist/fx/effect.d.ts +129 -0
  129. package/dist/fx/effect.js +349 -0
  130. package/dist/fx/effects/hit-spark.json +48 -0
  131. package/dist/fx/effects/index.d.ts +8 -0
  132. package/dist/fx/effects/index.js +9 -0
  133. package/dist/fx/effects/magic-aura.json +47 -0
  134. package/dist/fx/effects/smoke-puff.json +34 -0
  135. package/dist/fx/emit-surface.d.ts +40 -0
  136. package/dist/fx/emit-surface.js +124 -0
  137. package/dist/fx/flicker.d.ts +30 -0
  138. package/dist/fx/flicker.js +42 -0
  139. package/dist/fx/index.d.ts +31 -0
  140. package/dist/fx/index.js +20 -0
  141. package/dist/fx/particles-wgsl.d.ts +57 -0
  142. package/dist/fx/particles-wgsl.js +828 -0
  143. package/dist/fx/particles.d.ts +566 -0
  144. package/dist/fx/particles.js +1533 -0
  145. package/dist/fx/random.d.ts +15 -0
  146. package/dist/fx/random.js +20 -0
  147. package/dist/fx/sky-wgsl.d.ts +23 -0
  148. package/dist/fx/sky-wgsl.js +127 -0
  149. package/dist/fx/sky.d.ts +96 -0
  150. package/dist/fx/sky.js +175 -0
  151. package/dist/fx/text-atlas.d.ts +143 -0
  152. package/dist/fx/text-atlas.js +327 -0
  153. package/dist/fx/trail-wgsl.d.ts +11 -0
  154. package/dist/fx/trail-wgsl.js +42 -0
  155. package/dist/fx/trail.d.ts +194 -0
  156. package/dist/fx/trail.js +555 -0
  157. package/dist/fx/water-wgsl.d.ts +34 -0
  158. package/dist/fx/water-wgsl.js +197 -0
  159. package/dist/fx/water.d.ts +176 -0
  160. package/dist/fx/water.js +371 -0
  161. package/dist/geom/collide/bvh.d.ts +151 -0
  162. package/dist/geom/collide/bvh.js +493 -0
  163. package/dist/geom/collide/debug.d.ts +14 -0
  164. package/dist/geom/collide/debug.js +74 -0
  165. package/dist/geom/collide/grid.d.ts +26 -0
  166. package/dist/geom/collide/grid.js +91 -0
  167. package/dist/geom/collide/index.d.ts +8 -0
  168. package/dist/geom/collide/index.js +8 -0
  169. package/dist/geom/collide/queries.d.ts +70 -0
  170. package/dist/geom/collide/queries.js +583 -0
  171. package/dist/geom/collide/serialize.d.ts +14 -0
  172. package/dist/geom/collide/serialize.js +145 -0
  173. package/dist/geom/collide/shapes.d.ts +161 -0
  174. package/dist/geom/collide/shapes.js +686 -0
  175. package/dist/geom/collide/slide.d.ts +66 -0
  176. package/dist/geom/collide/slide.js +375 -0
  177. package/dist/geom/collide/tile-slide.d.ts +88 -0
  178. package/dist/geom/collide/tile-slide.js +255 -0
  179. package/dist/geom/geometry.d.ts +95 -0
  180. package/dist/geom/geometry.js +322 -0
  181. package/dist/geom/heightfield.d.ts +72 -0
  182. package/dist/geom/heightfield.js +295 -0
  183. package/dist/geom/hull.d.ts +27 -0
  184. package/dist/geom/hull.js +279 -0
  185. package/dist/geom/index.d.ts +35 -0
  186. package/dist/geom/index.js +23 -0
  187. package/dist/geom/iso.d.ts +74 -0
  188. package/dist/geom/iso.js +89 -0
  189. package/dist/geom/lod.d.ts +29 -0
  190. package/dist/geom/lod.js +27 -0
  191. package/dist/geom/ops.d.ts +47 -0
  192. package/dist/geom/ops.js +285 -0
  193. package/dist/geom/primitives.d.ts +97 -0
  194. package/dist/geom/primitives.js +359 -0
  195. package/dist/geom/shapes.d.ts +43 -0
  196. package/dist/geom/shapes.js +88 -0
  197. package/dist/geom/simplify.d.ts +38 -0
  198. package/dist/geom/simplify.js +519 -0
  199. package/dist/geom/split.d.ts +37 -0
  200. package/dist/geom/split.js +413 -0
  201. package/dist/geom/sweep.d.ts +83 -0
  202. package/dist/geom/sweep.js +534 -0
  203. package/dist/geom/tangents.d.ts +16 -0
  204. package/dist/geom/tangents.js +124 -0
  205. package/dist/geom/triangulate.d.ts +19 -0
  206. package/dist/geom/triangulate.js +147 -0
  207. package/dist/host/default.d.ts +10 -0
  208. package/dist/host/default.js +10 -0
  209. package/dist/host/index.d.ts +4 -0
  210. package/dist/host/index.js +3 -0
  211. package/dist/host/types.d.ts +144 -0
  212. package/dist/host/types.js +1 -0
  213. package/dist/host/web-links.d.ts +38 -0
  214. package/dist/host/web-links.js +110 -0
  215. package/dist/host/web.d.ts +30 -0
  216. package/dist/host/web.js +216 -0
  217. package/dist/host/xr.d.ts +24 -0
  218. package/dist/host/xr.js +59 -0
  219. package/dist/i18n/i18n.d.ts +59 -0
  220. package/dist/i18n/i18n.js +241 -0
  221. package/dist/i18n/index.d.ts +3 -0
  222. package/dist/i18n/index.js +3 -0
  223. package/dist/i18n/message.d.ts +72 -0
  224. package/dist/i18n/message.js +369 -0
  225. package/dist/i18n/negotiate.d.ts +17 -0
  226. package/dist/i18n/negotiate.js +58 -0
  227. package/dist/index.d.ts +7 -0
  228. package/dist/index.js +1 -0
  229. package/dist/input/bindings.d.ts +31 -0
  230. package/dist/input/bindings.js +141 -0
  231. package/dist/input/clock.d.ts +44 -0
  232. package/dist/input/clock.js +49 -0
  233. package/dist/input/follow.d.ts +133 -0
  234. package/dist/input/follow.js +218 -0
  235. package/dist/input/gestures.d.ts +281 -0
  236. package/dist/input/gestures.js +662 -0
  237. package/dist/input/glyphs.d.ts +35 -0
  238. package/dist/input/glyphs.js +216 -0
  239. package/dist/input/gyro.d.ts +85 -0
  240. package/dist/input/gyro.js +140 -0
  241. package/dist/input/index.d.ts +19 -0
  242. package/dist/input/index.js +16 -0
  243. package/dist/input/input.d.ts +543 -0
  244. package/dist/input/input.js +1492 -0
  245. package/dist/input/look.d.ts +327 -0
  246. package/dist/input/look.js +681 -0
  247. package/dist/input/magnify.d.ts +177 -0
  248. package/dist/input/magnify.js +131 -0
  249. package/dist/input/pointer-dispatch.d.ts +138 -0
  250. package/dist/input/pointer-dispatch.js +180 -0
  251. package/dist/input/pointer-router.d.ts +204 -0
  252. package/dist/input/pointer-router.js +533 -0
  253. package/dist/input/pointer-sources.d.ts +164 -0
  254. package/dist/input/pointer-sources.js +306 -0
  255. package/dist/input/rays.d.ts +138 -0
  256. package/dist/input/rays.js +615 -0
  257. package/dist/input/record.d.ts +209 -0
  258. package/dist/input/record.js +601 -0
  259. package/dist/input/touch-zones.d.ts +147 -0
  260. package/dist/input/touch-zones.js +300 -0
  261. package/dist/input/types.d.ts +158 -0
  262. package/dist/input/types.js +1 -0
  263. package/dist/input/xr-source.d.ts +53 -0
  264. package/dist/input/xr-source.js +164 -0
  265. package/dist/nav/astar.d.ts +45 -0
  266. package/dist/nav/astar.js +182 -0
  267. package/dist/nav/corridor.d.ts +40 -0
  268. package/dist/nav/corridor.js +95 -0
  269. package/dist/nav/crowd.d.ts +94 -0
  270. package/dist/nav/crowd.js +624 -0
  271. package/dist/nav/flow.d.ts +61 -0
  272. package/dist/nav/flow.js +193 -0
  273. package/dist/nav/grid.d.ts +108 -0
  274. package/dist/nav/grid.js +210 -0
  275. package/dist/nav/heap.d.ts +22 -0
  276. package/dist/nav/heap.js +92 -0
  277. package/dist/nav/index.d.ts +37 -0
  278. package/dist/nav/index.js +19 -0
  279. package/dist/nav/mesh/areas.d.ts +44 -0
  280. package/dist/nav/mesh/areas.js +98 -0
  281. package/dist/nav/mesh/bake-options.d.ts +33 -0
  282. package/dist/nav/mesh/bake-options.js +76 -0
  283. package/dist/nav/mesh/contour.d.ts +22 -0
  284. package/dist/nav/mesh/contour.js +323 -0
  285. package/dist/nav/mesh/flow3.d.ts +35 -0
  286. package/dist/nav/mesh/flow3.js +229 -0
  287. package/dist/nav/mesh/links.d.ts +38 -0
  288. package/dist/nav/mesh/links.js +72 -0
  289. package/dist/nav/mesh/mesh-path.d.ts +152 -0
  290. package/dist/nav/mesh/mesh-path.js +643 -0
  291. package/dist/nav/mesh/mesh-reach.d.ts +78 -0
  292. package/dist/nav/mesh/mesh-reach.js +450 -0
  293. package/dist/nav/mesh/navmesh.d.ts +117 -0
  294. package/dist/nav/mesh/navmesh.js +647 -0
  295. package/dist/nav/mesh/polymesh.d.ts +24 -0
  296. package/dist/nav/mesh/polymesh.js +384 -0
  297. package/dist/nav/mesh/regions.d.ts +12 -0
  298. package/dist/nav/mesh/regions.js +367 -0
  299. package/dist/nav/mesh/serialize.d.ts +13 -0
  300. package/dist/nav/mesh/serialize.js +197 -0
  301. package/dist/nav/mesh/voxelize.d.ts +111 -0
  302. package/dist/nav/mesh/voxelize.js +552 -0
  303. package/dist/nav/obstacles.d.ts +75 -0
  304. package/dist/nav/obstacles.js +294 -0
  305. package/dist/nav/path.d.ts +59 -0
  306. package/dist/nav/path.js +177 -0
  307. package/dist/nav/query-queue.d.ts +46 -0
  308. package/dist/nav/query-queue.js +308 -0
  309. package/dist/nav/reach.d.ts +53 -0
  310. package/dist/nav/reach.js +148 -0
  311. package/dist/nav/steer.d.ts +36 -0
  312. package/dist/nav/steer.js +78 -0
  313. package/dist/platform/boot.d.ts +28 -0
  314. package/dist/platform/boot.js +43 -0
  315. package/dist/platform/device-mode.d.ts +40 -0
  316. package/dist/platform/device-mode.js +104 -0
  317. package/dist/platform/device.d.ts +31 -0
  318. package/dist/platform/device.js +91 -0
  319. package/dist/platform/environment.d.ts +42 -0
  320. package/dist/platform/environment.js +82 -0
  321. package/dist/platform/gpu.d.ts +40 -0
  322. package/dist/platform/gpu.js +57 -0
  323. package/dist/platform/index.d.ts +18 -0
  324. package/dist/platform/index.js +10 -0
  325. package/dist/platform/letterbox.d.ts +43 -0
  326. package/dist/platform/letterbox.js +57 -0
  327. package/dist/platform/links.d.ts +20 -0
  328. package/dist/platform/links.js +43 -0
  329. package/dist/platform/screens.d.ts +36 -0
  330. package/dist/platform/screens.js +138 -0
  331. package/dist/platform/surface.d.ts +72 -0
  332. package/dist/platform/surface.js +157 -0
  333. package/dist/platform/xr-fake.d.ts +168 -0
  334. package/dist/platform/xr-fake.js +434 -0
  335. package/dist/platform/xr-surface.d.ts +120 -0
  336. package/dist/platform/xr-surface.js +214 -0
  337. package/dist/platform/xr-types.d.ts +262 -0
  338. package/dist/post/ao.d.ts +12 -0
  339. package/dist/post/ao.js +136 -0
  340. package/dist/post/blend.d.ts +27 -0
  341. package/dist/post/blend.js +129 -0
  342. package/dist/post/depth-pass.d.ts +43 -0
  343. package/dist/post/depth-pass.js +118 -0
  344. package/dist/post/dof.d.ts +1 -0
  345. package/dist/post/dof.js +66 -0
  346. package/dist/post/exposure.d.ts +80 -0
  347. package/dist/post/exposure.js +322 -0
  348. package/dist/post/index.d.ts +12 -0
  349. package/dist/post/index.js +11 -0
  350. package/dist/post/lens-dirt.d.ts +13 -0
  351. package/dist/post/lens-dirt.js +91 -0
  352. package/dist/post/lut.d.ts +19 -0
  353. package/dist/post/lut.js +50 -0
  354. package/dist/post/motion-blur.d.ts +9 -0
  355. package/dist/post/motion-blur.js +78 -0
  356. package/dist/post/post-stack.d.ts +302 -0
  357. package/dist/post/post-stack.js +1137 -0
  358. package/dist/post/settings.d.ts +485 -0
  359. package/dist/post/settings.js +416 -0
  360. package/dist/post/shaders.d.ts +25 -0
  361. package/dist/post/shaders.js +410 -0
  362. package/dist/post/ssr.d.ts +16 -0
  363. package/dist/post/ssr.js +155 -0
  364. package/dist/post/taa.d.ts +74 -0
  365. package/dist/post/taa.js +235 -0
  366. package/dist/post/white-balance.d.ts +25 -0
  367. package/dist/post/white-balance.js +98 -0
  368. package/dist/render/bindings.d.ts +98 -0
  369. package/dist/render/bindings.js +281 -0
  370. package/dist/render/blend.d.ts +13 -0
  371. package/dist/render/blend.js +26 -0
  372. package/dist/render/camera-2d.d.ts +72 -0
  373. package/dist/render/camera-2d.js +92 -0
  374. package/dist/render/camera.d.ts +302 -0
  375. package/dist/render/camera.js +671 -0
  376. package/dist/render/color.d.ts +16 -0
  377. package/dist/render/color.js +41 -0
  378. package/dist/render/compressed.d.ts +104 -0
  379. package/dist/render/compressed.js +248 -0
  380. package/dist/render/diagnostics.d.ts +15 -0
  381. package/dist/render/diagnostics.js +43 -0
  382. package/dist/render/dynamic-resolution.d.ts +70 -0
  383. package/dist/render/dynamic-resolution.js +126 -0
  384. package/dist/render/fake-gpu.d.ts +204 -0
  385. package/dist/render/fake-gpu.js +426 -0
  386. package/dist/render/flipbook.d.ts +56 -0
  387. package/dist/render/flipbook.js +87 -0
  388. package/dist/render/frame.d.ts +278 -0
  389. package/dist/render/frame.js +174 -0
  390. package/dist/render/fullscreen.d.ts +54 -0
  391. package/dist/render/fullscreen.js +91 -0
  392. package/dist/render/gpu-timer.d.ts +73 -0
  393. package/dist/render/gpu-timer.js +186 -0
  394. package/dist/render/index.d.ts +53 -0
  395. package/dist/render/index.js +29 -0
  396. package/dist/render/instance-list.d.ts +185 -0
  397. package/dist/render/instance-list.js +547 -0
  398. package/dist/render/lights.d.ts +181 -0
  399. package/dist/render/lights.js +233 -0
  400. package/dist/render/lod.d.ts +54 -0
  401. package/dist/render/lod.js +52 -0
  402. package/dist/render/material.d.ts +165 -0
  403. package/dist/render/material.js +292 -0
  404. package/dist/render/mesh.d.ts +284 -0
  405. package/dist/render/mesh.js +472 -0
  406. package/dist/render/mipmap.d.ts +16 -0
  407. package/dist/render/mipmap.js +121 -0
  408. package/dist/render/parallax.d.ts +50 -0
  409. package/dist/render/parallax.js +47 -0
  410. package/dist/render/pbr-wgsl.d.ts +31 -0
  411. package/dist/render/pbr-wgsl.js +119 -0
  412. package/dist/render/ping-pong.d.ts +42 -0
  413. package/dist/render/ping-pong.js +74 -0
  414. package/dist/render/renderer.d.ts +741 -0
  415. package/dist/render/renderer.js +2486 -0
  416. package/dist/render/scene-pipelines.d.ts +44 -0
  417. package/dist/render/scene-pipelines.js +92 -0
  418. package/dist/render/scene-target.d.ts +170 -0
  419. package/dist/render/scene-target.js +450 -0
  420. package/dist/render/shadow.d.ts +74 -0
  421. package/dist/render/shadow.js +208 -0
  422. package/dist/render/skin.d.ts +112 -0
  423. package/dist/render/skin.js +240 -0
  424. package/dist/render/snapshot.d.ts +36 -0
  425. package/dist/render/snapshot.js +34 -0
  426. package/dist/render/standard.d.ts +162 -0
  427. package/dist/render/standard.js +263 -0
  428. package/dist/render/texture.d.ts +142 -0
  429. package/dist/render/texture.js +228 -0
  430. package/dist/render/tile-layer.d.ts +110 -0
  431. package/dist/render/tile-layer.js +304 -0
  432. package/dist/render/transform.d.ts +115 -0
  433. package/dist/render/transform.js +318 -0
  434. package/dist/render/wgsl.d.ts +131 -0
  435. package/dist/render/wgsl.js +528 -0
  436. package/dist/save/codec.d.ts +49 -0
  437. package/dist/save/codec.js +169 -0
  438. package/dist/save/index.d.ts +8 -0
  439. package/dist/save/index.js +8 -0
  440. package/dist/save/journal.d.ts +24 -0
  441. package/dist/save/journal.js +102 -0
  442. package/dist/save/migrate.d.ts +15 -0
  443. package/dist/save/migrate.js +37 -0
  444. package/dist/save/schema.d.ts +72 -0
  445. package/dist/save/schema.js +154 -0
  446. package/dist/save/settings.d.ts +28 -0
  447. package/dist/save/settings.js +59 -0
  448. package/dist/save/slots.d.ts +56 -0
  449. package/dist/save/slots.js +200 -0
  450. package/dist/save/storage.d.ts +35 -0
  451. package/dist/save/storage.js +62 -0
  452. package/dist/save/store.d.ts +114 -0
  453. package/dist/save/store.js +269 -0
  454. package/dist/testing/fake-host.d.ts +103 -0
  455. package/dist/testing/fake-host.js +163 -0
  456. package/dist/testing/fixed-clock.d.ts +25 -0
  457. package/dist/testing/fixed-clock.js +30 -0
  458. package/dist/testing/index.d.ts +4 -0
  459. package/dist/testing/index.js +5 -0
  460. package/dist/testing/test-game.d.ts +76 -0
  461. package/dist/testing/test-game.js +92 -0
  462. package/dist/tween/ease.d.ts +77 -0
  463. package/dist/tween/ease.js +192 -0
  464. package/dist/tween/index.d.ts +8 -0
  465. package/dist/tween/index.js +7 -0
  466. package/dist/tween/timeline.d.ts +78 -0
  467. package/dist/tween/timeline.js +269 -0
  468. package/dist/tween/tween.d.ts +168 -0
  469. package/dist/tween/tween.js +320 -0
  470. package/dist/ui/anchor.d.ts +206 -0
  471. package/dist/ui/anchor.js +307 -0
  472. package/dist/ui/caption.d.ts +122 -0
  473. package/dist/ui/caption.js +286 -0
  474. package/dist/ui/cursor.d.ts +105 -0
  475. package/dist/ui/cursor.js +261 -0
  476. package/dist/ui/dom.d.ts +30 -0
  477. package/dist/ui/dom.js +92 -0
  478. package/dist/ui/floats.d.ts +86 -0
  479. package/dist/ui/floats.js +260 -0
  480. package/dist/ui/hud.d.ts +198 -0
  481. package/dist/ui/hud.js +431 -0
  482. package/dist/ui/index.d.ts +37 -0
  483. package/dist/ui/index.js +34 -0
  484. package/dist/ui/inventory-card.d.ts +52 -0
  485. package/dist/ui/inventory-card.js +173 -0
  486. package/dist/ui/inventory-context.d.ts +29 -0
  487. package/dist/ui/inventory-context.js +125 -0
  488. package/dist/ui/inventory-drag.d.ts +63 -0
  489. package/dist/ui/inventory-drag.js +394 -0
  490. package/dist/ui/inventory-grid.d.ts +66 -0
  491. package/dist/ui/inventory-grid.js +625 -0
  492. package/dist/ui/inventory-radial.d.ts +39 -0
  493. package/dist/ui/inventory-radial.js +168 -0
  494. package/dist/ui/inventory.d.ts +128 -0
  495. package/dist/ui/inventory.js +154 -0
  496. package/dist/ui/layout.d.ts +356 -0
  497. package/dist/ui/layout.js +856 -0
  498. package/dist/ui/link.d.ts +64 -0
  499. package/dist/ui/link.js +155 -0
  500. package/dist/ui/loading.d.ts +40 -0
  501. package/dist/ui/loading.js +157 -0
  502. package/dist/ui/loupe.d.ts +74 -0
  503. package/dist/ui/loupe.js +164 -0
  504. package/dist/ui/menu-nav.d.ts +33 -0
  505. package/dist/ui/menu-nav.js +28 -0
  506. package/dist/ui/minimap-canvas.d.ts +145 -0
  507. package/dist/ui/minimap-canvas.js +460 -0
  508. package/dist/ui/notify.d.ts +40 -0
  509. package/dist/ui/notify.js +141 -0
  510. package/dist/ui/orb.d.ts +53 -0
  511. package/dist/ui/orb.js +304 -0
  512. package/dist/ui/overlay.d.ts +213 -0
  513. package/dist/ui/overlay.js +394 -0
  514. package/dist/ui/padnav.d.ts +53 -0
  515. package/dist/ui/padnav.js +146 -0
  516. package/dist/ui/perf.d.ts +130 -0
  517. package/dist/ui/perf.js +329 -0
  518. package/dist/ui/rotate-card.d.ts +54 -0
  519. package/dist/ui/rotate-card.js +97 -0
  520. package/dist/ui/stack.d.ts +128 -0
  521. package/dist/ui/stack.js +312 -0
  522. package/dist/ui/styles.d.ts +13 -0
  523. package/dist/ui/styles.js +200 -0
  524. package/dist/ui/text-scale.d.ts +67 -0
  525. package/dist/ui/text-scale.js +102 -0
  526. package/dist/ui/theme.d.ts +70 -0
  527. package/dist/ui/theme.js +98 -0
  528. package/dist/ui/toast.d.ts +23 -0
  529. package/dist/ui/toast.js +46 -0
  530. package/dist/ui/tooltip.d.ts +89 -0
  531. package/dist/ui/tooltip.js +333 -0
  532. package/dist/ui/widgets-2.d.ts +134 -0
  533. package/dist/ui/widgets-2.js +399 -0
  534. package/dist/ui/widgets-3.d.ts +91 -0
  535. package/dist/ui/widgets-3.js +212 -0
  536. package/dist/ui/widgets.d.ts +34 -0
  537. package/dist/ui/widgets.js +50 -0
  538. package/dist/ui/world-panel/index.d.ts +5 -0
  539. package/dist/ui/world-panel/index.js +5 -0
  540. package/dist/ui/world-panel/layout.d.ts +38 -0
  541. package/dist/ui/world-panel/layout.js +68 -0
  542. package/dist/ui/world-panel/paint.d.ts +96 -0
  543. package/dist/ui/world-panel/paint.js +136 -0
  544. package/dist/ui/world-panel/panel.d.ts +235 -0
  545. package/dist/ui/world-panel/panel.js +762 -0
  546. package/dist/ui/world-panel/wgsl.d.ts +10 -0
  547. package/dist/ui/world-panel/wgsl.js +52 -0
  548. package/dist/ui/world-panel/widgets.d.ts +207 -0
  549. package/dist/ui/world-panel/widgets.js +569 -0
  550. package/dist/voxel/chunks.d.ts +77 -0
  551. package/dist/voxel/chunks.js +149 -0
  552. package/dist/voxel/grid.d.ts +84 -0
  553. package/dist/voxel/grid.js +223 -0
  554. package/dist/voxel/index.d.ts +22 -0
  555. package/dist/voxel/index.js +7 -0
  556. package/dist/voxel/mesher-worker.d.ts +14 -0
  557. package/dist/voxel/mesher-worker.js +47 -0
  558. package/dist/voxel/mesher.d.ts +73 -0
  559. package/dist/voxel/mesher.js +306 -0
  560. package/dist/voxel/ray.d.ts +32 -0
  561. package/dist/voxel/ray.js +73 -0
  562. package/dist/voxel/wgsl.d.ts +16 -0
  563. package/dist/voxel/wgsl.js +28 -0
  564. package/dist/voxel/worker-client.d.ts +79 -0
  565. package/dist/voxel/worker-client.js +122 -0
  566. package/package.json +177 -3
@@ -0,0 +1,97 @@
1
+ /**
2
+ * The "turn it sideways" card for a landscape game on a phone held upright (interim API: Phase 2's
3
+ * portrait layout regions retire it, D-041).
4
+ *
5
+ * The card covers the whole viewport, outside the scaled overlay (upright, the overlay is a thin
6
+ * strip), and goes the moment the phone turns. Nothing is paused: the game underneath just waits
7
+ * for input. All words are the game's, passed as options.
8
+ *
9
+ * It shows when the viewport is portrait and the device is a phone in hand: a handheld by
10
+ * `deviceTraits()`, or a finger on a phone-scale screen by the device mode. A narrow desktop
11
+ * window never gets it.
12
+ */
13
+ import { isPortrait } from '../platform/device.js';
14
+ import { deviceTraits } from '../platform/environment.js';
15
+ import { h } from './dom.js';
16
+ const STYLE_ATTR = 'rotate-card';
17
+ const CSS = /* css */ `
18
+ .tc-rotate {
19
+ position: fixed; inset: 0; z-index: 50; display: flex; flex-direction: column; align-items: center;
20
+ justify-content: center; gap: 22px; padding: max(24px, env(safe-area-inset-top)) 28px;
21
+ box-sizing: border-box; text-align: center; background: var(--tc-rotate-bg, #0b0d14);
22
+ color: var(--tc-rotate-text, #ece6d8); font-family: var(--tc-rotate-font, Georgia, serif);
23
+ touch-action: none; user-select: none; -webkit-user-select: none;
24
+ }
25
+ .tc-rotate[hidden] { display: none; }
26
+ .tc-rotate-phone {
27
+ width: 54px; height: 92px; border: 3px solid var(--tc-rotate-accent, #e0b45a); border-radius: 12px;
28
+ box-sizing: border-box; box-shadow: 0 0 24px var(--tc-rotate-glow, rgba(224,180,90,0.35));
29
+ animation: tc-rotate-turn 2.4s ease-in-out infinite;
30
+ }
31
+ .tc-rotate-phone::after {
32
+ content: ''; display: block; width: 14px; height: 3px; margin: 78px auto 0; border-radius: 2px;
33
+ background: var(--tc-rotate-accent, #e0b45a);
34
+ }
35
+ @keyframes tc-rotate-turn { 0%, 25% { transform: rotate(0deg); } 55%, 100% { transform: rotate(-90deg); } }
36
+ @media (prefers-reduced-motion: reduce) { .tc-rotate-phone { animation: none; transform: rotate(-90deg); } }
37
+ .tc-rotate h2 { margin: 0; font-size: 24px; color: var(--tc-rotate-accent, #e0b45a); letter-spacing: 1px; font-weight: 600; }
38
+ .tc-rotate p { margin: 0; max-width: 300px; font-size: 17px; line-height: 1.45; }
39
+ .tc-rotate .tc-rotate-note { font-size: 16px; font-style: italic; color: var(--tc-rotate-muted, #a7a091); }
40
+ .tc-rotate .tc-rotate-note b {
41
+ font-style: normal; color: var(--tc-rotate-accent, #e0b45a); letter-spacing: 1px; font-size: 13px; margin-right: 6px;
42
+ }
43
+ `;
44
+ export function createRotateCard(opts) {
45
+ const doc = opts.doc ?? document;
46
+ const win = doc.defaultView ?? window;
47
+ if (!doc.querySelector(`style[data-tc="${STYLE_ATTR}"]`)) {
48
+ doc.head.append(h('style', { 'data-tc': STYLE_ATTR }, CSS));
49
+ }
50
+ const c = opts.colors ?? {};
51
+ const style = {};
52
+ const vars = [
53
+ ['background', '--tc-rotate-bg'],
54
+ ['text', '--tc-rotate-text'],
55
+ ['accent', '--tc-rotate-accent'],
56
+ ['glow', '--tc-rotate-glow'],
57
+ ['muted', '--tc-rotate-muted'],
58
+ ['font', '--tc-rotate-font'],
59
+ ];
60
+ for (const [k, v] of vars)
61
+ if (c[k])
62
+ style[v] = c[k];
63
+ const note = opts.note
64
+ ? h('p', { class: 'tc-rotate-note' }, opts.note.tag ? h('b', null, opts.note.tag) : null, opts.note.text)
65
+ : null;
66
+ const element = h('div', {
67
+ class: 'tc-rotate',
68
+ role: 'dialog',
69
+ 'aria-modal': 'true',
70
+ 'aria-label': opts.ariaLabel ?? opts.title,
71
+ hidden: true,
72
+ style,
73
+ }, h('div', { class: 'tc-rotate-phone', 'aria-hidden': 'true' }), h('h2', null, opts.title), ...(opts.lines ?? []).map((t) => h('p', null, t)), note);
74
+ doc.body.append(element);
75
+ const device = opts.device;
76
+ const update = () => {
77
+ const phoneInHand = deviceTraits().handheld || (!!device?.touch && !!device?.phone);
78
+ element.hidden = !(isPortrait(win) && phoneInHand);
79
+ };
80
+ const offDevice = device?.onChange(update);
81
+ win.addEventListener('resize', update);
82
+ win.addEventListener('orientationchange', update);
83
+ update();
84
+ return {
85
+ element,
86
+ get visible() {
87
+ return !element.hidden;
88
+ },
89
+ update,
90
+ destroy() {
91
+ offDevice?.();
92
+ win.removeEventListener('resize', update);
93
+ win.removeEventListener('orientationchange', update);
94
+ element.remove();
95
+ },
96
+ };
97
+ }
@@ -0,0 +1,128 @@
1
+ export type CloseReason = 'escape' | 'backdrop' | 'programmatic' | 'replaced';
2
+ /**
3
+ * Which arrow keys walk focus through a panel's controls (`PanelOptions.arrowNav`):
4
+ * `vertical` (ArrowUp/ArrowDown, the default), `both` (ArrowLeft/ArrowRight too) or `none`.
5
+ */
6
+ export type ArrowNav = 'vertical' | 'both' | 'none';
7
+ export interface PanelOptions {
8
+ /** Panel body. */
9
+ content: Node;
10
+ /** Visible heading (also the accessible name). Omit for chrome-less panels; then pass `label`. */
11
+ title?: string;
12
+ /** Accessible name when there's no visible title. */
13
+ label?: string;
14
+ /** Dim the background, trap focus, make lower layers inert, set `hasModal`. Default true. */
15
+ modal?: boolean;
16
+ /** Esc closes it. Default true. */
17
+ dismissible?: boolean;
18
+ /** Clicking the backdrop closes it. Default: same as `dismissible`. */
19
+ closeOnBackdrop?: boolean;
20
+ /** `card` (themed surface) or `none` (you style it, e.g. a full-screen book). Default `card`. */
21
+ chrome?: 'card' | 'none';
22
+ /** Extra class on the panel element. */
23
+ className?: string;
24
+ /** Element (or selector within the panel) to focus on open. Default: first focusable, else the panel. */
25
+ initialFocus?: HTMLElement | string;
26
+ /** `dialog` (default) or `alertdialog`. */
27
+ role?: 'dialog' | 'alertdialog';
28
+ /**
29
+ * Arrow keys move focus among the panel's focusable elements, wrapping, while this panel is on
30
+ * top: `vertical` (ArrowUp/ArrowDown; the default), `both` (ArrowLeft/ArrowRight too) or
31
+ * `none`. A focused text field, textarea, select or slider keeps the arrows it uses itself.
32
+ */
33
+ arrowNav?: ArrowNav;
34
+ onClose?: (reason: CloseReason) => void;
35
+ }
36
+ export interface PanelHandle {
37
+ /** The panel element (`role=dialog`), containing `content`. */
38
+ readonly element: HTMLElement;
39
+ readonly open: boolean;
40
+ close(reason?: CloseReason): void;
41
+ }
42
+ /**
43
+ * Put this attribute on an opener (or any ancestor, e.g. a HUD bar) whose buttons double as
44
+ * keyboard shortcuts. When a panel it opened closes, focus does not go back to it, because a
45
+ * focused button would swallow the game's keys. Focus goes to the next panel down, else to
46
+ * the stack's focus home.
47
+ */
48
+ export declare const NO_RESTORE_FOCUS_ATTR = "data-tc-no-restore-focus";
49
+ /** Who the gamepad belongs to: the panel stack while a modal is open, else the game. */
50
+ export type PadOwner = 'stack' | 'game';
51
+ export interface PanelStackFocusOptions {
52
+ /**
53
+ * Where focus goes when a panel closes and there is nowhere better: the opener is gone,
54
+ * inert, marked `NO_RESTORE_FOCUS_ATTR` or was nothing (`<body>`), and no panel is left
55
+ * to focus. Typically the game canvas, so key handling keeps working.
56
+ */
57
+ home?: () => HTMLElement | null;
58
+ }
59
+ /**
60
+ * A stack of panels inside one container. The top panel gets keyboard focus; Esc closes it
61
+ * (if dismissible); Tab cycles inside the top modal; the arrow keys walk its controls
62
+ * (`arrowNav`); lower panels and `inertWhenModal` elements are made `inert` while a modal is
63
+ * on top.
64
+ */
65
+ export declare class PanelStack {
66
+ private readonly container;
67
+ private readonly inertWhenModal;
68
+ private readonly focus;
69
+ private readonly entries;
70
+ private readonly listeners;
71
+ private readonly padListeners;
72
+ private owner;
73
+ private readonly doc;
74
+ private readonly onKey;
75
+ /**
76
+ * @param container where panel hosts are appended (the overlay's `panels` layer).
77
+ * @param inertWhenModal other elements to make inert while a modal is open (e.g. the HUD layer).
78
+ * @param focus where focus goes on close when the opener shouldn't (or can't) get it back.
79
+ */
80
+ constructor(container: HTMLElement, inertWhenModal?: readonly HTMLElement[], focus?: PanelStackFocusOptions);
81
+ get size(): number;
82
+ /** True while any open panel is modal. Wire this to `input.blocked`. */
83
+ get hasModal(): boolean;
84
+ get top(): PanelHandle | null;
85
+ /**
86
+ * Who the gamepad belongs to (ws/ui-4): `'stack'` while any modal panel is open (PadNav drives
87
+ * the panel; the world cursor and the game's input yield), else `'game'`.
88
+ */
89
+ get padOwner(): PadOwner;
90
+ /**
91
+ * Called once per transition of `padOwner`, after the panel that caused it opened or closed
92
+ * (not on every push or pop: a second modal over the first changes nothing). Returns the
93
+ * remover. `PadNav` and `ui.cursor` subscribe to this instead of polling `hasModal` each frame.
94
+ */
95
+ onPadOwnerChange(fn: (owner: PadOwner) => void): () => void;
96
+ onChange(fn: (stack: PanelStack) => void): () => void;
97
+ push(opts: PanelOptions): PanelHandle;
98
+ /**
99
+ * What Esc does, for other input devices (e.g. a gamepad's back button): close the top
100
+ * panel if it is dismissible. Returns true if one closed.
101
+ */
102
+ dismissTop(): boolean;
103
+ /** The top panel's element if it is modal (the one keyboard focus is trapped in). */
104
+ get topModalElement(): HTMLElement | null;
105
+ /** Close the top panel. Returns false if the stack was empty. */
106
+ pop(reason?: CloseReason): boolean;
107
+ /** Close the top panel (if any) and push a new one. */
108
+ replace(opts: PanelOptions): PanelHandle;
109
+ /** Close everything, top first. */
110
+ clear(): void;
111
+ destroy(): void;
112
+ private closeEntry;
113
+ /** Tell the pad-owner listeners once when `hasModal` flipped. */
114
+ private syncPadOwner;
115
+ private canRestore;
116
+ /** Inert everything below the topmost modal. */
117
+ private refresh;
118
+ private focusInitial;
119
+ private handleKey;
120
+ /**
121
+ * ArrowUp/ArrowDown (and ArrowLeft/ArrowRight with `arrowNav: 'both'`) move focus through the
122
+ * top panel's focusable elements, wrapping, when focus is inside it (always, for a modal). A
123
+ * focused control that uses the arrow itself (a text field's caret, a select, a slider) keeps
124
+ * it; a consumed arrow is `preventDefault`ed and stopped, like Escape.
125
+ */
126
+ private handleArrow;
127
+ private emit;
128
+ }
@@ -0,0 +1,312 @@
1
+ import { consumesArrows, focusableWithin, h } from './dom.js';
2
+ let idCounter = 0;
3
+ /**
4
+ * Put this attribute on an opener (or any ancestor, e.g. a HUD bar) whose buttons double as
5
+ * keyboard shortcuts. When a panel it opened closes, focus does not go back to it, because a
6
+ * focused button would swallow the game's keys. Focus goes to the next panel down, else to
7
+ * the stack's focus home.
8
+ */
9
+ export const NO_RESTORE_FOCUS_ATTR = 'data-tc-no-restore-focus';
10
+ /** `ArrowNav` per key: the step through the focusable list, or 0 when the key is not an arrow. */
11
+ function arrowStep(key, nav) {
12
+ if (nav === 'none')
13
+ return 0;
14
+ if (key === 'ArrowDown')
15
+ return 1;
16
+ if (key === 'ArrowUp')
17
+ return -1;
18
+ if (nav === 'both') {
19
+ if (key === 'ArrowRight')
20
+ return 1;
21
+ if (key === 'ArrowLeft')
22
+ return -1;
23
+ }
24
+ return 0;
25
+ }
26
+ /**
27
+ * A stack of panels inside one container. The top panel gets keyboard focus; Esc closes it
28
+ * (if dismissible); Tab cycles inside the top modal; the arrow keys walk its controls
29
+ * (`arrowNav`); lower panels and `inertWhenModal` elements are made `inert` while a modal is
30
+ * on top.
31
+ */
32
+ export class PanelStack {
33
+ container;
34
+ inertWhenModal;
35
+ focus;
36
+ entries = [];
37
+ listeners = new Set();
38
+ padListeners = new Set();
39
+ owner = 'game';
40
+ doc;
41
+ onKey = (e) => this.handleKey(e);
42
+ /**
43
+ * @param container where panel hosts are appended (the overlay's `panels` layer).
44
+ * @param inertWhenModal other elements to make inert while a modal is open (e.g. the HUD layer).
45
+ * @param focus where focus goes on close when the opener shouldn't (or can't) get it back.
46
+ */
47
+ constructor(container, inertWhenModal = [], focus = {}) {
48
+ this.container = container;
49
+ this.inertWhenModal = inertWhenModal;
50
+ this.focus = focus;
51
+ this.doc = container.ownerDocument;
52
+ this.doc.addEventListener('keydown', this.onKey);
53
+ }
54
+ get size() {
55
+ return this.entries.length;
56
+ }
57
+ /** True while any open panel is modal. Wire this to `input.blocked`. */
58
+ get hasModal() {
59
+ return this.entries.some((e) => e.opts.modal !== false);
60
+ }
61
+ get top() {
62
+ return this.entries.at(-1)?.handle ?? null;
63
+ }
64
+ /**
65
+ * Who the gamepad belongs to (ws/ui-4): `'stack'` while any modal panel is open (PadNav drives
66
+ * the panel; the world cursor and the game's input yield), else `'game'`.
67
+ */
68
+ get padOwner() {
69
+ return this.owner;
70
+ }
71
+ /**
72
+ * Called once per transition of `padOwner`, after the panel that caused it opened or closed
73
+ * (not on every push or pop: a second modal over the first changes nothing). Returns the
74
+ * remover. `PadNav` and `ui.cursor` subscribe to this instead of polling `hasModal` each frame.
75
+ */
76
+ onPadOwnerChange(fn) {
77
+ this.padListeners.add(fn);
78
+ return () => this.padListeners.delete(fn);
79
+ }
80
+ onChange(fn) {
81
+ this.listeners.add(fn);
82
+ return () => this.listeners.delete(fn);
83
+ }
84
+ push(opts) {
85
+ const modal = opts.modal !== false;
86
+ const dismissible = opts.dismissible !== false;
87
+ const id = `tc-panel-${++idCounter}`;
88
+ const titleEl = opts.title
89
+ ? h('h2', { class: 'tc-panel-title', id: `${id}-title` }, opts.title)
90
+ : null;
91
+ const panel = h('div', {
92
+ class: ['tc-panel', opts.chrome === 'none' ? '' : 'tc-card', opts.className ?? '']
93
+ .join(' ')
94
+ .trim(),
95
+ role: opts.role ?? 'dialog',
96
+ 'aria-modal': modal ? 'true' : null,
97
+ 'aria-labelledby': titleEl ? titleEl.id : null,
98
+ 'aria-label': !titleEl && opts.label ? opts.label : null,
99
+ tabindex: '-1',
100
+ id,
101
+ }, titleEl, opts.content);
102
+ const host = h('div', { class: 'tc-panel-host', dataset: { modal: String(modal) } });
103
+ if (modal) {
104
+ const backdrop = h('div', { class: 'tc-backdrop', 'aria-hidden': 'true' });
105
+ if (opts.closeOnBackdrop ?? dismissible) {
106
+ backdrop.addEventListener('click', () => entry.handle.close('backdrop'));
107
+ }
108
+ host.append(backdrop);
109
+ }
110
+ host.append(panel);
111
+ const entry = {
112
+ host,
113
+ panel,
114
+ opts,
115
+ restoreFocus: this.doc.activeElement,
116
+ open: true,
117
+ handle: {
118
+ element: panel,
119
+ get open() {
120
+ return entry.open;
121
+ },
122
+ close: (reason = 'programmatic') => this.closeEntry(entry, reason),
123
+ },
124
+ };
125
+ this.entries.push(entry);
126
+ this.container.append(host);
127
+ this.refresh();
128
+ this.focusInitial(entry);
129
+ this.syncPadOwner();
130
+ this.emit();
131
+ return entry.handle;
132
+ }
133
+ /**
134
+ * What Esc does, for other input devices (e.g. a gamepad's back button): close the top
135
+ * panel if it is dismissible. Returns true if one closed.
136
+ */
137
+ dismissTop() {
138
+ const top = this.entries.at(-1);
139
+ if (!top || top.opts.dismissible === false)
140
+ return false;
141
+ top.handle.close('escape');
142
+ return true;
143
+ }
144
+ /** The top panel's element if it is modal (the one keyboard focus is trapped in). */
145
+ get topModalElement() {
146
+ const top = this.entries.at(-1);
147
+ return top && top.opts.modal !== false ? top.panel : null;
148
+ }
149
+ /** Close the top panel. Returns false if the stack was empty. */
150
+ pop(reason = 'programmatic') {
151
+ const top = this.entries.at(-1);
152
+ if (!top)
153
+ return false;
154
+ this.closeEntry(top, reason);
155
+ return true;
156
+ }
157
+ /** Close the top panel (if any) and push a new one. */
158
+ replace(opts) {
159
+ const top = this.entries.at(-1);
160
+ if (top)
161
+ this.closeEntry(top, 'replaced', false);
162
+ return this.push(opts);
163
+ }
164
+ /** Close everything, top first. */
165
+ clear() {
166
+ while (this.entries.length)
167
+ this.closeEntry(this.entries.at(-1), 'programmatic');
168
+ }
169
+ destroy() {
170
+ this.clear();
171
+ this.doc.removeEventListener('keydown', this.onKey);
172
+ this.listeners.clear();
173
+ this.padListeners.clear();
174
+ }
175
+ closeEntry(entry, reason, restore = true) {
176
+ if (!entry.open)
177
+ return;
178
+ const i = this.entries.indexOf(entry);
179
+ const wasTop = i === this.entries.length - 1;
180
+ entry.open = false;
181
+ this.entries.splice(i, 1);
182
+ entry.host.remove();
183
+ this.refresh();
184
+ if (restore && wasTop) {
185
+ const back = entry.restoreFocus;
186
+ const top = this.entries.at(-1);
187
+ if (this.canRestore(back)) {
188
+ back.focus();
189
+ }
190
+ else if (top) {
191
+ this.focusInitial(top);
192
+ }
193
+ else {
194
+ this.focus.home?.()?.focus({ preventScroll: true });
195
+ }
196
+ }
197
+ entry.opts.onClose?.(reason);
198
+ this.syncPadOwner();
199
+ this.emit();
200
+ }
201
+ /** Tell the pad-owner listeners once when `hasModal` flipped. */
202
+ syncPadOwner() {
203
+ const next = this.hasModal ? 'stack' : 'game';
204
+ if (next === this.owner)
205
+ return;
206
+ this.owner = next;
207
+ for (const fn of this.padListeners)
208
+ fn(next);
209
+ }
210
+ canRestore(el) {
211
+ if (!el?.isConnected || typeof el.focus !== 'function' || el.closest('[inert]'))
212
+ return false;
213
+ // With a focus home, "nothing was focused" restores to home rather than to <body>.
214
+ if (this.focus.home && (el === this.doc.body || el === this.doc.documentElement))
215
+ return false;
216
+ return !el.closest(`[${NO_RESTORE_FOCUS_ATTR}]`);
217
+ }
218
+ /** Inert everything below the topmost modal. */
219
+ refresh() {
220
+ let topModal = -1;
221
+ for (let i = this.entries.length - 1; i >= 0; i--) {
222
+ if (this.entries[i].opts.modal !== false) {
223
+ topModal = i;
224
+ break;
225
+ }
226
+ }
227
+ this.entries.forEach((e, i) => {
228
+ e.host.inert = i < topModal;
229
+ e.host.toggleAttribute('inert', i < topModal);
230
+ });
231
+ for (const el of this.inertWhenModal) {
232
+ el.inert = topModal >= 0;
233
+ el.toggleAttribute('inert', topModal >= 0);
234
+ }
235
+ }
236
+ focusInitial(entry) {
237
+ const f = entry.opts.initialFocus;
238
+ let target = null;
239
+ if (typeof f === 'string')
240
+ target = entry.panel.querySelector(f);
241
+ else if (f)
242
+ target = f;
243
+ target ??= focusableWithin(entry.panel)[0] ?? entry.panel;
244
+ target.focus({ preventScroll: true });
245
+ }
246
+ handleKey(e) {
247
+ const top = this.entries.at(-1);
248
+ if (!top || e.defaultPrevented)
249
+ return;
250
+ if (e.key === 'Escape') {
251
+ if (top.opts.dismissible === false)
252
+ return;
253
+ e.preventDefault();
254
+ // Keep the game's input map from also seeing this Escape.
255
+ e.stopPropagation();
256
+ top.handle.close('escape');
257
+ return;
258
+ }
259
+ if (e.key === 'Tab' && top.opts.modal !== false) {
260
+ const items = focusableWithin(top.panel);
261
+ if (items.length === 0) {
262
+ e.preventDefault();
263
+ top.panel.focus();
264
+ return;
265
+ }
266
+ const first = items[0];
267
+ const last = items[items.length - 1];
268
+ const active = this.doc.activeElement;
269
+ const inside = active instanceof Node && top.panel.contains(active);
270
+ if (e.shiftKey && (active === first || !inside)) {
271
+ e.preventDefault();
272
+ last.focus();
273
+ }
274
+ else if (!e.shiftKey && (active === last || !inside)) {
275
+ e.preventDefault();
276
+ first.focus();
277
+ }
278
+ return;
279
+ }
280
+ this.handleArrow(e, top);
281
+ }
282
+ /**
283
+ * ArrowUp/ArrowDown (and ArrowLeft/ArrowRight with `arrowNav: 'both'`) move focus through the
284
+ * top panel's focusable elements, wrapping, when focus is inside it (always, for a modal). A
285
+ * focused control that uses the arrow itself (a text field's caret, a select, a slider) keeps
286
+ * it; a consumed arrow is `preventDefault`ed and stopped, like Escape.
287
+ */
288
+ handleArrow(e, top) {
289
+ const step = arrowStep(e.key, top.opts.arrowNav ?? 'vertical');
290
+ if (step === 0 || e.altKey || e.ctrlKey || e.metaKey || e.shiftKey)
291
+ return;
292
+ const active = this.doc.activeElement;
293
+ const inside = active !== null && top.panel.contains(active);
294
+ if (!inside && top.opts.modal === false)
295
+ return;
296
+ const horizontal = e.key === 'ArrowLeft' || e.key === 'ArrowRight';
297
+ if (inside && active !== top.panel && consumesArrows(active, horizontal))
298
+ return;
299
+ const items = focusableWithin(top.panel);
300
+ if (items.length === 0)
301
+ return;
302
+ const i = inside ? items.indexOf(active) : -1;
303
+ const next = i < 0 ? (step > 0 ? 0 : items.length - 1) : (i + step + items.length) % items.length;
304
+ e.preventDefault();
305
+ e.stopPropagation();
306
+ items[next]?.focus();
307
+ }
308
+ emit() {
309
+ for (const fn of this.listeners)
310
+ fn(this);
311
+ }
312
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The overlay's base stylesheet: tokens, layers, panels, widgets and units. Injected once
3
+ * per document by `ensureStyles`; the HUD, cursor, tooltip and other widget sheets add
4
+ * their own `<style>` beside it. Everything is under `.tc-overlay`.
5
+ */
6
+ export declare const BASE_CSS: string;
7
+ /** Add the base stylesheet to `doc` once. */
8
+ export declare function ensureStyles(doc?: Document): void;
9
+ /**
10
+ * True if the user prefers reduced motion in `win` (default the global window; false where
11
+ * `matchMedia` is unavailable). `platform`'s `prefersReducedMotion()` reads the global window only.
12
+ */
13
+ export declare function prefersReducedMotionIn(win?: Window | undefined): boolean;