@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.
- package/CHANGELOG.md +390 -0
- package/LICENSE +21 -0
- package/README.md +333 -2
- package/dist/analytics/analytics.d.ts +80 -0
- package/dist/analytics/analytics.js +129 -0
- package/dist/analytics/index.d.ts +1 -0
- package/dist/analytics/index.js +1 -0
- package/dist/anim/aim.d.ts +43 -0
- package/dist/anim/aim.js +157 -0
- package/dist/anim/clip.d.ts +98 -0
- package/dist/anim/clip.js +282 -0
- package/dist/anim/compress.d.ts +65 -0
- package/dist/anim/compress.js +339 -0
- package/dist/anim/foot-ik.d.ts +74 -0
- package/dist/anim/foot-ik.js +241 -0
- package/dist/anim/graph.d.ts +153 -0
- package/dist/anim/graph.js +532 -0
- package/dist/anim/ik.d.ts +105 -0
- package/dist/anim/ik.js +435 -0
- package/dist/anim/index.d.ts +36 -0
- package/dist/anim/index.js +21 -0
- package/dist/anim/match.d.ts +66 -0
- package/dist/anim/match.js +153 -0
- package/dist/anim/mixer.d.ts +113 -0
- package/dist/anim/mixer.js +314 -0
- package/dist/anim/morph.d.ts +41 -0
- package/dist/anim/morph.js +94 -0
- package/dist/anim/pose.d.ts +74 -0
- package/dist/anim/pose.js +254 -0
- package/dist/anim/quat.d.ts +44 -0
- package/dist/anim/quat.js +187 -0
- package/dist/anim/retarget.d.ts +97 -0
- package/dist/anim/retarget.js +366 -0
- package/dist/anim/root-motion.d.ts +44 -0
- package/dist/anim/root-motion.js +224 -0
- package/dist/anim/skin-pose.d.ts +39 -0
- package/dist/anim/skin-pose.js +53 -0
- package/dist/anim/socket.d.ts +47 -0
- package/dist/anim/socket.js +84 -0
- package/dist/anim/spring-chain.d.ts +73 -0
- package/dist/anim/spring-chain.js +294 -0
- package/dist/assets/atlas.d.ts +90 -0
- package/dist/assets/atlas.js +212 -0
- package/dist/assets/baked.d.ts +33 -0
- package/dist/assets/baked.js +38 -0
- package/dist/assets/bundle.d.ts +88 -0
- package/dist/assets/bundle.js +464 -0
- package/dist/assets/cache.d.ts +56 -0
- package/dist/assets/cache.js +123 -0
- package/dist/assets/gltf-load.d.ts +252 -0
- package/dist/assets/gltf-load.js +661 -0
- package/dist/assets/gltf-skin.d.ts +36 -0
- package/dist/assets/gltf-skin.js +109 -0
- package/dist/assets/gltf.d.ts +706 -0
- package/dist/assets/gltf.js +1376 -0
- package/dist/assets/index.d.ts +20 -0
- package/dist/assets/index.js +10 -0
- package/dist/assets/ktx2.d.ts +71 -0
- package/dist/assets/ktx2.js +213 -0
- package/dist/assets/manifest.d.ts +79 -0
- package/dist/assets/manifest.js +7 -0
- package/dist/assets/meshopt.d.ts +23 -0
- package/dist/assets/meshopt.js +403 -0
- package/dist/audio/audio.d.ts +733 -0
- package/dist/audio/audio.js +2013 -0
- package/dist/audio/dsp.d.ts +61 -0
- package/dist/audio/dsp.js +159 -0
- package/dist/audio/duck.d.ts +44 -0
- package/dist/audio/duck.js +101 -0
- package/dist/audio/envelope.d.ts +53 -0
- package/dist/audio/envelope.js +106 -0
- package/dist/audio/index.d.ts +13 -0
- package/dist/audio/index.js +14 -0
- package/dist/audio/music.d.ts +113 -0
- package/dist/audio/music.js +548 -0
- package/dist/audio/occlusion.d.ts +55 -0
- package/dist/audio/occlusion.js +61 -0
- package/dist/audio/persist.d.ts +23 -0
- package/dist/audio/persist.js +27 -0
- package/dist/audio/recipes.d.ts +55 -0
- package/dist/audio/recipes.js +227 -0
- package/dist/audio/send.d.ts +41 -0
- package/dist/audio/send.js +118 -0
- package/dist/audio/sprites.d.ts +59 -0
- package/dist/audio/sprites.js +43 -0
- package/dist/audio/synth.d.ts +103 -0
- package/dist/audio/synth.js +197 -0
- package/dist/audio/variations.d.ts +25 -0
- package/dist/audio/variations.js +55 -0
- package/dist/audio/zones.d.ts +100 -0
- package/dist/audio/zones.js +167 -0
- package/dist/core/detached-surface.d.ts +44 -0
- package/dist/core/detached-surface.js +104 -0
- package/dist/core/events.d.ts +24 -0
- package/dist/core/events.js +43 -0
- package/dist/core/game.d.ts +409 -0
- package/dist/core/game.js +530 -0
- package/dist/core/hash.d.ts +24 -0
- package/dist/core/hash.js +49 -0
- package/dist/core/index.d.ts +17 -0
- package/dist/core/index.js +9 -0
- package/dist/core/loop.d.ts +113 -0
- package/dist/core/loop.js +185 -0
- package/dist/core/math.d.ts +58 -0
- package/dist/core/math.js +210 -0
- package/dist/core/noise.d.ts +40 -0
- package/dist/core/noise.js +247 -0
- package/dist/core/pool.d.ts +42 -0
- package/dist/core/pool.js +85 -0
- package/dist/core/random.d.ts +61 -0
- package/dist/core/random.js +199 -0
- package/dist/core/scene.d.ts +79 -0
- package/dist/core/scene.js +195 -0
- package/dist/core/timer.d.ts +66 -0
- package/dist/core/timer.js +286 -0
- package/dist/fx/billboard-wgsl.d.ts +29 -0
- package/dist/fx/billboard-wgsl.js +158 -0
- package/dist/fx/billboard.d.ts +249 -0
- package/dist/fx/billboard.js +702 -0
- package/dist/fx/debug-draw.d.ts +127 -0
- package/dist/fx/debug-draw.js +523 -0
- package/dist/fx/debug-wgsl.d.ts +7 -0
- package/dist/fx/debug-wgsl.js +32 -0
- package/dist/fx/decals-wgsl.d.ts +15 -0
- package/dist/fx/decals-wgsl.js +173 -0
- package/dist/fx/decals.d.ts +216 -0
- package/dist/fx/decals.js +607 -0
- package/dist/fx/effect.d.ts +129 -0
- package/dist/fx/effect.js +349 -0
- package/dist/fx/effects/hit-spark.json +48 -0
- package/dist/fx/effects/index.d.ts +8 -0
- package/dist/fx/effects/index.js +9 -0
- package/dist/fx/effects/magic-aura.json +47 -0
- package/dist/fx/effects/smoke-puff.json +34 -0
- package/dist/fx/emit-surface.d.ts +40 -0
- package/dist/fx/emit-surface.js +124 -0
- package/dist/fx/flicker.d.ts +30 -0
- package/dist/fx/flicker.js +42 -0
- package/dist/fx/index.d.ts +31 -0
- package/dist/fx/index.js +20 -0
- package/dist/fx/particles-wgsl.d.ts +57 -0
- package/dist/fx/particles-wgsl.js +828 -0
- package/dist/fx/particles.d.ts +566 -0
- package/dist/fx/particles.js +1533 -0
- package/dist/fx/random.d.ts +15 -0
- package/dist/fx/random.js +20 -0
- package/dist/fx/sky-wgsl.d.ts +23 -0
- package/dist/fx/sky-wgsl.js +127 -0
- package/dist/fx/sky.d.ts +96 -0
- package/dist/fx/sky.js +175 -0
- package/dist/fx/text-atlas.d.ts +143 -0
- package/dist/fx/text-atlas.js +327 -0
- package/dist/fx/trail-wgsl.d.ts +11 -0
- package/dist/fx/trail-wgsl.js +42 -0
- package/dist/fx/trail.d.ts +194 -0
- package/dist/fx/trail.js +555 -0
- package/dist/fx/water-wgsl.d.ts +34 -0
- package/dist/fx/water-wgsl.js +197 -0
- package/dist/fx/water.d.ts +176 -0
- package/dist/fx/water.js +371 -0
- package/dist/geom/collide/bvh.d.ts +151 -0
- package/dist/geom/collide/bvh.js +493 -0
- package/dist/geom/collide/debug.d.ts +14 -0
- package/dist/geom/collide/debug.js +74 -0
- package/dist/geom/collide/grid.d.ts +26 -0
- package/dist/geom/collide/grid.js +91 -0
- package/dist/geom/collide/index.d.ts +8 -0
- package/dist/geom/collide/index.js +8 -0
- package/dist/geom/collide/queries.d.ts +70 -0
- package/dist/geom/collide/queries.js +583 -0
- package/dist/geom/collide/serialize.d.ts +14 -0
- package/dist/geom/collide/serialize.js +145 -0
- package/dist/geom/collide/shapes.d.ts +161 -0
- package/dist/geom/collide/shapes.js +686 -0
- package/dist/geom/collide/slide.d.ts +66 -0
- package/dist/geom/collide/slide.js +375 -0
- package/dist/geom/collide/tile-slide.d.ts +88 -0
- package/dist/geom/collide/tile-slide.js +255 -0
- package/dist/geom/geometry.d.ts +95 -0
- package/dist/geom/geometry.js +322 -0
- package/dist/geom/heightfield.d.ts +72 -0
- package/dist/geom/heightfield.js +295 -0
- package/dist/geom/hull.d.ts +27 -0
- package/dist/geom/hull.js +279 -0
- package/dist/geom/index.d.ts +35 -0
- package/dist/geom/index.js +23 -0
- package/dist/geom/iso.d.ts +74 -0
- package/dist/geom/iso.js +89 -0
- package/dist/geom/lod.d.ts +29 -0
- package/dist/geom/lod.js +27 -0
- package/dist/geom/ops.d.ts +47 -0
- package/dist/geom/ops.js +285 -0
- package/dist/geom/primitives.d.ts +97 -0
- package/dist/geom/primitives.js +359 -0
- package/dist/geom/shapes.d.ts +43 -0
- package/dist/geom/shapes.js +88 -0
- package/dist/geom/simplify.d.ts +38 -0
- package/dist/geom/simplify.js +519 -0
- package/dist/geom/split.d.ts +37 -0
- package/dist/geom/split.js +413 -0
- package/dist/geom/sweep.d.ts +83 -0
- package/dist/geom/sweep.js +534 -0
- package/dist/geom/tangents.d.ts +16 -0
- package/dist/geom/tangents.js +124 -0
- package/dist/geom/triangulate.d.ts +19 -0
- package/dist/geom/triangulate.js +147 -0
- package/dist/host/default.d.ts +10 -0
- package/dist/host/default.js +10 -0
- package/dist/host/index.d.ts +4 -0
- package/dist/host/index.js +3 -0
- package/dist/host/types.d.ts +144 -0
- package/dist/host/types.js +1 -0
- package/dist/host/web-links.d.ts +38 -0
- package/dist/host/web-links.js +110 -0
- package/dist/host/web.d.ts +30 -0
- package/dist/host/web.js +216 -0
- package/dist/host/xr.d.ts +24 -0
- package/dist/host/xr.js +59 -0
- package/dist/i18n/i18n.d.ts +59 -0
- package/dist/i18n/i18n.js +241 -0
- package/dist/i18n/index.d.ts +3 -0
- package/dist/i18n/index.js +3 -0
- package/dist/i18n/message.d.ts +72 -0
- package/dist/i18n/message.js +369 -0
- package/dist/i18n/negotiate.d.ts +17 -0
- package/dist/i18n/negotiate.js +58 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +1 -0
- package/dist/input/bindings.d.ts +31 -0
- package/dist/input/bindings.js +141 -0
- package/dist/input/clock.d.ts +44 -0
- package/dist/input/clock.js +49 -0
- package/dist/input/follow.d.ts +133 -0
- package/dist/input/follow.js +218 -0
- package/dist/input/gestures.d.ts +281 -0
- package/dist/input/gestures.js +662 -0
- package/dist/input/glyphs.d.ts +35 -0
- package/dist/input/glyphs.js +216 -0
- package/dist/input/gyro.d.ts +85 -0
- package/dist/input/gyro.js +140 -0
- package/dist/input/index.d.ts +19 -0
- package/dist/input/index.js +16 -0
- package/dist/input/input.d.ts +543 -0
- package/dist/input/input.js +1492 -0
- package/dist/input/look.d.ts +327 -0
- package/dist/input/look.js +681 -0
- package/dist/input/magnify.d.ts +177 -0
- package/dist/input/magnify.js +131 -0
- package/dist/input/pointer-dispatch.d.ts +138 -0
- package/dist/input/pointer-dispatch.js +180 -0
- package/dist/input/pointer-router.d.ts +204 -0
- package/dist/input/pointer-router.js +533 -0
- package/dist/input/pointer-sources.d.ts +164 -0
- package/dist/input/pointer-sources.js +306 -0
- package/dist/input/rays.d.ts +138 -0
- package/dist/input/rays.js +615 -0
- package/dist/input/record.d.ts +209 -0
- package/dist/input/record.js +601 -0
- package/dist/input/touch-zones.d.ts +147 -0
- package/dist/input/touch-zones.js +300 -0
- package/dist/input/types.d.ts +158 -0
- package/dist/input/types.js +1 -0
- package/dist/input/xr-source.d.ts +53 -0
- package/dist/input/xr-source.js +164 -0
- package/dist/nav/astar.d.ts +45 -0
- package/dist/nav/astar.js +182 -0
- package/dist/nav/corridor.d.ts +40 -0
- package/dist/nav/corridor.js +95 -0
- package/dist/nav/crowd.d.ts +94 -0
- package/dist/nav/crowd.js +624 -0
- package/dist/nav/flow.d.ts +61 -0
- package/dist/nav/flow.js +193 -0
- package/dist/nav/grid.d.ts +108 -0
- package/dist/nav/grid.js +210 -0
- package/dist/nav/heap.d.ts +22 -0
- package/dist/nav/heap.js +92 -0
- package/dist/nav/index.d.ts +37 -0
- package/dist/nav/index.js +19 -0
- package/dist/nav/mesh/areas.d.ts +44 -0
- package/dist/nav/mesh/areas.js +98 -0
- package/dist/nav/mesh/bake-options.d.ts +33 -0
- package/dist/nav/mesh/bake-options.js +76 -0
- package/dist/nav/mesh/contour.d.ts +22 -0
- package/dist/nav/mesh/contour.js +323 -0
- package/dist/nav/mesh/flow3.d.ts +35 -0
- package/dist/nav/mesh/flow3.js +229 -0
- package/dist/nav/mesh/links.d.ts +38 -0
- package/dist/nav/mesh/links.js +72 -0
- package/dist/nav/mesh/mesh-path.d.ts +152 -0
- package/dist/nav/mesh/mesh-path.js +643 -0
- package/dist/nav/mesh/mesh-reach.d.ts +78 -0
- package/dist/nav/mesh/mesh-reach.js +450 -0
- package/dist/nav/mesh/navmesh.d.ts +117 -0
- package/dist/nav/mesh/navmesh.js +647 -0
- package/dist/nav/mesh/polymesh.d.ts +24 -0
- package/dist/nav/mesh/polymesh.js +384 -0
- package/dist/nav/mesh/regions.d.ts +12 -0
- package/dist/nav/mesh/regions.js +367 -0
- package/dist/nav/mesh/serialize.d.ts +13 -0
- package/dist/nav/mesh/serialize.js +197 -0
- package/dist/nav/mesh/voxelize.d.ts +111 -0
- package/dist/nav/mesh/voxelize.js +552 -0
- package/dist/nav/obstacles.d.ts +75 -0
- package/dist/nav/obstacles.js +294 -0
- package/dist/nav/path.d.ts +59 -0
- package/dist/nav/path.js +177 -0
- package/dist/nav/query-queue.d.ts +46 -0
- package/dist/nav/query-queue.js +308 -0
- package/dist/nav/reach.d.ts +53 -0
- package/dist/nav/reach.js +148 -0
- package/dist/nav/steer.d.ts +36 -0
- package/dist/nav/steer.js +78 -0
- package/dist/platform/boot.d.ts +28 -0
- package/dist/platform/boot.js +43 -0
- package/dist/platform/device-mode.d.ts +40 -0
- package/dist/platform/device-mode.js +104 -0
- package/dist/platform/device.d.ts +31 -0
- package/dist/platform/device.js +91 -0
- package/dist/platform/environment.d.ts +42 -0
- package/dist/platform/environment.js +82 -0
- package/dist/platform/gpu.d.ts +40 -0
- package/dist/platform/gpu.js +57 -0
- package/dist/platform/index.d.ts +18 -0
- package/dist/platform/index.js +10 -0
- package/dist/platform/letterbox.d.ts +43 -0
- package/dist/platform/letterbox.js +57 -0
- package/dist/platform/links.d.ts +20 -0
- package/dist/platform/links.js +43 -0
- package/dist/platform/screens.d.ts +36 -0
- package/dist/platform/screens.js +138 -0
- package/dist/platform/surface.d.ts +72 -0
- package/dist/platform/surface.js +157 -0
- package/dist/platform/xr-fake.d.ts +168 -0
- package/dist/platform/xr-fake.js +434 -0
- package/dist/platform/xr-surface.d.ts +120 -0
- package/dist/platform/xr-surface.js +214 -0
- package/dist/platform/xr-types.d.ts +262 -0
- package/dist/post/ao.d.ts +12 -0
- package/dist/post/ao.js +136 -0
- package/dist/post/blend.d.ts +27 -0
- package/dist/post/blend.js +129 -0
- package/dist/post/depth-pass.d.ts +43 -0
- package/dist/post/depth-pass.js +118 -0
- package/dist/post/dof.d.ts +1 -0
- package/dist/post/dof.js +66 -0
- package/dist/post/exposure.d.ts +80 -0
- package/dist/post/exposure.js +322 -0
- package/dist/post/index.d.ts +12 -0
- package/dist/post/index.js +11 -0
- package/dist/post/lens-dirt.d.ts +13 -0
- package/dist/post/lens-dirt.js +91 -0
- package/dist/post/lut.d.ts +19 -0
- package/dist/post/lut.js +50 -0
- package/dist/post/motion-blur.d.ts +9 -0
- package/dist/post/motion-blur.js +78 -0
- package/dist/post/post-stack.d.ts +302 -0
- package/dist/post/post-stack.js +1137 -0
- package/dist/post/settings.d.ts +485 -0
- package/dist/post/settings.js +416 -0
- package/dist/post/shaders.d.ts +25 -0
- package/dist/post/shaders.js +410 -0
- package/dist/post/ssr.d.ts +16 -0
- package/dist/post/ssr.js +155 -0
- package/dist/post/taa.d.ts +74 -0
- package/dist/post/taa.js +235 -0
- package/dist/post/white-balance.d.ts +25 -0
- package/dist/post/white-balance.js +98 -0
- package/dist/render/bindings.d.ts +98 -0
- package/dist/render/bindings.js +281 -0
- package/dist/render/blend.d.ts +13 -0
- package/dist/render/blend.js +26 -0
- package/dist/render/camera-2d.d.ts +72 -0
- package/dist/render/camera-2d.js +92 -0
- package/dist/render/camera.d.ts +302 -0
- package/dist/render/camera.js +671 -0
- package/dist/render/color.d.ts +16 -0
- package/dist/render/color.js +41 -0
- package/dist/render/compressed.d.ts +104 -0
- package/dist/render/compressed.js +248 -0
- package/dist/render/diagnostics.d.ts +15 -0
- package/dist/render/diagnostics.js +43 -0
- package/dist/render/dynamic-resolution.d.ts +70 -0
- package/dist/render/dynamic-resolution.js +126 -0
- package/dist/render/fake-gpu.d.ts +204 -0
- package/dist/render/fake-gpu.js +426 -0
- package/dist/render/flipbook.d.ts +56 -0
- package/dist/render/flipbook.js +87 -0
- package/dist/render/frame.d.ts +278 -0
- package/dist/render/frame.js +174 -0
- package/dist/render/fullscreen.d.ts +54 -0
- package/dist/render/fullscreen.js +91 -0
- package/dist/render/gpu-timer.d.ts +73 -0
- package/dist/render/gpu-timer.js +186 -0
- package/dist/render/index.d.ts +53 -0
- package/dist/render/index.js +29 -0
- package/dist/render/instance-list.d.ts +185 -0
- package/dist/render/instance-list.js +547 -0
- package/dist/render/lights.d.ts +181 -0
- package/dist/render/lights.js +233 -0
- package/dist/render/lod.d.ts +54 -0
- package/dist/render/lod.js +52 -0
- package/dist/render/material.d.ts +165 -0
- package/dist/render/material.js +292 -0
- package/dist/render/mesh.d.ts +284 -0
- package/dist/render/mesh.js +472 -0
- package/dist/render/mipmap.d.ts +16 -0
- package/dist/render/mipmap.js +121 -0
- package/dist/render/parallax.d.ts +50 -0
- package/dist/render/parallax.js +47 -0
- package/dist/render/pbr-wgsl.d.ts +31 -0
- package/dist/render/pbr-wgsl.js +119 -0
- package/dist/render/ping-pong.d.ts +42 -0
- package/dist/render/ping-pong.js +74 -0
- package/dist/render/renderer.d.ts +741 -0
- package/dist/render/renderer.js +2486 -0
- package/dist/render/scene-pipelines.d.ts +44 -0
- package/dist/render/scene-pipelines.js +92 -0
- package/dist/render/scene-target.d.ts +170 -0
- package/dist/render/scene-target.js +450 -0
- package/dist/render/shadow.d.ts +74 -0
- package/dist/render/shadow.js +208 -0
- package/dist/render/skin.d.ts +112 -0
- package/dist/render/skin.js +240 -0
- package/dist/render/snapshot.d.ts +36 -0
- package/dist/render/snapshot.js +34 -0
- package/dist/render/standard.d.ts +162 -0
- package/dist/render/standard.js +263 -0
- package/dist/render/texture.d.ts +142 -0
- package/dist/render/texture.js +228 -0
- package/dist/render/tile-layer.d.ts +110 -0
- package/dist/render/tile-layer.js +304 -0
- package/dist/render/transform.d.ts +115 -0
- package/dist/render/transform.js +318 -0
- package/dist/render/wgsl.d.ts +131 -0
- package/dist/render/wgsl.js +528 -0
- package/dist/save/codec.d.ts +49 -0
- package/dist/save/codec.js +169 -0
- package/dist/save/index.d.ts +8 -0
- package/dist/save/index.js +8 -0
- package/dist/save/journal.d.ts +24 -0
- package/dist/save/journal.js +102 -0
- package/dist/save/migrate.d.ts +15 -0
- package/dist/save/migrate.js +37 -0
- package/dist/save/schema.d.ts +72 -0
- package/dist/save/schema.js +154 -0
- package/dist/save/settings.d.ts +28 -0
- package/dist/save/settings.js +59 -0
- package/dist/save/slots.d.ts +56 -0
- package/dist/save/slots.js +200 -0
- package/dist/save/storage.d.ts +35 -0
- package/dist/save/storage.js +62 -0
- package/dist/save/store.d.ts +114 -0
- package/dist/save/store.js +269 -0
- package/dist/testing/fake-host.d.ts +103 -0
- package/dist/testing/fake-host.js +163 -0
- package/dist/testing/fixed-clock.d.ts +25 -0
- package/dist/testing/fixed-clock.js +30 -0
- package/dist/testing/index.d.ts +4 -0
- package/dist/testing/index.js +5 -0
- package/dist/testing/test-game.d.ts +76 -0
- package/dist/testing/test-game.js +92 -0
- package/dist/tween/ease.d.ts +77 -0
- package/dist/tween/ease.js +192 -0
- package/dist/tween/index.d.ts +8 -0
- package/dist/tween/index.js +7 -0
- package/dist/tween/timeline.d.ts +78 -0
- package/dist/tween/timeline.js +269 -0
- package/dist/tween/tween.d.ts +168 -0
- package/dist/tween/tween.js +320 -0
- package/dist/ui/anchor.d.ts +206 -0
- package/dist/ui/anchor.js +307 -0
- package/dist/ui/caption.d.ts +122 -0
- package/dist/ui/caption.js +286 -0
- package/dist/ui/cursor.d.ts +105 -0
- package/dist/ui/cursor.js +261 -0
- package/dist/ui/dom.d.ts +30 -0
- package/dist/ui/dom.js +92 -0
- package/dist/ui/floats.d.ts +86 -0
- package/dist/ui/floats.js +260 -0
- package/dist/ui/hud.d.ts +198 -0
- package/dist/ui/hud.js +431 -0
- package/dist/ui/index.d.ts +37 -0
- package/dist/ui/index.js +34 -0
- package/dist/ui/inventory-card.d.ts +52 -0
- package/dist/ui/inventory-card.js +173 -0
- package/dist/ui/inventory-context.d.ts +29 -0
- package/dist/ui/inventory-context.js +125 -0
- package/dist/ui/inventory-drag.d.ts +63 -0
- package/dist/ui/inventory-drag.js +394 -0
- package/dist/ui/inventory-grid.d.ts +66 -0
- package/dist/ui/inventory-grid.js +625 -0
- package/dist/ui/inventory-radial.d.ts +39 -0
- package/dist/ui/inventory-radial.js +168 -0
- package/dist/ui/inventory.d.ts +128 -0
- package/dist/ui/inventory.js +154 -0
- package/dist/ui/layout.d.ts +356 -0
- package/dist/ui/layout.js +856 -0
- package/dist/ui/link.d.ts +64 -0
- package/dist/ui/link.js +155 -0
- package/dist/ui/loading.d.ts +40 -0
- package/dist/ui/loading.js +157 -0
- package/dist/ui/loupe.d.ts +74 -0
- package/dist/ui/loupe.js +164 -0
- package/dist/ui/menu-nav.d.ts +33 -0
- package/dist/ui/menu-nav.js +28 -0
- package/dist/ui/minimap-canvas.d.ts +145 -0
- package/dist/ui/minimap-canvas.js +460 -0
- package/dist/ui/notify.d.ts +40 -0
- package/dist/ui/notify.js +141 -0
- package/dist/ui/orb.d.ts +53 -0
- package/dist/ui/orb.js +304 -0
- package/dist/ui/overlay.d.ts +213 -0
- package/dist/ui/overlay.js +394 -0
- package/dist/ui/padnav.d.ts +53 -0
- package/dist/ui/padnav.js +146 -0
- package/dist/ui/perf.d.ts +130 -0
- package/dist/ui/perf.js +329 -0
- package/dist/ui/rotate-card.d.ts +54 -0
- package/dist/ui/rotate-card.js +97 -0
- package/dist/ui/stack.d.ts +128 -0
- package/dist/ui/stack.js +312 -0
- package/dist/ui/styles.d.ts +13 -0
- package/dist/ui/styles.js +200 -0
- package/dist/ui/text-scale.d.ts +67 -0
- package/dist/ui/text-scale.js +102 -0
- package/dist/ui/theme.d.ts +70 -0
- package/dist/ui/theme.js +98 -0
- package/dist/ui/toast.d.ts +23 -0
- package/dist/ui/toast.js +46 -0
- package/dist/ui/tooltip.d.ts +89 -0
- package/dist/ui/tooltip.js +333 -0
- package/dist/ui/widgets-2.d.ts +134 -0
- package/dist/ui/widgets-2.js +399 -0
- package/dist/ui/widgets-3.d.ts +91 -0
- package/dist/ui/widgets-3.js +212 -0
- package/dist/ui/widgets.d.ts +34 -0
- package/dist/ui/widgets.js +50 -0
- package/dist/ui/world-panel/index.d.ts +5 -0
- package/dist/ui/world-panel/index.js +5 -0
- package/dist/ui/world-panel/layout.d.ts +38 -0
- package/dist/ui/world-panel/layout.js +68 -0
- package/dist/ui/world-panel/paint.d.ts +96 -0
- package/dist/ui/world-panel/paint.js +136 -0
- package/dist/ui/world-panel/panel.d.ts +235 -0
- package/dist/ui/world-panel/panel.js +762 -0
- package/dist/ui/world-panel/wgsl.d.ts +10 -0
- package/dist/ui/world-panel/wgsl.js +52 -0
- package/dist/ui/world-panel/widgets.d.ts +207 -0
- package/dist/ui/world-panel/widgets.js +569 -0
- package/dist/voxel/chunks.d.ts +77 -0
- package/dist/voxel/chunks.js +149 -0
- package/dist/voxel/grid.d.ts +84 -0
- package/dist/voxel/grid.js +223 -0
- package/dist/voxel/index.d.ts +22 -0
- package/dist/voxel/index.js +7 -0
- package/dist/voxel/mesher-worker.d.ts +14 -0
- package/dist/voxel/mesher-worker.js +47 -0
- package/dist/voxel/mesher.d.ts +73 -0
- package/dist/voxel/mesher.js +306 -0
- package/dist/voxel/ray.d.ts +32 -0
- package/dist/voxel/ray.js +73 -0
- package/dist/voxel/wgsl.d.ts +16 -0
- package/dist/voxel/wgsl.js +28 -0
- package/dist/voxel/worker-client.d.ts +79 -0
- package/dist/voxel/worker-client.js +122 -0
- package/package.json +177 -3
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Host } from '../host/types.js';
|
|
2
|
+
import { type CopyResult, type LinkActivation, type OpenResult } from '../platform/links.js';
|
|
3
|
+
import { type Child } from './dom.js';
|
|
4
|
+
export interface ExternalLinkLabels {
|
|
5
|
+
/** Copy button text, e.g. "Copy link". */
|
|
6
|
+
copy: string;
|
|
7
|
+
/** Shown on the button for a moment after a copy, e.g. "Copied!". */
|
|
8
|
+
copied: string;
|
|
9
|
+
/** Read by screen readers after a copy, e.g. "Link copied." */
|
|
10
|
+
copiedAnnounce: string;
|
|
11
|
+
/** Copy button's accessible name; should start with `copy` (WCAG 2.5.3). Default: `copy`. */
|
|
12
|
+
copyAriaLabel?: string;
|
|
13
|
+
/** Fallback heading when copying failed, e.g. "Your browser blocked copying. Copy the link:". */
|
|
14
|
+
copyBlocked: string;
|
|
15
|
+
/** Fallback heading when the new tab was blocked, e.g. "Your browser blocked the new tab. Copy the link:". */
|
|
16
|
+
popupBlocked: string;
|
|
17
|
+
/** The fallback's close button, e.g. "Close". */
|
|
18
|
+
close: string;
|
|
19
|
+
}
|
|
20
|
+
export interface ExternalLinkOptions {
|
|
21
|
+
url: string;
|
|
22
|
+
/** Opens and copies the link. Default: `defaultHost()`. */
|
|
23
|
+
host?: Pick<Host, 'openExternal' | 'copyText'>;
|
|
24
|
+
/** The link's visible content (text, icon). */
|
|
25
|
+
content: Child | Child[];
|
|
26
|
+
labels: ExternalLinkLabels;
|
|
27
|
+
/** Extra class on the wrapper. */
|
|
28
|
+
className?: string;
|
|
29
|
+
/** Class on the anchor. */
|
|
30
|
+
linkClass?: string;
|
|
31
|
+
/**
|
|
32
|
+
* `data-action` names for tests and analytics-free debugging: the anchor gets `action`, the
|
|
33
|
+
* parts get `<action>-copy`, `<action>-fallback`, `<action>-close`.
|
|
34
|
+
*/
|
|
35
|
+
action?: string;
|
|
36
|
+
/** Where the fallback popover opens relative to the row. Default `below`. */
|
|
37
|
+
place?: 'above' | 'below';
|
|
38
|
+
/** How the popover lines up with the row. Default `center`. */
|
|
39
|
+
align?: 'start' | 'center' | 'end';
|
|
40
|
+
/** How long "Copied" shows, ms. Default 2200. */
|
|
41
|
+
copiedMs?: number;
|
|
42
|
+
}
|
|
43
|
+
export interface ExternalLink {
|
|
44
|
+
/** Wrapper to insert into the page. */
|
|
45
|
+
readonly element: HTMLElement;
|
|
46
|
+
readonly anchor: HTMLAnchorElement;
|
|
47
|
+
readonly copyButton: HTMLButtonElement;
|
|
48
|
+
/** Opens the link for `activation`; on `blocked` the fallback appears. */
|
|
49
|
+
open(activation: LinkActivation): OpenResult;
|
|
50
|
+
/** Copies the URL (the Copy button calls this); on `manual` the fallback appears. */
|
|
51
|
+
copy(): Promise<CopyResult>;
|
|
52
|
+
showFallback(reason: 'copy' | 'popup'): void;
|
|
53
|
+
hideFallback(): void;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Builds the widget, or returns null when the URL is not a safe https link (hide the control).
|
|
57
|
+
*/
|
|
58
|
+
export declare function externalLink(opts: ExternalLinkOptions): ExternalLink | null;
|
|
59
|
+
/**
|
|
60
|
+
* Activates `el` for a non-native input (a gamepad press): an external-link anchor opens through
|
|
61
|
+
* its host with block detection (`window.open` on the web); anything else is clicked. Returns the open result for an
|
|
62
|
+
* external link, else null.
|
|
63
|
+
*/
|
|
64
|
+
export declare function activateLink(el: HTMLElement, activation: LinkActivation): OpenResult | null;
|
package/dist/ui/link.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External link widget (D-036): a real new-tab link, a "Copy link" button beside it with
|
|
3
|
+
* "Copied" feedback, and a fallback popover with the URL in a selected read-only field for when
|
|
4
|
+
* the browser blocks the copy or the new tab. Opening and copying go through the host
|
|
5
|
+
* (`Host.openExternal`, `Host.copyText`; engine/host).
|
|
6
|
+
*
|
|
7
|
+
* - Mouse and Enter use the anchor natively (a user gesture, so popup blockers allow it).
|
|
8
|
+
* - A gamepad press is not a gesture: route it through `activateLink(el, 'gamepad')`
|
|
9
|
+
* (PadNav does this for modals), which opens through the host with block detection (the web
|
|
10
|
+
* host uses `window.open`) and shows the fallback if blocked.
|
|
11
|
+
* - Nothing opens or copies without the player acting. No network calls.
|
|
12
|
+
* Labels come from the caller; the look comes from theme tokens (override the `tc-link-*` classes).
|
|
13
|
+
*/
|
|
14
|
+
import { defaultHost } from '../host/default.js';
|
|
15
|
+
import { safeExternalUrl, } from '../platform/links.js';
|
|
16
|
+
import { h } from './dom.js';
|
|
17
|
+
const SVG = 'http://www.w3.org/2000/svg';
|
|
18
|
+
const registry = new WeakMap();
|
|
19
|
+
let ids = 0;
|
|
20
|
+
function copyIcon() {
|
|
21
|
+
const svg = document.createElementNS(SVG, 'svg');
|
|
22
|
+
svg.setAttribute('viewBox', '0 0 24 24');
|
|
23
|
+
svg.setAttribute('aria-hidden', 'true');
|
|
24
|
+
svg.setAttribute('class', 'tc-link-copy-icon');
|
|
25
|
+
svg.setAttribute('fill', 'none');
|
|
26
|
+
svg.setAttribute('stroke', 'currentColor');
|
|
27
|
+
svg.setAttribute('stroke-width', '2');
|
|
28
|
+
svg.setAttribute('stroke-linejoin', 'round');
|
|
29
|
+
for (const d of ['M8 8h11v13H8z', 'M5 16H4V3h11v1']) {
|
|
30
|
+
const p = document.createElementNS(SVG, 'path');
|
|
31
|
+
p.setAttribute('d', d);
|
|
32
|
+
svg.append(p);
|
|
33
|
+
}
|
|
34
|
+
return svg;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Builds the widget, or returns null when the URL is not a safe https link (hide the control).
|
|
38
|
+
*/
|
|
39
|
+
export function externalLink(opts) {
|
|
40
|
+
const url = safeExternalUrl(opts.url);
|
|
41
|
+
if (!url)
|
|
42
|
+
return null;
|
|
43
|
+
const { labels } = opts;
|
|
44
|
+
const host = opts.host ?? defaultHost();
|
|
45
|
+
const act = (part) => (opts.action ? { action: `${opts.action}${part}` } : undefined);
|
|
46
|
+
const anchor = h('a', {
|
|
47
|
+
class: ['tc-link-anchor', opts.linkClass ?? ''].join(' ').trim(),
|
|
48
|
+
href: url,
|
|
49
|
+
target: '_blank',
|
|
50
|
+
rel: 'noopener noreferrer',
|
|
51
|
+
dataset: act(''),
|
|
52
|
+
}, ...(Array.isArray(opts.content) ? opts.content : [opts.content]));
|
|
53
|
+
const copyLabel = h('span', null, labels.copy);
|
|
54
|
+
const copyButton = h('button', {
|
|
55
|
+
type: 'button',
|
|
56
|
+
class: 'tc-link-copy',
|
|
57
|
+
'aria-label': labels.copyAriaLabel ?? labels.copy,
|
|
58
|
+
dataset: act('-copy'),
|
|
59
|
+
}, copyIcon(), copyLabel);
|
|
60
|
+
const fieldId = `tc-link-field-${++ids}`;
|
|
61
|
+
const message = h('label', { class: 'tc-link-message', for: fieldId }, '');
|
|
62
|
+
const field = h('input', {
|
|
63
|
+
id: fieldId,
|
|
64
|
+
class: 'tc-link-field',
|
|
65
|
+
type: 'text',
|
|
66
|
+
readonly: true,
|
|
67
|
+
value: url,
|
|
68
|
+
// Wide enough to show the whole URL; the popover grows to fit (text scale included).
|
|
69
|
+
size: url.length + 1,
|
|
70
|
+
spellcheck: 'false',
|
|
71
|
+
autocomplete: 'off',
|
|
72
|
+
on: { focus: () => field.select(), click: () => field.select() },
|
|
73
|
+
});
|
|
74
|
+
const close = h('button', { type: 'button', class: 'tc-link-close', dataset: act('-close') }, labels.close);
|
|
75
|
+
const fallback = h('div', {
|
|
76
|
+
class: 'tc-link-fallback',
|
|
77
|
+
dataset: { ...act('-fallback'), place: opts.place ?? 'below', align: opts.align ?? 'center' },
|
|
78
|
+
}, h('div', { class: 'tc-link-fallback-head' }, message, close), field);
|
|
79
|
+
fallback.hidden = true;
|
|
80
|
+
const live = h('span', { class: 'tc-sr-only', 'aria-live': 'polite' }, '');
|
|
81
|
+
const element = h('div', { class: ['tc-link', opts.className ?? ''].join(' ').trim() }, h('div', { class: 'tc-link-row' }, anchor, copyButton), fallback, live);
|
|
82
|
+
const announce = (text) => {
|
|
83
|
+
// Cleared first, so the same message twice is still read.
|
|
84
|
+
live.textContent = '';
|
|
85
|
+
setTimeout(() => {
|
|
86
|
+
live.textContent = text;
|
|
87
|
+
}, 60);
|
|
88
|
+
};
|
|
89
|
+
let resetTimer;
|
|
90
|
+
const widget = {
|
|
91
|
+
element,
|
|
92
|
+
anchor,
|
|
93
|
+
copyButton,
|
|
94
|
+
open(activation) {
|
|
95
|
+
const r = host.openExternal(url, { activation });
|
|
96
|
+
if (r === 'blocked')
|
|
97
|
+
widget.showFallback('popup');
|
|
98
|
+
return r;
|
|
99
|
+
},
|
|
100
|
+
async copy() {
|
|
101
|
+
const r = await host.copyText(url);
|
|
102
|
+
if (r === 'manual') {
|
|
103
|
+
widget.showFallback('copy');
|
|
104
|
+
return r;
|
|
105
|
+
}
|
|
106
|
+
copyLabel.textContent = labels.copied;
|
|
107
|
+
copyButton.dataset.copied = '';
|
|
108
|
+
announce(labels.copiedAnnounce);
|
|
109
|
+
clearTimeout(resetTimer);
|
|
110
|
+
resetTimer = setTimeout(() => {
|
|
111
|
+
copyLabel.textContent = labels.copy;
|
|
112
|
+
delete copyButton.dataset.copied;
|
|
113
|
+
}, opts.copiedMs ?? 2200);
|
|
114
|
+
return r;
|
|
115
|
+
},
|
|
116
|
+
showFallback(reason) {
|
|
117
|
+
const text = reason === 'popup' ? labels.popupBlocked : labels.copyBlocked;
|
|
118
|
+
message.textContent = text;
|
|
119
|
+
field.value = url;
|
|
120
|
+
fallback.hidden = false;
|
|
121
|
+
field.focus({ preventScroll: true });
|
|
122
|
+
field.select();
|
|
123
|
+
announce(text);
|
|
124
|
+
},
|
|
125
|
+
hideFallback() {
|
|
126
|
+
const had = !fallback.hidden;
|
|
127
|
+
fallback.hidden = true;
|
|
128
|
+
if (had)
|
|
129
|
+
copyButton.focus({ preventScroll: true });
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
copyButton.addEventListener('click', () => void widget.copy());
|
|
133
|
+
close.addEventListener('click', () => widget.hideFallback());
|
|
134
|
+
fallback.addEventListener('keydown', (e) => {
|
|
135
|
+
if (e.key !== 'Escape')
|
|
136
|
+
return;
|
|
137
|
+
e.preventDefault();
|
|
138
|
+
e.stopPropagation();
|
|
139
|
+
widget.hideFallback();
|
|
140
|
+
});
|
|
141
|
+
registry.set(anchor, widget);
|
|
142
|
+
return widget;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Activates `el` for a non-native input (a gamepad press): an external-link anchor opens through
|
|
146
|
+
* its host with block detection (`window.open` on the web); anything else is clicked. Returns the open result for an
|
|
147
|
+
* external link, else null.
|
|
148
|
+
*/
|
|
149
|
+
export function activateLink(el, activation) {
|
|
150
|
+
const w = registry.get(el);
|
|
151
|
+
if (w)
|
|
152
|
+
return w.open(activation);
|
|
153
|
+
el.click();
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { PanelStack } from './stack.js';
|
|
2
|
+
export interface LoadingOptions {
|
|
3
|
+
title?: string;
|
|
4
|
+
/** One tip, or several to rotate through every `tipInterval` ms. */
|
|
5
|
+
tip?: string | readonly string[];
|
|
6
|
+
/** ms between tips when `tip` is a list. Default 4000. */
|
|
7
|
+
tipInterval?: number;
|
|
8
|
+
/** Initial progress, 0..1. Default 0. */
|
|
9
|
+
progress?: number;
|
|
10
|
+
/** Accessible name of the bar. Default `Loading`. */
|
|
11
|
+
label?: string;
|
|
12
|
+
/** Skip the fade (also skipped under `prefers-reduced-motion`). */
|
|
13
|
+
reducedMotion?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface LoadingScreen {
|
|
16
|
+
readonly element: HTMLElement;
|
|
17
|
+
/** 0..1 as last set. */
|
|
18
|
+
readonly progress: number;
|
|
19
|
+
/** True from creation until `done()` resolves. */
|
|
20
|
+
readonly visible: boolean;
|
|
21
|
+
/** Progress 0..1; `label` replaces the text beside the bar. */
|
|
22
|
+
set(progress: number, label?: string): void;
|
|
23
|
+
/** Show this tip now (and stop rotating). */
|
|
24
|
+
tip(text: string): void;
|
|
25
|
+
/** Fade out, close the panel, release the root; resolves after the transition. Idempotent. */
|
|
26
|
+
done(): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
/** What the loading screen needs from its overlay (`Overlay` fits). */
|
|
29
|
+
export interface LoadingHost {
|
|
30
|
+
readonly root: HTMLElement;
|
|
31
|
+
readonly stack: PanelStack;
|
|
32
|
+
}
|
|
33
|
+
/** Show a loading screen; see the module comment. Also reachable as `ui.loading(opts)`. */
|
|
34
|
+
export declare function createLoading(host: LoadingHost, opts?: LoadingOptions): LoadingScreen;
|
|
35
|
+
/**
|
|
36
|
+
* Stylesheet for the loading screen (design px, theme tokens).
|
|
37
|
+
*/
|
|
38
|
+
export declare const LOADING_CSS: string;
|
|
39
|
+
/** Add `LOADING_CSS` to `doc` once (the first loading screen does it). */
|
|
40
|
+
export declare function ensureLoadingStyles(doc?: Document): void;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loading screen (ws/ui-3): a full-frame, non-dismissible panel on the `panels` layer with a
|
|
3
|
+
* title, a progress `bar` and a tip line that can rotate through strings. While it is shown the
|
|
4
|
+
* overlay root carries `aria-busy="true"`, the lower layers are inert (a modal panel), the game's
|
|
5
|
+
* `Input` is blocked through `stack.hasModal`, and keys other than Tab are swallowed at the
|
|
6
|
+
* document's capture phase. `done()` fades it out and resolves after the transition.
|
|
7
|
+
*/
|
|
8
|
+
import { h } from './dom.js';
|
|
9
|
+
import { prefersReducedMotionIn } from './styles.js';
|
|
10
|
+
import { toFrameUnits } from './text-scale.js';
|
|
11
|
+
import { bar } from './widgets-2.js';
|
|
12
|
+
/** Fallback for when no `transitionend` arrives (display: none, a hidden tab). */
|
|
13
|
+
const FADE_FALLBACK_MS = 400;
|
|
14
|
+
/** Show a loading screen; see the module comment. Also reachable as `ui.loading(opts)`. */
|
|
15
|
+
export function createLoading(host, opts = {}) {
|
|
16
|
+
const doc = host.root.ownerDocument;
|
|
17
|
+
ensureLoadingStyles(doc);
|
|
18
|
+
const label = opts.label ?? 'Loading';
|
|
19
|
+
const progressBar = bar(label, opts.progress ?? 0, {
|
|
20
|
+
format: (v) => `${Math.round(v * 100)}%`,
|
|
21
|
+
});
|
|
22
|
+
const tipEl = h('p', { class: 'tc-loading-tip', role: 'status', 'aria-live': 'polite' });
|
|
23
|
+
const titleEl = opts.title ? h('h2', { class: 'tc-loading-title' }, opts.title) : null;
|
|
24
|
+
const body = h('div', { class: 'tc-loading-body' }, titleEl, progressBar.element, tipEl);
|
|
25
|
+
let progress = opts.progress ?? 0;
|
|
26
|
+
let visible = true;
|
|
27
|
+
let closing = null;
|
|
28
|
+
let tipTimer = null;
|
|
29
|
+
const tips = typeof opts.tip === 'string' ? [opts.tip] : [...(opts.tip ?? [])];
|
|
30
|
+
let tipIndex = 0;
|
|
31
|
+
const showTip = (text) => {
|
|
32
|
+
if (tipEl.textContent !== text)
|
|
33
|
+
tipEl.textContent = text;
|
|
34
|
+
};
|
|
35
|
+
if (tips.length > 0) {
|
|
36
|
+
showTip(tips[0]);
|
|
37
|
+
if (tips.length > 1) {
|
|
38
|
+
tipTimer = setInterval(() => {
|
|
39
|
+
tipIndex = (tipIndex + 1) % tips.length;
|
|
40
|
+
showTip(tips[tipIndex]);
|
|
41
|
+
}, opts.tipInterval ?? 4000);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const panel = host.stack.push({
|
|
45
|
+
content: body,
|
|
46
|
+
modal: true,
|
|
47
|
+
dismissible: false,
|
|
48
|
+
closeOnBackdrop: false,
|
|
49
|
+
chrome: 'none',
|
|
50
|
+
className: 'tc-loading',
|
|
51
|
+
label: opts.title ?? label,
|
|
52
|
+
arrowNav: 'none',
|
|
53
|
+
});
|
|
54
|
+
const element = panel.element;
|
|
55
|
+
host.root.setAttribute('aria-busy', 'true');
|
|
56
|
+
// Swallow keys at the document: the panel traps focus, this keeps Enter/Space/arrows from
|
|
57
|
+
// reaching a game's own key handlers behind it. Tab still moves focus for screen readers.
|
|
58
|
+
const onKey = (e) => {
|
|
59
|
+
if (e.key === 'Tab')
|
|
60
|
+
return;
|
|
61
|
+
e.preventDefault();
|
|
62
|
+
e.stopPropagation();
|
|
63
|
+
};
|
|
64
|
+
doc.addEventListener('keydown', onKey, true);
|
|
65
|
+
doc.addEventListener('keyup', onKey, true);
|
|
66
|
+
const release = () => {
|
|
67
|
+
if (tipTimer) {
|
|
68
|
+
clearInterval(tipTimer);
|
|
69
|
+
tipTimer = null;
|
|
70
|
+
}
|
|
71
|
+
doc.removeEventListener('keydown', onKey, true);
|
|
72
|
+
doc.removeEventListener('keyup', onKey, true);
|
|
73
|
+
host.root.removeAttribute('aria-busy');
|
|
74
|
+
visible = false;
|
|
75
|
+
if (panel.open)
|
|
76
|
+
panel.close('programmatic');
|
|
77
|
+
};
|
|
78
|
+
const screen = {
|
|
79
|
+
element,
|
|
80
|
+
get progress() {
|
|
81
|
+
return progress;
|
|
82
|
+
},
|
|
83
|
+
get visible() {
|
|
84
|
+
return visible;
|
|
85
|
+
},
|
|
86
|
+
set(p, text) {
|
|
87
|
+
progress = Math.min(1, Math.max(0, p));
|
|
88
|
+
progressBar.set(progress);
|
|
89
|
+
if (text !== undefined) {
|
|
90
|
+
const value = element.querySelector('.tc-ui-bar-value');
|
|
91
|
+
if (value && value.textContent !== text)
|
|
92
|
+
value.textContent = text;
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
tip(text) {
|
|
96
|
+
if (tipTimer) {
|
|
97
|
+
clearInterval(tipTimer);
|
|
98
|
+
tipTimer = null;
|
|
99
|
+
}
|
|
100
|
+
showTip(text);
|
|
101
|
+
},
|
|
102
|
+
done() {
|
|
103
|
+
if (closing)
|
|
104
|
+
return closing;
|
|
105
|
+
const skipFade = opts.reducedMotion || prefersReducedMotionIn();
|
|
106
|
+
closing = new Promise((resolve) => {
|
|
107
|
+
if (skipFade) {
|
|
108
|
+
release();
|
|
109
|
+
resolve();
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
let settled = false;
|
|
113
|
+
const finish = () => {
|
|
114
|
+
if (settled)
|
|
115
|
+
return;
|
|
116
|
+
settled = true;
|
|
117
|
+
element.removeEventListener('transitionend', onEnd);
|
|
118
|
+
clearTimeout(fallback);
|
|
119
|
+
release();
|
|
120
|
+
resolve();
|
|
121
|
+
};
|
|
122
|
+
const onEnd = (e) => {
|
|
123
|
+
if (e.target === element)
|
|
124
|
+
finish();
|
|
125
|
+
};
|
|
126
|
+
element.addEventListener('transitionend', onEnd);
|
|
127
|
+
const fallback = setTimeout(finish, FADE_FALLBACK_MS);
|
|
128
|
+
element.dataset.done = '';
|
|
129
|
+
});
|
|
130
|
+
return closing;
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
return screen;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Stylesheet for the loading screen (design px, theme tokens).
|
|
137
|
+
*/
|
|
138
|
+
export const LOADING_CSS = toFrameUnits(`
|
|
139
|
+
.tc-panel.tc-loading { position: absolute; inset: 0; display: grid; place-items: center; max-width: none; max-height: none;
|
|
140
|
+
width: auto; height: auto; margin: 0; padding: 0; background: var(--tc-color-surface); border-radius: 0;
|
|
141
|
+
transition: opacity var(--tc-duration-normal) var(--tc-easing); }
|
|
142
|
+
.tc-panel.tc-loading[data-done] { opacity: 0; pointer-events: none; }
|
|
143
|
+
.tc-loading-body { display: grid; gap: var(--tc-space-4); width: min(720px, 80%); color: var(--tc-color-text); font-family: var(--tc-font-ui); }
|
|
144
|
+
.tc-loading-title { margin: 0; font-size: var(--tc-font-size-l); font-weight: 700; text-align: center; }
|
|
145
|
+
.tc-loading-tip { margin: 0; min-height: 1.4em; font-size: var(--tc-font-size-s); color: var(--tc-color-text-muted); text-align: center; }
|
|
146
|
+
@media (prefers-reduced-motion: reduce) { .tc-panel.tc-loading { transition: none; } }
|
|
147
|
+
`);
|
|
148
|
+
const STYLE_ID = 'tc-ui-loading';
|
|
149
|
+
/** Add `LOADING_CSS` to `doc` once (the first loading screen does it). */
|
|
150
|
+
export function ensureLoadingStyles(doc = document) {
|
|
151
|
+
if (doc.getElementById(STYLE_ID))
|
|
152
|
+
return;
|
|
153
|
+
const style = doc.createElement('style');
|
|
154
|
+
style.id = STYLE_ID;
|
|
155
|
+
style.textContent = LOADING_CSS;
|
|
156
|
+
doc.head.appendChild(style);
|
|
157
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export interface LoupeOptions {
|
|
2
|
+
/** Diameter in CSS px (default 120). */
|
|
3
|
+
size?: number;
|
|
4
|
+
/** Magnification (default 2.5). */
|
|
5
|
+
zoom?: number;
|
|
6
|
+
/** Where the loupe element goes (default document.body). It is `position: fixed`. */
|
|
7
|
+
parent?: HTMLElement;
|
|
8
|
+
/** Extra class names for styling. */
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface LoupeView {
|
|
12
|
+
/** Client point the loupe is centred on (what it magnifies). */
|
|
13
|
+
focus: {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
};
|
|
17
|
+
/** Client point where the loupe itself is drawn (its centre). */
|
|
18
|
+
at: {
|
|
19
|
+
x: number;
|
|
20
|
+
y: number;
|
|
21
|
+
};
|
|
22
|
+
/** Optional ring: a client point and radius (CSS px, before zoom). */
|
|
23
|
+
ring?: {
|
|
24
|
+
x: number;
|
|
25
|
+
y: number;
|
|
26
|
+
r: number;
|
|
27
|
+
color?: string;
|
|
28
|
+
} | null;
|
|
29
|
+
/**
|
|
30
|
+
* Optional leader: a thin line from the loupe's rim to a client point and radius (the real
|
|
31
|
+
* spot the loupe magnifies, outside it), so the eye joins the two. Hidden when the spot is
|
|
32
|
+
* under or touching the loupe.
|
|
33
|
+
*/
|
|
34
|
+
leader?: {
|
|
35
|
+
x: number;
|
|
36
|
+
y: number;
|
|
37
|
+
r: number;
|
|
38
|
+
} | null;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The leader's segment (client px) from the rim of a loupe `size` across at `at` to `r` short
|
|
42
|
+
* of the spot, or `null` when there is no room for one.
|
|
43
|
+
*/
|
|
44
|
+
export declare function loupeLeader(at: {
|
|
45
|
+
x: number;
|
|
46
|
+
y: number;
|
|
47
|
+
}, size: number, spot: {
|
|
48
|
+
x: number;
|
|
49
|
+
y: number;
|
|
50
|
+
r: number;
|
|
51
|
+
}): {
|
|
52
|
+
x0: number;
|
|
53
|
+
y0: number;
|
|
54
|
+
x1: number;
|
|
55
|
+
y1: number;
|
|
56
|
+
} | null;
|
|
57
|
+
export declare class Loupe {
|
|
58
|
+
private readonly source;
|
|
59
|
+
readonly el: HTMLElement;
|
|
60
|
+
readonly size: number;
|
|
61
|
+
readonly zoom: number;
|
|
62
|
+
private readonly cv;
|
|
63
|
+
/** The leader line (`tc-loupe-leader`), a sibling of the loupe so it draws outside it. */
|
|
64
|
+
readonly leaderEl: HTMLElement;
|
|
65
|
+
private readonly g;
|
|
66
|
+
private on;
|
|
67
|
+
private pending;
|
|
68
|
+
constructor(source: HTMLCanvasElement, opts?: LoupeOptions);
|
|
69
|
+
get visible(): boolean;
|
|
70
|
+
show(v: LoupeView): void;
|
|
71
|
+
private draw;
|
|
72
|
+
hide(): void;
|
|
73
|
+
destroy(): void;
|
|
74
|
+
}
|
package/dist/ui/loupe.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loupe: a round magnifier over a canvas, for fingers on small screens. It copies a patch of
|
|
3
|
+
* the source canvas (WebGPU or 2D: whatever the page last showed) into its own small 2D canvas
|
|
4
|
+
* each time `show` is called, and can ring one spot in the patch. Everything is in client (CSS)
|
|
5
|
+
* px. It never takes input (`pointer-events: none`), and fades out on `hide`.
|
|
6
|
+
*
|
|
7
|
+
* Styling is the caller's: the root element has class `tc-loupe` (plus `className`), and is
|
|
8
|
+
* `data-on` while shown.
|
|
9
|
+
*/
|
|
10
|
+
import { h } from './dom.js';
|
|
11
|
+
/**
|
|
12
|
+
* The leader's segment (client px) from the rim of a loupe `size` across at `at` to `r` short
|
|
13
|
+
* of the spot, or `null` when there is no room for one.
|
|
14
|
+
*/
|
|
15
|
+
export function loupeLeader(at, size, spot) {
|
|
16
|
+
const dx = spot.x - at.x;
|
|
17
|
+
const dy = spot.y - at.y;
|
|
18
|
+
const d = Math.hypot(dx, dy);
|
|
19
|
+
const from = size / 2 + 1;
|
|
20
|
+
const to = d - spot.r - 2;
|
|
21
|
+
if (to - from < 6)
|
|
22
|
+
return null;
|
|
23
|
+
const ux = dx / d;
|
|
24
|
+
const uy = dy / d;
|
|
25
|
+
return { x0: at.x + ux * from, y0: at.y + uy * from, x1: at.x + ux * to, y1: at.y + uy * to };
|
|
26
|
+
}
|
|
27
|
+
export class Loupe {
|
|
28
|
+
source;
|
|
29
|
+
el;
|
|
30
|
+
size;
|
|
31
|
+
zoom;
|
|
32
|
+
cv;
|
|
33
|
+
/** The leader line (`tc-loupe-leader`), a sibling of the loupe so it draws outside it. */
|
|
34
|
+
leaderEl;
|
|
35
|
+
g;
|
|
36
|
+
on = false;
|
|
37
|
+
pending = null;
|
|
38
|
+
constructor(source, opts = {}) {
|
|
39
|
+
this.source = source;
|
|
40
|
+
this.size = opts.size ?? 120;
|
|
41
|
+
this.zoom = opts.zoom ?? 2.5;
|
|
42
|
+
const doc = source.ownerDocument;
|
|
43
|
+
this.cv = h('canvas', { 'aria-hidden': 'true' });
|
|
44
|
+
this.el = h('div', {
|
|
45
|
+
class: `tc-loupe${opts.className ? ` ${opts.className}` : ''}`,
|
|
46
|
+
'aria-hidden': 'true',
|
|
47
|
+
style: {
|
|
48
|
+
position: 'fixed',
|
|
49
|
+
left: '0px',
|
|
50
|
+
top: '0px',
|
|
51
|
+
width: `${this.size}px`,
|
|
52
|
+
height: `${this.size}px`,
|
|
53
|
+
pointerEvents: 'none',
|
|
54
|
+
opacity: '0',
|
|
55
|
+
},
|
|
56
|
+
}, this.cv);
|
|
57
|
+
Object.assign(this.cv.style, { width: '100%', height: '100%', display: 'block' });
|
|
58
|
+
this.g = this.cv.getContext('2d');
|
|
59
|
+
this.leaderEl = h('div', {
|
|
60
|
+
class: `tc-loupe-leader${opts.className ? ` ${opts.className}-leader` : ''}`,
|
|
61
|
+
'aria-hidden': 'true',
|
|
62
|
+
hidden: true,
|
|
63
|
+
style: {
|
|
64
|
+
position: 'fixed',
|
|
65
|
+
left: '0px',
|
|
66
|
+
top: '0px',
|
|
67
|
+
height: '2px',
|
|
68
|
+
transformOrigin: '0 50%',
|
|
69
|
+
pointerEvents: 'none',
|
|
70
|
+
background: '#ffd56a',
|
|
71
|
+
boxShadow: '0 0 0 1px #000a',
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
(opts.parent ?? doc.body).append(this.leaderEl, this.el);
|
|
75
|
+
}
|
|
76
|
+
get visible() {
|
|
77
|
+
return this.on;
|
|
78
|
+
}
|
|
79
|
+
show(v) {
|
|
80
|
+
const win = this.source.ownerDocument.defaultView ?? window;
|
|
81
|
+
const dpr = Math.min(3, win.devicePixelRatio || 1);
|
|
82
|
+
const px = Math.round(this.size * dpr);
|
|
83
|
+
if (this.cv.width !== px) {
|
|
84
|
+
this.cv.width = px;
|
|
85
|
+
this.cv.height = px;
|
|
86
|
+
}
|
|
87
|
+
const d = this.size;
|
|
88
|
+
this.el.style.transform = `translate(${Math.round(v.at.x - d / 2)}px, ${Math.round(v.at.y - d / 2)}px)`;
|
|
89
|
+
const seg = v.leader ? loupeLeader(v.at, d, v.leader) : null;
|
|
90
|
+
this.leaderEl.hidden = !seg;
|
|
91
|
+
if (seg) {
|
|
92
|
+
const len = Math.hypot(seg.x1 - seg.x0, seg.y1 - seg.y0);
|
|
93
|
+
const deg = (Math.atan2(seg.y1 - seg.y0, seg.x1 - seg.x0) * 180) / Math.PI;
|
|
94
|
+
this.leaderEl.style.width = `${len.toFixed(1)}px`;
|
|
95
|
+
this.leaderEl.style.transform = `translate(${seg.x0.toFixed(1)}px, ${(seg.y0 - 1).toFixed(1)}px) rotate(${deg.toFixed(2)}deg)`;
|
|
96
|
+
}
|
|
97
|
+
if (!this.on) {
|
|
98
|
+
this.on = true;
|
|
99
|
+
this.el.dataset.on = '';
|
|
100
|
+
this.el.style.opacity = '1';
|
|
101
|
+
}
|
|
102
|
+
// Copy after the caller's frame is drawn: a WebGPU canvas is only readable between its
|
|
103
|
+
// render and the browser presenting it, so a copy made earlier in the frame comes out blank.
|
|
104
|
+
// A microtask runs once the current task (update then render) is over.
|
|
105
|
+
const queued = this.pending !== null;
|
|
106
|
+
this.pending = { view: v, px, dpr };
|
|
107
|
+
if (!queued)
|
|
108
|
+
queueMicrotask(() => this.draw());
|
|
109
|
+
}
|
|
110
|
+
draw() {
|
|
111
|
+
const p = this.pending;
|
|
112
|
+
this.pending = null;
|
|
113
|
+
const g = this.g;
|
|
114
|
+
if (!p || !g || !this.on)
|
|
115
|
+
return;
|
|
116
|
+
const { view: v, px, dpr } = p;
|
|
117
|
+
const d = this.size;
|
|
118
|
+
const r = this.source.getBoundingClientRect();
|
|
119
|
+
if (r.width <= 0 || r.height <= 0)
|
|
120
|
+
return;
|
|
121
|
+
const kx = this.source.width / r.width;
|
|
122
|
+
const ky = this.source.height / r.height;
|
|
123
|
+
const span = d / this.zoom; // client px shown across the loupe
|
|
124
|
+
g.setTransform(1, 0, 0, 1, 0, 0);
|
|
125
|
+
g.clearRect(0, 0, px, px);
|
|
126
|
+
g.fillStyle = '#000';
|
|
127
|
+
g.fillRect(0, 0, px, px);
|
|
128
|
+
try {
|
|
129
|
+
g.drawImage(this.source, (v.focus.x - span / 2 - r.left) * kx, (v.focus.y - span / 2 - r.top) * ky, span * kx, span * ky, 0, 0, px, px);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// A source that can't be read yet: the loupe stays dark for this frame.
|
|
133
|
+
}
|
|
134
|
+
const ring = v.ring;
|
|
135
|
+
if (ring) {
|
|
136
|
+
const s = px / span; // device px per client px inside the loupe
|
|
137
|
+
const cx = (ring.x - (v.focus.x - span / 2)) * s;
|
|
138
|
+
const cy = (ring.y - (v.focus.y - span / 2)) * s;
|
|
139
|
+
g.lineWidth = 3 * dpr;
|
|
140
|
+
g.strokeStyle = '#000a';
|
|
141
|
+
g.beginPath();
|
|
142
|
+
g.arc(cx, cy, ring.r * s + 2 * dpr, 0, Math.PI * 2);
|
|
143
|
+
g.stroke();
|
|
144
|
+
g.lineWidth = 2.5 * dpr;
|
|
145
|
+
g.strokeStyle = ring.color ?? '#ffd56a';
|
|
146
|
+
g.beginPath();
|
|
147
|
+
g.arc(cx, cy, ring.r * s, 0, Math.PI * 2);
|
|
148
|
+
g.stroke();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
hide() {
|
|
152
|
+
this.pending = null;
|
|
153
|
+
if (!this.on)
|
|
154
|
+
return;
|
|
155
|
+
this.on = false;
|
|
156
|
+
this.leaderEl.hidden = true;
|
|
157
|
+
delete this.el.dataset.on;
|
|
158
|
+
this.el.style.opacity = '0';
|
|
159
|
+
}
|
|
160
|
+
destroy() {
|
|
161
|
+
this.el.remove();
|
|
162
|
+
this.leaderEl.remove();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Up/down and gamepad confirm for a non-modal menu (a title screen, an end card), so every
|
|
3
|
+
* control on it, links included, is reachable without a mouse. Modal panels use `PadNav`
|
|
4
|
+
* instead.
|
|
5
|
+
*
|
|
6
|
+
* Arrow keys reach this while a button or link has focus if the game lists them as Input
|
|
7
|
+
* `buttonKeys`, but Enter and Space do not: the focused control handles those natively. So
|
|
8
|
+
* `confirm` is only taken here when it came from a gamepad.
|
|
9
|
+
*
|
|
10
|
+
* A pad press is not a user gesture, so a new-tab link is opened with `window.open` and checked:
|
|
11
|
+
* if the browser blocks it, the link's copy field appears instead (`activateLink`).
|
|
12
|
+
*/
|
|
13
|
+
import type { InputDevice } from '../input/types.js';
|
|
14
|
+
/** What a tick of menu input did: moved focus, clicked the focused control, or nothing. */
|
|
15
|
+
export type MenuNavResult = 'moved' | 'clicked' | null;
|
|
16
|
+
/** The slice of `Input` menu navigation reads. */
|
|
17
|
+
export interface MenuNavInput<A extends string> {
|
|
18
|
+
pressed(action: A): boolean;
|
|
19
|
+
readonly lastDevice: InputDevice;
|
|
20
|
+
}
|
|
21
|
+
/** The game's action names for up, down and confirm. Default `up`, `down`, `confirm`. */
|
|
22
|
+
export interface MenuNavActions<A extends string> {
|
|
23
|
+
up: A;
|
|
24
|
+
down: A;
|
|
25
|
+
confirm: A;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* One tick of menu navigation over the focusable controls in `root`. Call it from the scene's
|
|
29
|
+
* update while no modal is open. Up and down move focus (wrapping; from outside the menu, down
|
|
30
|
+
* goes to the first control and up to the last). Confirm from a gamepad activates the focused
|
|
31
|
+
* control.
|
|
32
|
+
*/
|
|
33
|
+
export declare function menuNav<A extends string>(input: MenuNavInput<A>, root: HTMLElement, actions?: MenuNavActions<A>): MenuNavResult;
|