@weasel-js/core 1.9.3 → 1.9.4

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 (542) hide show
  1. package/CHANGELOG.md +356 -0
  2. package/README.md +2 -2
  3. package/dist/WeaselProvider.js +5 -5
  4. package/dist/affordances/composeAffordanceLayer.js +10 -20
  5. package/dist/affordances/composeAffordanceLayer.js.map +1 -1
  6. package/dist/affordances/cornerResize.js +25 -17
  7. package/dist/affordances/cornerResize.js.map +1 -1
  8. package/dist/affordances/hitAffordanceRegions.js +27 -20
  9. package/dist/affordances/hitAffordanceRegions.js.map +1 -1
  10. package/dist/affordances/rotationHandle.js +18 -18
  11. package/dist/affordances/rotationHandle.js.map +1 -1
  12. package/dist/animation/behaviors/momentum.js +93 -55
  13. package/dist/animation/behaviors/momentum.js.map +1 -1
  14. package/dist/animation/loop.js +10 -12
  15. package/dist/animation/loop.js.map +1 -1
  16. package/dist/animation/reflow.js +8 -8
  17. package/dist/animation/reflow.js.map +1 -1
  18. package/dist/animation/stagger.js +64 -56
  19. package/dist/animation/stagger.js.map +1 -1
  20. package/dist/animation/timeline/booking.js +62 -54
  21. package/dist/animation/timeline/booking.js.map +1 -1
  22. package/dist/animation/timeline/createTimeline.js +85 -76
  23. package/dist/animation/timeline/createTimeline.js.map +1 -1
  24. package/dist/animation/timeline/sampleTrack.js +13 -5
  25. package/dist/animation/timeline/sampleTrack.js.map +1 -1
  26. package/dist/animation/useAnimator.js +45 -35
  27. package/dist/animation/useAnimator.js.map +1 -1
  28. package/dist/animation/wrappers/animateLifecycle.js +31 -24
  29. package/dist/animation/wrappers/animateLifecycle.js.map +1 -1
  30. package/dist/animation/wrappers/animateOnSetPose.js +28 -26
  31. package/dist/animation/wrappers/animateOnSetPose.js.map +1 -1
  32. package/dist/animation/wrappers/instantPose.js +13 -0
  33. package/dist/animation/wrappers/instantPose.js.map +1 -0
  34. package/dist/{poseDescriptor-D-OH-tu5.d.ts → autoPoseDescriptor-Ct7yqivU.d.ts} +28 -3
  35. package/dist/barrel/chrome.js +16 -0
  36. package/dist/barrel/input.js +19 -0
  37. package/dist/barrel/interactions.js +42 -0
  38. package/dist/barrel/paint.js +18 -0
  39. package/dist/barrel/paths.js +19 -0
  40. package/dist/barrel/renderer.js +20 -0
  41. package/dist/barrel/routing.js +51 -0
  42. package/dist/barrel/text.js +18 -0
  43. package/dist/barrel/viewport.js +52 -0
  44. package/dist/builtins-DPcdghtB.d.ts +1223 -0
  45. package/dist/canvas/Canvas.js +363 -448
  46. package/dist/canvas/Canvas.js.map +1 -1
  47. package/dist/canvas/CanvasView.js +164 -165
  48. package/dist/canvas/CanvasView.js.map +1 -1
  49. package/dist/canvas/CursorCoordsHud.js +37 -32
  50. package/dist/canvas/CursorCoordsHud.js.map +1 -1
  51. package/dist/canvas/DrawCanvas.js +49 -43
  52. package/dist/canvas/DrawCanvas.js.map +1 -1
  53. package/dist/canvas/MinimapCanvas.js +26 -25
  54. package/dist/canvas/MinimapCanvas.js.map +1 -1
  55. package/dist/canvas/NodeShape.js +59 -377
  56. package/dist/canvas/NodeShape.js.map +1 -1
  57. package/dist/canvas/PickHud.js +50 -47
  58. package/dist/canvas/PickHud.js.map +1 -1
  59. package/dist/canvas/SceneCanvas/GestureDispatcherMounter.js +167 -0
  60. package/dist/canvas/SceneCanvas/GestureDispatcherMounter.js.map +1 -0
  61. package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js +13 -13
  62. package/dist/canvas/SceneCanvas/StandardActionsRegistrar.js +67 -0
  63. package/dist/canvas/SceneCanvas/StandardActionsRegistrar.js.map +1 -0
  64. package/dist/canvas/SceneCanvas/ToolKeybindingsMounter.js +16 -0
  65. package/dist/canvas/SceneCanvas/ToolKeybindingsMounter.js.map +1 -0
  66. package/dist/canvas/SceneCanvas/coordTrace.js +10 -0
  67. package/dist/canvas/SceneCanvas/coordTrace.js.map +1 -0
  68. package/dist/canvas/SceneCanvas/defaultNodeProperties.js +112 -70
  69. package/dist/canvas/SceneCanvas/defaultNodeProperties.js.map +1 -1
  70. package/dist/canvas/SceneCanvas/defaultNodeRouting.js +9 -8
  71. package/dist/canvas/SceneCanvas/defaultNodeRouting.js.map +1 -1
  72. package/dist/canvas/SceneCanvas/features.js +1 -0
  73. package/dist/canvas/SceneCanvas/features.js.map +1 -1
  74. package/dist/canvas/SceneCanvas/isDev.js +12 -0
  75. package/dist/canvas/SceneCanvas/isDev.js.map +1 -0
  76. package/dist/canvas/SceneCanvas/poseGeometry.js +29 -28
  77. package/dist/canvas/SceneCanvas/poseGeometry.js.map +1 -1
  78. package/dist/canvas/SceneCanvas/poseRegistrars.js +13 -0
  79. package/dist/canvas/SceneCanvas/poseRegistrars.js.map +1 -0
  80. package/dist/canvas/SceneCanvas/props.js +38 -0
  81. package/dist/canvas/SceneCanvas/props.js.map +1 -0
  82. package/dist/canvas/SceneCanvas/useBuiltinShapeTools.js +21 -62
  83. package/dist/canvas/SceneCanvas/useBuiltinShapeTools.js.map +1 -1
  84. package/dist/canvas/SceneCanvas/useDispatcherOverlayLayer.js.map +1 -1
  85. package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js +46 -48
  86. package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js.map +1 -1
  87. package/dist/canvas/SceneCanvas/useSceneSelectTool.js +33 -33
  88. package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
  89. package/dist/canvas/SceneCanvas.js +534 -788
  90. package/dist/canvas/SceneCanvas.js.map +1 -1
  91. package/dist/canvas/SceneViewCanvas.js +45 -43
  92. package/dist/canvas/SceneViewCanvas.js.map +1 -1
  93. package/dist/canvas/buildSceneLayer.js +91 -0
  94. package/dist/canvas/buildSceneLayer.js.map +1 -0
  95. package/dist/canvas/buildSceneTree.js +16 -19
  96. package/dist/canvas/buildSceneTree.js.map +1 -1
  97. package/dist/canvas/canvasRenderer.js +68 -52
  98. package/dist/canvas/canvasRenderer.js.map +1 -1
  99. package/dist/canvas/deps/changes.js +13 -0
  100. package/dist/canvas/deps/changes.js.map +1 -0
  101. package/dist/canvas/deps/editAnchors.js +28 -28
  102. package/dist/canvas/deps/insert.js +119 -91
  103. package/dist/canvas/deps/insert.js.map +1 -1
  104. package/dist/canvas/deps/slice.js +46 -45
  105. package/dist/canvas/deps/slice.js.map +1 -1
  106. package/dist/canvas/deps/textEdit.js +10 -9
  107. package/dist/canvas/deps/textEdit.js.map +1 -1
  108. package/dist/canvas/derivedPath.js +46 -23
  109. package/dist/canvas/derivedPath.js.map +1 -1
  110. package/dist/canvas/getNodeAtPoint.js +7 -7
  111. package/dist/canvas/getNodeAtPoint.js.map +1 -1
  112. package/dist/canvas/hud.module.js +1 -1
  113. package/dist/canvas/hud.module.js.map +1 -1
  114. package/dist/canvas/kitRequirements.js +2 -2
  115. package/dist/canvas/kitRequirements.js.map +1 -1
  116. package/dist/canvas/layerOrder.js +13 -6
  117. package/dist/canvas/layerOrder.js.map +1 -1
  118. package/dist/canvas/minimapMath.js +12 -10
  119. package/dist/canvas/minimapMath.js.map +1 -1
  120. package/dist/canvas/nodeShapes/derivedPainter.js +34 -0
  121. package/dist/canvas/nodeShapes/derivedPainter.js.map +1 -0
  122. package/dist/canvas/nodeShapes/imagePainter.js +87 -0
  123. package/dist/canvas/nodeShapes/imagePainter.js.map +1 -0
  124. package/dist/canvas/nodeShapes/memo.js +9 -0
  125. package/dist/canvas/nodeShapes/memo.js.map +1 -0
  126. package/dist/canvas/nodeShapes/nodePaint.js +57 -0
  127. package/dist/canvas/nodeShapes/nodePaint.js.map +1 -0
  128. package/dist/canvas/nodeShapes/pathPainter.js +38 -0
  129. package/dist/canvas/nodeShapes/pathPainter.js.map +1 -0
  130. package/dist/canvas/nodeShapes/poseBox.js +20 -0
  131. package/dist/canvas/nodeShapes/poseBox.js.map +1 -0
  132. package/dist/canvas/nodeShapes/rectFallbackPainter.js +38 -0
  133. package/dist/canvas/nodeShapes/rectFallbackPainter.js.map +1 -0
  134. package/dist/canvas/nodeShapes/shapePainter.js +72 -0
  135. package/dist/canvas/nodeShapes/shapePainter.js.map +1 -0
  136. package/dist/canvas/nodeShapes/textPainter.js +55 -0
  137. package/dist/canvas/nodeShapes/textPainter.js.map +1 -0
  138. package/dist/canvas/overlayRoleStrokes.js.map +1 -1
  139. package/dist/canvas/paintCull.js +10 -11
  140. package/dist/canvas/paintCull.js.map +1 -1
  141. package/dist/canvas/pickWalk.js +52 -63
  142. package/dist/canvas/pickWalk.js.map +1 -1
  143. package/dist/canvas/poseRotation.js +11 -23
  144. package/dist/canvas/poseRotation.js.map +1 -1
  145. package/dist/canvas/renderDebugSnapshot.js +5 -5
  146. package/dist/canvas/renderSceneToPixels.js +3 -1
  147. package/dist/canvas/renderSceneToPixels.js.map +1 -1
  148. package/dist/canvas/sceneAdapter.js +66 -148
  149. package/dist/canvas/sceneAdapter.js.map +1 -1
  150. package/dist/canvas/sceneClipboard.js +112 -0
  151. package/dist/canvas/sceneClipboard.js.map +1 -0
  152. package/dist/canvas/sceneSlotWiring.js +4 -4
  153. package/dist/canvas/sceneSlotWiring.js.map +1 -1
  154. package/dist/canvas/sceneViewRender.js +39 -40
  155. package/dist/canvas/sceneViewRender.js.map +1 -1
  156. package/dist/canvas/useCanvasRenderer.js +14 -6
  157. package/dist/canvas/useCanvasRenderer.js.map +1 -1
  158. package/dist/canvas/useCanvasView.js +58 -0
  159. package/dist/canvas/useCanvasView.js.map +1 -0
  160. package/dist/canvas/useFrameLoop.js +35 -26
  161. package/dist/canvas/useFrameLoop.js.map +1 -1
  162. package/dist/canvas/useSceneLayerList.js +21 -0
  163. package/dist/canvas/useSceneLayerList.js.map +1 -0
  164. package/dist/clipboard.d.ts +10 -6
  165. package/dist/clone.d.ts +4 -3
  166. package/dist/core/adapters/arrayAdapter.js +48 -39
  167. package/dist/core/adapters/arrayAdapter.js.map +1 -1
  168. package/dist/core/device/index.js +5 -4
  169. package/dist/core/device/useDeviceProfile.js +38 -44
  170. package/dist/core/device/useDeviceProfile.js.map +1 -1
  171. package/dist/core/device/watchDevicePixelRatio.js +17 -0
  172. package/dist/core/device/watchDevicePixelRatio.js.map +1 -0
  173. package/dist/core/fillInPoseFrame.js.map +1 -1
  174. package/dist/core/geometry/poseDescriptor.js +21 -19
  175. package/dist/core/geometry/poseDescriptor.js.map +1 -1
  176. package/dist/core/geometry/poseFrame.js +44 -0
  177. package/dist/core/geometry/poseFrame.js.map +1 -0
  178. package/dist/core/geometry/poseRotation.js.map +1 -1
  179. package/dist/core/geometry/regionHit.js +9 -10
  180. package/dist/core/geometry/regionHit.js.map +1 -1
  181. package/dist/core/geometry/unionBounds.js +31 -29
  182. package/dist/core/geometry/unionBounds.js.map +1 -1
  183. package/dist/core/isPlainObject.js +2 -11
  184. package/dist/core/ops/arrange.js +1 -0
  185. package/dist/core/ops/arrange.js.map +1 -1
  186. package/dist/core/ops/create.js +10 -5
  187. package/dist/core/ops/create.js.map +1 -1
  188. package/dist/core/ops/delete.js +12 -24
  189. package/dist/core/ops/delete.js.map +1 -1
  190. package/dist/core/ops/reorder/index.js +43 -40
  191. package/dist/core/ops/reorder/index.js.map +1 -1
  192. package/dist/core/ops/reorder/setChildOrder.js +34 -0
  193. package/dist/core/ops/reorder/setChildOrder.js.map +1 -0
  194. package/dist/core/paintKinds.js +5 -5
  195. package/dist/core/paintKinds.js.map +1 -1
  196. package/dist/core/scene/NodeProperties.js.map +1 -1
  197. package/dist/core/scene/appliedLog.js +37 -0
  198. package/dist/core/scene/appliedLog.js.map +1 -0
  199. package/dist/core/scene/arrivalWindow.js +101 -0
  200. package/dist/core/scene/arrivalWindow.js.map +1 -0
  201. package/dist/core/scene/derivedPath.js.map +1 -1
  202. package/dist/core/scene/historyFacade.js +84 -0
  203. package/dist/core/scene/historyFacade.js.map +1 -0
  204. package/dist/core/scene/kitOps.js +173 -0
  205. package/dist/core/scene/kitOps.js.map +1 -0
  206. package/dist/core/scene/layerStack.js +38 -0
  207. package/dist/core/scene/layerStack.js.map +1 -0
  208. package/dist/core/scene/lockGuard.js +37 -0
  209. package/dist/core/scene/lockGuard.js.map +1 -0
  210. package/dist/core/scene/nodeFnFields.js +5 -6
  211. package/dist/core/scene/nodeFnFields.js.map +1 -1
  212. package/dist/core/scene/nodeMemo.js +45 -19
  213. package/dist/core/scene/nodeMemo.js.map +1 -1
  214. package/dist/core/scene/nodeStore.js +126 -0
  215. package/dist/core/scene/nodeStore.js.map +1 -0
  216. package/dist/core/scene/pickability.js +6 -0
  217. package/dist/core/scene/pickability.js.map +1 -0
  218. package/dist/core/scene/poseFeed.js +38 -38
  219. package/dist/core/scene/poseFeed.js.map +1 -1
  220. package/dist/core/scene/poseOverrides.js +34 -38
  221. package/dist/core/scene/poseOverrides.js.map +1 -1
  222. package/dist/core/scene/renderOrder.js +84 -0
  223. package/dist/core/scene/renderOrder.js.map +1 -0
  224. package/dist/core/scene/scene.js +289 -866
  225. package/dist/core/scene/scene.js.map +1 -1
  226. package/dist/core/scene/serialization.js +68 -0
  227. package/dist/core/scene/serialization.js.map +1 -0
  228. package/dist/core/scene/treeOrder.js +13 -0
  229. package/dist/core/scene/treeOrder.js.map +1 -0
  230. package/dist/core/scene/types.js.map +1 -1
  231. package/dist/core/viewport/meanScale.js +1 -1
  232. package/dist/core/viewport/meanScale.js.map +1 -1
  233. package/dist/core/viewport/view.js +12 -12
  234. package/dist/core/viewport/view.js.map +1 -1
  235. package/dist/core/viewport/zoomBounds.js +10 -8
  236. package/dist/core/viewport/zoomBounds.js.map +1 -1
  237. package/dist/{cornerHandles-BCrDp_mH.d.ts → cornerHandles-CtCb8a3G.d.ts} +1 -1
  238. package/dist/debug/createDebugOverlayLayer.js +14 -14
  239. package/dist/debug/createDebugOverlayLayer.js.map +1 -1
  240. package/dist/debug/flag.js +2 -41
  241. package/dist/features/chrome-caps/useHoverTracking.js +22 -17
  242. package/dist/features/chrome-caps/useHoverTracking.js.map +1 -1
  243. package/dist/features/focus/gateLayer.js +3 -4
  244. package/dist/features/focus/gateLayer.js.map +1 -1
  245. package/dist/features/images/imageCache.js +19 -9
  246. package/dist/features/images/imageCache.js.map +1 -1
  247. package/dist/features/ingestion/contentHandlers.js +14 -11
  248. package/dist/features/ingestion/contentHandlers.js.map +1 -1
  249. package/dist/features/ingestion/index.js +1 -1
  250. package/dist/features/ingestion/registerKitHandlers.js +7 -7
  251. package/dist/features/ingestion/weaselJsonHandler.js +17 -14
  252. package/dist/features/ingestion/weaselJsonHandler.js.map +1 -1
  253. package/dist/features/meshPaint/lazyKind.js +201 -35
  254. package/dist/features/meshPaint/lazyKind.js.map +1 -1
  255. package/dist/features/meshPaint/meshPaint.js +82 -79
  256. package/dist/features/meshPaint/meshPaint.js.map +1 -1
  257. package/dist/features/minimap/createMinimapContribution.js +42 -42
  258. package/dist/features/minimap/layers.js +44 -62
  259. package/dist/features/minimap/layers.js.map +1 -1
  260. package/dist/features/parallax/createParallaxPlane.js +10 -14
  261. package/dist/features/parallax/createParallaxPlane.js.map +1 -1
  262. package/dist/features/paths/anchorEdits.js +19 -8
  263. package/dist/features/paths/anchorEdits.js.map +1 -1
  264. package/dist/features/paths/anchors.js +31 -26
  265. package/dist/features/paths/anchors.js.map +1 -1
  266. package/dist/features/paths/booleans.js +2 -2
  267. package/dist/features/paths/index.js +13 -11
  268. package/dist/features/paths/markerCommands.js +37 -36
  269. package/dist/features/paths/markerCommands.js.map +1 -1
  270. package/dist/features/paths/pathInWorld.js +19 -19
  271. package/dist/features/paths/pathInWorld.js.map +1 -1
  272. package/dist/features/paths/pathLayer.js.map +1 -1
  273. package/dist/features/paths/strokeToPath.js +40 -0
  274. package/dist/features/paths/strokeToPath.js.map +1 -0
  275. package/dist/features/paths/tessellate/stroke.js.map +1 -1
  276. package/dist/features/patterns/commandPaint.js +18 -0
  277. package/dist/features/patterns/commandPaint.js.map +1 -0
  278. package/dist/features/patterns/index.js +5 -17
  279. package/dist/features/patterns/index.js.map +1 -1
  280. package/dist/features/patterns/rasterizeTile.js +17 -0
  281. package/dist/features/patterns/rasterizeTile.js.map +1 -0
  282. package/dist/features/patterns/resolveSpec.js +52 -44
  283. package/dist/features/patterns/resolveSpec.js.map +1 -1
  284. package/dist/features/poseRun/usePoseRun.js +8 -3
  285. package/dist/features/poseRun/usePoseRun.js.map +1 -1
  286. package/dist/features/selection/SelectionContext.js +5 -0
  287. package/dist/features/selection/SelectionContext.js.map +1 -1
  288. package/dist/features/selection/overlay.js +18 -18
  289. package/dist/features/selection/overlay.js.map +1 -1
  290. package/dist/features/text/domOffsets.js +82 -0
  291. package/dist/features/text/domOffsets.js.map +1 -0
  292. package/dist/features/text/domRuns.js +103 -131
  293. package/dist/features/text/domRuns.js.map +1 -1
  294. package/dist/features/text/hitTest.js +5 -2
  295. package/dist/features/text/hitTest.js.map +1 -1
  296. package/dist/features/text/isTextData.js +8 -0
  297. package/dist/features/text/isTextData.js.map +1 -0
  298. package/dist/features/text/overlayDirection.js +15 -0
  299. package/dist/features/text/overlayDirection.js.map +1 -0
  300. package/dist/features/text/runs/rangeStyle.js +32 -34
  301. package/dist/features/text/runs/rangeStyle.js.map +1 -1
  302. package/dist/features/text/textCommand.js +34 -33
  303. package/dist/features/text/textCommand.js.map +1 -1
  304. package/dist/features/text/textToPath.js +40 -40
  305. package/dist/features/text/textToPath.js.map +1 -1
  306. package/dist/features/text/useSceneTextEdit.js +76 -82
  307. package/dist/features/text/useSceneTextEdit.js.map +1 -1
  308. package/dist/features/text/useTextEdit.js +184 -160
  309. package/dist/features/text/useTextEdit.js.map +1 -1
  310. package/dist/features/tiling/createTiledLayer.js +24 -22
  311. package/dist/features/tiling/createTiledLayer.js.map +1 -1
  312. package/dist/index.d.ts +11033 -7749
  313. package/dist/index.js +316 -309
  314. package/dist/input/keyState.js +42 -43
  315. package/dist/input/keyState.js.map +1 -1
  316. package/dist/input/useKeyModifiers.js +11 -0
  317. package/dist/input/useKeyModifiers.js.map +1 -0
  318. package/dist/interactions/actions/align/align.js +8 -8
  319. package/dist/interactions/actions/clipboard/clipboardOps.js +34 -32
  320. package/dist/interactions/actions/clipboard/clipboardOps.js.map +1 -1
  321. package/dist/interactions/actions/clipboard/materializePaste.js +10 -7
  322. package/dist/interactions/actions/clipboard/materializePaste.js.map +1 -1
  323. package/dist/interactions/actions/clipboard/wireFormat.js.map +1 -1
  324. package/dist/interactions/actions/clone/behaviors/cloneByAltDrag.js.map +1 -1
  325. package/dist/interactions/actions/defaultCommitAdapter.js +20 -18
  326. package/dist/interactions/actions/defaultCommitAdapter.js.map +1 -1
  327. package/dist/interactions/actions/defaults/align.js +9 -10
  328. package/dist/interactions/actions/defaults/align.js.map +1 -1
  329. package/dist/interactions/actions/defaults/anchorEditing.js +59 -81
  330. package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
  331. package/dist/interactions/actions/defaults/areaSelect.js +35 -38
  332. package/dist/interactions/actions/defaults/areaSelect.js.map +1 -1
  333. package/dist/interactions/actions/defaults/clipboard.js +26 -10
  334. package/dist/interactions/actions/defaults/clipboard.js.map +1 -1
  335. package/dist/interactions/actions/defaults/clone.js +44 -60
  336. package/dist/interactions/actions/defaults/clone.js.map +1 -1
  337. package/dist/interactions/actions/defaults/copySubtree.js +40 -0
  338. package/dist/interactions/actions/defaults/copySubtree.js.map +1 -0
  339. package/dist/interactions/actions/defaults/distribute.js +14 -13
  340. package/dist/interactions/actions/defaults/distribute.js.map +1 -1
  341. package/dist/interactions/actions/defaults/duplicate.js +17 -57
  342. package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
  343. package/dist/interactions/actions/defaults/editAnchors.js +18 -18
  344. package/dist/interactions/actions/defaults/enterTextEdit.js.map +1 -1
  345. package/dist/interactions/actions/defaults/flip.js +52 -46
  346. package/dist/interactions/actions/defaults/flip.js.map +1 -1
  347. package/dist/interactions/actions/defaults/group.js +43 -48
  348. package/dist/interactions/actions/defaults/group.js.map +1 -1
  349. package/dist/interactions/actions/defaults/ingest.js +35 -33
  350. package/dist/interactions/actions/defaults/ingest.js.map +1 -1
  351. package/dist/interactions/actions/defaults/insert.js +50 -51
  352. package/dist/interactions/actions/defaults/insert.js.map +1 -1
  353. package/dist/interactions/actions/defaults/insertPathAnchor.js +7 -7
  354. package/dist/interactions/actions/defaults/lassoSelect.js +67 -70
  355. package/dist/interactions/actions/defaults/lassoSelect.js.map +1 -1
  356. package/dist/interactions/actions/defaults/move.js +267 -249
  357. package/dist/interactions/actions/defaults/move.js.map +1 -1
  358. package/dist/interactions/actions/defaults/nudge.js +40 -68
  359. package/dist/interactions/actions/defaults/nudge.js.map +1 -1
  360. package/dist/interactions/actions/defaults/nudgeSteps.js +37 -0
  361. package/dist/interactions/actions/defaults/nudgeSteps.js.map +1 -0
  362. package/dist/interactions/actions/defaults/reorder.js +2 -0
  363. package/dist/interactions/actions/defaults/reorder.js.map +1 -1
  364. package/dist/interactions/actions/defaults/resize.js +104 -98
  365. package/dist/interactions/actions/defaults/resize.js.map +1 -1
  366. package/dist/interactions/actions/defaults/rotate.js +111 -110
  367. package/dist/interactions/actions/defaults/rotate.js.map +1 -1
  368. package/dist/interactions/actions/defaults/setFill.js +7 -7
  369. package/dist/interactions/actions/defaults/setFillOpacity.js +4 -4
  370. package/dist/interactions/actions/defaults/setStroke.js +12 -12
  371. package/dist/interactions/actions/defaults/setStrokeOpacity.js +6 -6
  372. package/dist/interactions/actions/defaults/sliceOps.js +19 -19
  373. package/dist/interactions/actions/defaults/viewportDragPan.js +37 -26
  374. package/dist/interactions/actions/defaults/viewportDragPan.js.map +1 -1
  375. package/dist/interactions/actions/edit-anchors/geometry.js +28 -23
  376. package/dist/interactions/actions/edit-anchors/geometry.js.map +1 -1
  377. package/dist/interactions/actions/flip/helpers.js +30 -17
  378. package/dist/interactions/actions/flip/helpers.js.map +1 -1
  379. package/dist/interactions/actions/hitSelection.js +8 -0
  380. package/dist/interactions/actions/hitSelection.js.map +1 -0
  381. package/dist/interactions/actions/lasso-select/behaviors/selectFromLasso.js +14 -18
  382. package/dist/interactions/actions/lasso-select/behaviors/selectFromLasso.js.map +1 -1
  383. package/dist/interactions/actions/move/behaviors/snapBackOrDelete.js +5 -5
  384. package/dist/interactions/actions/move/behaviors/snapBackOrDelete.js.map +1 -1
  385. package/dist/interactions/actions/move/behaviors/snapToContainer.js +32 -21
  386. package/dist/interactions/actions/move/behaviors/snapToContainer.js.map +1 -1
  387. package/dist/interactions/actions/move/gestureAdapter.js +32 -15
  388. package/dist/interactions/actions/move/gestureAdapter.js.map +1 -1
  389. package/dist/interactions/actions/move/landedDescendants.js +33 -0
  390. package/dist/interactions/actions/move/landedDescendants.js.map +1 -0
  391. package/dist/interactions/actions/outlines/createOutlines.js +18 -18
  392. package/dist/interactions/actions/poseDescriptorDep.js +16 -4
  393. package/dist/interactions/actions/poseDescriptorDep.js.map +1 -1
  394. package/dist/interactions/actions/poseFrame.js +11 -6
  395. package/dist/interactions/actions/poseFrame.js.map +1 -1
  396. package/dist/interactions/actions/previewOverrides.js +5 -5
  397. package/dist/interactions/actions/previewOverrides.js.map +1 -1
  398. package/dist/interactions/actions/selectionSubtree.js +69 -0
  399. package/dist/interactions/actions/selectionSubtree.js.map +1 -0
  400. package/dist/interactions/actions/useStandardActions.js +66 -62
  401. package/dist/interactions/actions/useStandardActions.js.map +1 -1
  402. package/dist/interactions/gestures/dragGesture/dragGesture.js +4 -7
  403. package/dist/interactions/gestures/dragGesture/dragGesture.js.map +1 -1
  404. package/dist/interactions/gestures/dragGesture/dragPointOf.js +23 -0
  405. package/dist/interactions/gestures/dragGesture/dragPointOf.js.map +1 -0
  406. package/dist/interactions/gestures/handleDrag/handleDrag.js +21 -26
  407. package/dist/interactions/gestures/handleDrag/handleDrag.js.map +1 -1
  408. package/dist/interactions/gestures/pointerDrag/pointerDrag.js +8 -6
  409. package/dist/interactions/gestures/pointerDrag/pointerDrag.js.map +1 -1
  410. package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js +8 -4
  411. package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js.map +1 -1
  412. package/dist/{math-Drrw4ewZ.d.ts → math-J-ACHyEg.d.ts} +1 -2
  413. package/dist/math.d.ts +2 -3
  414. package/dist/move.d.ts +12 -11
  415. package/dist/{originProjection-DEklpDc_.d.ts → originProjection-gHnOup4e.d.ts} +1 -1
  416. package/dist/{registerProgram-BMZRDgrh.d.ts → registerProgram-T9S_PfAX.d.ts} +18 -7
  417. package/dist/renderer/DrawCommand.js.map +1 -1
  418. package/dist/renderer/WeaselRenderer.js +75 -73
  419. package/dist/renderer/WeaselRenderer.js.map +1 -1
  420. package/dist/renderer/cache/GLImageCache.js +15 -5
  421. package/dist/renderer/cache/GLImageCache.js.map +1 -1
  422. package/dist/renderer/cache/GLMeshCache.js +23 -23
  423. package/dist/renderer/cache/GLMeshCache.js.map +1 -1
  424. package/dist/renderer/cache/GLTextureCache.js +18 -1
  425. package/dist/renderer/cache/GLTextureCache.js.map +1 -1
  426. package/dist/renderer/cache/cache.js.map +1 -1
  427. package/dist/renderer/cache/markerCommandCache.js.map +1 -1
  428. package/dist/renderer/cache/strokeMeshCache.js.map +1 -1
  429. package/dist/renderer/cullDrawCommands.js +11 -11
  430. package/dist/renderer/draw/batch.js +133 -0
  431. package/dist/renderer/draw/batch.js.map +1 -0
  432. package/dist/renderer/draw/clip.js +89 -0
  433. package/dist/renderer/draw/clip.js.map +1 -0
  434. package/dist/renderer/draw/customShader.js +230 -0
  435. package/dist/renderer/draw/customShader.js.map +1 -0
  436. package/dist/renderer/draw/drawTriangles.js +8 -0
  437. package/dist/renderer/draw/drawTriangles.js.map +1 -0
  438. package/dist/renderer/draw/effects.js +28 -0
  439. package/dist/renderer/draw/effects.js.map +1 -0
  440. package/dist/renderer/draw/flushBatch.js +35 -0
  441. package/dist/renderer/draw/flushBatch.js.map +1 -0
  442. package/dist/renderer/draw/gradient.js +90 -0
  443. package/dist/renderer/draw/gradient.js.map +1 -0
  444. package/dist/renderer/draw/image.js +38 -0
  445. package/dist/renderer/draw/image.js.map +1 -0
  446. package/dist/renderer/draw/mesh.js +16 -0
  447. package/dist/renderer/draw/mesh.js.map +1 -0
  448. package/dist/renderer/draw/paintBind.js +70 -0
  449. package/dist/renderer/draw/paintBind.js.map +1 -0
  450. package/dist/renderer/draw/pathPaint.js +142 -0
  451. package/dist/renderer/draw/pathPaint.js.map +1 -0
  452. package/dist/renderer/draw/scratchPass.js +83 -0
  453. package/dist/renderer/draw/scratchPass.js.map +1 -0
  454. package/dist/renderer/draw/stencil.js +28 -0
  455. package/dist/renderer/draw/stencil.js.map +1 -0
  456. package/dist/renderer/draw/text.js +167 -0
  457. package/dist/renderer/draw/text.js.map +1 -0
  458. package/dist/renderer/draw/uniforms.js +77 -0
  459. package/dist/renderer/draw/uniforms.js.map +1 -0
  460. package/dist/renderer/draw.js +60 -1116
  461. package/dist/renderer/draw.js.map +1 -1
  462. package/dist/renderer/effects/EffectTargets.js +3 -0
  463. package/dist/renderer/effects/EffectTargets.js.map +1 -1
  464. package/dist/renderer/effects/composite.js +7 -5
  465. package/dist/renderer/effects/composite.js.map +1 -1
  466. package/dist/renderer/index.js +18 -17
  467. package/dist/renderer/math/color.js +63 -83
  468. package/dist/renderer/math/color.js.map +1 -1
  469. package/dist/renderer/math/mat3.js +15 -11
  470. package/dist/renderer/math/mat3.js.map +1 -1
  471. package/dist/renderer/math/viewToMat3.js +1 -1
  472. package/dist/renderer/math/viewToMat3.js.map +1 -1
  473. package/dist/renderer/shaders/ShaderProgram.js +13 -13
  474. package/dist/renderer/shaders/ShaderProgram.js.map +1 -1
  475. package/dist/renderer/shaders/glyphPaint.js +3 -3
  476. package/dist/renderer/shaders/glyphPaint.js.map +1 -1
  477. package/dist/renderer/shaders/gradFill.js +2 -1
  478. package/dist/renderer/shaders/gradFill.js.map +1 -1
  479. package/dist/renderer/shaders/pathFill.js +2 -1
  480. package/dist/renderer/shaders/pathFill.js.map +1 -1
  481. package/dist/renderer/shaders/patternFill.js +2 -1
  482. package/dist/renderer/shaders/patternFill.js.map +1 -1
  483. package/dist/renderer/shaders/registerProgram.js.map +1 -1
  484. package/dist/renderer/strokeLengths.js.map +1 -1
  485. package/dist/renderer/textures/registerTexture.js +25 -5
  486. package/dist/renderer/textures/registerTexture.js.map +1 -1
  487. package/dist/renderer.d.ts +16 -15
  488. package/dist/renderer.js +17 -16
  489. package/dist/resize.d.ts +5 -6
  490. package/dist/resize.js +5 -5
  491. package/dist/style.css +1 -1
  492. package/dist/test-hook/install.js +12 -6
  493. package/dist/test-hook/install.js.map +1 -1
  494. package/dist/{test-seams-CuIxe0xY.d.ts → test-seams-DM2B87-g.d.ts} +4 -4
  495. package/dist/test-seams.d.ts +2 -2
  496. package/dist/tools/builtin/ellipse/useEllipseTool.js +1 -0
  497. package/dist/tools/builtin/ellipse/useEllipseTool.js.map +1 -1
  498. package/dist/tools/builtin/hand/useHandTool.js +1 -0
  499. package/dist/tools/builtin/hand/useHandTool.js.map +1 -1
  500. package/dist/tools/builtin/lasso/useLassoTool.js +11 -11
  501. package/dist/tools/builtin/lasso/useLassoTool.js.map +1 -1
  502. package/dist/tools/builtin/line/useLineTool.js +1 -0
  503. package/dist/tools/builtin/line/useLineTool.js.map +1 -1
  504. package/dist/tools/builtin/pen/usePenTool.js +153 -103
  505. package/dist/tools/builtin/pen/usePenTool.js.map +1 -1
  506. package/dist/tools/builtin/pencil/usePencilTool.js +1 -0
  507. package/dist/tools/builtin/pencil/usePencilTool.js.map +1 -1
  508. package/dist/tools/builtin/polygon/usePolygonTool.js +13 -8
  509. package/dist/tools/builtin/polygon/usePolygonTool.js.map +1 -1
  510. package/dist/tools/builtin/rect/useRectTool.js +1 -0
  511. package/dist/tools/builtin/rect/useRectTool.js.map +1 -1
  512. package/dist/tools/builtin/select/selectionContributions.js +24 -23
  513. package/dist/tools/builtin/select/selectionContributions.js.map +1 -1
  514. package/dist/tools/builtin/select/useSelectTool.js +51 -48
  515. package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
  516. package/dist/tools/builtin/shared/pointerFollower.js +17 -0
  517. package/dist/tools/builtin/shared/pointerFollower.js.map +1 -0
  518. package/dist/tools/builtin/slice/useSliceTool.js +49 -54
  519. package/dist/tools/builtin/slice/useSliceTool.js.map +1 -1
  520. package/dist/tools/builtin/star/useStarTool.js +22 -16
  521. package/dist/tools/builtin/star/useStarTool.js.map +1 -1
  522. package/dist/tools/builtin/text/useTextTool.js +1 -0
  523. package/dist/tools/builtin/text/useTextTool.js.map +1 -1
  524. package/dist/tools/useKeybindings.js +14 -30
  525. package/dist/tools/useKeybindings.js.map +1 -1
  526. package/dist/{types-B_9g6hMM.d.ts → types-B4iJRHC3.d.ts} +5 -1
  527. package/dist/{types-A37hYkeR.d.ts → types-k-5Oqh-U.d.ts} +31 -7
  528. package/dist/util/color.js +3 -3
  529. package/dist/util/color.js.map +1 -1
  530. package/dist/util/paint.js.map +1 -1
  531. package/dist/version.js +1 -1
  532. package/package.json +20 -18
  533. package/dist/autoPoseDescriptor-vCdt55xm.d.ts +0 -27
  534. package/dist/builtins-C-VgUlWI.d.ts +0 -4514
  535. package/dist/core/isPlainObject.js.map +0 -1
  536. package/dist/debug/flag.js.map +0 -1
  537. package/dist/interactions/actions/defaults/freshNodeId.js +0 -10
  538. package/dist/interactions/actions/defaults/freshNodeId.js.map +0 -1
  539. package/dist/renderer/shaders/imageFill.js +0 -14
  540. package/dist/renderer/shaders/imageFill.js.map +0 -1
  541. package/dist/tools/prefs.js +0 -62
  542. package/dist/tools/prefs.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"slice.js","names":["useSliceDep","depRef","useLatest","dep","useSliceDepSource","sceneRef","scene","selectionRef","selection","adapterRef","adapter","compositionRef","poseComposition","descriptorRef","poseDescriptor","sc","frame","scenePoseFrame","descriptor","AUTO_POSE_DESCRIPTOR","leaves","drawnAt","node","path","sliceablePath","drawn","drawnPose","poseDescriptorForNode","siblingIndex","pathInWorld","sel","computeSliceOps","freshNodeId","stored","storePiece","leaf","piece","ops","nextSelection","b","g","world","rotation","keepsRotation","box","baked","worldEditToStorage","dst","pose"],"sources":["../../../src/canvas/deps/slice.ts"],"sourcesContent":["/**\n * The `slice` dep `sliceAction` and `useSliceTool` commit their cuts through.\n *\n * `<SceneCanvas>` publishes `useSliceDepSource` over its own scene. A consumer\n * replaces it with `useSliceDep` (or `useDepSource('slice', …)`) from under\n * the canvas; the newest registration wins, and a canvas child registers\n * after the canvas's own.\n */\nimport { useDepSource } from '@weasel-js/routing/react';\nimport { useLatest } from '@weasel-js/react';\nimport type { SliceDep } from 'interactions/actions/depSchema';\nimport type { Scene, NodeId, Node } from 'core/scene/types';\nimport type { SelectionApi } from 'core/selection/useSelection';\nimport type { Op } from 'core/ops/types';\nimport type { Path } from '@weasel-js/geom';\nimport { AUTO_POSE_DESCRIPTOR } from 'interactions/actions/resize/autoPoseDescriptor';\nimport { poseDescriptorForNode, type PoseDescriptor } from 'core/geometry/poseDescriptor';\nimport { pathInWorld, worldEditToStorage, type PathInWorldPose } from 'features/paths/pathInWorld';\nimport { scenePoseFrame } from 'interactions/actions/poseFrame';\nimport { freshNodeId } from 'interactions/actions/defaults/freshNodeId';\nimport { computeSliceOps, type SliceLeaf } from 'interactions/actions/defaults/sliceOps';\nimport type { PoseComposition } from 'features/groups/composePose';\n\n/** Publish how a slice (knife cut) is performed, so the `slice` action can\n * run against the consumer's geometry. */\nexport function useSliceDep(dep: SliceDep): void {\n const depRef = useLatest(dep);\n\n useDepSource('slice', (): SliceDep => depRef.current);\n}\n\ninterface OpsApplier {\n applyOps(ops: Op[], label?: string): void;\n}\n\n/**\n * The kit `slice` dep: cut every leaf the cut crosses and swap it for its\n * pieces in one undoable batch.\n *\n * A leaf is cut when its `data.path` is a `Path` and `poseDescriptor` can read\n * its world pose as a box and a rotation; a leaf on a locked layer, or one\n * whose geometry derives from other nodes, is passed over. Each path is drawn\n * into world space the way the renderer draws it, so a child is cut where it\n * appears, and each piece is mapped back into the source's frame: it keeps the\n * source's rotation in its pose when the descriptor can write one\n * (`withRotation`), and has the rotation baked into its path otherwise. The\n * piece's pose is then stored in its parent's frame through `poseComposition`.\n * The pieces of a selected leaf take its place in the selection.\n */\nexport function useSliceDepSource(\n scene: Scene<unknown, string, unknown>,\n selection: SelectionApi,\n adapter: OpsApplier,\n poseComposition?: PoseComposition<unknown>,\n poseDescriptor?: PoseDescriptor<unknown>,\n): void {\n const sceneRef = useLatest(scene);\n const selectionRef = useLatest(selection);\n const adapterRef = useLatest(adapter);\n const compositionRef = useLatest(poseComposition);\n const descriptorRef = useLatest(poseDescriptor);\n\n useDepSource('slice', (): SliceDep => ({\n commit(cut) {\n const sc = sceneRef.current;\n const frame = scenePoseFrame(sc, compositionRef.current);\n const descriptor = descriptorRef.current ?? AUTO_POSE_DESCRIPTOR;\n const leaves: SliceLeaf<Node<unknown, string, unknown>>[] = [];\n const drawnAt = new Map<string, DrawnPose>();\n for (const node of sc.renderOrderNodes()) {\n const path = sliceablePath(sc, node);\n if (!path) continue;\n const drawn = drawnPose(poseDescriptorForNode(descriptor, node), frame.world(node.id));\n if (!drawn) continue;\n drawnAt.set(node.id, drawn);\n leaves.push({ node, index: siblingIndex(sc, node), worldPath: pathInWorld(path, drawn.box) });\n }\n\n const sel = selectionRef.current;\n const { ops, nextSelection } = computeSliceOps({\n leaves,\n cut,\n nextId: freshNodeId,\n selection: sel.get(),\n placePiece: (leaf, piece) => {\n const stored = storePiece(drawnAt.get(leaf.node.id)!, piece);\n return { pose: frame.local(leaf.node.id, stored.pose), path: stored.path };\n },\n });\n if (ops.length === 0) return;\n adapterRef.current.applyOps(ops, 'Slice');\n if (nextSelection) sel.set(nextSelection as NodeId[]);\n },\n }));\n}\n\n/** A leaf's world pose, and the box and rotation its path is drawn through. */\ninterface DrawnPose {\n world: unknown;\n g: PoseDescriptor<unknown>;\n box: PathInWorldPose;\n}\n\nfunction drawnPose(g: PoseDescriptor<unknown>, world: unknown): DrawnPose | null {\n const b = g.getBounds(world);\n if (!b || ![b.x, b.y, b.width, b.height].every(Number.isFinite)) return null;\n const rotation = g.getRotation?.(world) ?? 0;\n return { world, g, box: { x: b.x, y: b.y, width: b.width, height: b.height, rotation } };\n}\n\n/** A world-space piece as a world pose of the source's kind plus the path it draws. */\nfunction storePiece({ world, g, box }: DrawnPose, piece: Path): { pose: unknown; path: Path } {\n const keepsRotation = !!g.withRotation && g.supportsRotation?.(world) !== false;\n const rotation = box.rotation ?? 0;\n if (rotation !== 0 && !keepsRotation) {\n const baked = worldEditToStorage({ ...box, rotation: 0 }, piece);\n return { pose: g.fromBounds(baked.pose, world), path: baked.path };\n }\n const { pose: b, path } = worldEditToStorage(box, piece);\n const dst = { x: b.x, y: b.y, width: b.width, height: b.height };\n const pose = g.remapBounds(world, g.getBounds(world), dst);\n return { pose: rotation !== 0 ? g.withRotation!(pose, rotation) : pose, path };\n}\n\nfunction sliceablePath(scene: Scene<unknown, string, unknown>, node: Node<unknown, string, unknown>): Path | null {\n if (node.kind !== 'leaf' || node.derivePath || node.derivePose) return null;\n if (scene.isLocked(node.id)) return null;\n const path = (node.data as { path?: unknown } | null)?.path as Path | undefined;\n return path && (path.kind === 'rect' || path.kind === 'polygon') ? path : null;\n}\n\nfunction siblingIndex(scene: Scene<unknown, string, unknown>, node: Node<unknown, string, unknown>): number {\n const siblings = node.parent == null ? scene.roots : scene.childrenOf(node.parent);\n return siblings.indexOf(node.id);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"slice.js","names":["useSliceDep","depRef","useLatest","dep","useSliceDepSource","sceneRef","scene","selectionRef","selection","adapterRef","adapter","compositionRef","poseComposition","descriptorRef","poseDescriptor","sc","frame","scenePoseFrame","descriptor","AUTO_POSE_DESCRIPTOR","leaves","drawnAt","node","path","sliceablePath","drawn","drawnPose","poseDescriptorForNode","siblingIndex","pathInWorld","sel","computeSliceOps","stored","storePiece","leaf","piece","ops","nextSelection","b","g","world","rotation","transform","poseLinearOf","keepsRotation","box","baked","worldEditToStorage","dst","pose"],"sources":["../../../src/canvas/deps/slice.ts"],"sourcesContent":["/**\n * The `slice` dep `sliceAction` and `useSliceTool` commit their cuts through.\n *\n * `<SceneCanvas>` publishes `useSliceDepSource` over its own scene. A consumer\n * replaces it with `useSliceDep` (or `useDepSource('slice', …)`) from under\n * the canvas; the newest registration wins, and a canvas child registers\n * after the canvas's own.\n */\nimport { useDepSource } from '@weasel-js/routing/react';\nimport { useLatest } from '@weasel-js/react';\nimport type { SliceDep } from 'interactions/actions/depSchema';\nimport type { Scene, NodeId, Node } from 'core/scene/types';\nimport type { SelectionApi } from 'core/selection/useSelection';\nimport type { Op } from 'core/ops/types';\nimport type { Path } from '@weasel-js/geom';\nimport { AUTO_POSE_DESCRIPTOR } from 'interactions/actions/resize/autoPoseDescriptor';\nimport { poseDescriptorForNode, type PoseDescriptor } from 'core/geometry/poseDescriptor';\nimport { pathInWorld, worldEditToStorage, type PathInWorldPose } from 'features/paths/pathInWorld';\nimport { poseLinearOf } from 'core/geometry/poseFrame';\nimport { scenePoseFrame } from 'interactions/actions/poseFrame';\nimport { computeSliceOps, type SliceLeaf } from 'interactions/actions/defaults/sliceOps';\nimport type { PoseComposition } from 'features/groups/composePose';\n\n/** Publish how a slice (knife cut) is performed, so the `slice` action can\n * run against the consumer's geometry. */\nexport function useSliceDep(dep: SliceDep): void {\n const depRef = useLatest(dep);\n\n useDepSource('slice', (): SliceDep => depRef.current);\n}\n\ninterface OpsApplier {\n applyOps(ops: Op[], label?: string): void;\n}\n\n/**\n * The kit `slice` dep: cut every leaf the cut crosses and swap it for its\n * pieces in one undoable batch.\n *\n * A leaf is cut when its `data.path` is a `Path` and `poseDescriptor` can read\n * its world pose as a box and a rotation; a leaf on a locked layer, or one\n * whose geometry derives from other nodes, is passed over. Each path is drawn\n * into world space the way the renderer draws it, so a child is cut where it\n * appears, and each piece is mapped back into the source's frame: it keeps the\n * source's rotation in its pose when the descriptor can write one\n * (`withRotation`), and has the rotation baked into its path otherwise. The\n * piece's pose is then stored in its parent's frame through `poseComposition`.\n * The pieces of a selected leaf take its place in the selection.\n */\nexport function useSliceDepSource(\n scene: Scene<unknown, string, unknown>,\n selection: SelectionApi,\n adapter: OpsApplier,\n poseComposition?: PoseComposition<unknown>,\n poseDescriptor?: PoseDescriptor<unknown>,\n): void {\n const sceneRef = useLatest(scene);\n const selectionRef = useLatest(selection);\n const adapterRef = useLatest(adapter);\n const compositionRef = useLatest(poseComposition);\n const descriptorRef = useLatest(poseDescriptor);\n\n useDepSource('slice', (): SliceDep => ({\n commit(cut) {\n const sc = sceneRef.current;\n const frame = scenePoseFrame(sc, compositionRef.current);\n const descriptor = descriptorRef.current ?? AUTO_POSE_DESCRIPTOR;\n const leaves: SliceLeaf<Node<unknown, string, unknown>>[] = [];\n const drawnAt = new Map<string, DrawnPose>();\n for (const node of sc.renderOrderNodes()) {\n const path = sliceablePath(sc, node);\n if (!path) continue;\n const drawn = drawnPose(poseDescriptorForNode(descriptor, node), frame.world(node.id));\n if (!drawn) continue;\n drawnAt.set(node.id, drawn);\n leaves.push({ node, index: siblingIndex(sc, node), worldPath: pathInWorld(path, drawn.box) });\n }\n\n const sel = selectionRef.current;\n const { ops, nextSelection } = computeSliceOps({\n leaves,\n cut,\n nextId: () => sc.mintId(),\n selection: sel.get(),\n placePiece: (leaf, piece) => {\n const stored = storePiece(drawnAt.get(leaf.node.id)!, piece);\n return { pose: frame.local(leaf.node.id, stored.pose), path: stored.path };\n },\n });\n if (ops.length === 0) return;\n adapterRef.current.applyOps(ops, 'Slice');\n if (nextSelection) sel.set(nextSelection as NodeId[]);\n },\n }));\n}\n\n/** A leaf's world pose, and the box, rotation, and transform its path is drawn through. */\ninterface DrawnPose {\n world: unknown;\n g: PoseDescriptor<unknown>;\n box: PathInWorldPose;\n}\n\nfunction drawnPose(g: PoseDescriptor<unknown>, world: unknown): DrawnPose | null {\n const b = g.getBounds(world);\n if (!b || ![b.x, b.y, b.width, b.height].every(Number.isFinite)) return null;\n const rotation = g.getRotation?.(world) ?? 0;\n const transform = poseLinearOf(world) ?? undefined;\n return { world, g, box: { x: b.x, y: b.y, width: b.width, height: b.height, rotation, transform } };\n}\n\n/** A world-space piece as a world pose of the source's kind plus the path it draws. */\nfunction storePiece({ world, g, box }: DrawnPose, piece: Path): { pose: unknown; path: Path } {\n const keepsRotation = !!g.withRotation && g.supportsRotation?.(world) !== false;\n const rotation = box.rotation ?? 0;\n if (rotation !== 0 && !keepsRotation) {\n const baked = worldEditToStorage({ ...box, rotation: 0 }, piece);\n return { pose: g.fromBounds(baked.pose, world), path: baked.path };\n }\n const { pose: b, path } = worldEditToStorage(box, piece);\n const dst = { x: b.x, y: b.y, width: b.width, height: b.height };\n const pose = g.remapBounds(world, g.getBounds(world), dst);\n return { pose: rotation !== 0 ? g.withRotation!(pose, rotation) : pose, path };\n}\n\nfunction sliceablePath(scene: Scene<unknown, string, unknown>, node: Node<unknown, string, unknown>): Path | null {\n if (node.kind !== 'leaf' || node.derivePath || node.derivePose) return null;\n if (scene.isLocked(node.id)) return null;\n const path = (node.data as { path?: unknown } | null)?.path as Path | undefined;\n return path && (path.kind === 'rect' || path.kind === 'polygon') ? path : null;\n}\n\nfunction siblingIndex(scene: Scene<unknown, string, unknown>, node: Node<unknown, string, unknown>): number {\n const siblings = node.parent == null ? scene.roots : scene.childrenOf(node.parent);\n return siblings.indexOf(node.id);\n}\n"],"mappings":"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"}
@@ -1,21 +1,22 @@
1
- import { useLatest as e } from "@weasel-js/react";
2
- import { useDepSource as t } from "@weasel-js/routing/react";
1
+ import { isTextData as e } from "../../features/text/isTextData.js";
2
+ import { useLatest as t } from "@weasel-js/react";
3
+ import { useDepSource as n } from "@weasel-js/routing/react";
3
4
  //#region src/canvas/deps/textEdit.ts
4
- function n(n) {
5
- let r = e(n);
6
- t("textEdit", () => {
7
- let e = r.current;
5
+ function r(r) {
6
+ let i = t(r);
7
+ n("textEdit", () => {
8
+ let t = i.current;
8
9
  return {
9
10
  startEdit(e, t) {
10
11
  process.env.NODE_ENV !== "production" && console.warn("weasel textEditDep: startEdit called but no useTextEdit hook is mounted in SceneCanvas. Override the \"textEdit\" dep via useDepSource(\"textEdit\", ...) to enable in-place text editing.");
11
12
  },
12
- isTextNode(t) {
13
- return e.get(t)?.data?.kind === "text";
13
+ isTextNode(n) {
14
+ return e(t.get(n)?.data);
14
15
  }
15
16
  };
16
17
  });
17
18
  }
18
19
  //#endregion
19
- export { n as useTextEditDepSource };
20
+ export { r as useTextEditDepSource };
20
21
 
21
22
  //# sourceMappingURL=textEdit.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"textEdit.js","names":["useTextEditDepSource","sceneRef","useLatest","scene","sc","id"],"sources":["../../../src/canvas/deps/textEdit.ts"],"sourcesContent":["/**\n * `useTextEditDepSource` — wires the `textEdit` dep consumed by\n * `enterTextEditAction`. Kit-default implementation is a no-op stub for\n * `startEdit` plus a convention-based `isTextNode` predicate\n * (`data.kind === 'text'`). Consumers wanting real text editing override the\n * dep via their own `useDepSource('textEdit', ...)` sourced from a\n * `useSceneTextEdit` instance.\n */\nimport { useDepSource } from '@weasel-js/routing/react';\nimport { useLatest } from '@weasel-js/react';\nimport type { TextEditDep } from 'interactions/actions/depSchema';\nimport type { Scene, NodeId } from 'core/scene/types';\n\nexport function useTextEditDepSource(\n scene: Scene<unknown, string, unknown>,\n): void {\n const sceneRef = useLatest(scene);\n\n useDepSource('textEdit', (): TextEditDep => {\n const sc = sceneRef.current;\n return {\n startEdit(_id: string, _opts?: { caret?: number | 'all' }) {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n 'weasel textEditDep: startEdit called but no useTextEdit hook is mounted in SceneCanvas. ' +\n 'Override the \"textEdit\" dep via useDepSource(\"textEdit\", ...) to enable in-place text editing.',\n );\n }\n },\n isTextNode(id: string) {\n const node = sc.get(id as NodeId);\n return (node?.data as { kind?: string } | null | undefined)?.kind === 'text';\n },\n };\n });\n}\n"],"mappings":";;;AAaA,SAAgBA,EACd,GACM;CACN,IAAMC,IAAWC,EAAUC,CAAK;CAEhC,EAAa,kBAA+B;EAC1C,IAAMC,IAAKH,EAAS;EACpB,OAAO;GACL,UAAU,GAAa,GAAoC;IACzD,AAAA,QAAA,IAAA,aAA6B,gBAC3B,QAAQ,KACN,4LAEF;GAEJ;GACA,WAAW,GAAY;IAErB,OADaG,EAAG,IAAIC,CACZ,CAAA,EAAM,MAA+C,SAAS;GACxE;EACF;CACF,CAAC;AACH"}
1
+ {"version":3,"file":"textEdit.js","names":["useTextEditDepSource","sceneRef","useLatest","scene","sc","isTextData","id"],"sources":["../../../src/canvas/deps/textEdit.ts"],"sourcesContent":["/**\n * `useTextEditDepSource` — wires the `textEdit` dep consumed by\n * `enterTextEditAction`. Kit-default implementation is a no-op stub for\n * `startEdit` plus an `isTextNode` that asks `isTextData`. Consumers wanting real text editing override the\n * dep via their own `useDepSource('textEdit', ...)` sourced from a\n * `useSceneTextEdit` instance.\n */\nimport { useDepSource } from '@weasel-js/routing/react';\nimport { useLatest } from '@weasel-js/react';\nimport type { TextEditDep } from 'interactions/actions/depSchema';\nimport type { Scene, NodeId } from 'core/scene/types';\nimport { isTextData } from 'features/text/isTextData';\n\nexport function useTextEditDepSource(\n scene: Scene<unknown, string, unknown>,\n): void {\n const sceneRef = useLatest(scene);\n\n useDepSource('textEdit', (): TextEditDep => {\n const sc = sceneRef.current;\n return {\n startEdit(_id: string, _opts?: { caret?: number | 'all' }) {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n 'weasel textEditDep: startEdit called but no useTextEdit hook is mounted in SceneCanvas. ' +\n 'Override the \"textEdit\" dep via useDepSource(\"textEdit\", ...) to enable in-place text editing.',\n );\n }\n },\n isTextNode(id: string) {\n return isTextData(sc.get(id as NodeId)?.data);\n },\n };\n });\n}\n"],"mappings":";;;;AAaA,SAAgBA,EACd,GACM;CACN,IAAMC,IAAWC,EAAUC,CAAK;CAEhC,EAAa,kBAA+B;EAC1C,IAAMC,IAAKH,EAAS;EACpB,OAAO;GACL,UAAU,GAAa,GAAoC;IACzD,AAAA,QAAA,IAAA,aAA6B,gBAC3B,QAAQ,KACN,4LAEF;GAEJ;GACA,WAAW,GAAY;IACrB,OAAOI,EAAWD,EAAG,IAAIE,CAAY,CAAC,EAAE,IAAI;GAC9C;EACF;CACF,CAAC;AACH"}
@@ -1,20 +1,41 @@
1
- import { resolveDerivedPath as e } from "../core/scene/derivedPath.js";
2
- import { derivedDepOf as t } from "../core/scene/effectivePose.js";
1
+ import { createNodeMemoScope as e } from "../core/scene/nodeMemo.js";
2
+ import { composeWorldPose as t } from "../core/scene/composeFrame.js";
3
+ import { resolveDerivedPath as n } from "../core/scene/derivedPath.js";
4
+ import { definesFrame as r, derivedDepOf as i, effectivePose as a } from "../core/scene/effectivePose.js";
5
+ import { asNodeId as o } from "../core/scene/types.js";
3
6
  //#region src/canvas/derivedPath.ts
4
- function n(e, n) {
5
- if (n === void 0) return (n) => t(e, n);
6
- let i = {
7
- at: n,
8
- slot: s(n),
7
+ var s = (e) => e !== void 0 && e.closure !== "identity";
8
+ function c(e, n, i) {
9
+ let c = n ?? ((t) => a(e, t));
10
+ if (!s(i)) return c;
11
+ let l = {
12
+ getPose: (t) => {
13
+ let n = e.get(o(t));
14
+ return n ? c(n) : void 0;
15
+ },
16
+ getParent: (t) => e.get(o(t))?.parent ?? null,
17
+ definesFrame: (t) => {
18
+ let n = e.get(o(t));
19
+ return n === void 0 || r(n);
20
+ }
21
+ };
22
+ return (e) => t(l, e.id, i.compose);
23
+ }
24
+ function l(e, t, n) {
25
+ let r = p(t, n);
26
+ if (r === void 0) return (t) => i(e, t);
27
+ let a = {
28
+ at: c(e, t, n),
29
+ slot: r,
9
30
  childrenOf: (t) => e.childrenOf(t),
10
31
  lookup: (t) => {
11
32
  let n = e.get(t);
12
- return n === void 0 ? void 0 : new r(n, i);
33
+ return n === void 0 ? void 0 : new u(n, a);
13
34
  }
14
35
  };
15
- return i.lookup;
36
+ return a.lookup;
16
37
  }
17
- var r = class {
38
+ var u = class {
18
39
  ctx;
19
40
  node;
20
41
  pose;
@@ -22,26 +43,28 @@ var r = class {
22
43
  this.ctx = t, this.node = e, this.pose = t.at(e);
23
44
  }
24
45
  get path() {
25
- return e(this.node, this.ctx.lookup, this.ctx.childrenOf, this.ctx.slot);
46
+ return n(this.node, this.ctx.lookup, this.ctx.childrenOf, this.ctx.slot);
26
47
  }
27
48
  };
28
- function i(t, r) {
29
- let i = n(t, r), a = r === void 0 ? void 0 : s(r), o = (e) => t.childrenOf(e);
30
- return (t) => e(t, i, o, a);
49
+ function d(e, t, r) {
50
+ let i = l(e, t, r), a = p(t, r), o = (t) => e.childrenOf(t);
51
+ return (e) => n(e, i, o, a);
31
52
  }
32
- var a = /* @__PURE__ */ new WeakMap(), o = 0;
33
- function s(e) {
34
- let t = a.get(e);
35
- return t === void 0 && (t = `kit:derivedPath:toPose:${o++}`, a.set(e, t)), t;
53
+ var f = /* @__PURE__ */ new WeakMap();
54
+ function p(t, n) {
55
+ let r = s(n) ? "world" : "local";
56
+ if (t === void 0) return r === "world" ? "kit:derivedPath:world" : void 0;
57
+ let i = f.get(t);
58
+ return i === void 0 && (i = {}, f.set(t, i)), i[r] ??= e();
36
59
  }
37
- function c(e, t, n) {
38
- let r = i(e, n);
39
- return (e, n, i, a) => e.dependsOn === void 0 ? t(e, n, i, a) : t(e, n, i, {
60
+ function m(e, t, n, r) {
61
+ let i = d(e, n, r);
62
+ return (e, n, r, a) => e.dependsOn === void 0 ? t(e, n, r, a) : t(e, n, r, {
40
63
  ...a,
41
- derivedPath: r(e)
64
+ derivedPath: i(e)
42
65
  });
43
66
  }
44
67
  //#endregion
45
- export { e as resolveDerivedPath, n as sceneDepLookup, i as sceneDerivedPathOf, c as withDerivedPaths };
68
+ export { n as resolveDerivedPath, l as sceneDepLookup, d as sceneDerivedPathOf, c as scenePaintedPoseOf, m as withDerivedPaths };
46
69
 
47
70
  //# sourceMappingURL=derivedPath.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"derivedPath.js","names":["sceneDepLookup","toPose","derivedDepOf","scene","id","ctx","memoSlotFor","node","SlotDep","posed","resolveDerivedPath","sceneDerivedPathOf","depOf","slot","childrenOf","toPoseSlots","nextToPoseSlot","withDerivedPaths","derivedPathOf","drawOne","pose","view"],"sources":["../../src/canvas/derivedPath.ts"],"sourcesContent":["/**\n * The render walk's half of derived paths: resolving one against a scene, and\n * handing it to the painter.\n *\n * The resolver itself lives in `core/scene/derivedPath.ts`, beside\n * `derivedPose` — a *pose* can derive from a dependency's path, so\n * `effectivePose` has to be able to reach it.\n */\nimport type { DerivedDep, Node, NodeId, Scene } from 'core/scene/types';\nimport type { Path } from '@weasel-js/geom';\nimport { derivedDepOf } from 'core/scene/effectivePose';\nimport { resolveDerivedPath } from 'core/scene/derivedPath';\nimport type { SceneViewDrawOne } from './NodeShape';\n\nexport { resolveDerivedPath } from 'core/scene/derivedPath';\n\n/**\n * `(id) => that node, the pose it is painted at, and the path it derives` —\n * resolved against the scene, so a dependency that is itself derived resolves\n * before it is read.\n *\n * `Scene` stores absolute poses and the render walks hand `getPose` straight to\n * `drawOne`, composing nothing, so this has to read exactly what the render\n * adapters read: a derived edge is drawn from these coordinates and must meet\n * the nodes it connects. A slot's `toPose` replaces that read, so `toPose`\n * replaces it here too.\n */\nexport function sceneDepLookup<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n toPose?: (node: Node<TData, TLayer, TPose>) => TPose,\n): (id: NodeId) => DerivedDep<TPose> | undefined {\n if (toPose === undefined) return (id) => derivedDepOf(scene, id);\n const ctx: SlotLookup<TPose> = {\n at: toPose as (node: unknown) => TPose,\n slot: memoSlotFor(toPose),\n childrenOf: (id: NodeId) => scene.childrenOf(id),\n lookup: (id: NodeId) => {\n const node = scene.get(id);\n return node === undefined ? undefined : new SlotDep(node, ctx);\n },\n };\n return ctx.lookup;\n}\n\ninterface SlotLookup<TPose> {\n at: (node: unknown) => TPose;\n slot: string;\n childrenOf: (id: NodeId) => readonly NodeId[];\n lookup: (id: NodeId) => DerivedDep<TPose> | undefined;\n}\n\n/** A class, not an object literal with a `get path()` — see `SceneDep` in\n * `core/scene/effectivePose.ts` for why. */\nclass SlotDep<TPose> implements DerivedDep<TPose> {\n readonly node: Node<unknown, string, TPose>;\n readonly pose: TPose;\n\n constructor(posed: unknown, private readonly ctx: SlotLookup<TPose>) {\n this.node = posed as Node<unknown, string, TPose>;\n this.pose = ctx.at(posed);\n }\n\n get path(): Path | null {\n return resolveDerivedPath(\n this.node as never, this.ctx.lookup, this.ctx.childrenOf, this.ctx.slot,\n );\n }\n}\n\n/** `(node) => the path it derives`, read through {@link sceneDepLookup}. */\nexport function sceneDerivedPathOf<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n toPose?: (node: Node<TData, TLayer, TPose>) => TPose,\n): (node: Node<TData, TLayer, TPose>) => Path | null {\n const depOf = sceneDepLookup(scene, toPose);\n const slot = toPose === undefined ? undefined : memoSlotFor(toPose);\n const childrenOf = (id: NodeId): readonly NodeId[] => scene.childrenOf(id);\n return (node) => resolveDerivedPath(node, depOf, childrenOf, slot);\n}\n\nconst toPoseSlots = new WeakMap<object, string>();\nlet nextToPoseSlot = 0;\n\n/** One memo slot per `toPose`: two slots painting one scene at different\n * poses must not serve each other's paths. */\nfunction memoSlotFor(toPose: object): string {\n let slot = toPoseSlots.get(toPose);\n if (slot === undefined) {\n slot = `kit:derivedPath:toPose:${nextToPoseSlot++}`;\n toPoseSlots.set(toPose, slot);\n }\n return slot;\n}\n\n/**\n * Wrap a `drawOne` so a node that derives its geometry arrives at the painter\n * with the resolved path. The painter has no scene handle and so cannot read\n * the dependencies' poses itself; both scene walks wrap here instead.\n *\n * A node that derives nothing passes the caller's `ctx` through untouched —\n * this runs per node per frame, and an added `{ derivedPath: null }` would be\n * an allocation per node for a field no painter reads.\n */\nexport function withDerivedPaths<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n drawOne: SceneViewDrawOne<TData, TLayer, TPose>,\n toPose?: (node: Node<TData, TLayer, TPose>) => TPose,\n): SceneViewDrawOne<TData, TLayer, TPose> {\n const derivedPathOf = sceneDerivedPathOf(scene, toPose);\n return (node, pose, view, ctx) => {\n if (node.dependsOn === undefined) return drawOne(node, pose, view, ctx);\n return drawOne(node, pose, view, { ...ctx, derivedPath: derivedPathOf(node) });\n };\n}\n"],"mappings":";;;AA2BA,SAAgBA,EACd,GACA,GAC+C;CAC/C,IAAIC,MAAW,KAAA,GAAW,QAAQ,MAAOC,EAAaC,GAAOC,CAAE;CAC/D,IAAMC,IAAyB;EAC7B,IAAIJ;EACJ,MAAMK,EAAYL,CAAM;EACxB,aAAa,MAAeE,EAAM,WAAWC,CAAE;EAC/C,SAAS,MAAe;GACtB,IAAMG,IAAOJ,EAAM,IAAIC,CAAE;GACzB,OAAOG,MAAS,KAAA,IAAY,KAAA,IAAY,IAAIC,EAAQD,GAAMF,CAAG;EAC/D;CACF;CACA,OAAOA,EAAI;AACb;AAWA,IAAMG,IAAN,MAAkD;CAIH;CAH7C;CACA;CAEA,YAAY,GAAgB,GAAyC;EAEnE,AAF2C,KAAA,MAAAH,GAC3C,KAAK,OAAOI,GACZ,KAAK,OAAOJ,EAAI,GAAGI,CAAK;CAC1B;CAEA,IAAI,OAAoB;EACtB,OAAOC,EACL,KAAK,MAAe,KAAK,IAAI,QAAQ,KAAK,IAAI,YAAY,KAAK,IAAI,IACrE;CACF;AACF;AAGA,SAAgBC,EACd,GACA,GACmD;CACnD,IAAMC,IAAQZ,EAAeG,GAAOF,CAAM,GACpCY,IAAOZ,MAAW,KAAA,IAAY,KAAA,IAAYK,EAAYL,CAAM,GAC5Da,KAAc,MAAkCX,EAAM,WAAWC,CAAE;CACzE,QAAQ,MAASM,EAAmBH,GAAMK,GAAOE,GAAYD,CAAI;AACnE;AAEA,IAAME,oBAAc,IAAI,QAAwB,GAC5CC,IAAiB;AAIrB,SAASV,EAAY,GAAwB;CAC3C,IAAIO,IAAOE,EAAY,IAAId,CAAM;CAKjC,OAJIY,MAAS,KAAA,MACX,IAAO,0BAA0B,OACjCE,EAAY,IAAId,GAAQY,CAAI,IAEvBA;AACT;AAWA,SAAgBI,EACd,GACA,GACA,GACwC;CACxC,IAAMC,IAAgBP,EAAmBR,GAAOF,CAAM;CACtD,QAAQ,GAAM,GAAM,GAAM,MACpBM,EAAK,cAAc,KAAA,IAAkBY,EAAQZ,GAAMa,GAAMC,GAAMhB,CAAG,IAC/Dc,EAAQZ,GAAMa,GAAMC,GAAM;EAAE,GAAGhB;EAAK,aAAaa,EAAcX,CAAI;CAAE,CAAC;AAEjF"}
1
+ {"version":3,"file":"derivedPath.js","names":["composes","composition","scenePaintedPoseOf","local","toPose","effectivePose","scene","node","poseComposition","chain","asNodeId","id","definesFrame","composeWorldPose","sceneDepLookup","slot","memoSlotFor","derivedDepOf","ctx","SlotDep","posed","resolveDerivedPath","sceneDerivedPathOf","depOf","childrenOf","toPoseScopes","frame","scopes","createNodeMemoScope","withDerivedPaths","derivedPathOf","drawOne","pose","view"],"sources":["../../src/canvas/derivedPath.ts"],"sourcesContent":["/**\n * The render walk's half of derived paths: resolving one against a scene, and\n * handing it to the painter.\n *\n * The resolver itself lives in `core/scene/derivedPath.ts`, beside\n * `derivedPose` — a *pose* can derive from a dependency's path, so\n * `effectivePose` has to be able to reach it.\n */\nimport type { DerivedDep, Node, NodeId, Scene } from 'core/scene/types';\nimport type { Path } from '@weasel-js/geom';\nimport { asNodeId } from 'core/scene/types';\nimport { definesFrame, derivedDepOf, effectivePose } from 'core/scene/effectivePose';\nimport { composeWorldPose, type PoseAdapter, type PoseComposition } from 'core/scene/composeFrame';\nimport { resolveDerivedPath } from 'core/scene/derivedPath';\nimport { createNodeMemoScope, type NodeMemoScope } from 'core/scene/nodeMemo';\nimport type { SceneViewDrawOne } from './NodeShape';\n\nexport { resolveDerivedPath } from 'core/scene/derivedPath';\n\nconst composes = <TPose>(composition: PoseComposition<TPose> | undefined): composition is PoseComposition<TPose> =>\n composition !== undefined && composition.closure !== 'identity';\n\n/**\n * `(node) => the pose it is painted at`: the slot's `toPose` if it has one,\n * else the scene's effective pose, folded through the parent chain when\n * `poseComposition` makes a stored pose local to its container.\n */\nexport function scenePaintedPoseOf<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n toPose?: (node: Node<TData, TLayer, TPose>) => TPose,\n poseComposition?: PoseComposition<TPose>,\n): (node: Node<TData, TLayer, TPose>) => TPose {\n const local = toPose ?? ((node: Node<TData, TLayer, TPose>) => effectivePose(scene, node));\n if (!composes(poseComposition)) return local;\n const chain: PoseAdapter<TPose> = {\n getPose: (id) => {\n const n = scene.get(asNodeId(id));\n return n ? local(n) : (undefined as unknown as TPose);\n },\n getParent: (id) => scene.get(asNodeId(id))?.parent ?? null,\n definesFrame: (id) => {\n const n = scene.get(asNodeId(id));\n return n === undefined || definesFrame(n);\n },\n };\n return (node) => composeWorldPose(chain, node.id, poseComposition.compose);\n}\n\n/**\n * `(id) => that node, the pose it is painted at, and the path it derives` —\n * resolved against the scene, so a dependency that is itself derived resolves\n * before it is read.\n *\n * A derived edge is drawn from these coordinates and must meet the nodes it\n * connects, so this reads the pose each one is painted at: see\n * {@link scenePaintedPoseOf}.\n */\nexport function sceneDepLookup<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n toPose?: (node: Node<TData, TLayer, TPose>) => TPose,\n poseComposition?: PoseComposition<TPose>,\n): (id: NodeId) => DerivedDep<TPose> | undefined {\n const slot = memoSlotFor(toPose, poseComposition);\n if (slot === undefined) return (id) => derivedDepOf(scene, id);\n const ctx: SlotLookup<TPose> = {\n at: scenePaintedPoseOf(scene, toPose, poseComposition) as (node: unknown) => TPose,\n slot,\n childrenOf: (id: NodeId) => scene.childrenOf(id),\n lookup: (id: NodeId) => {\n const node = scene.get(id);\n return node === undefined ? undefined : new SlotDep(node, ctx);\n },\n };\n return ctx.lookup;\n}\n\ninterface SlotLookup<TPose> {\n at: (node: unknown) => TPose;\n slot: string | NodeMemoScope;\n childrenOf: (id: NodeId) => readonly NodeId[];\n lookup: (id: NodeId) => DerivedDep<TPose> | undefined;\n}\n\n/** A class, not an object literal with a `get path()` — see `SceneDep` in\n * `core/scene/effectivePose.ts` for why. */\nclass SlotDep<TPose> implements DerivedDep<TPose> {\n readonly node: Node<unknown, string, TPose>;\n readonly pose: TPose;\n\n constructor(posed: unknown, private readonly ctx: SlotLookup<TPose>) {\n this.node = posed as Node<unknown, string, TPose>;\n this.pose = ctx.at(posed);\n }\n\n get path(): Path | null {\n return resolveDerivedPath(\n this.node as never, this.ctx.lookup, this.ctx.childrenOf, this.ctx.slot,\n );\n }\n}\n\n/** `(node) => the path it derives`, read through {@link sceneDepLookup}. */\nexport function sceneDerivedPathOf<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n toPose?: (node: Node<TData, TLayer, TPose>) => TPose,\n poseComposition?: PoseComposition<TPose>,\n): (node: Node<TData, TLayer, TPose>) => Path | null {\n const depOf = sceneDepLookup(scene, toPose, poseComposition);\n const slot = memoSlotFor(toPose, poseComposition);\n const childrenOf = (id: NodeId): readonly NodeId[] => scene.childrenOf(id);\n return (node) => resolveDerivedPath(node, depOf, childrenOf, slot);\n}\n\nconst toPoseScopes = new WeakMap<object, { local?: NodeMemoScope; world?: NodeMemoScope }>();\n\n/** Where one way of reading poses memoizes its paths, or `undefined` for the\n * scene's own slot: two readers painting one scene at different poses must\n * not serve each other's paths. Every reader composing stored poses shares a\n * slot; a `toPose` gets a scope that is collected with the function. */\nfunction memoSlotFor<TPose>(\n toPose: object | undefined,\n poseComposition: PoseComposition<TPose> | undefined,\n): string | NodeMemoScope | undefined {\n const frame = composes(poseComposition) ? 'world' : 'local';\n if (toPose === undefined) return frame === 'world' ? 'kit:derivedPath:world' : undefined;\n let scopes = toPoseScopes.get(toPose);\n if (scopes === undefined) {\n scopes = {};\n toPoseScopes.set(toPose, scopes);\n }\n return (scopes[frame] ??= createNodeMemoScope());\n}\n\n/**\n * Wrap a `drawOne` so a node that derives its geometry arrives at the painter\n * with the resolved path. The painter has no scene handle and so cannot read\n * the dependencies' poses itself; both scene walks wrap here instead.\n *\n * A node that derives nothing passes the caller's `ctx` through untouched —\n * this runs per node per frame, and an added `{ derivedPath: null }` would be\n * an allocation per node for a field no painter reads.\n */\nexport function withDerivedPaths<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n drawOne: SceneViewDrawOne<TData, TLayer, TPose>,\n toPose?: (node: Node<TData, TLayer, TPose>) => TPose,\n poseComposition?: PoseComposition<TPose>,\n): SceneViewDrawOne<TData, TLayer, TPose> {\n const derivedPathOf = sceneDerivedPathOf(scene, toPose, poseComposition);\n return (node, pose, view, ctx) => {\n if (node.dependsOn === undefined) return drawOne(node, pose, view, ctx);\n return drawOne(node, pose, view, { ...ctx, derivedPath: derivedPathOf(node) });\n };\n}\n"],"mappings":";;;;;;AAmBA,IAAMA,KAAmB,MACvBC,MAAgB,KAAA,KAAaA,EAAY,YAAY;AAOvD,SAAgBC,EACd,GACA,GACA,GAC6C;CAC7C,IAAMC,IAAQC,OAAY,MAAqCC,EAAcC,GAAOC,CAAI;CACxF,IAAI,CAACP,EAASQ,CAAe,GAAG,OAAOL;CACvC,IAAMM,IAA4B;EAChC,UAAU,MAAO;GACf,IAAM,IAAIH,EAAM,IAAII,EAASC,CAAE,CAAC;GAChC,OAAO,IAAIR,EAAM,CAAC,IAAK,KAAA;EACzB;EACA,YAAY,MAAOG,EAAM,IAAII,EAASC,CAAE,CAAC,CAAC,EAAE,UAAU;EACtD,eAAe,MAAO;GACpB,IAAM,IAAIL,EAAM,IAAII,EAASC,CAAE,CAAC;GAChC,OAAO,MAAM,KAAA,KAAaC,EAAa,CAAC;EAC1C;CACF;CACA,QAAQ,MAASC,EAAiBJ,GAAOF,EAAK,IAAIC,EAAgB,OAAO;AAC3E;AAWA,SAAgBM,EACd,GACA,GACA,GAC+C;CAC/C,IAAMC,IAAOC,EAAYZ,GAAQI,CAAe;CAChD,IAAIO,MAAS,KAAA,GAAW,QAAQ,MAAOE,EAAaX,GAAOK,CAAE;CAC7D,IAAMO,IAAyB;EAC7B,IAAIhB,EAAmBI,GAAOF,GAAQI,CAAe;EACrD;EACA,aAAa,MAAeF,EAAM,WAAWK,CAAE;EAC/C,SAAS,MAAe;GACtB,IAAMJ,IAAOD,EAAM,IAAIK,CAAE;GACzB,OAAOJ,MAAS,KAAA,IAAY,KAAA,IAAY,IAAIY,EAAQZ,GAAMW,CAAG;EAC/D;CACF;CACA,OAAOA,EAAI;AACb;AAWA,IAAMC,IAAN,MAAkD;CAIH;CAH7C;CACA;CAEA,YAAY,GAAgB,GAAyC;EAEnE,AAF2C,KAAA,MAAAD,GAC3C,KAAK,OAAOE,GACZ,KAAK,OAAOF,EAAI,GAAGE,CAAK;CAC1B;CAEA,IAAI,OAAoB;EACtB,OAAOC,EACL,KAAK,MAAe,KAAK,IAAI,QAAQ,KAAK,IAAI,YAAY,KAAK,IAAI,IACrE;CACF;AACF;AAGA,SAAgBC,EACd,GACA,GACA,GACmD;CACnD,IAAMC,IAAQT,EAAeR,GAAOF,GAAQI,CAAe,GACrDO,IAAOC,EAAYZ,GAAQI,CAAe,GAC1CgB,KAAc,MAAkClB,EAAM,WAAWK,CAAE;CACzE,QAAQ,MAASU,EAAmBd,GAAMgB,GAAOC,GAAYT,CAAI;AACnE;AAEA,IAAMU,oBAAe,IAAI,QAAkE;AAM3F,SAAST,EACP,GACA,GACoC;CACpC,IAAMU,IAAQ1B,EAASQ,CAAe,IAAI,UAAU;CACpD,IAAIJ,MAAW,KAAA,GAAW,OAAOsB,MAAU,UAAU,0BAA0B,KAAA;CAC/E,IAAIC,IAASF,EAAa,IAAIrB,CAAM;CAKpC,OAJIuB,MAAW,KAAA,MACb,IAAS,CAAC,GACVF,EAAa,IAAIrB,GAAQuB,CAAM,IAEzB,EAAOD,OAAWE,EAAoB;AAChD;AAWA,SAAgBC,EACd,GACA,GACA,GACA,GACwC;CACxC,IAAMC,IAAgBR,EAAmBhB,GAAOF,GAAQI,CAAe;CACvE,QAAQ,GAAM,GAAM,GAAM,MACpBD,EAAK,cAAc,KAAA,IAAkBwB,EAAQxB,GAAMyB,GAAMC,GAAMf,CAAG,IAC/Da,EAAQxB,GAAMyB,GAAMC,GAAM;EAAE,GAAGf;EAAK,aAAaY,EAAcvB,CAAI;CAAE,CAAC;AAEjF"}
@@ -1,14 +1,14 @@
1
1
  //#region src/canvas/getNodeAtPoint.ts
2
2
  function e(e, t) {
3
3
  return (n, r, i) => {
4
- let a = e(n, r, i), o = Array.isArray(a) ? a[0] ?? null : a;
5
- if (o == null) return null;
6
- let s = t?.(o) ?? null;
4
+ let a = e(n, r, i);
5
+ if (a == null) return null;
6
+ let o = t?.(a) ?? null;
7
7
  return {
8
- id: o,
9
- kind: s?.kind ?? "unknown",
10
- pose: s?.pose,
11
- data: s?.data ?? { id: o }
8
+ id: a,
9
+ kind: o?.kind ?? "unknown",
10
+ pose: o?.pose,
11
+ data: o?.data ?? { id: a }
12
12
  };
13
13
  };
14
14
  }
@@ -1 +1 @@
1
- {"version":3,"file":"getNodeAtPoint.js","names":["makeGetNodeAtPoint","raw","pickEvery","wx","wy","view","id","resolved","nodeResolver"],"sources":["../../src/canvas/getNodeAtPoint.ts"],"sourcesContent":["/**\n * Stable helper for synthesizing the dispatcher's `getNodeAtPoint` callback\n * from a `pickEvery` function and an optional per-id node resolver.\n *\n * Extracted from `Canvas.tsx`'s inline `__setGetNodeAtPoint` synthesizer so\n * that `SceneCanvas` (and any other scene-aware wrapper) can build the\n * function externally and hand it to `<Canvas>` as a prop. The caller supplies\n * a `nodeResolver` that derives each hit's kind/pose/data (SceneCanvas\n * classifies the node's `data` via its routing registry).\n */\n\nimport type { PickView } from './SceneCanvas/useSceneSelectTool';\n\n/** The hit record returned by a `getNodeAtPoint` function. Matches the\n * shape expected by `ToolsDispatcher.__setGetNodeAtPoint`. */\nexport interface NodeAtPointHit {\n id: string;\n kind: string;\n pose: unknown;\n data: unknown;\n meta?: Record<string, unknown>;\n}\n\n/**\n * Resolves per-id metadata for a node (kind, pose, data). When absent,\n * kind falls back to `'unknown'` and pose/data fall back to trivial values.\n */\nexport type NodeResolver = (id: string) => { kind: string; pose: unknown; data: unknown } | null;\n\n/**\n * Build a stable `getNodeAtPoint(wx, wy)` function suitable for passing to\n * the tool dispatcher via `Canvas`'s `getNodeAtPoint` prop.\n *\n * Algorithm matches the inline synthesizer in Canvas.tsx exactly:\n * - `pickEvery` returns `string | string[] | null`\n * - When an array, the **first** element is the topmost id (front-to-back order)\n * - `nodeResolver` is called for the topmost id to get kind/pose/data\n * - When `nodeResolver` is absent or returns null, kind defaults to `'unknown'`,\n * pose to `undefined`, data to `{ id }`\n *\n * @param pickEvery Raw hit-test function. Same signature as the Canvas prop.\n * @param nodeResolver Optional per-id resolver. SceneCanvas builds this from its adapter.\n */\nexport function makeGetNodeAtPoint(\n pickEvery: (wx: number, wy: number, view?: PickView | null) => string | string[] | null,\n nodeResolver?: NodeResolver,\n): (wx: number, wy: number, view?: PickView | null) => NodeAtPointHit | null {\n return (wx: number, wy: number, view?: PickView | null): NodeAtPointHit | null => {\n const raw = pickEvery(wx, wy, view);\n // Normalize `string | string[] | null` to topmost id (first entry).\n const id = Array.isArray(raw) ? raw[0] ?? null : raw;\n if (id == null) return null;\n\n const resolved = nodeResolver?.(id) ?? null;\n const kind = resolved?.kind ?? 'unknown';\n const pose = resolved?.pose;\n const data = resolved?.data ?? { id };\n\n return { id, kind, pose, data };\n };\n}\n"],"mappings":";AA2CA,SAAgBA,EACd,GACA,GAC2E;CAC3E,QAAQ,GAAY,GAAY,MAAkD;EAChF,IAAMC,IAAMC,EAAUC,GAAIC,GAAIC,CAAI,GAE5BC,IAAK,MAAM,QAAQL,CAAG,IAAIA,EAAI,MAAM,OAAOA;EACjD,IAAIK,KAAM,MAAM,OAAO;EAEvB,IAAMC,IAAWC,IAAeF,CAAE,KAAK;EAKvC,OAAO;GAAE;GAAI,MAJAC,GAAU,QAAQ;GAIZ,MAHNA,GAAU;GAGE,MAFZA,GAAU,QAAQ,EAAE,MAAG;EAEN;CAChC;AACF"}
1
+ {"version":3,"file":"getNodeAtPoint.js","names":["makeGetNodeAtPoint","id","pickBest","wx","wy","view","resolved","nodeResolver"],"sources":["../../src/canvas/getNodeAtPoint.ts"],"sourcesContent":["/**\n * Synthesizes the dispatcher's `getNodeAtPoint` callback from a single-hit\n * picker and an optional per-id node resolver, so `SceneCanvas` can build the\n * function and hand it to `<Canvas>` as a prop.\n */\n\nimport type { PickView } from './SceneCanvas/useSceneSelectTool';\n\n/** The hit record returned by a `getNodeAtPoint` function. Matches the\n * shape expected by `ToolsDispatcher.__setGetNodeAtPoint`. */\nexport interface NodeAtPointHit {\n id: string;\n kind: string;\n pose: unknown;\n data: unknown;\n meta?: Record<string, unknown>;\n}\n\n/**\n * Resolves per-id metadata for a node (kind, pose, data). When absent,\n * kind falls back to `'unknown'` and pose/data fall back to trivial values.\n */\nexport type NodeResolver = (id: string) => { kind: string; pose: unknown; data: unknown } | null;\n\n/**\n * Build a stable `getNodeAtPoint(wx, wy)` function suitable for passing to\n * the tool dispatcher via `Canvas`'s `getNodeAtPoint` prop.\n *\n * `nodeResolver` supplies the hit's kind/pose/data. When it is absent or\n * returns null, kind defaults to `'unknown'`, pose to `undefined`, and data to\n * `{ id }`.\n *\n * @param pickBest Answers the one id under a world point. SceneCanvas passes\n * the select tool's own resolution, so a click reports the node it selects.\n * @param nodeResolver Optional per-id resolver. SceneCanvas builds this from its adapter.\n */\nexport function makeGetNodeAtPoint(\n pickBest: (wx: number, wy: number, view?: PickView | null) => string | null,\n nodeResolver?: NodeResolver,\n): (wx: number, wy: number, view?: PickView | null) => NodeAtPointHit | null {\n return (wx: number, wy: number, view?: PickView | null): NodeAtPointHit | null => {\n const id = pickBest(wx, wy, view);\n if (id == null) return null;\n\n const resolved = nodeResolver?.(id) ?? null;\n const kind = resolved?.kind ?? 'unknown';\n const pose = resolved?.pose;\n const data = resolved?.data ?? { id };\n\n return { id, kind, pose, data };\n };\n}\n"],"mappings":";AAoCA,SAAgBA,EACd,GACA,GAC2E;CAC3E,QAAQ,GAAY,GAAY,MAAkD;EAChF,IAAMC,IAAKC,EAASC,GAAIC,GAAIC,CAAI;EAChC,IAAIJ,KAAM,MAAM,OAAO;EAEvB,IAAMK,IAAWC,IAAeN,CAAE,KAAK;EAKvC,OAAO;GAAE;GAAI,MAJAK,GAAU,QAAQ;GAIZ,MAHNA,GAAU;GAGE,MAFZA,GAAU,QAAQ,EAAE,MAAG;EAEN;CAChC;AACF"}
@@ -1,5 +1,5 @@
1
1
  //#region src/canvas/hud.module.css
2
- var e = "_hud_1hv8c_3", t = "_sized_1hv8c_17", n = "_readings_1hv8c_22 _numeric_zns9n_4", r = "_muted_1hv8c_39", i = "_absent_1hv8c_44", a = "_header_1hv8c_49", o = "_list_1hv8c_53", s = "_item_1hv8c_62", c = "_itemBest_1hv8c_68", l = "_row_1hv8c_72", u = "_stack_1hv8c_79", d = {
2
+ var e = "_hud_4467cb", t = "_sized_4467cb", n = "_readings_4467cb _numeric_25fd0b", r = "_muted_4467cb", i = "_absent_4467cb", a = "_header_4467cb", o = "_list_4467cb", s = "_item_4467cb", c = "_itemBest_4467cb", l = "_row_4467cb", u = "_stack_4467cb", d = {
3
3
  hud: e,
4
4
  sized: t,
5
5
  readings: n,
@@ -1 +1 @@
1
- {"version":3,"file":"hud.module.js","names":["hud","sized","readings","muted","absent","header","list","item","itemBest","row","stack","hud_module_default"],"sources":["../../src/canvas/hud.module.css"],"sourcesContent":["/* The debug HUDs <Canvas> can draw: CursorCoordsHud, PickHud, ModalityHud. The\n mono face is for the ids the pick and modality HUDs list; figures take the\n numeric face, on a child so `.hud`'s own font cannot override it. */\n.hud {\n position: fixed;\n z-index: 10000;\n background: rgba(0, 0, 0, 0.7);\n color: #e8e8e8;\n font: 11px ui-monospace, SFMono-Regular, Menlo, monospace;\n padding: 6px 8px;\n border-radius: 4px;\n pointer-events: none;\n line-height: 1.4;\n text-align: right;\n}\n\n.sized {\n min-width: 120px;\n max-width: 260px;\n}\n\n.readings {\n composes: numeric from '@weasel-js/theme/numeric.module.css';\n display: grid;\n grid-template-columns: auto auto;\n column-gap: 8px;\n margin: 0;\n}\n\n.readings dt {\n text-align: left;\n}\n\n.readings dd {\n margin: 0;\n white-space: nowrap;\n}\n\n.muted {\n color: #888;\n}\n\n/* A value that is not there: an em dash, \"off-canvas\". */\n.absent {\n color: #888;\n font-style: italic;\n}\n\n.header {\n margin-bottom: 2px;\n}\n\n.list {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 1px;\n}\n\n.item {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.itemBest {\n font-weight: 700;\n}\n\n.row {\n display: flex;\n gap: 8px;\n justify-content: flex-end;\n white-space: nowrap;\n}\n\n.stack {\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n}\n"],"mappings":""}
1
+ {"version":3,"file":"hud.module.js","names":["hud","sized","readings","muted","absent","header","list","item","itemBest","row","stack","hud_module_default"],"sources":["../../src/canvas/hud.module.css"],"sourcesContent":["/* The debug HUDs <Canvas> can draw: CursorCoordsHud, PickHud, ModalityHud. The\n mono face is for the ids the pick and modality HUDs list; figures take the\n numeric face, on a child so `.hud`'s own font cannot override it. */\n.hud {\n position: fixed;\n z-index: 900;\n background: rgba(0, 0, 0, 0.7);\n color: #e8e8e8;\n font: 11px ui-monospace, SFMono-Regular, Menlo, monospace;\n padding: 6px 8px;\n border-radius: 4px;\n pointer-events: none;\n line-height: 1.4;\n text-align: right;\n}\n\n.sized {\n min-width: 120px;\n max-width: 260px;\n}\n\n.readings {\n composes: numeric from '@weasel-js/theme/numeric.module.css';\n display: grid;\n grid-template-columns: auto auto;\n column-gap: 8px;\n margin: 0;\n}\n\n.readings dt {\n text-align: left;\n}\n\n.readings dd {\n margin: 0;\n white-space: nowrap;\n}\n\n.muted {\n color: #888;\n}\n\n/* A value that is not there: an em dash, \"off-canvas\". */\n.absent {\n color: #888;\n font-style: italic;\n}\n\n.header {\n margin-bottom: 2px;\n}\n\n.list {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 1px;\n}\n\n.item {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.itemBest {\n font-weight: 700;\n}\n\n.row {\n display: flex;\n gap: 8px;\n justify-content: flex-end;\n white-space: nowrap;\n}\n\n.stack {\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n}\n"],"mappings":""}
@@ -122,7 +122,7 @@ function u(e, t) {
122
122
  case ">=": return n >= 0;
123
123
  case ">": return n > 0;
124
124
  case "<=": return n <= 0;
125
- case "<": return n < 0;
125
+ case "<": return t.v.pre.length > 0 ? n < 0 : s(c(...e.triple), t.v) < 0;
126
126
  }
127
127
  }
128
128
  function d(e, t) {
@@ -132,7 +132,7 @@ function d(e, t) {
132
132
  for (let e of r) {
133
133
  if (e === "") return null;
134
134
  let t = [];
135
- for (let n of e.split(/\s+/)) {
135
+ for (let n of e.replace(/(\^|~|>=|<=|>|<|=)\s+/g, "$1").split(/\s+/)) {
136
136
  let e = l(n);
137
137
  if (!e) return null;
138
138
  t.push(...e);
@@ -1 +1 @@
1
- {"version":3,"file":"kitRequirements.js","names":["KIT_VERSIONS","VERSION","UNKNOWN_SUFFIX","VERSION_RE","PARTIAL_RE","preOf","s","id","parseVersion","m","compare","i","d","a","b","x","y","v","c","parseComparator","token","op","p","nums","part","pre","M","mi","pa","lower","upper","top","holds","version","satisfiesRange","parsed","alternatives","range","alt","any","set","cs","checkRequirements","out","pkg","requires","have","running","ok"],"sources":["../../src/canvas/kitRequirements.ts"],"sourcesContent":["/**\n * Version negotiation for contributions: a `requires` map of semver ranges,\n * matched against the versions the running kit packages report.\n *\n * The range grammar is npm's everyday subset — `^`, `~`, `>=`, `>`, `<=`, `<`,\n * `=`, bare and partial versions (`1.7`, `1.x`, `*`), space for AND and `||`\n * for OR. Hyphen ranges are not read. A prerelease orders below its release,\n * without npm's rule that a range must opt into prereleases by naming one.\n */\nimport { VERSION } from '../version';\n\n/**\n * The versions a contribution can require, keyed by package without its\n * `@weasel-js/` scope. Only core reports its version today.\n */\nexport interface KitVersions {\n core: string;\n}\n\n/** A contribution's `requires`: a semver range per package it was written against. */\nexport type KitRequirements = { readonly [K in keyof KitVersions]?: string };\n\n/** The running kit's versions. */\nexport const KIT_VERSIONS: Readonly<KitVersions> = { core: VERSION };\n\n/** One requirement the running kit does not meet. */\nexport interface RequirementMismatch {\n package: string;\n range: string;\n /** The running version, or `undefined` when that package reports none. */\n running: string | undefined;\n reason: 'unsatisfied' | 'invalid-range' | 'unknown-package';\n}\n\ntype Triple = readonly [number, number, number];\ninterface Version { triple: Triple; pre: readonly (string | number)[] }\ninterface Comparator { op: '>=' | '<' | '>' | '<='; v: Version }\n\nconst UNKNOWN_SUFFIX = '-unknown';\nconst VERSION_RE = /^v?(\\d+)\\.(\\d+)\\.(\\d+)(?:-([0-9A-Za-z.-]+))?(?:\\+[0-9A-Za-z.-]+)?$/;\nconst PARTIAL_RE = /^v?(\\d+|[xX*])(?:\\.(\\d+|[xX*]))?(?:\\.(\\d+|[xX*]))?(?:-([0-9A-Za-z.-]+))?(?:\\+[0-9A-Za-z.-]+)?$/;\n\nfunction preOf(s: string | undefined): (string | number)[] {\n return s ? s.split('.').map((id) => (/^\\d+$/.test(id) ? Number(id) : id)) : [];\n}\n\nfunction parseVersion(s: string): Version | null {\n const m = VERSION_RE.exec(s.trim());\n return m ? { triple: [Number(m[1]), Number(m[2]), Number(m[3])], pre: preOf(m[4]) } : null;\n}\n\nfunction compare(a: Version, b: Version): number {\n for (let i = 0; i < 3; i++) {\n const d = a.triple[i]! - b.triple[i]!;\n if (d !== 0) return d;\n }\n if (a.pre.length === 0 || b.pre.length === 0) return b.pre.length - a.pre.length;\n for (let i = 0; i < Math.max(a.pre.length, b.pre.length); i++) {\n const x = a.pre[i];\n const y = b.pre[i];\n if (x === undefined) return -1;\n if (y === undefined) return 1;\n if (x === y) continue;\n if (typeof x === 'number' && typeof y === 'number') return x - y;\n if (typeof x === 'number') return -1;\n if (typeof y === 'number') return 1;\n return x < y ? -1 : 1;\n }\n return 0;\n}\n\nconst v = (a: number, b: number, c: number, pre: readonly (string | number)[] = []): Version =>\n ({ triple: [a, b, c], pre });\n\n/** One comparator token as the interval it names. `null` when unreadable. */\nfunction parseComparator(token: string): Comparator[] | null {\n const m = /^(\\^|~|>=|<=|>|<|=)?(.*)$/.exec(token)!;\n const op = m[1] ?? '';\n const p = PARTIAL_RE.exec(m[2]!);\n if (!p) return null;\n const nums: number[] = [];\n for (const part of [p[1], p[2], p[3]]) {\n if (part === undefined || /^[xX*]$/.test(part)) break;\n nums.push(Number(part));\n }\n const pre = preOf(p[4]);\n if (pre.length > 0 && nums.length < 3) return null;\n const [M = 0, mi = 0, pa = 0] = nums;\n const lower = v(M, mi, pa, pre);\n // The first version past everything the partial names: `1.7` → `1.8.0`.\n const upper = nums.length === 0 ? null\n : nums.length === 1 ? v(M + 1, 0, 0)\n : nums.length === 2 ? v(M, mi + 1, 0)\n : v(M, mi, pa + 1);\n\n switch (op) {\n case '>=': return [{ op: '>=', v: lower }];\n case '<': return nums.length === 0 ? [{ op: '<', v: v(0, 0, 0) }] : [{ op: '<', v: lower }];\n case '>': return nums.length === 3 ? [{ op: '>', v: lower }] : upper ? [{ op: '>=', v: upper }] : [{ op: '<', v: v(0, 0, 0) }];\n case '<=': return nums.length === 3 ? [{ op: '<=', v: lower }] : upper ? [{ op: '<', v: upper }] : [];\n case '~': {\n const top = nums.length <= 1 ? v(M + 1, 0, 0) : v(M, mi + 1, 0);\n return [{ op: '>=', v: lower }, { op: '<', v: top }];\n }\n case '^': {\n const top = M > 0 || nums.length === 1 ? v(M + 1, 0, 0)\n : mi > 0 || nums.length === 2 ? v(0, mi + 1, 0)\n : v(0, 0, pa + 1);\n return nums.length === 0 ? [] : [{ op: '>=', v: lower }, { op: '<', v: top }];\n }\n default:\n if (nums.length === 3) return [{ op: '>=', v: lower }, { op: '<=', v: lower }];\n return upper ? [{ op: '>=', v: lower }, { op: '<', v: upper }] : [];\n }\n}\n\nfunction holds(version: Version, c: Comparator): boolean {\n const d = compare(version, c.v);\n switch (c.op) {\n case '>=': return d >= 0;\n case '>': return d > 0;\n case '<=': return d <= 0;\n case '<': return d < 0;\n }\n}\n\n/**\n * Whether `version` falls in `range`. `null` when either cannot be read, so a\n * caller can tell a mismatch from a requirement it could not check.\n */\nexport function satisfiesRange(version: string, range: string): boolean | null {\n const parsed = parseVersion(version);\n if (!parsed) return null;\n const alternatives = range.split('||').map((alt) => alt.trim());\n let any = false;\n for (const alt of alternatives) {\n if (alt === '') return null;\n const set: Comparator[] = [];\n for (const token of alt.split(/\\s+/)) {\n const cs = parseComparator(token);\n if (!cs) return null;\n set.push(...cs);\n }\n if (set.every((c) => holds(parsed, c))) any = true;\n }\n return any;\n}\n\n/**\n * Every requirement in `requires` the running kit does not meet. A package\n * whose build does not know its own version (`0.0.0-unknown`, core's source\n * bundled without its define) is skipped: nothing can be said about it.\n */\nexport function checkRequirements(\n requires: KitRequirements,\n running: Readonly<Partial<Record<string, string>>> = KIT_VERSIONS,\n): RequirementMismatch[] {\n const out: RequirementMismatch[] = [];\n for (const [pkg, range] of Object.entries(requires)) {\n if (range === undefined) continue;\n const have = running[pkg];\n if (have === undefined) {\n out.push({ package: pkg, range, running: undefined, reason: 'unknown-package' });\n continue;\n }\n if (have.endsWith(UNKNOWN_SUFFIX)) continue;\n const ok = satisfiesRange(have, range);\n if (ok === null) out.push({ package: pkg, range, running: have, reason: 'invalid-range' });\n else if (!ok) out.push({ package: pkg, range, running: have, reason: 'unsatisfied' });\n }\n return out;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"kitRequirements.js","names":["KIT_VERSIONS","VERSION","UNKNOWN_SUFFIX","VERSION_RE","PARTIAL_RE","preOf","s","id","parseVersion","m","compare","i","d","a","b","x","y","v","c","parseComparator","token","op","p","nums","part","pre","M","mi","pa","lower","upper","top","holds","version","satisfiesRange","parsed","alternatives","range","alt","any","set","cs","checkRequirements","out","pkg","requires","have","running","ok"],"sources":["../../src/canvas/kitRequirements.ts"],"sourcesContent":["/**\n * Version negotiation for contributions: a `requires` map of semver ranges,\n * matched against the versions the running kit packages report.\n *\n * The range grammar is npm's everyday subset — `^`, `~`, `>=`, `>`, `<=`, `<`,\n * `=`, bare and partial versions (`1.7`, `1.x`, `*`), space for AND and `||`\n * for OR. Hyphen ranges are not read. A prerelease orders below its release,\n * without npm's rule that a range must opt into prereleases by naming one —\n * except at an exclusive upper bound, which also excludes that version's own\n * prereleases, so `^1.4` does not admit `2.0.0-rc.1`.\n */\nimport { VERSION } from '../version';\n\n/**\n * The versions a contribution can require, keyed by package without its\n * `@weasel-js/` scope. Only core reports its version today.\n */\nexport interface KitVersions {\n core: string;\n}\n\n/** A contribution's `requires`: a semver range per package it was written against. */\nexport type KitRequirements = { readonly [K in keyof KitVersions]?: string };\n\n/** The running kit's versions. */\nexport const KIT_VERSIONS: Readonly<KitVersions> = { core: VERSION };\n\n/** One requirement the running kit does not meet. */\nexport interface RequirementMismatch {\n package: string;\n range: string;\n /** The running version, or `undefined` when that package reports none. */\n running: string | undefined;\n reason: 'unsatisfied' | 'invalid-range' | 'unknown-package';\n}\n\ntype Triple = readonly [number, number, number];\ninterface Version { triple: Triple; pre: readonly (string | number)[] }\ninterface Comparator { op: '>=' | '<' | '>' | '<='; v: Version }\n\nconst UNKNOWN_SUFFIX = '-unknown';\nconst VERSION_RE = /^v?(\\d+)\\.(\\d+)\\.(\\d+)(?:-([0-9A-Za-z.-]+))?(?:\\+[0-9A-Za-z.-]+)?$/;\nconst PARTIAL_RE = /^v?(\\d+|[xX*])(?:\\.(\\d+|[xX*]))?(?:\\.(\\d+|[xX*]))?(?:-([0-9A-Za-z.-]+))?(?:\\+[0-9A-Za-z.-]+)?$/;\n\nfunction preOf(s: string | undefined): (string | number)[] {\n return s ? s.split('.').map((id) => (/^\\d+$/.test(id) ? Number(id) : id)) : [];\n}\n\nfunction parseVersion(s: string): Version | null {\n const m = VERSION_RE.exec(s.trim());\n return m ? { triple: [Number(m[1]), Number(m[2]), Number(m[3])], pre: preOf(m[4]) } : null;\n}\n\nfunction compare(a: Version, b: Version): number {\n for (let i = 0; i < 3; i++) {\n const d = a.triple[i]! - b.triple[i]!;\n if (d !== 0) return d;\n }\n if (a.pre.length === 0 || b.pre.length === 0) return b.pre.length - a.pre.length;\n for (let i = 0; i < Math.max(a.pre.length, b.pre.length); i++) {\n const x = a.pre[i];\n const y = b.pre[i];\n if (x === undefined) return -1;\n if (y === undefined) return 1;\n if (x === y) continue;\n if (typeof x === 'number' && typeof y === 'number') return x - y;\n if (typeof x === 'number') return -1;\n if (typeof y === 'number') return 1;\n return x < y ? -1 : 1;\n }\n return 0;\n}\n\nconst v = (a: number, b: number, c: number, pre: readonly (string | number)[] = []): Version =>\n ({ triple: [a, b, c], pre });\n\n/** One comparator token as the interval it names. `null` when unreadable. */\nfunction parseComparator(token: string): Comparator[] | null {\n const m = /^(\\^|~|>=|<=|>|<|=)?(.*)$/.exec(token)!;\n const op = m[1] ?? '';\n const p = PARTIAL_RE.exec(m[2]!);\n if (!p) return null;\n const nums: number[] = [];\n for (const part of [p[1], p[2], p[3]]) {\n if (part === undefined || /^[xX*]$/.test(part)) break;\n nums.push(Number(part));\n }\n const pre = preOf(p[4]);\n if (pre.length > 0 && nums.length < 3) return null;\n const [M = 0, mi = 0, pa = 0] = nums;\n const lower = v(M, mi, pa, pre);\n // The first version past everything the partial names: `1.7` → `1.8.0`.\n const upper = nums.length === 0 ? null\n : nums.length === 1 ? v(M + 1, 0, 0)\n : nums.length === 2 ? v(M, mi + 1, 0)\n : v(M, mi, pa + 1);\n\n switch (op) {\n case '>=': return [{ op: '>=', v: lower }];\n case '<': return nums.length === 0 ? [{ op: '<', v: v(0, 0, 0) }] : [{ op: '<', v: lower }];\n case '>': return nums.length === 3 ? [{ op: '>', v: lower }] : upper ? [{ op: '>=', v: upper }] : [{ op: '<', v: v(0, 0, 0) }];\n case '<=': return nums.length === 3 ? [{ op: '<=', v: lower }] : upper ? [{ op: '<', v: upper }] : [];\n case '~': {\n const top = nums.length <= 1 ? v(M + 1, 0, 0) : v(M, mi + 1, 0);\n return [{ op: '>=', v: lower }, { op: '<', v: top }];\n }\n case '^': {\n const top = M > 0 || nums.length === 1 ? v(M + 1, 0, 0)\n : mi > 0 || nums.length === 2 ? v(0, mi + 1, 0)\n : v(0, 0, pa + 1);\n return nums.length === 0 ? [] : [{ op: '>=', v: lower }, { op: '<', v: top }];\n }\n default:\n if (nums.length === 3) return [{ op: '>=', v: lower }, { op: '<=', v: lower }];\n return upper ? [{ op: '>=', v: lower }, { op: '<', v: upper }] : [];\n }\n}\n\nfunction holds(version: Version, c: Comparator): boolean {\n const d = compare(version, c.v);\n switch (c.op) {\n case '>=': return d >= 0;\n case '>': return d > 0;\n case '<=': return d <= 0;\n case '<': return c.v.pre.length > 0 ? d < 0 : compare(v(...version.triple), c.v) < 0;\n }\n}\n\n/**\n * Whether `version` falls in `range`. `null` when either cannot be read, so a\n * caller can tell a mismatch from a requirement it could not check.\n */\nexport function satisfiesRange(version: string, range: string): boolean | null {\n const parsed = parseVersion(version);\n if (!parsed) return null;\n const alternatives = range.split('||').map((alt) => alt.trim());\n let any = false;\n for (const alt of alternatives) {\n if (alt === '') return null;\n const set: Comparator[] = [];\n for (const token of alt.replace(/(\\^|~|>=|<=|>|<|=)\\s+/g, '$1').split(/\\s+/)) {\n const cs = parseComparator(token);\n if (!cs) return null;\n set.push(...cs);\n }\n if (set.every((c) => holds(parsed, c))) any = true;\n }\n return any;\n}\n\n/**\n * Every requirement in `requires` the running kit does not meet. A package\n * whose build does not know its own version (`0.0.0-unknown`, core's source\n * bundled without its define) is skipped: nothing can be said about it.\n */\nexport function checkRequirements(\n requires: KitRequirements,\n running: Readonly<Partial<Record<string, string>>> = KIT_VERSIONS,\n): RequirementMismatch[] {\n const out: RequirementMismatch[] = [];\n for (const [pkg, range] of Object.entries(requires)) {\n if (range === undefined) continue;\n const have = running[pkg];\n if (have === undefined) {\n out.push({ package: pkg, range, running: undefined, reason: 'unknown-package' });\n continue;\n }\n if (have.endsWith(UNKNOWN_SUFFIX)) continue;\n const ok = satisfiesRange(have, range);\n if (ok === null) out.push({ package: pkg, range, running: have, reason: 'invalid-range' });\n else if (!ok) out.push({ package: pkg, range, running: have, reason: 'unsatisfied' });\n }\n return out;\n}\n"],"mappings":"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"}
@@ -23,11 +23,7 @@ function r(r, i, a, o) {
23
23
  }
24
24
  let d = /* @__PURE__ */ new Set(), f = o ? [o] : [];
25
25
  function p(e) {
26
- if (d.has(e.key)) {
27
- console.warn(`composeOrderedLayers: cycle detected involving custom layer "${e.key}"`);
28
- return;
29
- }
30
- d.add(e.key), h(e.key), f.push(e.layer), m(e.key);
26
+ d.has(e.key) || (d.add(e.key), h(e.key), f.push(e.layer), m(e.key));
31
27
  }
32
28
  function m(e) {
33
29
  for (let t of s.get(e) ?? []) p(t);
@@ -47,12 +43,23 @@ function r(r, i, a, o) {
47
43
  e && f.push(e), m(t);
48
44
  }
49
45
  for (let e of l) d.has(e.key) || (d.add(e.key), f.push(e.layer));
46
+ let g = (e) => {
47
+ let n = r[e];
48
+ return t(n) ? n.after ?? n.before : void 0;
49
+ }, _ = (e) => {
50
+ let t = g(e);
51
+ for (let n = 0; t !== void 0 && n < u.size; n++) {
52
+ if (t === e) return !0;
53
+ t = g(t);
54
+ }
55
+ return !1;
56
+ };
50
57
  for (let e of u) {
51
58
  if (d.has(e)) continue;
52
59
  let n = r[e];
53
60
  if (!t(n)) continue;
54
61
  let i = n.after ?? n.before;
55
- console.warn(`composeOrderedLayers: dangling reference "${i}" from custom layer "${e}" — falling back to tail`), d.add(e), f.push(n.layer);
62
+ console.warn(_(e) ? `composeOrderedLayers: cycle detected involving custom layer "${e}" — falling back to tail` : `composeOrderedLayers: dangling reference "${i}" from custom layer "${e}" — falling back to tail`), d.add(e), f.push(n.layer);
56
63
  }
57
64
  return f;
58
65
  }
@@ -1 +1 @@
1
- {"version":3,"file":"layerOrder.js","names":["STANDARD_SLOT_SET","STANDARD_SLOTS","composeOrderedLayers","afterByParent","beforeByParent","tail","allCustomKeys","key","value","layersMap","isCustomEntry","arr","emitted","out","background","emitChild","child","emitBefore","emitAfter","parentKey","slot","sceneLayers","sl","layer","standardLayers","t","ref","placeToolOverlays","before","overlays","after","at","selectionOverlay","ordered"],"sources":["../../src/canvas/layerOrder.ts"],"sourcesContent":["import type { RenderLayer } from '../core/layers/render';\nimport type { LayerSlotValue } from './Canvas';\nimport { STANDARD_SLOTS, isCustomEntry } from './layerSlots';\nimport type { OverlayPosition } from '@weasel-js/routing';\n\nconst STANDARD_SLOT_SET = new Set<string>(STANDARD_SLOTS);\n\n/**\n * Compose the final ordered layer list from a layersMap and the resolved\n * standard-slot layers.\n *\n * Semantics:\n * - Standard slots are emitted in `STANDARD_SLOTS` order.\n * - Each standard-slot layer is preceded by its `before` chain (deepest first)\n * and followed by its `after` chain (ancestors first).\n * - `after: 'X'` / `before: 'X'` accept standard slot names OR other\n * custom-layer keys, and chains may mix the two — a custom anchored\n * `before` a slot can itself carry an `after` chain, and vice versa.\n * - Customs with neither `after` nor `before` go to a tail bucket emitted\n * after all standard slots.\n * - Dangling references (target unresolvable) go to the tail with a\n * `console.warn`. Cycles are detected and broken with a warning.\n *\n * Pure function — no side effects beyond `console.warn` on malformed input.\n */\nexport function composeOrderedLayers<TNode extends { id: string }, TPose>(\n layersMap: Record<string, LayerSlotValue<TNode, TPose> | null | undefined>,\n standardLayers: Partial<Record<(typeof STANDARD_SLOTS)[number], RenderLayer<unknown>>>,\n /** When the `scene` slot is split into per-scene-layer canvas layers (keyed\n * `scene:<layerId>`), pass them here in render order. Each key becomes an\n * anchor point, so customs can target `before/after: 'scene:<layerId>'`.\n * `before/after: 'scene'` still works (before the first / after the last).\n * When omitted, the `scene` slot uses `standardLayers.scene` as before. */\n sceneLayers?: ReadonlyArray<{ key: string; layer: RenderLayer<unknown> }>,\n /** Drawn beneath everything else, grid and customs anchored before the\n * scene included — a fill that must never cover what is laid over it. */\n background?: RenderLayer<unknown>,\n): RenderLayer<unknown>[] {\n // Map<parent key, [{ key, layer }, ...]> — children grouped by their declared anchor.\n const afterByParent = new Map<string, Array<{ key: string; layer: RenderLayer<unknown> }>>();\n const beforeByParent = new Map<string, Array<{ key: string; layer: RenderLayer<unknown> }>>();\n const tail: Array<{ key: string; layer: RenderLayer<unknown> }> = [];\n const allCustomKeys = new Set<string>();\n\n for (const [key, value] of Object.entries(layersMap)) {\n if (STANDARD_SLOT_SET.has(key)) continue;\n if (!isCustomEntry(value)) continue;\n allCustomKeys.add(key);\n if (value.after) {\n const arr = afterByParent.get(value.after) ?? [];\n arr.push({ key, layer: value.layer });\n afterByParent.set(value.after, arr);\n } else if (value.before) {\n const arr = beforeByParent.get(value.before) ?? [];\n arr.push({ key, layer: value.layer });\n beforeByParent.set(value.before, arr);\n } else {\n tail.push({ key, layer: value.layer });\n }\n }\n\n const emitted = new Set<string>();\n const out: RenderLayer<unknown>[] = background ? [background] : [];\n\n function emitChild(child: { key: string; layer: RenderLayer<unknown> }) {\n if (emitted.has(child.key)) {\n // Cycle — already emitted via another path. Warn once.\n console.warn(`composeOrderedLayers: cycle detected involving custom layer \"${child.key}\"`);\n return;\n }\n emitted.add(child.key);\n emitBefore(child.key);\n out.push(child.layer);\n emitAfter(child.key);\n }\n\n function emitAfter(parentKey: string) {\n for (const child of afterByParent.get(parentKey) ?? []) emitChild(child);\n }\n\n function emitBefore(parentKey: string) {\n for (const child of beforeByParent.get(parentKey) ?? []) emitChild(child);\n }\n\n for (const slot of STANDARD_SLOTS) {\n if (slot === 'scene' && sceneLayers) {\n // Split scene slot: emit each per-scene-layer canvas layer in order, with\n // its own anchor point. `before/after: 'scene'` brackets the whole group.\n emitBefore('scene');\n for (const sl of sceneLayers) {\n emitBefore(sl.key);\n out.push(sl.layer);\n emitAfter(sl.key);\n }\n emitAfter('scene');\n continue;\n }\n emitBefore(slot);\n const layer = standardLayers[slot];\n if (layer) out.push(layer);\n emitAfter(slot);\n }\n\n // Tail customs (neither after nor before).\n for (const t of tail) {\n if (emitted.has(t.key)) continue;\n emitted.add(t.key);\n out.push(t.layer);\n }\n\n // Orphans: customs that declared an `after`/`before` but never got emitted\n // (target didn't resolve through any chain to a known standard slot or custom).\n for (const key of allCustomKeys) {\n if (emitted.has(key)) continue;\n const value = layersMap[key];\n if (!isCustomEntry(value)) continue;\n const ref = value.after ?? value.before;\n console.warn(`composeOrderedLayers: dangling reference \"${ref}\" from custom layer \"${key}\" — falling back to tail`);\n emitted.add(key);\n out.push(value.layer);\n }\n\n return out;\n}\n\n/**\n * Splice a tool's positioned overlays into an already-composed layer list.\n *\n * `'top'` appends; the other two anchor on the selection-chrome layer's index\n * in `ordered`. With no selection chrome present the anchored overlays fall\n * back to the tail, keeping their relative order. Mutates and returns\n * `ordered`.\n */\nexport function placeToolOverlays(\n ordered: RenderLayer<unknown>[],\n selectionOverlay: RenderLayer<unknown> | undefined,\n overlays: (position: OverlayPosition) => RenderLayer<unknown>[],\n): RenderLayer<unknown>[] {\n const before = overlays('before-selection');\n const after = overlays('after-selection');\n const at = selectionOverlay ? ordered.indexOf(selectionOverlay) : -1;\n if (at >= 0) {\n ordered.splice(at + 1, 0, ...after);\n ordered.splice(at, 0, ...before);\n } else {\n ordered.push(...before, ...after);\n }\n ordered.push(...overlays('top'));\n return ordered;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"layerOrder.js","names":["STANDARD_SLOT_SET","STANDARD_SLOTS","composeOrderedLayers","afterByParent","beforeByParent","tail","allCustomKeys","key","value","layersMap","isCustomEntry","arr","emitted","out","background","emitChild","child","emitBefore","emitAfter","parentKey","slot","sceneLayers","sl","layer","standardLayers","t","anchorOf","inCycle","at","hops","ref","placeToolOverlays","before","overlays","after","selectionOverlay","ordered"],"sources":["../../src/canvas/layerOrder.ts"],"sourcesContent":["import type { RenderLayer } from '../core/layers/render';\nimport type { LayerSlotValue } from './Canvas';\nimport { STANDARD_SLOTS, isCustomEntry } from './layerSlots';\nimport type { OverlayPosition } from '@weasel-js/routing';\n\nconst STANDARD_SLOT_SET = new Set<string>(STANDARD_SLOTS);\n\n/**\n * Compose the final ordered layer list from a layersMap and the resolved\n * standard-slot layers.\n *\n * Semantics:\n * - Standard slots are emitted in `STANDARD_SLOTS` order.\n * - Each standard-slot layer is preceded by its `before` chain (deepest first)\n * and followed by its `after` chain (ancestors first).\n * - `after: 'X'` / `before: 'X'` accept standard slot names OR other\n * custom-layer keys, and chains may mix the two — a custom anchored\n * `before` a slot can itself carry an `after` chain, and vice versa.\n * - Customs with neither `after` nor `before` go to a tail bucket emitted\n * after all standard slots.\n * - Dangling references (target unresolvable) and cycles go to the tail\n * with a `console.warn`.\n * - An entry with both `after` and `before` is placed by `after`.\n *\n * Pure function — no side effects beyond `console.warn` on malformed input.\n */\nexport function composeOrderedLayers<TNode extends { id: string }, TPose>(\n layersMap: Record<string, LayerSlotValue<TNode, TPose> | null | undefined>,\n standardLayers: Partial<Record<(typeof STANDARD_SLOTS)[number], RenderLayer<unknown>>>,\n /** When the `scene` slot is split into per-scene-layer canvas layers (keyed\n * `scene:<layerId>`), pass them here in render order. Each key becomes an\n * anchor point, so customs can target `before/after: 'scene:<layerId>'`.\n * `before/after: 'scene'` still works (before the first / after the last).\n * When omitted, the `scene` slot uses `standardLayers.scene` as before. */\n sceneLayers?: ReadonlyArray<{ key: string; layer: RenderLayer<unknown> }>,\n /** Drawn beneath everything else, grid and customs anchored before the\n * scene included — a fill that must never cover what is laid over it. */\n background?: RenderLayer<unknown>,\n): RenderLayer<unknown>[] {\n // Map<parent key, [{ key, layer }, ...]> — children grouped by their declared anchor.\n const afterByParent = new Map<string, Array<{ key: string; layer: RenderLayer<unknown> }>>();\n const beforeByParent = new Map<string, Array<{ key: string; layer: RenderLayer<unknown> }>>();\n const tail: Array<{ key: string; layer: RenderLayer<unknown> }> = [];\n const allCustomKeys = new Set<string>();\n\n for (const [key, value] of Object.entries(layersMap)) {\n if (STANDARD_SLOT_SET.has(key)) continue;\n if (!isCustomEntry(value)) continue;\n allCustomKeys.add(key);\n if (value.after) {\n const arr = afterByParent.get(value.after) ?? [];\n arr.push({ key, layer: value.layer });\n afterByParent.set(value.after, arr);\n } else if (value.before) {\n const arr = beforeByParent.get(value.before) ?? [];\n arr.push({ key, layer: value.layer });\n beforeByParent.set(value.before, arr);\n } else {\n tail.push({ key, layer: value.layer });\n }\n }\n\n const emitted = new Set<string>();\n const out: RenderLayer<unknown>[] = background ? [background] : [];\n\n function emitChild(child: { key: string; layer: RenderLayer<unknown> }) {\n // A custom has one anchor, so it is reached twice only when `sceneLayers`\n // repeats a key.\n if (emitted.has(child.key)) return;\n emitted.add(child.key);\n emitBefore(child.key);\n out.push(child.layer);\n emitAfter(child.key);\n }\n\n function emitAfter(parentKey: string) {\n for (const child of afterByParent.get(parentKey) ?? []) emitChild(child);\n }\n\n function emitBefore(parentKey: string) {\n for (const child of beforeByParent.get(parentKey) ?? []) emitChild(child);\n }\n\n for (const slot of STANDARD_SLOTS) {\n if (slot === 'scene' && sceneLayers) {\n // Split scene slot: emit each per-scene-layer canvas layer in order, with\n // its own anchor point. `before/after: 'scene'` brackets the whole group.\n emitBefore('scene');\n for (const sl of sceneLayers) {\n emitBefore(sl.key);\n out.push(sl.layer);\n emitAfter(sl.key);\n }\n emitAfter('scene');\n continue;\n }\n emitBefore(slot);\n const layer = standardLayers[slot];\n if (layer) out.push(layer);\n emitAfter(slot);\n }\n\n // Tail customs (neither after nor before).\n for (const t of tail) {\n if (emitted.has(t.key)) continue;\n emitted.add(t.key);\n out.push(t.layer);\n }\n\n const anchorOf = (key: string): string | undefined => {\n const value = layersMap[key];\n return isCustomEntry(value) ? value.after ?? value.before : undefined;\n };\n const inCycle = (key: string): boolean => {\n let at = anchorOf(key);\n for (let hops = 0; at !== undefined && hops < allCustomKeys.size; hops++) {\n if (at === key) return true;\n at = anchorOf(at);\n }\n return false;\n };\n\n // Orphans: customs that declared an `after`/`before` but never got emitted,\n // because the chain of anchors loops or ends on a key nothing defines.\n for (const key of allCustomKeys) {\n if (emitted.has(key)) continue;\n const value = layersMap[key];\n if (!isCustomEntry(value)) continue;\n const ref = value.after ?? value.before;\n console.warn(inCycle(key)\n ? `composeOrderedLayers: cycle detected involving custom layer \"${key}\" — falling back to tail`\n : `composeOrderedLayers: dangling reference \"${ref}\" from custom layer \"${key}\" — falling back to tail`);\n emitted.add(key);\n out.push(value.layer);\n }\n\n return out;\n}\n\n/**\n * Splice a tool's positioned overlays into an already-composed layer list.\n *\n * `'top'` appends; the other two anchor on the selection-chrome layer's index\n * in `ordered`. With no selection chrome present the anchored overlays fall\n * back to the tail, keeping their relative order. Mutates and returns\n * `ordered`.\n */\nexport function placeToolOverlays(\n ordered: RenderLayer<unknown>[],\n selectionOverlay: RenderLayer<unknown> | undefined,\n overlays: (position: OverlayPosition) => RenderLayer<unknown>[],\n): RenderLayer<unknown>[] {\n const before = overlays('before-selection');\n const after = overlays('after-selection');\n const at = selectionOverlay ? ordered.indexOf(selectionOverlay) : -1;\n if (at >= 0) {\n ordered.splice(at + 1, 0, ...after);\n ordered.splice(at, 0, ...before);\n } else {\n ordered.push(...before, ...after);\n }\n ordered.push(...overlays('top'));\n return ordered;\n}\n"],"mappings":"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"}
@@ -10,8 +10,8 @@ var n = 8, r = {
10
10
  }
11
11
  }, i = "#ffffff", a = 1, o = [2, 3];
12
12
  function s(e, t, n) {
13
- let r = Math.max(1, t.width - n * 2), i = Math.max(1, t.height - n * 2);
14
- if (e.width <= 0 || e.height <= 0) return {
13
+ let r = Math.max(1, t.width - n * 2), i = Math.max(1, t.height - n * 2), a = e.width > 0, o = e.height > 0;
14
+ if (!a && !o) return {
15
15
  x: e.x - t.width / 2,
16
16
  y: e.y - t.height / 2,
17
17
  scale: {
@@ -19,7 +19,7 @@ function s(e, t, n) {
19
19
  y: 1
20
20
  }
21
21
  };
22
- let a = r / e.width, o = i / e.height, s = Math.min(a, o), c = e.x + e.width / 2, l = e.y + e.height / 2;
22
+ let s = Math.min(a ? r / e.width : Infinity, o ? i / e.height : Infinity), c = e.x + Math.max(0, e.width) / 2, l = e.y + Math.max(0, e.height) / 2;
23
23
  return {
24
24
  x: c - t.width / (2 * s),
25
25
  y: l - t.height / (2 * s),
@@ -34,6 +34,7 @@ function c(n, r) {
34
34
  for (let l of n.nodes.values()) {
35
35
  if (l.kind !== "leaf") continue;
36
36
  let u = t(r, l).getBounds(e(n, l));
37
+ if (!Number.isFinite(u.x + u.y + u.width + u.height)) continue;
37
38
  u.x < i && (i = u.x), u.y < a && (a = u.y);
38
39
  let d = u.x + u.width, f = u.y + u.height;
39
40
  d > o && (o = d), f > s && (s = f), c = !0;
@@ -54,23 +55,24 @@ function l(e, t, i, a, o = {}) {
54
55
  }
55
56
  return s(i.rect, t, l);
56
57
  }
57
- function u(e, t, n = {}) {
58
+ function u(e, t, n = {}, r) {
59
+ let s = e.x, c = e.y, l = t.width / e.scale.x, u = t.height / e.scale.y, d = r ? 1 / Math.abs(r.scale.x) : 1;
58
60
  return {
59
61
  kind: "path",
60
62
  path: {
61
63
  kind: "rect",
62
- x: e.x,
63
- y: e.y,
64
- width: t.width / e.scale.x,
65
- height: t.height / e.scale.y
64
+ x: s,
65
+ y: c,
66
+ width: l,
67
+ height: u
66
68
  },
67
69
  stroke: {
68
70
  paint: {
69
71
  fill: "solid",
70
72
  color: n.stroke ?? i
71
73
  },
72
- width: n.width ?? a,
73
- dash: n.dash ?? [...o]
74
+ width: (n.width ?? a) * d,
75
+ dash: (n.dash ?? o).map((e) => e * d)
74
76
  }
75
77
  };
76
78
  }
@@ -1 +1 @@
1
- {"version":3,"file":"minimapMath.js","names":["DEFAULT_FIT_PADDING","FALLBACK_FIT_VIEW","DEFAULT_INDICATOR_STROKE","DEFAULT_INDICATOR_WIDTH","DEFAULT_INDICATOR_DASH","fitRectInto","availW","dims","padding","availH","rect","sx","sy","worldCx","worldCy","sceneLeafBounds","minX","minY","maxX","maxY","any","node","scene","b","poseDescriptorForNode","descriptor","documentPose","right","bottom","computeFitView","opts","fit","computeIndicatorCommand","mainView","mainViewDims","style"],"sources":["../../src/canvas/minimapMath.ts"],"sourcesContent":["/**\n * Pure math helpers for `<MinimapCanvas>`.\n *\n * `computeFitView` derives a `View` that fits a chosen world rect into a\n * minimap canvas of given pixel dims. `computeIndicatorCommand` builds the\n * dashed \"visible window\" rect — the main canvas's screen window projected\n * into the minimap's own screen coordinates.\n *\n * Neither helper touches GL, the DOM, or the renderer; they're pure\n * functions, unit-testable with `expect`.\n */\nimport { documentPose } from '../core/scene';\nimport type { Scene } from '../core/scene';\nimport type { View } from '../core/viewport/view';\nimport type { Bounds, ViewportDims } from '../core/viewport/fitViewToBounds';\nimport { poseDescriptorForNode, type PoseDescriptor } from '../core/geometry/poseDescriptor';\nimport type { PathDrawCommand } from '../renderer/DrawCommand';\n\n/** Inset (CSS px) around the fit rect on each side. Matches the spec note. */\nconst DEFAULT_FIT_PADDING = 8;\n\n/**\n * Fallback view returned when there is nothing to fit (empty scene, etc.).\n * Identity view at the origin — produces a 1:1 mapping centered on (0, 0).\n */\nexport const FALLBACK_FIT_VIEW: View = { x: 0, y: 0, scale: { x: 1, y: 1 } };\n\n/**\n * Fit policy for `<MinimapCanvas>` / `computeFitView`.\n *\n * - `\"scene\"` — union of leaf-node pose AABBs (via the pose descriptor).\n * - `{ kind: \"world\", rect }` — caller-supplied world rect.\n * - `(scene, dims) => View` — caller derives the view directly.\n */\nexport type MinimapFit<TData, TLayer extends string, TPose> =\n | 'scene'\n | { kind: 'world'; rect: Bounds }\n | ((scene: Scene<TData, TLayer, TPose>, dims: ViewportDims) => View);\n\n/** Style for the visible-window indicator rect drawn over the minimap. */\nexport interface IndicatorStyle {\n /** Stroke color (any CSS color). Defaults to `'#ffffff'`. */\n stroke?: string;\n /** Stroke width in CSS px. Defaults to `1`. */\n width?: number;\n /** Dash pattern in CSS px. Defaults to `[2, 3]`. */\n dash?: number[];\n}\n\nconst DEFAULT_INDICATOR_STROKE = '#ffffff';\nconst DEFAULT_INDICATOR_WIDTH = 1;\nconst DEFAULT_INDICATOR_DASH: readonly number[] = [2, 3];\n\n/** Internal: fit a world rect uniformly into dims with padding. */\nfunction fitRectInto(rect: Bounds, dims: ViewportDims, padding: number): View {\n const availW = Math.max(1, dims.width - padding * 2);\n const availH = Math.max(1, dims.height - padding * 2);\n // Degenerate rect (zero / negative area): fall back to identity at the\n // rect's top-left so we at least center *something* sensible.\n if (rect.width <= 0 || rect.height <= 0) {\n return {\n x: rect.x - dims.width / 2,\n y: rect.y - dims.height / 2,\n scale: { x: 1, y: 1 },\n };\n }\n const sx = availW / rect.width;\n const sy = availH / rect.height;\n const s = Math.min(sx, sy);\n const worldCx = rect.x + rect.width / 2;\n const worldCy = rect.y + rect.height / 2;\n return {\n x: worldCx - dims.width / (2 * s),\n y: worldCy - dims.height / (2 * s),\n scale: { x: s, y: s },\n };\n}\n\n/**\n * Compute the AABB union of all leaf-node poses in the scene, via\n * `descriptor`. Container nodes are skipped — their effective bounds come\n * from their descendant leaves. Returns `null` when the scene has no\n * leaves.\n */\nfunction sceneLeafBounds<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n descriptor: PoseDescriptor<TPose>,\n): Bounds | null {\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n let any = false;\n for (const node of scene.nodes.values()) {\n if (node.kind !== 'leaf') continue;\n const b = poseDescriptorForNode(descriptor, node).getBounds(documentPose(scene, node));\n if (b.x < minX) minX = b.x;\n if (b.y < minY) minY = b.y;\n const right = b.x + b.width;\n const bottom = b.y + b.height;\n if (right > maxX) maxX = right;\n if (bottom > maxY) maxY = bottom;\n any = true;\n }\n if (!any) return null;\n return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n}\n\n/** Options for {@link computeFitView}. */\nexport interface ComputeFitViewOptions {\n /** Inset (CSS px) around the fit rect on each side. Defaults to `8`. */\n padding?: number;\n}\n\n/**\n * Derive a `View` that fits the chosen world rect into a minimap of\n * `dims.width × dims.height` CSS px. Uniform scale on both axes; the rect\n * is centered with `padding` (default 8) on each side. The smaller-axis\n * fit ratio wins, so the whole rect is visible.\n *\n * - `fit === \"scene\"`: union AABB of leaf-node poses via `descriptor`.\n * Empty scene → {@link FALLBACK_FIT_VIEW}.\n * - `fit` is `{ kind: \"world\", rect }`: fit `rect`. A degenerate rect\n * (zero or negative area) falls back to identity scale centered on the\n * rect's top-left — we don't divide by zero.\n * - `fit` is a function: called with `(scene, dims)`; its return is\n * passed through unchanged.\n */\nexport function computeFitView<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n dims: ViewportDims,\n fit: MinimapFit<TData, TLayer, TPose>,\n descriptor: PoseDescriptor<TPose>,\n opts: ComputeFitViewOptions = {},\n): View {\n const padding = opts.padding ?? DEFAULT_FIT_PADDING;\n\n if (typeof fit === 'function') {\n return fit(scene, dims);\n }\n if (fit === 'scene') {\n const rect = sceneLeafBounds(scene, descriptor);\n if (rect === null) return FALLBACK_FIT_VIEW;\n return fitRectInto(rect, dims, padding);\n }\n // { kind: 'world', rect }\n return fitRectInto(fit.rect, dims, padding);\n}\n\n/**\n * Build the visible-window indicator `DrawCommand` for the minimap.\n *\n * The main canvas's visible world rect is:\n * ```\n * worldX = mainView.x\n * worldY = mainView.y\n * worldW = mainViewDims.width / mainView.scale.x\n * worldH = mainViewDims.height / mainView.scale.y\n * ```\n * That world rect is then transformed into the minimap's screen\n * coordinates through `minimapView`.\n *\n * The returned command is a dashed-stroke `path` rect with no fill.\n */\nexport function computeIndicatorCommand(\n mainView: View,\n mainViewDims: ViewportDims,\n style: IndicatorStyle = {},\n): PathDrawCommand {\n // World-space rect: returned as-is. The caller wraps the command list\n // (scene + extras) in a group transformed by the minimap's view, so the\n // indicator lands in screen coords automatically. Returning screen coords\n // here would get the view transform applied a second time — a bug that\n // manifested as the indicator drifting at non-identity minimap scale.\n const x = mainView.x;\n const y = mainView.y;\n const width = mainViewDims.width / mainView.scale.x;\n const height = mainViewDims.height / mainView.scale.y;\n return {\n kind: 'path',\n path: { kind: 'rect', x, y, width, height },\n stroke: {\n paint: { fill: 'solid', color: style.stroke ?? DEFAULT_INDICATOR_STROKE },\n width: style.width ?? DEFAULT_INDICATOR_WIDTH,\n dash: style.dash ?? [...DEFAULT_INDICATOR_DASH],\n },\n };\n}\n"],"mappings":";;;AAmBA,IAAMA,IAAsB,GAMfC,IAA0B;CAAE,GAAG;CAAG,GAAG;CAAG,OAAO;EAAE,GAAG;EAAG,GAAG;CAAE;AAAE,GAwBrEC,IAA2B,WAC3BC,IAA0B,GAC1BC,IAA4C,CAAC,GAAG,CAAC;AAGvD,SAASC,EAAY,GAAc,GAAoB,GAAuB;CAC5E,IAAMC,IAAS,KAAK,IAAI,GAAGC,EAAK,QAAQC,IAAU,CAAC,GAC7CC,IAAS,KAAK,IAAI,GAAGF,EAAK,SAASC,IAAU,CAAC;CAGpD,IAAIE,EAAK,SAAS,KAAKA,EAAK,UAAU,GACpC,OAAO;EACL,GAAGA,EAAK,IAAIH,EAAK,QAAQ;EACzB,GAAGG,EAAK,IAAIH,EAAK,SAAS;EAC1B,OAAO;GAAE,GAAG;GAAG,GAAG;EAAE;CACtB;CAEF,IAAMI,IAAKL,IAASI,EAAK,OACnBE,IAAKH,IAASC,EAAK,QACnB,IAAI,KAAK,IAAIC,GAAIC,CAAE,GACnBC,IAAUH,EAAK,IAAIA,EAAK,QAAQ,GAChCI,IAAUJ,EAAK,IAAIA,EAAK,SAAS;CACvC,OAAO;EACL,GAAGG,IAAUN,EAAK,SAAS,IAAI;EAC/B,GAAGO,IAAUP,EAAK,UAAU,IAAI;EAChC,OAAO;GAAE,GAAG;GAAG,GAAG;EAAE;CACtB;AACF;AAQA,SAASQ,EACP,GACA,GACe;CACf,IAAIC,IAAO,UACPC,IAAO,UACPC,IAAO,WACPC,IAAO,WACPC,IAAM;CACV,KAAK,IAAMC,KAAQC,EAAM,MAAM,OAAO,GAAG;EACvC,IAAID,EAAK,SAAS,QAAQ;EAC1B,IAAME,IAAIC,EAAsBC,GAAYJ,CAAI,CAAC,CAAC,UAAUK,EAAaJ,GAAOD,CAAI,CAAC;EAErF,AADIE,EAAE,IAAIP,MAAM,IAAOO,EAAE,IACrBA,EAAE,IAAIN,MAAM,IAAOM,EAAE;EACzB,IAAMI,IAAQJ,EAAE,IAAIA,EAAE,OAChBK,IAASL,EAAE,IAAIA,EAAE;EAGvB,AAFII,IAAQT,MAAM,IAAOS,IACrBC,IAAST,MAAM,IAAOS,IAC1B,IAAM;CACR;CAEA,OADKR,IACE;EAAE,GAAGJ;EAAM,GAAGC;EAAM,OAAOC,IAAOF;EAAM,QAAQG,IAAOF;CAAK,IADlD;AAEnB;AAsBA,SAAgBY,EACd,GACA,GACA,GACA,GACA,IAA8B,CAAC,GACzB;CACN,IAAMrB,IAAUsB,EAAK,WAAW9B;CAEhC,IAAI,OAAO+B,KAAQ,YACjB,OAAOA,EAAIT,GAAOf,CAAI;CAExB,IAAIwB,MAAQ,SAAS;EACnB,IAAMrB,IAAOK,EAAgBO,GAAOG,CAAU;EAE9C,OADIf,MAAS,OAAaT,IACnBI,EAAYK,GAAMH,GAAMC,CAAO;CACxC;CAEA,OAAOH,EAAY0B,EAAI,MAAMxB,GAAMC,CAAO;AAC5C;AAiBA,SAAgBwB,EACd,GACA,GACA,IAAwB,CAAC,GACR;CAUjB,OAAO;EACL,MAAM;EACN,MAAM;GAAE,MAAM;GAAQ,GANdC,EAAS;GAMQ,GALjBA,EAAS;GAKW,OAJhBC,EAAa,QAAQD,EAAS,MAAM;GAIb,QAHtBC,EAAa,SAASD,EAAS,MAAM;EAGR;EAC1C,QAAQ;GACN,OAAO;IAAE,MAAM;IAAS,OAAOE,EAAM,UAAUjC;GAAyB;GACxE,OAAOiC,EAAM,SAAShC;GACtB,MAAMgC,EAAM,QAAQ,CAAC,GAAG/B,CAAsB;EAChD;CACF;AACF"}
1
+ {"version":3,"file":"minimapMath.js","names":["DEFAULT_FIT_PADDING","FALLBACK_FIT_VIEW","DEFAULT_INDICATOR_STROKE","DEFAULT_INDICATOR_WIDTH","DEFAULT_INDICATOR_DASH","fitRectInto","availW","dims","padding","availH","hasW","rect","hasH","worldCx","worldCy","sceneLeafBounds","minX","minY","maxX","maxY","any","node","scene","b","poseDescriptorForNode","descriptor","documentPose","right","bottom","computeFitView","opts","fit","computeIndicatorCommand","x","mainView","y","width","mainViewDims","height","px","minimapView","style","d"],"sources":["../../src/canvas/minimapMath.ts"],"sourcesContent":["/**\n * Pure math helpers for `<MinimapCanvas>`.\n *\n * `computeFitView` derives a `View` that fits a chosen world rect into a\n * minimap canvas of given pixel dims. `computeIndicatorCommand` builds the\n * dashed \"visible window\" rect — the main canvas's screen window projected\n * into the minimap's own screen coordinates.\n *\n * Neither helper touches GL, the DOM, or the renderer; they're pure\n * functions, unit-testable with `expect`.\n */\nimport { documentPose } from '../core/scene';\nimport type { Scene } from '../core/scene';\nimport type { View } from '../core/viewport/view';\nimport type { Bounds, ViewportDims } from '../core/viewport/fitViewToBounds';\nimport { poseDescriptorForNode, type PoseDescriptor } from '../core/geometry/poseDescriptor';\nimport type { PathDrawCommand } from '../renderer/DrawCommand';\n\n/** Inset (CSS px) around the fit rect on each side. Matches the spec note. */\nconst DEFAULT_FIT_PADDING = 8;\n\n/**\n * Fallback view returned when there is nothing to fit (empty scene, etc.).\n * Identity view at the origin — produces a 1:1 mapping centered on (0, 0).\n */\nexport const FALLBACK_FIT_VIEW: View = { x: 0, y: 0, scale: { x: 1, y: 1 } };\n\n/**\n * Fit policy for `<MinimapCanvas>` / `computeFitView`.\n *\n * - `\"scene\"` — union of leaf-node pose AABBs (via the pose descriptor).\n * - `{ kind: \"world\", rect }` — caller-supplied world rect.\n * - `(scene, dims) => View` — caller derives the view directly.\n */\nexport type MinimapFit<TData, TLayer extends string, TPose> =\n | 'scene'\n | { kind: 'world'; rect: Bounds }\n | ((scene: Scene<TData, TLayer, TPose>, dims: ViewportDims) => View);\n\n/** Style for the visible-window indicator rect drawn over the minimap. */\nexport interface IndicatorStyle {\n /** Stroke color (any CSS color). Defaults to `'#ffffff'`. */\n stroke?: string;\n /** Stroke width in CSS px. Defaults to `1`. */\n width?: number;\n /** Dash pattern in CSS px. Defaults to `[2, 3]`. */\n dash?: number[];\n}\n\nconst DEFAULT_INDICATOR_STROKE = '#ffffff';\nconst DEFAULT_INDICATOR_WIDTH = 1;\nconst DEFAULT_INDICATOR_DASH: readonly number[] = [2, 3];\n\n/** Internal: fit a world rect uniformly into dims with padding. */\nfunction fitRectInto(rect: Bounds, dims: ViewportDims, padding: number): View {\n const availW = Math.max(1, dims.width - padding * 2);\n const availH = Math.max(1, dims.height - padding * 2);\n const hasW = rect.width > 0;\n const hasH = rect.height > 0;\n if (!hasW && !hasH) {\n return {\n x: rect.x - dims.width / 2,\n y: rect.y - dims.height / 2,\n scale: { x: 1, y: 1 },\n };\n }\n const s = Math.min(\n hasW ? availW / rect.width : Infinity,\n hasH ? availH / rect.height : Infinity,\n );\n const worldCx = rect.x + Math.max(0, rect.width) / 2;\n const worldCy = rect.y + Math.max(0, rect.height) / 2;\n return {\n x: worldCx - dims.width / (2 * s),\n y: worldCy - dims.height / (2 * s),\n scale: { x: s, y: s },\n };\n}\n\n/**\n * Compute the AABB union of all leaf-node poses in the scene, via\n * `descriptor`. Container nodes are skipped — their effective bounds come\n * from their descendant leaves, and so is a leaf whose bounds are not finite,\n * which would otherwise make the whole union non-finite. Returns `null` when\n * the scene has no leaf with bounds.\n */\nfunction sceneLeafBounds<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n descriptor: PoseDescriptor<TPose>,\n): Bounds | null {\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n let any = false;\n for (const node of scene.nodes.values()) {\n if (node.kind !== 'leaf') continue;\n const b = poseDescriptorForNode(descriptor, node).getBounds(documentPose(scene, node));\n if (!Number.isFinite(b.x + b.y + b.width + b.height)) continue;\n if (b.x < minX) minX = b.x;\n if (b.y < minY) minY = b.y;\n const right = b.x + b.width;\n const bottom = b.y + b.height;\n if (right > maxX) maxX = right;\n if (bottom > maxY) maxY = bottom;\n any = true;\n }\n if (!any) return null;\n return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n}\n\n/** Options for {@link computeFitView}. */\nexport interface ComputeFitViewOptions {\n /** Inset (CSS px) around the fit rect on each side. Defaults to `8`. */\n padding?: number;\n}\n\n/**\n * Derive a `View` that fits the chosen world rect into a minimap of\n * `dims.width × dims.height` CSS px. Uniform scale on both axes; the rect\n * is centered with `padding` (default 8) on each side. The smaller-axis\n * fit ratio wins, so the whole rect is visible.\n *\n * - `fit === \"scene\"`: union AABB of leaf-node poses via `descriptor`.\n * Empty scene → {@link FALLBACK_FIT_VIEW}.\n * - `fit` is `{ kind: \"world\", rect }`: fit `rect`.\n * - `fit` is a function: called with `(scene, dims)`; its return is\n * passed through unchanged.\n *\n * A rect with one zero axis (a line) is fitted along the other. One with no\n * positive axis falls back to identity scale centered on its top-left.\n */\nexport function computeFitView<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n dims: ViewportDims,\n fit: MinimapFit<TData, TLayer, TPose>,\n descriptor: PoseDescriptor<TPose>,\n opts: ComputeFitViewOptions = {},\n): View {\n const padding = opts.padding ?? DEFAULT_FIT_PADDING;\n\n if (typeof fit === 'function') {\n return fit(scene, dims);\n }\n if (fit === 'scene') {\n const rect = sceneLeafBounds(scene, descriptor);\n if (rect === null) return FALLBACK_FIT_VIEW;\n return fitRectInto(rect, dims, padding);\n }\n // { kind: 'world', rect }\n return fitRectInto(fit.rect, dims, padding);\n}\n\n/**\n * Build the visible-window indicator `DrawCommand` for the minimap.\n *\n * The main canvas's visible world rect is:\n * ```\n * worldX = mainView.x\n * worldY = mainView.y\n * worldW = mainViewDims.width / mainView.scale.x\n * worldH = mainViewDims.height / mainView.scale.y\n * ```\n * That world rect is then transformed into the minimap's screen\n * coordinates through `minimapView`.\n *\n * The returned command is a dashed-stroke `path` rect with no fill. Pass the\n * `minimapView` it will be drawn through, so the stroke width and dash come\n * out in CSS px at any fit scale; without it they are in world units.\n */\nexport function computeIndicatorCommand(\n mainView: View,\n mainViewDims: ViewportDims,\n style: IndicatorStyle = {},\n minimapView?: View,\n): PathDrawCommand {\n // World-space rect: returned as-is. The caller wraps the command list\n // (scene + extras) in a group transformed by the minimap's view, so the\n // indicator lands in screen coords automatically. Returning screen coords\n // here would get the view transform applied a second time — a bug that\n // manifested as the indicator drifting at non-identity minimap scale.\n const x = mainView.x;\n const y = mainView.y;\n const width = mainViewDims.width / mainView.scale.x;\n const height = mainViewDims.height / mainView.scale.y;\n const px = minimapView ? 1 / Math.abs(minimapView.scale.x) : 1;\n return {\n kind: 'path',\n path: { kind: 'rect', x, y, width, height },\n stroke: {\n paint: { fill: 'solid', color: style.stroke ?? DEFAULT_INDICATOR_STROKE },\n width: (style.width ?? DEFAULT_INDICATOR_WIDTH) * px,\n dash: (style.dash ?? DEFAULT_INDICATOR_DASH).map((d) => d * px),\n },\n };\n}\n"],"mappings":"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"}