@weasel-js/core 1.10.0 → 1.10.1

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 (327) hide show
  1. package/CHANGELOG.md +168 -0
  2. package/README.md +33 -0
  3. package/dist/affordances/composeAffordanceLayer.js +21 -21
  4. package/dist/affordances/composeAffordanceLayer.js.map +1 -1
  5. package/dist/affordances/hitAffordanceRegions.js +48 -32
  6. package/dist/affordances/hitAffordanceRegions.js.map +1 -1
  7. package/dist/affordances/pathAnchors.js +50 -28
  8. package/dist/affordances/pathAnchors.js.map +1 -1
  9. package/dist/affordances/rotateCursor.js +12 -0
  10. package/dist/affordances/rotateCursor.js.map +1 -0
  11. package/dist/affordances/rotationHandle.js +40 -43
  12. package/dist/affordances/rotationHandle.js.map +1 -1
  13. package/dist/animation/rig/rigNode.js +9 -3
  14. package/dist/animation/rig/rigNode.js.map +1 -1
  15. package/dist/animation/rig/rigSeat.js +46 -13
  16. package/dist/animation/rig/rigSeat.js.map +1 -1
  17. package/dist/animation/rig/skinBinding.js +22 -14
  18. package/dist/animation/rig/skinBinding.js.map +1 -1
  19. package/dist/barrel/canvas.js +29 -28
  20. package/dist/barrel/interactions.js +33 -32
  21. package/dist/barrel/paths.js +11 -10
  22. package/dist/barrel/routing.js +49 -48
  23. package/dist/barrel/scene.js +10 -10
  24. package/dist/barrel/viewport.js +24 -21
  25. package/dist/canvas/Canvas.js +6 -17
  26. package/dist/canvas/Canvas.js.map +1 -1
  27. package/dist/canvas/CanvasView.js +34 -37
  28. package/dist/canvas/CanvasView.js.map +1 -1
  29. package/dist/canvas/CursorCoordsHud.js +3 -3
  30. package/dist/canvas/CursorCoordsHud.js.map +1 -1
  31. package/dist/canvas/DrawCanvas.js +3 -3
  32. package/dist/canvas/DrawCanvas.js.map +1 -1
  33. package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js +24 -23
  34. package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js.map +1 -1
  35. package/dist/canvas/SceneCanvas/StandardActionsRegistrar.js +40 -49
  36. package/dist/canvas/SceneCanvas/StandardActionsRegistrar.js.map +1 -1
  37. package/dist/canvas/SceneCanvas/defaultNodeProperties.js +297 -259
  38. package/dist/canvas/SceneCanvas/defaultNodeProperties.js.map +1 -1
  39. package/dist/canvas/SceneCanvas/features.js +3 -24
  40. package/dist/canvas/SceneCanvas/features.js.map +1 -1
  41. package/dist/canvas/SceneCanvas/insertPreviewCommands.js +97 -0
  42. package/dist/canvas/SceneCanvas/insertPreviewCommands.js.map +1 -0
  43. package/dist/canvas/SceneCanvas/poseGeometry.js.map +1 -1
  44. package/dist/canvas/SceneCanvas/props.js.map +1 -1
  45. package/dist/canvas/SceneCanvas/scenePointQuery.js +50 -0
  46. package/dist/canvas/SceneCanvas/scenePointQuery.js.map +1 -0
  47. package/dist/canvas/SceneCanvas/shapeGeneratorProperties.js +52 -0
  48. package/dist/canvas/SceneCanvas/shapeGeneratorProperties.js.map +1 -0
  49. package/dist/canvas/SceneCanvas/useActionTunings.js +17 -0
  50. package/dist/canvas/SceneCanvas/useActionTunings.js.map +1 -0
  51. package/dist/canvas/SceneCanvas/useCanvasCamera.js +37 -29
  52. package/dist/canvas/SceneCanvas/useCanvasCamera.js.map +1 -1
  53. package/dist/canvas/SceneCanvas/useCanvasDispatcher.js +11 -11
  54. package/dist/canvas/SceneCanvas/useCanvasDispatcher.js.map +1 -1
  55. package/dist/canvas/SceneCanvas/useDispatcherOverlayLayer.js +45 -109
  56. package/dist/canvas/SceneCanvas/useDispatcherOverlayLayer.js.map +1 -1
  57. package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js +49 -41
  58. package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js.map +1 -1
  59. package/dist/canvas/SceneCanvas/useSceneSelectTool.js +64 -96
  60. package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
  61. package/dist/canvas/SceneCanvas/worldPointAt.js +3 -4
  62. package/dist/canvas/SceneCanvas/worldPointAt.js.map +1 -1
  63. package/dist/canvas/SceneCanvas.js +77 -80
  64. package/dist/canvas/SceneCanvas.js.map +1 -1
  65. package/dist/canvas/SceneViewCanvas.js +3 -3
  66. package/dist/canvas/SceneViewCanvas.js.map +1 -1
  67. package/dist/canvas/SurfaceCanvas.js +40 -0
  68. package/dist/canvas/SurfaceCanvas.js.map +1 -0
  69. package/dist/canvas/adapterDerivedPath.js +13 -13
  70. package/dist/canvas/adapterDerivedPath.js.map +1 -1
  71. package/dist/canvas/adapterPickSource.js +34 -0
  72. package/dist/canvas/adapterPickSource.js.map +1 -0
  73. package/dist/canvas/affordanceAt.js +1 -1
  74. package/dist/canvas/affordanceAt.js.map +1 -1
  75. package/dist/canvas/buildSceneLayer.js.map +1 -1
  76. package/dist/canvas/buildSceneTree.js +56 -34
  77. package/dist/canvas/buildSceneTree.js.map +1 -1
  78. package/dist/canvas/copiesAbove.js +27 -0
  79. package/dist/canvas/copiesAbove.js.map +1 -0
  80. package/dist/canvas/defaultDrawOne.js +24 -23
  81. package/dist/canvas/defaultDrawOne.js.map +1 -1
  82. package/dist/canvas/deps/editAnchors.js +45 -50
  83. package/dist/canvas/deps/editAnchors.js.map +1 -1
  84. package/dist/canvas/deps/hitTestArea.js +30 -15
  85. package/dist/canvas/deps/hitTestArea.js.map +1 -1
  86. package/dist/canvas/deps/insert.js +134 -100
  87. package/dist/canvas/deps/insert.js.map +1 -1
  88. package/dist/canvas/deps/layout.js +9 -15
  89. package/dist/canvas/deps/layout.js.map +1 -1
  90. package/dist/canvas/derivedPath.js +53 -29
  91. package/dist/canvas/derivedPath.js.map +1 -1
  92. package/dist/canvas/insertPreviewExtent.js +33 -31
  93. package/dist/canvas/insertPreviewExtent.js.map +1 -1
  94. package/dist/canvas/overChildren.js +29 -0
  95. package/dist/canvas/overChildren.js.map +1 -0
  96. package/dist/canvas/paintedCursorLayer.js.map +1 -1
  97. package/dist/canvas/pickReach.js +23 -0
  98. package/dist/canvas/pickReach.js.map +1 -0
  99. package/dist/canvas/pickWalk.js +51 -80
  100. package/dist/canvas/pickWalk.js.map +1 -1
  101. package/dist/canvas/sceneAdapter.js +14 -35
  102. package/dist/canvas/sceneAdapter.js.map +1 -1
  103. package/dist/canvas/sceneSlotWiring.js +11 -10
  104. package/dist/canvas/sceneSlotWiring.js.map +1 -1
  105. package/dist/canvas/sceneViewRender.js +38 -44
  106. package/dist/canvas/sceneViewRender.js.map +1 -1
  107. package/dist/canvas/useCanvasView.js +42 -44
  108. package/dist/canvas/useCanvasView.js.map +1 -1
  109. package/dist/canvas/useSceneAdapter.js +22 -0
  110. package/dist/canvas/useSceneAdapter.js.map +1 -0
  111. package/dist/canvas/useSurfaceInput.js +13 -16
  112. package/dist/canvas/useSurfaceInput.js.map +1 -1
  113. package/dist/canvas/viewRegistry.js +31 -23
  114. package/dist/canvas/viewRegistry.js.map +1 -1
  115. package/dist/canvas/wrapNodeOutput.js +7 -2
  116. package/dist/canvas/wrapNodeOutput.js.map +1 -1
  117. package/dist/clipboard.d.ts +1 -1
  118. package/dist/{clone-2a5hVbBd.d.ts → clone-C0eADtS0.d.ts} +13 -13
  119. package/dist/clone.d.ts +2 -2
  120. package/dist/core/geometry/poseDescriptor.js +49 -23
  121. package/dist/core/geometry/poseDescriptor.js.map +1 -1
  122. package/dist/core/geometry/regionHit.js +2 -2
  123. package/dist/core/geometry/regionHit.js.map +1 -1
  124. package/dist/core/geometry/targetFrame.js.map +1 -1
  125. package/dist/core/layers/children.js +1 -3
  126. package/dist/core/layers/children.js.map +1 -1
  127. package/dist/core/ops/create.js +2 -2
  128. package/dist/core/ops/create.js.map +1 -1
  129. package/dist/core/ops/delete.js +19 -23
  130. package/dist/core/ops/delete.js.map +1 -1
  131. package/dist/core/ops/reorder/index.js +3 -7
  132. package/dist/core/ops/reorder/index.js.map +1 -1
  133. package/dist/core/ops/reorder/setChildOrder.js +2 -4
  134. package/dist/core/ops/reorder/setChildOrder.js.map +1 -1
  135. package/dist/core/ops/reparent.js +0 -1
  136. package/dist/core/ops/reparent.js.map +1 -1
  137. package/dist/core/ops/slot.js +3 -6
  138. package/dist/core/ops/slot.js.map +1 -1
  139. package/dist/core/pages/fitViewToPage.js +15 -0
  140. package/dist/core/pages/fitViewToPage.js.map +1 -0
  141. package/dist/core/pages/pages.js +65 -0
  142. package/dist/core/pages/pages.js.map +1 -0
  143. package/dist/core/pages/pagesLayer.js +27 -0
  144. package/dist/core/pages/pagesLayer.js.map +1 -0
  145. package/dist/core/paint/meshKindLazy.js +2 -2
  146. package/dist/core/paint/meshKindLazy.js.map +1 -1
  147. package/dist/core/scene/NodeProperties.js.map +1 -1
  148. package/dist/core/scene/composeFrame.js.map +1 -1
  149. package/dist/core/scene/composePose.js +58 -20
  150. package/dist/core/scene/composePose.js.map +1 -1
  151. package/dist/core/scene/effectivePose.js +5 -1
  152. package/dist/core/scene/effectivePose.js.map +1 -1
  153. package/dist/core/scene/historyFacade.js +19 -13
  154. package/dist/core/scene/historyFacade.js.map +1 -1
  155. package/dist/core/scene/nodeFnFields.js +7 -0
  156. package/dist/core/scene/nodeFnFields.js.map +1 -1
  157. package/dist/core/scene/outerOps.js +2 -1
  158. package/dist/core/scene/outerOps.js.map +1 -1
  159. package/dist/core/scene/reconcileSpecs.js +1 -1
  160. package/dist/core/scene/reconcileSpecs.js.map +1 -1
  161. package/dist/core/scene/sceneRecording.js +2 -1
  162. package/dist/core/scene/sceneRecording.js.map +1 -1
  163. package/dist/core/scene/types.js.map +1 -1
  164. package/dist/core/viewport/screenSlop.js.map +1 -1
  165. package/dist/core/viewport/useDecayLoop.js +28 -30
  166. package/dist/core/viewport/useDecayLoop.js.map +1 -1
  167. package/dist/core/viewport/useNodeOverlayFrame.js.map +1 -1
  168. package/dist/core/viewport/useViewAnimation.js +70 -69
  169. package/dist/core/viewport/useViewAnimation.js.map +1 -1
  170. package/dist/core/viewport/view.js +8 -5
  171. package/dist/core/viewport/view.js.map +1 -1
  172. package/dist/core/viewport/zoomAt.js +11 -2
  173. package/dist/core/viewport/zoomAt.js.map +1 -1
  174. package/dist/{cornerHandles-BEshc4z3.d.ts → cornerHandles-BSKbyuvQ.d.ts} +1 -1
  175. package/dist/features/paths/editablePath.js +24 -8
  176. package/dist/features/paths/editablePath.js.map +1 -1
  177. package/dist/features/paths/index.js +4 -4
  178. package/dist/features/paths/shapeGenerator.js +89 -0
  179. package/dist/features/paths/shapeGenerator.js.map +1 -0
  180. package/dist/features/paths/usePathEdit.js +13 -13
  181. package/dist/features/poseRun/usePoseRun.js +25 -25
  182. package/dist/features/poseRun/usePoseRun.js.map +1 -1
  183. package/dist/features/text/useSceneTextEdit.js +82 -64
  184. package/dist/features/text/useSceneTextEdit.js.map +1 -1
  185. package/dist/features/viewports/viewResolver.js +28 -24
  186. package/dist/features/viewports/viewResolver.js.map +1 -1
  187. package/dist/icons/SliceIcon.js +24 -0
  188. package/dist/icons/SliceIcon.js.map +1 -0
  189. package/dist/index.d.ts +956 -581
  190. package/dist/index.js +260 -251
  191. package/dist/interactions/actions/align/align.js +9 -9
  192. package/dist/interactions/actions/align/align.js.map +1 -1
  193. package/dist/interactions/actions/booleans/booleans.js +18 -18
  194. package/dist/interactions/actions/booleans/booleans.js.map +1 -1
  195. package/dist/interactions/actions/booleans/operands.js.map +1 -1
  196. package/dist/interactions/actions/clone/action.js +35 -30
  197. package/dist/interactions/actions/clone/action.js.map +1 -1
  198. package/dist/interactions/actions/defaults/anchorEditing.js +56 -59
  199. package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
  200. package/dist/interactions/actions/defaults/anchorHandles.js +42 -0
  201. package/dist/interactions/actions/defaults/anchorHandles.js.map +1 -0
  202. package/dist/interactions/actions/defaults/anchorJoin.js +92 -0
  203. package/dist/interactions/actions/defaults/anchorJoin.js.map +1 -0
  204. package/dist/interactions/actions/defaults/bendSegment.js +46 -0
  205. package/dist/interactions/actions/defaults/bendSegment.js.map +1 -0
  206. package/dist/interactions/actions/defaults/copySubtree.js +1 -1
  207. package/dist/interactions/actions/defaults/copySubtree.js.map +1 -1
  208. package/dist/interactions/actions/defaults/duplicate.js +32 -17
  209. package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
  210. package/dist/interactions/actions/defaults/enterPathEdit.js +19 -7
  211. package/dist/interactions/actions/defaults/enterPathEdit.js.map +1 -1
  212. package/dist/interactions/actions/defaults/icons/anchorIcons.js +75 -0
  213. package/dist/interactions/actions/defaults/icons/anchorIcons.js.map +1 -0
  214. package/dist/interactions/actions/defaults/icons/editIcons.js +35 -8
  215. package/dist/interactions/actions/defaults/icons/editIcons.js.map +1 -1
  216. package/dist/interactions/actions/defaults/insertPathAnchor.js +16 -31
  217. package/dist/interactions/actions/defaults/insertPathAnchor.js.map +1 -1
  218. package/dist/interactions/actions/defaults/nudge.js +28 -23
  219. package/dist/interactions/actions/defaults/nudge.js.map +1 -1
  220. package/dist/interactions/actions/defaults/pinchZoom.js +29 -29
  221. package/dist/interactions/actions/defaults/pinchZoom.js.map +1 -1
  222. package/dist/interactions/actions/defaults/segmentUnder.js +22 -0
  223. package/dist/interactions/actions/defaults/segmentUnder.js.map +1 -0
  224. package/dist/interactions/actions/defaults/viewportZoom.js +82 -79
  225. package/dist/interactions/actions/defaults/viewportZoom.js.map +1 -1
  226. package/dist/interactions/actions/distribute/distribute.js +8 -8
  227. package/dist/interactions/actions/distribute/distribute.js.map +1 -1
  228. package/dist/interactions/actions/edit-anchors/action.js +58 -55
  229. package/dist/interactions/actions/edit-anchors/action.js.map +1 -1
  230. package/dist/interactions/actions/edit-anchors/preview.js +26 -0
  231. package/dist/interactions/actions/edit-anchors/preview.js.map +1 -0
  232. package/dist/interactions/actions/edit-anchors/symmetry.js +11 -0
  233. package/dist/interactions/actions/edit-anchors/symmetry.js.map +1 -0
  234. package/dist/interactions/actions/flip/action.js.map +1 -1
  235. package/dist/interactions/actions/geometryProjection.js.map +1 -1
  236. package/dist/interactions/actions/insert/action.js +37 -30
  237. package/dist/interactions/actions/insert/action.js.map +1 -1
  238. package/dist/interactions/actions/insert/frame.js +9 -2
  239. package/dist/interactions/actions/insert/frame.js.map +1 -1
  240. package/dist/interactions/actions/insert/repositionAction.js +31 -0
  241. package/dist/interactions/actions/insert/repositionAction.js.map +1 -0
  242. package/dist/interactions/actions/lasso-select/action.js +32 -32
  243. package/dist/interactions/actions/move/action.js +29 -25
  244. package/dist/interactions/actions/move/action.js.map +1 -1
  245. package/dist/interactions/actions/move/behaviors/snapToContainer.js +12 -6
  246. package/dist/interactions/actions/move/behaviors/snapToContainer.js.map +1 -1
  247. package/dist/interactions/actions/move/capture.js +32 -23
  248. package/dist/interactions/actions/move/capture.js.map +1 -1
  249. package/dist/interactions/actions/move/frame.js +2 -14
  250. package/dist/interactions/actions/move/frame.js.map +1 -1
  251. package/dist/interactions/actions/move/session.js +1 -1
  252. package/dist/interactions/actions/move/session.js.map +1 -1
  253. package/dist/interactions/actions/move/translate.js.map +1 -1
  254. package/dist/interactions/actions/outlines/createOutlines.js.map +1 -1
  255. package/dist/interactions/actions/outlines/replaceWithPaths.js +13 -13
  256. package/dist/interactions/actions/outlines/replaceWithPaths.js.map +1 -1
  257. package/dist/interactions/actions/outlines/strokeRegion.js.map +1 -1
  258. package/dist/interactions/actions/pathCommands/cutJoin.js +102 -0
  259. package/dist/interactions/actions/pathCommands/cutJoin.js.map +1 -0
  260. package/dist/interactions/actions/pathCommands/livePathEdit.js +64 -0
  261. package/dist/interactions/actions/pathCommands/livePathEdit.js.map +1 -0
  262. package/dist/interactions/actions/pathCommands/pathNodes.js +99 -0
  263. package/dist/interactions/actions/pathCommands/pathNodes.js.map +1 -0
  264. package/dist/interactions/actions/pathCommands/simplifyOffset.js +42 -0
  265. package/dist/interactions/actions/pathCommands/simplifyOffset.js.map +1 -0
  266. package/dist/interactions/actions/resize/action.js +31 -20
  267. package/dist/interactions/actions/resize/action.js.map +1 -1
  268. package/dist/interactions/actions/resize/commit.js.map +1 -1
  269. package/dist/interactions/actions/resize/frame.js +29 -15
  270. package/dist/interactions/actions/resize/frame.js.map +1 -1
  271. package/dist/interactions/actions/resize/replay.js +47 -0
  272. package/dist/interactions/actions/resize/replay.js.map +1 -0
  273. package/dist/interactions/actions/resize/session.js +49 -35
  274. package/dist/interactions/actions/resize/session.js.map +1 -1
  275. package/dist/interactions/actions/resolveOverlays.js +2 -1
  276. package/dist/interactions/actions/resolveOverlays.js.map +1 -1
  277. package/dist/interactions/actions/rotate/action.js +40 -25
  278. package/dist/interactions/actions/rotate/action.js.map +1 -1
  279. package/dist/interactions/actions/rotate/frame.js +1 -1
  280. package/dist/interactions/actions/rotate/frame.js.map +1 -1
  281. package/dist/interactions/actions/rotate/replay.js +22 -0
  282. package/dist/interactions/actions/rotate/replay.js.map +1 -0
  283. package/dist/interactions/actions/rotate/session.js +42 -32
  284. package/dist/interactions/actions/rotate/session.js.map +1 -1
  285. package/dist/interactions/actions/transform-again/action.js +63 -0
  286. package/dist/interactions/actions/transform-again/action.js.map +1 -0
  287. package/dist/interactions/actions/transform-again/memory.js +35 -0
  288. package/dist/interactions/actions/transform-again/memory.js.map +1 -0
  289. package/dist/interactions/actions/useStandardActions.js +101 -83
  290. package/dist/interactions/actions/useStandardActions.js.map +1 -1
  291. package/dist/move.d.ts +4 -4
  292. package/dist/{originProjection-PPT88jZv.d.ts → originProjection-CoNVpITV.d.ts} +1 -1
  293. package/dist/resize.d.ts +3 -3
  294. package/dist/style.css +1 -1
  295. package/dist/tools/builtin/ellipse/useEllipseTool.js +16 -15
  296. package/dist/tools/builtin/ellipse/useEllipseTool.js.map +1 -1
  297. package/dist/tools/builtin/line/useLineTool.js +16 -15
  298. package/dist/tools/builtin/line/useLineTool.js.map +1 -1
  299. package/dist/tools/builtin/pen/usePenTool.js +111 -111
  300. package/dist/tools/builtin/pen/usePenTool.js.map +1 -1
  301. package/dist/tools/builtin/pickTopMostHit.js +11 -14
  302. package/dist/tools/builtin/pickTopMostHit.js.map +1 -1
  303. package/dist/tools/builtin/polygon/usePolygonTool.js +17 -15
  304. package/dist/tools/builtin/polygon/usePolygonTool.js.map +1 -1
  305. package/dist/tools/builtin/rect/useRectTool.js +15 -14
  306. package/dist/tools/builtin/rect/useRectTool.js.map +1 -1
  307. package/dist/tools/builtin/select/adapterPointQuery.js +22 -18
  308. package/dist/tools/builtin/select/adapterPointQuery.js.map +1 -1
  309. package/dist/tools/builtin/select/selectionContributions.js +1 -1
  310. package/dist/tools/builtin/select/selectionContributions.js.map +1 -1
  311. package/dist/tools/builtin/select/useSelectTool.js +2 -1
  312. package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
  313. package/dist/tools/builtin/slice/useSliceTool.js +42 -40
  314. package/dist/tools/builtin/slice/useSliceTool.js.map +1 -1
  315. package/dist/tools/builtin/star/useStarTool.js +18 -16
  316. package/dist/tools/builtin/star/useStarTool.js.map +1 -1
  317. package/dist/tools/builtin/text/useTextTool.js +2 -2
  318. package/dist/tools/builtin/text/useTextTool.js.map +1 -1
  319. package/dist/{types-CIKDwlsP.d.ts → types-DBbbCy0i.d.ts} +22 -15
  320. package/dist/version.js +1 -1
  321. package/package.json +22 -22
  322. package/dist/canvas/surface.module.js +0 -6
  323. package/dist/canvas/surface.module.js.map +0 -1
  324. package/dist/canvas/surfaceBox.js +0 -16
  325. package/dist/canvas/surfaceBox.js.map +0 -1
  326. package/dist/layout/arrivals.js +0 -33
  327. package/dist/layout/arrivals.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,173 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.10.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 113c088: Every adapter carries the tree. `getParent` and `getChildren` are required on `MoveAdapter` (and so on `SelectAdapter`), `getNode`, `getParent`, and `getChildren` on `InsertAdapter`, both methods on `OrderedAdapter`, and `getParent` on `AlignAdapter`, `DistributeAdapter`, and `PickTopMostHitAdapter`. `BooleansAdapter` and `CreateOutlinesAdapter` require `getNode`, `getParent`, and `getChildren`, and `BooleansAdapter` requires `setChildOrder` too. The readers no longer carry a second path for an adapter that leaves one out: the default pick of `useSelectTool` walks `getChildren` and never `getNodes()`, and the insert, delete, reparent, and reorder ops call the tree reads without checking for them.
8
+
9
+ Breaking: an adapter written by hand without these methods no longer compiles, and an op applied against an object without them throws where it used to skip the step. A store with no nesting answers as a tree of roots: `getParent: () => null` and `getChildren: (parentId) => (parentId === null ? ids : [])`. A node with no children must answer `[]`; an adapter that answers the whole list for every parent makes a delete op recurse until the stack overflows.
10
+
11
+ Also breaking: `BooleansAdapter.getZOrder` is removed, since the slot it overrode is now always read through `getParent` and `getChildren`, and `pickTopMostHit` takes an adapter, no longer `null` or `undefined`.
12
+
13
+ `sceneAdapter.ts` no longer imports React (`useSceneAdapter` lives in its own module, exported as before), and a detached scene view (`renderSceneToCanvas`, `renderSceneToPixels`) reads the scene through `sceneToAdapter`.
14
+ - cbd5b4d: `@weasel-js/geom`'s six-number affine type is `Affine`, no longer `Mat3`. It sat one capital letter from `mat3`, the namespace over the nine-element column-major `GlMat3` a GL uniform takes, and the two are different matrices. `mat3.toAffine` and `mat3.fromAffine` already named the conversion this way.
15
+
16
+ Breaking: `import type { Mat3 } from '@weasel-js/geom'` becomes `import type { Affine }`. The values are unchanged, and so are the free functions beside the type (`identity`, `multiply`, `invert`, and the rest).
17
+ - d3b73b9: The text Align bar no longer lights Left for `align: 'start'` (or Right for `'end'`) when the style states no `direction`. An omitted direction is `auto`, each paragraph's own, so right-to-left text aligned `start` painted from the right under a bar reading Left. The bar now lights nothing for `start` / `end` unless `direction` is `ltr` or `rtl`, and resolves them to an edge as before when it is.
18
+ - aaa2b4f: Path edit has two commands for the selected anchors' handles. `retractHandles` collapses them onto their anchors, making sharp corners; a segment whose ends both lose their handles turns straight. `makeHandlesSymmetric` gives each anchor two handles of equal length pointing opposite ways, making it smooth: the shared direction splits the difference between the two handles' own, the shared length is their mean, and an anchor with one handle mirrors it. The ends of an open path are skipped. Both are in the standard actions and the `paths` feature preset, in the `anchors` group, with icons `RetractHandlesIcon` and `SymmetricHandlesIcon`, and are disabled when they would change nothing.
19
+
20
+ Dragging a control handle with Shift held keeps it at the angle it had at the press and changes only its length; it can shrink onto its anchor but not flip through it.
21
+
22
+ `@weasel-js/geom` adds the edits underneath: `retractHandles(set, flatIndexes)`, `makeHandlesSymmetric(set, flatIndexes)`, and `handleAlongItsAngle(anchor, handle, to)`.
23
+ - c7b7ed5: In path edit, dragging a segment bends it: the point pressed follows the pointer and the segment's two handles move to carry it, and a straight segment becomes a curve. An end anchor that was smooth stays smooth, its other handle turning at its own length. It is the `bendSegment` action, in the standard actions and the `paths` feature preset. A drag that starts on an anchor or off the path still moves anchors or draws the marquee, and on a canvas with no mode registry a drag that starts on the edge no longer moves the shape.
24
+
25
+ Path edit now lays a `segment` affordance along the edited path's outline, under its anchors and controls, and `bendSegment` binds to it. Affordance regions take a new shape for it, `{ kind: 'outline', path, hitRadiusPx }`: hit within that many screen pixels of the path's outline. `@weasel-js/routing` adds `isSegment` for that hit and `isPathEditPart` for any anchor, control, or segment hit; `move` turns down a press on any of them.
26
+
27
+ `bendSegment(set, grip, dx, dy)` (`@weasel-js/geom`) is the edit underneath, taking the `{ sub, segIdx, t }` that `segmentAt` reports.
28
+ - faf8ff3: A kit canvas brings its own size rule. `<Canvas>`, `<SceneViewCanvas>`, and `<DrawCanvas>` used to take their CSS size from a class in `@weasel-js/core/style.css`, so a page that did not load that stylesheet showed each one at its drawing buffer's size, `dpr` times too large. Each canvas now adopts the one rule into the document or shadow root it mounts in, so a popout window and a shadow root are covered too. The rule keys on a `data-wzl-surface` attribute at zero specificity, as before, so a class passed to the canvas still resizes it. The rule is gone from `style.css`, and the canvas no longer carries the kit's `surface` class.
29
+ - 8bee821: `SceneCanvasApi.ingest` works from the canvas's first render. It reached the action registry through a ref an effect filled, so a call made before that effect ran was dropped with a warning.
30
+ - 67a7de8: On a controlled `<SceneCanvas>`, writing `view` in your own state now stops a camera glide in flight, the way `setView` and input already did; before, the glide's next frame overwrote the write. The canvas tells the write from your handling of the glide's own frames by order, not by value: a `view` prop that moves when the glide has written no frame since the prop last moved is yours. So a consumer who clamps or rounds inside `onViewChange` still gets a whole glide, and one who moves the prop a second time for one frame (from an effect, or a deferred value) stops it. A write that lands in the same render as a frame's is not seen, and the glide carries on. Behavior change.
31
+ - f6e3e90: Duplicate and Alt-drag clone point a copied node's `dependsOn` list at the copy of each dependency that was copied with it, and at the original of each that was not. A copy used to follow the copies only when every dependency was copied, so duplicating an edge with one of its endpoints left the new edge on both original endpoints, drawn over the first. A copy none of whose dependencies were copied still follows the originals.
32
+
33
+ Changes behavior: a copied derived node with some of its dependencies in the copy now derives from a mix of copies and originals.
34
+ - 74ba77e: Additive: a document's pages as geometry. `layoutPages` places a list of `Page`s in one world space, in a row or a column; `pageAt` and `pageOfBounds` answer which page a point or an object is on; `pagesBounds` is the box around all of them; `adjacentPage` steps the page order, for a next-slide move; `pagesLayer` is a world-space render layer that paints each sheet; and `fitViewToPage` is the view that frames one.
35
+ - a8e8048: A node that derives from a skinned path follows it as it bends. `DerivedDep.path` now answers the path a pose override shows the dependency by, ahead of one the dependency derives, so a `derivePose` or `derivePath` reading `deps[0].path` gets a skinned path as its rig bends it where it used to get `null`. A derivation is run again when that path is replaced, even inside the same bounds. The document reading (`documentPose`) and a lookup through a scene slot's `toPose` still answer the derived path alone.
36
+
37
+ Changes behavior: a derivation that took a non-null `path` to mean "this dependency derives one" now also sees one on any node a pose override gives a `path`.
38
+ - 755b7ff: A painter can draw over a container's children. `overChildren(commands)` wraps part of what a `drawOne` returns, and the scene walk (`buildSceneTree`, so `<SceneCanvas>` and `buildSceneViewCommands` alike) paints that part after the node's children, in the node's own layer, with the node's rotation, alpha, and clips. `defaultDrawOne` uses it for a container's `data.label`, which the container's children used to cover: a group made by Outline Stroke from a labeled rect shows its label again. Additive: `overChildren`, `isOverChildren`, and `splitOverChildren` are new exports, and outside a scene walk the wrapped part is an ordinary group painted where it sits.
39
+ - 1d3ca5c: A drawn polygon or star stays editable as one. The `insert` dep stores the parameters it was drawn with on the node as `data.generator` (`{ kind: 'polygon', sides, rotation }` or `{ kind: 'star', points, innerRadiusRatio, rotation }`) beside `data.path`, which stays the geometry every reader uses. The `polygon`, `star`, and inferred `path` entries of `defaultNodeProperties` / `inferredNodeProperties` show Sides, or Points and Inner, for a node holding one, and changing one makes the shape again on the same circle, under the same rotation, in the same undo step.
40
+
41
+ Editing the path in path edit drops the generator, leaving plain geometry; so does changing a parameter of a shape whose path no longer matches its generator.
42
+
43
+ `NodePropertiesEntry` gains `settle(next, prev)`: what a property write stores, given the node as the write left it and as it was. `SelectionPanel` applies it to commits and to slider drafts. Core exports the generator helpers: `regenerateShape`, `settleShapeGenerator`, `shapeGeneratorOf`, `shapeGeneratorPath`, `clampShapeGenerator`, `isShapeGenerator`, `withoutShapeGenerator`, `SHAPE_COUNT_RANGE`, `STAR_RATIO_RANGE`, and the `ShapeGenerator` types.
44
+
45
+ A committed polygon or star is no longer stretched: its pose was the box of its circle, and the stored path's own box was stretched to fill it, so a five-point star came out stretched 5% along one axis and 10% along the other, unlike its preview. The pose, and the insert's reported bounds, are now the box of the shape itself.
46
+ - 566f15e: A double-click on a path with several nodes selected now opens the path under the pointer for anchor editing. Before, `enterPathEdit` opened the first editable path in the selection, which need not be the one double-clicked. It still falls back to that path when no editable path is under the pointer. The action now requires the `nodeAtPoint` dep, which `<SceneCanvas>` sources.
47
+ - ee59ccd: Deleting an anchor keeps the curve's shape. The segment that replaces the two either side of it keeps the directions they left and arrived with, and its handle lengths are now fitted to them rather than set to a third of the chord, so deleting one anchor of a circle leaves a round arc instead of a flat lobe. An anchor that was inserted on a curve and then deleted gives back the original curve. A gap between two straight segments closes with a straight one.
48
+
49
+ `fitCubicWithTangents` (`@weasel-js/geom`) is the fit underneath: the one cubic through a run of samples whose control points keep given end tangents.
50
+
51
+ Breaking: `fitCubicThroughDeletion(prev, next)` becomes `fitCubicThroughDeletion(prev, removed, next)`, since the fit needs the anchor being removed.
52
+ - f22f5ce: Resizing several nodes at once keeps a turned or skewed one's shape. A non-uniform resize makes a parallelogram of a turned box, and `remapRotatedLeaf` now writes that to the leaf's `pose.transform` where it used to drop it; it also reads a `transform` the leaf already carries, and a skewed leaf with no rotation goes through it too. Breaking for a caller that relied on the result never carrying a `transform`: a turned leaf resized unevenly now has one. A uniform resize, an upright plain leaf, and a leaf turned by a quarter turn still get none.
53
+ - 92e6291: A container's layout is the one its scene node declares, and nothing else. The `layouts` prop of `<SceneCanvas>` and the `layouts` option of `sceneToAdapter` and `useSceneAdapter` are removed, along with `useLayoutArrivals`, `layoutArrivalHandler`, `LayoutSource`, and `LayoutArrivalOptions`, which existed to carry that map into the scene's layout pass.
54
+
55
+ Breaking: declare the layout on the node (`scene.add({ kind: 'container', layout })`) or set it with `scene.setLayout(id, layout)`, which is undoable. The scene's own layout pass arranges it, measured by `useScene({ layoutFrame })`; a canvas's `poseDescriptor` no longer measures a layout, so a scene whose poses are not rects passes `layoutFrame`. `scene.setArrivalHandler` is unchanged.
56
+ - 2216d1b: A marquee and a lasso now agree on a node whose pose descriptor bounds reach past what it draws. A marquee took such a node as soon as its bounds overlapped the rect, and an enclosing lasso refused it when its bounds poked out; both now decide by the drawn outline, as an `intersect` lasso already did.
57
+
58
+ Changes behavior: a node whose descriptor (`forNode` included) reports a box larger than its painter's silhouette is no longer taken by a marquee over the part of that box where nothing is drawn. Give the node a painter whose `silhouette` covers what it draws.
59
+ - a7144f4: Breaking: a move behavior moves the drag by returning `transform`, and nothing else. `BehaviorResult.pose`, the deprecated override that was turned into a translate for the first dragged id, is gone, and so is the deprecated `BehaviorMoveResult` alias of `BehaviorResult`. Return `{ transform: { kind: 'translate', dx, dy } }`.
60
+
61
+ A move gesture's context carries `offset(from, to)`, the translation between two poses' bounds, so a behavior that knows where a node lands can return it as a `transform`. `snapToContainer` does.
62
+ - 35c4879: One answer to "is a pointer held on this canvas". `heldPointers(element)` is new in `@weasel-js/gestures` (additive): it counts the pointers pressed on an element with any button and not yet released, and every reader of one element shares its listeners. The dispatcher's `hover` gesture and hover cursor, a layer's `onUncapturedMove`, and the `pointer` dep's published position all read it, where each used to track a press its own way.
63
+
64
+ What changes: a move with a secondary or middle button held is no longer a `hover`; `onUncapturedMove` stays off until the last of several pointers is released, and through a `pointerleave` mid-press; and the `pointer` dep no longer reports a position for a drag that began outside the canvas and passed over the page.
65
+
66
+ `giveKeys` (`@weasel-js/react/testing/giveKeys`) releases the middle-button press it makes.
67
+ - 886380f: Each README's "API reference" link now opens the package's page on the site, `https://orochi235.github.io/weasel/#pkg/<name>`, where it used to open the TypeDoc site at `/weasel/api/`, which is no longer built. A file comment that sat on an `export { … }` list is now marked as the file's, so an entry's description no longer shows up as the summary of each function the entry re-exports. No code changed. `@weasel-js/labkit` drops `vitepress` from its devDependencies, which nothing used.
68
+ - f8faa63: The default node properties show a typed number to at most two decimal places: position, size, font size, tracking, leading, and tab size. A node left at `123.45678901234567` by a drag read that way in the panel. Only the text changes; the stored value keeps its digits until someone types a new one.
69
+ - 3bc85c9: Path commands as kit actions, in the `paths` feature preset. `simplifyPath` and `offsetPath` are ongoing actions driven by one number — `tolerance` and `distance` — so a slider can preview them live with `useOngoingAction` and commit once: Simplify refits each selected path in place, and Offset Path adds the grown or shrunk outline beside each, behind its source when grown and in front when shrunk, and selects the new paths. `snipAtCrossings` (Cut at Crossings) replaces each selected path with its pieces wherever another selected path crosses it. `joinPaths` (Cmd/Ctrl+J) closes a selected open path or joins two at their nearest open ends. In path edit, `joinAnchors` (Cmd/Ctrl+J) joins the two selected end anchors and `averageAnchors` (Cmd/Ctrl+Alt+J) moves the selected anchors to their mean, with variants that average one axis only. The object commands are in a new `path` group; the anchor ones join the `anchors` group.
70
+
71
+ These commands drop a node's `skin` and shape generator, since the new geometry has neither the anchors the skin follows nor the shape the generator makes. The geometry behind them — `simplifyPath`, `offsetPath`, `snipPathsAtCrossings`, `averageAnchorsAt`, `joinAnchorsAt` — is exported too.
72
+ - d0c3896: Delete, Backspace, and the arrow keys in path edit now act on the selected anchors in a `<SceneCanvas>` with no mode registry. They used to delete or nudge the whole shape, because the node-level `delete` and `nudge.*` actions were registered ahead of `deleteAnchors` and `nudgeAnchors.*` and took the keystroke. The anchor actions now register first and still decline when no anchor is selected, so the same keys reach the node then.
73
+ - 3448831: The default node properties have a Skew row for `pose.transform`, in Layout under Rotation. It reads on while a node's pose carries a skew or a per-axis scale, and turning it off removes the transform, so `SelectionPanel` can show and clear one. Additive: the row is a switch, not a matrix editor, and turning it on writes nothing new.
74
+ - 27fe0fb: `useRigNode` keeps a rig moving when its skeleton's joints are added, removed, or reordered. The rig still gets a new `target`, but that target takes over the placement and the committed pose of every joint the skeleton still names, where it used to start at the bind pose, and an animation that was running goes on running on the target it was started on and shows through the new one. A joint that was renamed is a different joint, and stops moving.
75
+ - 8bbf878: `composeRigidPose` composes a container's `transform` into its children, and `decomposeRigidPose` takes it back out. A child under a skewed or unevenly scaled container now answers a world pose whose box center, rotation, and `transform` draw it where the container's frame puts it; what the child's box and rotation cannot hold of the result is written to the child's own `transform`. Breaking for a scene that set a `transform` on a container under `RIGID_POSE_COMPOSITION` and relied on its children ignoring it. `composeRectPose` is unchanged: under it a container's pose only offsets its children, so neither its rotation nor its `transform` reaches them.
76
+ - b5267f9: The rotate cursor now turns with the pointer, so the opening of its arc faces the selection's pivot from wherever the pointer is: over the rotation ring, and through the rotate drag, which used to show no rotate cursor at all. It used to be turned to match the target's own rotation.
77
+
78
+ Two new surfaces carry it. An `AffordanceRegion`'s `cursor` may be a function of the pointer's world point and the view, worked out on each hit and reported on `AffordanceRegionHit.cursor`; read the cursor there rather than off `region.cursor`, which may now be a function. An `OngoingHandle` may declare `cursor()`, read after every move and ahead of its action's `activeCursor`.
79
+ - 62e766c: Editing the points of a skinned path keeps its weights with its points. Inserting, deleting, cutting, nudging, and dragging anchors, and continuing or joining the path with the pen, now write the node's `data.skin` in the same undo entry as its path, so one undo restores both. Before, the weight arrays kept their old numbering and every point after the edit followed the wrong joints. A new point takes weights between those of the anchors either side of it, by its distance to each; a point drawn past an end takes the end's. Hand-painted weights are kept: nothing is weighed again from the bones.
80
+
81
+ Adds API: `editAnchorSetTracked(path, edit)` is `editAnchorSet` that also answers `origins`, where each point of the new path came from in the old one, and `remapPointValues(values, origins)` carries an array kept per point through that edit. `remapSkin(skin, origins)` does the same for a skin. `EditAnchorsDep.applyEdit` and `editOps` take `origins` as an optional fourth argument; a dep of your own that ignores it leaves a skin as it was.
82
+ - 5485d35: The slice tool declares a palette icon: `SliceIcon`, a knife, exported from `@weasel-js/core` and `@weasel-js/ui` beside the other tool icons. A `ToolPalette` drew its placeholder `?` for the tool before. Additive.
83
+ - 690ea72: Hold Space while drawing a rectangle, ellipse, line, polygon, or star to move the shape instead of resizing it; let go and the drag resizes again from where the shape now sits. It is the `insert.reposition` action, which each kit shape tool binds through `INSERT_REPOSITION_BINDING` (a Space hold gated to the tool's own engaged phase), so a consumer's own drag-to-draw tool can bind it the same way. A grid-snapped start stays on the grid as it moves.
84
+
85
+ An offhand tool now engages only between gestures: `buildToolOffhandBindings` gates its key-held specs to `[{ channel: '*', phase: 'initial' }]`. Before, pressing Space mid-drag pushed the hand tool, and the tool switch canceled the drag.
86
+ - e962494: New package, `@weasel-js/symmetry`: live symmetry groups. A group's copies (a mirror at any angle, both axes, a point, radial, kaleidoscope, or a repeat with an optional glide) are drawn from its real children. Every edit to a child therefore reaches every copy, a click on a copy selects the child it copies, and `bakeSymmetry` turns the copies into real nodes. `createSymmetry(scene).contribution` draws the guide with draggable center, axis, and step handles, sends every tool's shapes into the active group, and keeps a path that is its own image under its group symmetric while its anchors are dragged or nudged.
87
+
88
+ Core gains what the package is built on, and each piece is usable on its own:
89
+
90
+ - `ContainerNode.copiesFromPose`: a container draws its subtree again under each affine it returns. Hit tests reach into the copies, and the clip travels with each one. Additive.
91
+ - The `insertInto` dep: the container the `insert` dep puts new nodes into.
92
+ - The `pathSymmetry` dep: transforms that anchor drags and nudges keep a path symmetric under.
93
+
94
+ `@weasel-js/geom` adds `anchorSymmetry`, `anchorSymmetries`, `translateAnchorsSymmetric`, and `moveHandleSymmetric`. `@weasel-js/svg` writes a container's copies as transformed groups.
95
+ - bad37cf: `useSceneTextEdit` edits text where it is drawn. The overlay and the double-click test both read the pose a node is painted at, so text inside a container whose pose is a frame (`RIGID_POSE_COMPOSITION`) opens over the turned or skewed text and takes a click there; it used to open where the text would sit without the container. Under a view whose `scale.x` and `scale.y` differ, the overlay is stretched to match the canvas, where it used to scale both axes by `scale.x`.
96
+ - 9e84e5d: Additive: `useTextEdit` and `useSceneTextEdit` take `onStart(id)` and `onEnd(id)`. `onStart` is called as an edit opens, whichever way it was started (`startEdit`, a double-click, the `textEdit` dep). `onEnd` is called as it closes, however it does: a commit, a cancel, Escape, a click away, or another edit opening over it, and after any commit has been written. Both are called from the call that changes the edit, not from an effect, so state that follows the edit is never a render behind it. Neither is called when the hook unmounts with an edit open.
97
+ - ef29364: A scene slot's `toPose` is handed each node as the scene shows it: its `pose` is a pose override's or a derived one, where it used to be the stored pose. A view drawn through a `toPose` now shows a drag in flight, a rig's bound nodes, and a skinned path, whose override `path` is carried from the box the scene shows the node at into the box `toPose` answers. A dependency read through such a slot answers the carried path too, and so does one in a scene that composes frames. Breaking for a `toPose` that relied on seeing the document's pose mid-gesture; one that places a node from its data alone, ignoring `node.pose`, still shows no motion.
98
+ - 2f48645: Cmd/Ctrl+D is now Transform Again: it repeats the last move, nudge, rotate, resize, duplicate, or Alt-drag clone on whatever is selected, as one undo entry. A rotate repeats about the new selection's center and a resize repeats as the same scale held at the same handle's opposite corner. Duplicating and then moving the copies makes the copies' offset the step, so pressing Cmd+D again lays down another copy one step further each time. With nothing to repeat it duplicates, as Cmd+D did before.
99
+
100
+ Duplicate moves to Cmd/Ctrl+Shift+D. This changes a default binding: an app relying on Cmd+D to duplicate after a move now gets the move repeated.
101
+
102
+ The kit keeps the last transform per scene: `lastTransform(scene)`, `recordTransform(scene, transform, selectedIds)`, and `forgetTransform(scene)`, with the `RepeatableTransform` type. The action is `transformAgain` in the standard actions and the `edit` feature preset, in the `edit` group with a new `TransformAgainIcon`.
103
+ - 92c2e8e: `<SceneCanvas tune>` changes a built-in action on one canvas without restating it. Keyed by action id, an entry sets `params` every invocation starts from (a thunk is read on each call, so a value changed mid-drag reaches an ongoing action) and adds `bindings` beside the action's own. A binding's or a `trigger` call's own params are laid over the tuned ones. The tuning applies to whichever descriptor holds the id, from a preset or a tool, so setting a param no longer means copying the gesture that reaches the action. Additive: `ActionsRegistry.tune(id, tuning)` underneath it, `tuneAction`, the types `ActionTuning`, `ActionTunings`, `ActionParams` and `TunedBinding`, and `ActionParamSchema`, which types params by action id for the kit's parametric actions and takes an app's own entries by declaration merging, as `DepSchema` does. `BoundGesture` is no longer marked experimental.
104
+
105
+ `SceneCanvasViewport` names the type of `<SceneCanvas viewport>`.
106
+ - 711ae46: While a scene has an active journal (`getActiveJournal` / `setActiveJournalAccessor`), `scene.undo`, `scene.redo`, `canUndo`, and `canRedo` (and the same on `scene.history`) now step that journal, the same place `applyBatch` sends edits. Before, undo stepped the scene's own history and skipped the journal's edits entirely: the `undo` action took back whatever came before the journal opened instead of the edit just made. Once the journal is suspended, committed, or canceled, undo steps the scene's history again. `historyEntries`, `historyIndex`, and `jumpToHistoryIndex` still read the scene's own history.
107
+ - 365eae0: A linear unit stores a decimal typed at a decimal `scale` as the decimal the two multiply to: 33.3 at a scale of 0.01 is 0.333, where it was 0.33299999999999996. Nothing is rounded away, and a scale that is no short decimal (degrees over radians) converts as before.
108
+
109
+ The text tool's percent unit for `fontScale` and `baselineShift` is that data now (`{ scale: 0.01, precision: 1, suffix: '%' }`) and no longer a pair of functions, so the run properties print as a literal. Breaking in one detail: a percentage typed to more than three places is stored as typed, where it was cut to five places of the stored fraction.
110
+
111
+ labkit's one-line summary of a compound leaf writes each number that has a unit as its field shows it, in an object's fields, a list's and a map's entries, and a union's variant, and writes a list of objects entry by entry where it wrote `[object Object]`. Additive: `summarizeLeaf(leaf, value)` is exported beside `summarizeValue`, and `inDialog`'s default summary is the leaf's.
112
+ - 781a15e: A canvas's `clientToWorld` override reaches the views declared on it. Which `<CanvasView>` a client point is in, where it lands in that view's world, and the `pointer` dep's position over a view were all worked out from the element's bounding rect, so a canvas under a CSS transform routed a press to the wrong view or converted it wrong.
113
+
114
+ Breaking, in the experimental view resolver: `createViewResolver` takes `toLocal(clientX, clientY)`, a client point in the canvas's own pixels, where it took `canvasOrigin`; and a `ViewTarget` carries `world`, the point in the resolved view's world, where it carried `origin`.
115
+ - a092978: Zoom can be held to one axis. `viewport={{ zoom: { axis: 'x' } }}` makes the wheel, the trackpad pinch, and Cmd+=/- scale only x, leaving y's scale and translation alone; `pinchZoom: { axis }` does the same for a two-finger pinch, which still pans on both. `makeViewportZoomAction` and `makePinchZoomAction` take the same option, and `axisZoomFactor` and the `ZoomAxis` type are exported for a host calling `zoomAt` itself. Additive.
116
+ - Updated dependencies [b6e04e2]
117
+ - Updated dependencies [cbd5b4d]
118
+ - Updated dependencies [aaa2b4f]
119
+ - Updated dependencies [c7b7ed5]
120
+ - Updated dependencies [3fda6e6]
121
+ - Updated dependencies [906fd19]
122
+ - Updated dependencies [7c5af8a]
123
+ - Updated dependencies [ee59ccd]
124
+ - Updated dependencies [789e80d]
125
+ - Updated dependencies [924edc4]
126
+ - Updated dependencies [b8bfb2d]
127
+ - Updated dependencies [35c4879]
128
+ - Updated dependencies [b67b6cd]
129
+ - Updated dependencies [9eefcc7]
130
+ - Updated dependencies [886380f]
131
+ - Updated dependencies [0db6d9a]
132
+ - Updated dependencies [d8b51a4]
133
+ - Updated dependencies [10449af]
134
+ - Updated dependencies [e05a5f9]
135
+ - Updated dependencies [d30823f]
136
+ - Updated dependencies [0be7a53]
137
+ - Updated dependencies [b5267f9]
138
+ - Updated dependencies [9253a8b]
139
+ - Updated dependencies [62e766c]
140
+ - Updated dependencies [f4100e9]
141
+ - Updated dependencies [690ea72]
142
+ - Updated dependencies [ab9ea7a]
143
+ - Updated dependencies [e962494]
144
+ - Updated dependencies [8a9ed26]
145
+ - Updated dependencies [9e84e5d]
146
+ - Updated dependencies [f5870a4]
147
+ - Updated dependencies [bd6c203]
148
+ - Updated dependencies [92c2e8e]
149
+ - Updated dependencies [365eae0]
150
+ - Updated dependencies [10c5d41]
151
+ - @weasel-js/prefs@1.10.1
152
+ - @weasel-js/geom@1.10.1
153
+ - @weasel-js/stroke@1.10.1
154
+ - @weasel-js/text@1.10.1
155
+ - @weasel-js/routing@1.10.1
156
+ - @weasel-js/animator@1.10.1
157
+ - @weasel-js/renderer@1.10.1
158
+ - @weasel-js/gestures@1.10.1
159
+ - @weasel-js/history@1.10.1
160
+ - @weasel-js/select@1.10.1
161
+ - @weasel-js/cursor@1.10.1
162
+ - @weasel-js/react@1.10.1
163
+ - @weasel-js/font@1.10.1
164
+ - @weasel-js/modes@1.10.1
165
+ - @weasel-js/paint@1.10.1
166
+ - @weasel-js/quantity@1.10.1
167
+ - @weasel-js/registry@1.10.1
168
+ - @weasel-js/simulation@1.10.1
169
+ - @weasel-js/text-dom@1.10.1
170
+
3
171
  ## 1.10.0
4
172
 
5
173
  ### Patch Changes
package/README.md CHANGED
@@ -102,6 +102,39 @@ An `Action` is a named operation — `delete`, `duplicate`, `group`, `insert`, `
102
102
 
103
103
  `<SceneCanvas>` mounts a provider when none is above it and registers the kit-standard actions its `features` presets name — under `draw`, all of them: escape, select-all, delete, duplicate, group and ungroup, undo and redo, flip, nudge, reorder, align, distribute, the pathfinder booleans, path-anchor editing, fill and stroke, clipboard, and the pointer-driven move, resize, rotate, clone, area-select and lasso. `FEATURE_ACTION_IDS` lists which preset registers which, and `COMPOSITE_FEATURES` what `pick`, `transform` and `draw` abbreviate. `insert` comes with the shape tools that bind it.
104
104
 
105
+ ### Tuning an action
106
+
107
+ `tune` changes a built-in action on one canvas without restating it. Each entry, keyed by action id, can carry:
108
+
109
+ | Field | What it does |
110
+ |---|---|
111
+ | `params` | Params every invocation of the action starts from. Whatever a binding or a `trigger(id, params)` call passes is laid over them, so these fill in only what the call leaves out. A thunk is read on every call, so a value that changes mid-drag reaches an ongoing action the next time it reads its params. |
112
+ | `bindings` | Gestures added beside the action's own `defaultBinding`, each a `GestureSpec` or `{ spec, opts: { params } }`. |
113
+
114
+ A tuning applies to whichever descriptor holds the id, whether a preset or a tool registered it, and whether it registered before the tuning or after. Changing `params` never requires knowing what gesture reaches the action.
115
+
116
+ ```tsx
117
+ const radius = useRef(8);
118
+ const tune = useMemo<ActionTunings>(() => ({
119
+ brushDeleteAnchors: { params: () => ({ radiusPx: radius.current }) },
120
+ cycleCornerStyle: { bindings: [{ kind: 'key', key: 'k' }] },
121
+ }), []);
122
+
123
+ <SceneCanvas scene={scene} features={['paths']} tune={tune} />
124
+ ```
125
+
126
+ `ActionParamSchema` types `params` for the kit actions that read them (`flip`, `align.*`, `distribute.*`, `reorder.*`, `nudge.*`, `tool.activate`); any other id takes a plain record. An action of your own declares its params the way `DepSchema` takes deps:
127
+
128
+ ```ts
129
+ declare module '@weasel-js/core' {
130
+ interface ActionParamSchema { 'app.stamp': { size: number } }
131
+ }
132
+ ```
133
+
134
+ Pass `tune` a stable object — a module constant or a `useMemo` — since a new one re-applies every tuning. The registry underneath is `ActionsRegistry.tune(id, tuning)`, which returns its release.
135
+
136
+ ### Replacing actions (`@experimental`)
137
+
105
138
  ```tsx
106
139
  <SceneCanvas
107
140
  width={W}
@@ -2,30 +2,30 @@ import { viewToTransform as e } from "../core/viewport/view.js";
2
2
  import { worldToScreen as t } from "../core/viewport/viewTransform.js";
3
3
  import { angleOf as n, isRigid as r, localToWorld as i, pxExtentIn as a, targetCorners as o } from "../core/geometry/targetFrame.js";
4
4
  import { annulusSemiAxes as s, hitAffordanceRegions as c, pointRegionAnchor as l, pointRegionFrame as u, pointRegionScreenQuad as d, transformOf as f } from "./hitAffordanceRegions.js";
5
- import { PATH_L as p, PATH_M as m, PATH_Z as h } from "@weasel-js/geom";
5
+ import { PATH_L as p, PATH_M as m, PATH_Z as h, boundsOfPath as g } from "@weasel-js/geom";
6
6
  //#region src/affordances/composeAffordanceLayer.ts
7
- function g(t, n, r) {
7
+ function _(t, n, r) {
8
8
  return {
9
9
  id: t,
10
10
  label: n,
11
11
  space: "screen",
12
12
  draw: (t, n, i) => {
13
- let a = _(t), o = v(t), s = y(t), c = [], l = e(n);
13
+ let a = v(t), o = y(t), s = b(t), c = [], l = e(n);
14
14
  for (let e of r) if (!s || s(e.id)) {
15
15
  for (let t of e.regions(a)) {
16
16
  let r = f(a, t.targetId);
17
- t.paint && S(t, r, n, a, l, c), o && b(o, e.id, t, r, n);
17
+ t.paint && C(t, r, n, a, l, c), o && x(o, e.id, t, r, n);
18
18
  }
19
19
  if (e.decorate) for (let t of e.decorate(a, n)) c.push(t);
20
20
  }
21
21
  return c;
22
22
  },
23
23
  hitTest: (e, t, n, i, a, o) => {
24
- let s = _(n);
24
+ let s = v(n);
25
25
  if (!s) return null;
26
- let l = c(r, e, t, s, i, o ?? y(n) ?? void 0);
26
+ let l = c(r, e, t, s, i, o ?? b(n) ?? void 0);
27
27
  if (!l) return null;
28
- let { cursor: u, strength: d, claimedKinds: f } = l.region;
28
+ let { cursor: u } = l, { strength: d, claimedKinds: f } = l.region;
29
29
  return {
30
30
  ...l.binding,
31
31
  ...u === void 0 ? {} : { cursor: u },
@@ -35,19 +35,19 @@ function g(t, n, r) {
35
35
  }
36
36
  };
37
37
  }
38
- function _(e) {
38
+ function v(e) {
39
39
  let t = e;
40
40
  return typeof t?.getChromeState == "function" ? t.getChromeState() : e;
41
41
  }
42
- function v(e) {
42
+ function y(e) {
43
43
  let t = e;
44
44
  return typeof t?.getDebug == "function" ? t.getDebug() : null;
45
45
  }
46
- function y(e) {
46
+ function b(e) {
47
47
  let t = e;
48
48
  return typeof t?.getIsVisible == "function" ? t.getIsVisible() : null;
49
49
  }
50
- function b(e, t, n, r, i) {
50
+ function x(e, t, n, r, i) {
51
51
  if (n.shape.kind === "point") {
52
52
  let a = u(n.shape, r, i), o = d(n.shape, r, i, (e, t) => [e, t]);
53
53
  e.recordHitbox(t, "handle", {
@@ -61,7 +61,7 @@ function b(e, t, n, r, i) {
61
61
  }
62
62
  if (n.shape.kind === "annulus") {
63
63
  let a = n.shape, { rx: o, ry: c } = s(a, i, r);
64
- x(e, t, r, {
64
+ S(e, t, r, {
65
65
  x: a.cx - o,
66
66
  y: a.cy - c,
67
67
  width: 2 * o,
@@ -69,9 +69,9 @@ function b(e, t, n, r, i) {
69
69
  });
70
70
  return;
71
71
  }
72
- x(e, t, r, n.shape);
72
+ S(e, t, r, n.shape.kind === "outline" ? g(n.shape.path) : n.shape);
73
73
  }
74
- function x(e, t, a, s) {
74
+ function S(e, t, a, s) {
75
75
  if (!r(a)) {
76
76
  e.recordHitbox(t, "handle", {
77
77
  kind: "polygon",
@@ -89,7 +89,7 @@ function x(e, t, a, s) {
89
89
  ...a.identity ? {} : { rotation: n(a) }
90
90
  });
91
91
  }
92
- function S(e, n, r, i, o, c) {
92
+ function C(e, n, r, i, o, c) {
93
93
  let l = e.paint;
94
94
  if (l.kind === "square") {
95
95
  if (e.shape.kind !== "point") return;
@@ -138,13 +138,13 @@ function S(e, n, r, i, o, c) {
138
138
  }
139
139
  if (l.kind === "annulus") {
140
140
  if (e.shape.kind !== "annulus") return;
141
- let t = e.shape, i = a(l.insetPx ?? 0, r.scale, n), u = C(t, s(t, r, n), i, n, o, l.fill, l.stroke);
141
+ let t = e.shape, i = a(l.insetPx ?? 0, r.scale, n), u = w(t, s(t, r, n), i, n, o, l.fill, l.stroke);
142
142
  u && c.push(u);
143
143
  return;
144
144
  }
145
145
  if (l.kind === "custom") {
146
146
  let t = {
147
- world: w(e, n, r),
147
+ world: T(e, n, r),
148
148
  local: e.shape,
149
149
  view: r,
150
150
  state: i
@@ -153,7 +153,7 @@ function S(e, n, r, i, o, c) {
153
153
  return;
154
154
  }
155
155
  }
156
- function C(e, n, r, a, o, s, c) {
156
+ function w(e, n, r, a, o, s, c) {
157
157
  let l = n.rx - r.x, u = n.ry - r.y;
158
158
  if (l <= 0 || u <= 0) return null;
159
159
  let d = e.cx, f = e.cy, g = e.innerWidth / 2 + r.x, _ = e.innerHeight / 2 + r.y, v = _ < u, y = g < l;
@@ -191,7 +191,7 @@ function C(e, n, r, a, o, s, c) {
191
191
  ...c ? { stroke: c } : {}
192
192
  };
193
193
  }
194
- function w(e, t, n) {
194
+ function T(e, t, n) {
195
195
  if (e.shape.kind === "point") {
196
196
  let r = l(e.shape, t, n);
197
197
  return {
@@ -208,7 +208,7 @@ function w(e, t, n) {
208
208
  height: 2 * m
209
209
  };
210
210
  }
211
- let r = e.shape, a = i(t, r.x, r.y), o = i(t, r.x + r.width, r.y), c = i(t, r.x, r.y + r.height), u = i(t, r.x + r.width, r.y + r.height), d = Math.min(a.x, o.x, c.x, u.x), f = Math.min(a.y, o.y, c.y, u.y), p = Math.max(a.x, o.x, c.x, u.x), m = Math.max(a.y, o.y, c.y, u.y);
211
+ let r = e.shape.kind === "outline" ? g(e.shape.path) : e.shape, a = i(t, r.x, r.y), o = i(t, r.x + r.width, r.y), c = i(t, r.x, r.y + r.height), u = i(t, r.x + r.width, r.y + r.height), d = Math.min(a.x, o.x, c.x, u.x), f = Math.min(a.y, o.y, c.y, u.y), p = Math.max(a.x, o.x, c.x, u.x), m = Math.max(a.y, o.y, c.y, u.y);
212
212
  return {
213
213
  x: d,
214
214
  y: f,
@@ -217,6 +217,6 @@ function w(e, t, n) {
217
217
  };
218
218
  }
219
219
  //#endregion
220
- export { g as composeAffordanceLayer };
220
+ export { _ as composeAffordanceLayer };
221
221
 
222
222
  //# sourceMappingURL=composeAffordanceLayer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"composeAffordanceLayer.js","names":["composeAffordanceLayer","state","asChromeState","data","debug","asDebugSink","isVisible","asIsVisible","out","t","viewToTransform","view","a","affordances","region","xf","transformOf","paintRegion","recordRegionHitbox","cmd","hit","hitAffordanceRegions","wx","wy","cursor","strength","claimedKinds","maybe","f","pointRegionFrame","quad","pointRegionScreenQuad","x","y","affordanceId","q","s","annulusSemiAxes","rx","ry","recordLocalRect","isRigid","targetCorners","r","w","localToWorld","angleOf","paint","sx","sy","worldToScreen","viewT","half","fill","stroke","c","sn","coords","u","v","i","PATH_M","PATH_L","PATH_Z","insetLocal","pxExtentIn","annulusCommand","ctx","worldOf","semiAxes","cx","shape","cy","rectHW","rectHH","haveTopBottom","haveLeftRight","xT","yR","cmds","localPts","appendPetal","t0","t1","pointRegionAnchor","ma","mb","mc","md","hw","hh","b","d","minX","minY","maxX","maxY"],"sources":["../../src/affordances/composeAffordanceLayer.ts"],"sourcesContent":["import type { RenderLayer } from 'core/layers/render';\nimport type { DrawCommand } from '@weasel-js/renderer';\nimport type { ChromeState } from 'core/selection/chromeState';\nimport type { View } from 'core/viewport/view';\nimport { viewToTransform } from 'core/viewport/view';\nimport { worldToScreen } from 'core/viewport/viewTransform';\nimport { isRigid, pxExtentIn, targetCorners } from 'core/geometry/targetFrame';\nimport type { DebugSink } from '../debug/types';\nimport type { CommandFill, CommandStroke } from '@weasel-js/paint';\nimport { PATH_M, PATH_L, PATH_Z } from '@weasel-js/geom';\nimport {\n angleOf,\n annulusSemiAxes,\n hitAffordanceRegions,\n localToWorld,\n pointRegionAnchor,\n pointRegionFrame,\n pointRegionScreenQuad,\n transformOf,\n type TargetTransform,\n} from './hitAffordanceRegions';\nimport type {\n Affordance,\n AffordanceRegion,\n CustomPaintContext,\n LayerHit,\n} from './types';\n\n/**\n * @experimental\n * Bundle a list of Affordances into a single RenderLayer.\n *\n * Paint order matches array order (first → last = bottom → top). Hit-test walks affordances in\n * reverse and stops at the first that claims the point; within it the nearest region wins.\n *\n * `decorate(state, view)` runs *after* a given affordance's regions, for paint only.\n */\nexport function composeAffordanceLayer(\n id: string,\n label: string,\n affordances: readonly Affordance[],\n): RenderLayer<ChromeState> & {\n hitTest(\n wx: number,\n wy: number,\n state: unknown,\n view: View,\n dims: { width: number; height: number },\n isVisible?: (id: string) => boolean,\n ): LayerHit | null;\n} {\n return {\n id,\n label,\n space: 'screen',\n draw: (data, view, _dims): DrawCommand[] => {\n const state = asChromeState(data);\n const debug = asDebugSink(data);\n const isVisible = asIsVisible(data);\n const out: DrawCommand[] = [];\n const t = viewToTransform(view);\n for (const a of affordances) {\n if (isVisible && !isVisible(a.id)) continue;\n for (const region of a.regions(state)) {\n const xf = transformOf(state, region.targetId);\n if (region.paint) paintRegion(region, xf, view, state, t, out);\n if (debug) recordRegionHitbox(debug, a.id, region, xf, view);\n }\n if (a.decorate) {\n for (const cmd of a.decorate(state, view)) out.push(cmd);\n }\n }\n return out;\n },\n // The walk lives in `hitAffordanceRegions` so this layer and `buildAffordanceAt` share one\n // implementation. This wrapper lifts the region's declared cursor and claim onto the binding.\n hitTest: (wx, wy, data, view, _dims, isVisible): LayerHit | null => {\n const state = asChromeState(data);\n if (!state) return null;\n const hit = hitAffordanceRegions(\n affordances, wx, wy, state, view, isVisible ?? asIsVisible(data) ?? undefined,\n );\n if (!hit) return null;\n const { cursor, strength, claimedKinds } = hit.region;\n return {\n ...hit.binding,\n ...(cursor !== undefined ? { cursor } : {}),\n ...(strength !== undefined ? { strength } : {}),\n ...(claimedKinds !== undefined ? { claimedKinds } : {}),\n };\n },\n };\n}\n\n// `draw` is called by Canvas with `CanvasHelpers` and by tests with a bare `ChromeState`.\nfunction asChromeState(data: unknown): ChromeState {\n const maybe = data as { getChromeState?: () => ChromeState };\n if (typeof maybe?.getChromeState === 'function') return maybe.getChromeState();\n return data as ChromeState;\n}\n\nfunction asDebugSink(data: unknown): DebugSink | null {\n const maybe = data as { getDebug?: () => DebugSink | null };\n if (typeof maybe?.getDebug === 'function') return maybe.getDebug();\n return null;\n}\n\n/** The chrome-caps visibility predicate off the data envelope. When absent (tests passing\n * bare state), every affordance renders. */\nfunction asIsVisible(data: unknown): ((id: string) => boolean) | null {\n const maybe = data as { getIsVisible?: () => (id: string) => boolean };\n if (typeof maybe?.getIsVisible === 'function') return maybe.getIsVisible();\n return null;\n}\n\nfunction recordRegionHitbox(\n debug: DebugSink,\n affordanceId: string,\n region: AffordanceRegion,\n xf: TargetTransform,\n view: View,\n): void {\n if (region.shape.kind === 'point') {\n // The hit square is a square on *screen* — turned with the target when the\n // region asks — so in world it is a parallelogram, recorded as one.\n const f = pointRegionFrame(region.shape, xf, view);\n const quad = pointRegionScreenQuad(region.shape, xf, view, (x, y) => [x, y]);\n debug.recordHitbox(affordanceId, 'handle', {\n kind: 'polygon',\n points: quad.map((q) => ({\n x: f.x + (q.x - f.x) / view.scale.x,\n y: f.y + (q.y - f.y) / view.scale.y,\n })),\n });\n return;\n }\n if (region.shape.kind === 'annulus') {\n // Approximated as the outer ellipse's AABB: the DebugSink's hitbox primitives don't\n // model ring shapes.\n const s = region.shape;\n const { rx, ry } = annulusSemiAxes(s, view, xf);\n recordLocalRect(debug, affordanceId, xf, { x: s.cx - rx, y: s.cy - ry, width: 2 * rx, height: 2 * ry });\n return;\n }\n recordLocalRect(debug, affordanceId, xf, region.shape);\n}\n\n/** A rect of the target's own frame as a debug hitbox: the unturned box plus\n * the rotation while the frame only turns it, and the quad it lands on once\n * the frame skews or stretches it. */\nfunction recordLocalRect(\n debug: DebugSink,\n affordanceId: string,\n xf: TargetTransform,\n r: { x: number; y: number; width: number; height: number },\n): void {\n if (!isRigid(xf)) {\n debug.recordHitbox(affordanceId, 'handle', { kind: 'polygon', points: targetCorners(r, xf) });\n return;\n }\n const w = localToWorld(xf, r.x + r.width / 2, r.y + r.height / 2);\n debug.recordHitbox(affordanceId, 'handle', {\n kind: 'rect',\n x: w.x - r.width / 2,\n y: w.y - r.height / 2,\n width: r.width,\n height: r.height,\n ...(xf.identity ? {} : { rotation: angleOf(xf) }),\n });\n}\n\nfunction paintRegion(\n region: AffordanceRegion,\n xf: TargetTransform,\n view: View,\n state: ChromeState,\n viewT: ReturnType<typeof viewToTransform>,\n out: DrawCommand[],\n): void {\n const paint = region.paint!;\n if (paint.kind === 'square') {\n if (region.shape.kind !== 'point') {\n // 'square' only makes sense over a point shape.\n return;\n }\n // Same frame the hit-test reads, at the paint's own size.\n const f = pointRegionFrame(region.shape, xf, view);\n const [sx, sy] = worldToScreen(f.x, f.y, viewT);\n const half = paint.sizePx / 2;\n const fill = paint.fill ? { fill: paint.fill } : {};\n const stroke = paint.stroke ? { stroke: paint.stroke } : {};\n if (f.angle === 0) {\n out.push({\n kind: 'path',\n path: { kind: 'rect', x: sx - half, y: sy - half, width: paint.sizePx, height: paint.sizePx },\n ...fill, ...stroke,\n });\n return;\n }\n const c = Math.cos(f.angle) * half, sn = Math.sin(f.angle) * half;\n const coords = new Float32Array(8);\n [[-1, -1], [1, -1], [1, 1], [-1, 1]].forEach(([u, v], i) => {\n coords[i * 2] = sx + c * u - sn * v;\n coords[i * 2 + 1] = sy + sn * u + c * v;\n });\n out.push({\n kind: 'path',\n path: { kind: 'polygon', commands: new Uint8Array([PATH_M, PATH_L, PATH_L, PATH_L, PATH_Z]), coords, fillRule: 'nonzero' },\n ...fill, ...stroke,\n });\n return;\n }\n if (paint.kind === 'annulus') {\n if (region.shape.kind !== 'annulus') return; // type mismatch — silent skip\n const s = region.shape;\n // The screen-px inset in target-local units, so it visually matches at any zoom.\n const insetLocal = pxExtentIn(paint.insetPx ?? 0, view.scale, xf);\n // Same `minBandPx` clamp the hit-test applies, so the ring you can see is\n // the ring you can grab.\n const cmd = annulusCommand(s, annulusSemiAxes(s, view, xf), insetLocal, xf, viewT, paint.fill, paint.stroke);\n if (cmd) out.push(cmd);\n return;\n }\n if (paint.kind === 'custom') {\n const ctx: CustomPaintContext = {\n world: worldOf(region, xf, view),\n local: region.shape,\n view,\n state,\n };\n for (const cmd of paint.draw(ctx)) out.push(cmd);\n return;\n }\n}\n\n/** Build a screen-space DrawCommand for an annulus region: the area\n * inside the (contracted) outer ellipse but outside the (expanded)\n * inner rect. Renders as four disjoint petal subpaths — one per rect\n * edge — so the visible fill is true `ellipse \\ rect`. A naive\n * ellipse + rect even-odd path would also fill the rect's corner\n * triangles where they poke past the contracted ellipse. Returns null\n * when the band collapses on both axes. */\nfunction annulusCommand(\n shape: Extract<AffordanceRegion['shape'], { kind: 'annulus' }>,\n semiAxes: { rx: number; ry: number },\n insetLocal: { x: number; y: number },\n xf: TargetTransform,\n viewT: ReturnType<typeof viewToTransform>,\n fill: CommandFill | undefined,\n stroke: CommandStroke | undefined,\n): DrawCommand | null {\n const rx = semiAxes.rx - insetLocal.x;\n const ry = semiAxes.ry - insetLocal.y;\n if (rx <= 0 || ry <= 0) return null;\n const cx = shape.cx;\n const cy = shape.cy;\n // Expanded inner rect (still in target-local coords).\n const rectHW = shape.innerWidth / 2 + insetLocal.x;\n const rectHH = shape.innerHeight / 2 + insetLocal.y;\n // A petal exists only when its rect edge intersects the ellipse interior — otherwise the\n // rect already extends past the ellipse on that axis.\n const haveTopBottom = rectHH < ry;\n const haveLeftRight = rectHW < rx;\n if (!haveTopBottom && !haveLeftRight) return null;\n const xT = haveTopBottom ? rx * Math.sqrt(1 - (rectHH / ry) ** 2) : 0;\n const yR = haveLeftRight ? ry * Math.sqrt(1 - (rectHW / rx) ** 2) : 0;\n\n // Line segments per petal arc; 24 is smooth at typical zooms.\n const SAMPLES = 24;\n const cmds: number[] = [];\n const localPts: { x: number; y: number }[] = [];\n\n function appendPetal(t0: number, t1: number): void {\n cmds.push(PATH_M);\n localPts.push({ x: cx + rx * Math.cos(t0), y: cy + ry * Math.sin(t0) });\n for (let i = 1; i <= SAMPLES; i++) {\n const t = t0 + (t1 - t0) * (i / SAMPLES);\n cmds.push(PATH_L);\n localPts.push({ x: cx + rx * Math.cos(t), y: cy + ry * Math.sin(t) });\n }\n // PATH_Z closes along the rect edge: both endpoints share the rect-edge coord by\n // construction, so the closing segment IS the chord.\n cmds.push(PATH_Z);\n }\n\n if (haveTopBottom) {\n // Top petal: y < cy. Arc from right endpoint over t = -π/2 to left.\n appendPetal(\n Math.atan2(-rectHH / ry, xT / rx), // in (-π/2, 0)\n Math.atan2(-rectHH / ry, -xT / rx), // in (-π, -π/2), decreasing through -π/2\n );\n // Bottom petal: y > cy. Arc through t = +π/2.\n appendPetal(\n Math.atan2(rectHH / ry, xT / rx), // in (0, π/2)\n Math.atan2(rectHH / ry, -xT / rx), // in (π/2, π), increasing through π/2\n );\n }\n if (haveLeftRight) {\n // Right petal: x > cx. Arc through t = 0.\n appendPetal(\n Math.atan2(-yR / ry, rectHW / rx), // in (-π/2, 0)\n Math.atan2(yR / ry, rectHW / rx), // in (0, π/2)\n );\n // Left petal: x < cx. Arc through t = ±π. Shift the end angle by\n // -2π so the parametric trace passes through the leftmost point\n // monotonically instead of crossing the right side.\n appendPetal(\n Math.atan2(-yR / ry, -rectHW / rx), // in (-π, -π/2)\n Math.atan2(yR / ry, -rectHW / rx) - 2 * Math.PI, // in (-3π/2, -π), decreasing through -π\n );\n }\n\n const coords = new Float32Array(localPts.length * 2);\n for (let i = 0; i < localPts.length; i++) {\n const w = localToWorld(xf, localPts[i].x, localPts[i].y);\n const [sx, sy] = worldToScreen(w.x, w.y, viewT);\n coords[i * 2] = sx;\n coords[i * 2 + 1] = sy;\n }\n return {\n kind: 'path',\n path: { kind: 'polygon', commands: new Uint8Array(cmds), coords, fillRule: 'nonzero' },\n ...(fill ? { fill } : {}),\n ...(stroke ? { stroke } : {}),\n };\n}\n\nfunction worldOf(region: AffordanceRegion, xf: TargetTransform, view: View): CustomPaintContext['world'] {\n if (region.shape.kind === 'point') {\n const w = pointRegionAnchor(region.shape, xf, view);\n return { x: w.x, y: w.y };\n }\n if (region.shape.kind === 'annulus') {\n // The outer ellipse's bounding box in world coords: each half-extent is\n // the length of that row of the frame applied to the semi-axes.\n const s = region.shape;\n const { rx, ry } = annulusSemiAxes(s, view, xf);\n const c = localToWorld(xf, s.cx, s.cy);\n const [ma, mb, mc, md] = xf.toWorld;\n const hw = Math.hypot(rx * ma, ry * mc);\n const hh = Math.hypot(rx * mb, ry * md);\n return { x: c.x - hw, y: c.y - hh, width: 2 * hw, height: 2 * hh };\n }\n // Rect: under rotation, width/height describe the AABB of the rotated rect. Affordances\n // needing exact rotated geometry can read `local` and reapply the rotation themselves.\n const r = region.shape;\n const a = localToWorld(xf, r.x, r.y);\n const b = localToWorld(xf, r.x + r.width, r.y);\n const c = localToWorld(xf, r.x, r.y + r.height);\n const d = localToWorld(xf, r.x + r.width, r.y + r.height);\n const minX = Math.min(a.x, b.x, c.x, d.x);\n const minY = Math.min(a.y, b.y, c.y, d.y);\n const maxX = Math.max(a.x, b.x, c.x, d.x);\n const maxY = Math.max(a.y, b.y, c.y, d.y);\n return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n}\n\n// Hit-testing lives in `hitAffordanceRegions.ts` — see `hitRegion` there.\n"],"mappings":"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"}
1
+ {"version":3,"file":"composeAffordanceLayer.js","names":["composeAffordanceLayer","state","asChromeState","data","debug","asDebugSink","isVisible","asIsVisible","out","t","viewToTransform","view","a","affordances","region","xf","transformOf","paintRegion","recordRegionHitbox","cmd","hit","hitAffordanceRegions","wx","wy","cursor","strength","claimedKinds","maybe","f","pointRegionFrame","quad","pointRegionScreenQuad","x","y","affordanceId","q","s","annulusSemiAxes","rx","ry","boundsOfPath","recordLocalRect","isRigid","targetCorners","r","w","localToWorld","angleOf","paint","sx","sy","worldToScreen","viewT","half","fill","stroke","c","sn","coords","u","v","i","PATH_M","PATH_L","PATH_Z","insetLocal","pxExtentIn","annulusCommand","ctx","worldOf","semiAxes","cx","shape","cy","rectHW","rectHH","haveTopBottom","haveLeftRight","xT","yR","cmds","localPts","appendPetal","t0","t1","pointRegionAnchor","ma","mb","mc","md","hw","hh","b","d","minX","minY","maxX","maxY"],"sources":["../../src/affordances/composeAffordanceLayer.ts"],"sourcesContent":["import type { RenderLayer } from 'core/layers/render';\nimport type { DrawCommand } from '@weasel-js/renderer';\nimport type { ChromeState } from 'core/selection/chromeState';\nimport type { View } from 'core/viewport/view';\nimport { viewToTransform } from 'core/viewport/view';\nimport { worldToScreen } from 'core/viewport/viewTransform';\nimport { isRigid, pxExtentIn, targetCorners } from 'core/geometry/targetFrame';\nimport type { DebugSink } from '../debug/types';\nimport type { CommandFill, CommandStroke } from '@weasel-js/paint';\nimport { boundsOfPath, PATH_M, PATH_L, PATH_Z } from '@weasel-js/geom';\nimport {\n angleOf,\n annulusSemiAxes,\n hitAffordanceRegions,\n localToWorld,\n pointRegionAnchor,\n pointRegionFrame,\n pointRegionScreenQuad,\n transformOf,\n type TargetTransform,\n} from './hitAffordanceRegions';\nimport type {\n Affordance,\n AffordanceRegion,\n CustomPaintContext,\n LayerHit,\n} from './types';\n\n/**\n * @experimental\n * Bundle a list of Affordances into a single RenderLayer.\n *\n * Paint order matches array order (first → last = bottom → top). Hit-test walks affordances in\n * reverse and stops at the first that claims the point; within it the nearest region wins.\n *\n * `decorate(state, view)` runs *after* a given affordance's regions, for paint only.\n */\nexport function composeAffordanceLayer(\n id: string,\n label: string,\n affordances: readonly Affordance[],\n): RenderLayer<ChromeState> & {\n hitTest(\n wx: number,\n wy: number,\n state: unknown,\n view: View,\n dims: { width: number; height: number },\n isVisible?: (id: string) => boolean,\n ): LayerHit | null;\n} {\n return {\n id,\n label,\n space: 'screen',\n draw: (data, view, _dims): DrawCommand[] => {\n const state = asChromeState(data);\n const debug = asDebugSink(data);\n const isVisible = asIsVisible(data);\n const out: DrawCommand[] = [];\n const t = viewToTransform(view);\n for (const a of affordances) {\n if (isVisible && !isVisible(a.id)) continue;\n for (const region of a.regions(state)) {\n const xf = transformOf(state, region.targetId);\n if (region.paint) paintRegion(region, xf, view, state, t, out);\n if (debug) recordRegionHitbox(debug, a.id, region, xf, view);\n }\n if (a.decorate) {\n for (const cmd of a.decorate(state, view)) out.push(cmd);\n }\n }\n return out;\n },\n // The walk lives in `hitAffordanceRegions` so this layer and `buildAffordanceAt` share one\n // implementation. This wrapper lifts the region's declared cursor and claim onto the binding.\n hitTest: (wx, wy, data, view, _dims, isVisible): LayerHit | null => {\n const state = asChromeState(data);\n if (!state) return null;\n const hit = hitAffordanceRegions(\n affordances, wx, wy, state, view, isVisible ?? asIsVisible(data) ?? undefined,\n );\n if (!hit) return null;\n const { cursor } = hit;\n const { strength, claimedKinds } = hit.region;\n return {\n ...hit.binding,\n ...(cursor !== undefined ? { cursor } : {}),\n ...(strength !== undefined ? { strength } : {}),\n ...(claimedKinds !== undefined ? { claimedKinds } : {}),\n };\n },\n };\n}\n\n// `draw` is called by Canvas with `CanvasHelpers` and by tests with a bare `ChromeState`.\nfunction asChromeState(data: unknown): ChromeState {\n const maybe = data as { getChromeState?: () => ChromeState };\n if (typeof maybe?.getChromeState === 'function') return maybe.getChromeState();\n return data as ChromeState;\n}\n\nfunction asDebugSink(data: unknown): DebugSink | null {\n const maybe = data as { getDebug?: () => DebugSink | null };\n if (typeof maybe?.getDebug === 'function') return maybe.getDebug();\n return null;\n}\n\n/** The chrome-caps visibility predicate off the data envelope. When absent (tests passing\n * bare state), every affordance renders. */\nfunction asIsVisible(data: unknown): ((id: string) => boolean) | null {\n const maybe = data as { getIsVisible?: () => (id: string) => boolean };\n if (typeof maybe?.getIsVisible === 'function') return maybe.getIsVisible();\n return null;\n}\n\nfunction recordRegionHitbox(\n debug: DebugSink,\n affordanceId: string,\n region: AffordanceRegion,\n xf: TargetTransform,\n view: View,\n): void {\n if (region.shape.kind === 'point') {\n // The hit square is a square on *screen* — turned with the target when the\n // region asks — so in world it is a parallelogram, recorded as one.\n const f = pointRegionFrame(region.shape, xf, view);\n const quad = pointRegionScreenQuad(region.shape, xf, view, (x, y) => [x, y]);\n debug.recordHitbox(affordanceId, 'handle', {\n kind: 'polygon',\n points: quad.map((q) => ({\n x: f.x + (q.x - f.x) / view.scale.x,\n y: f.y + (q.y - f.y) / view.scale.y,\n })),\n });\n return;\n }\n if (region.shape.kind === 'annulus') {\n // Approximated as the outer ellipse's AABB: the DebugSink's hitbox primitives don't\n // model ring shapes.\n const s = region.shape;\n const { rx, ry } = annulusSemiAxes(s, view, xf);\n recordLocalRect(debug, affordanceId, xf, { x: s.cx - rx, y: s.cy - ry, width: 2 * rx, height: 2 * ry });\n return;\n }\n const box = region.shape.kind === 'outline' ? boundsOfPath(region.shape.path) : region.shape;\n recordLocalRect(debug, affordanceId, xf, box);\n}\n\n/** A rect of the target's own frame as a debug hitbox: the unturned box plus\n * the rotation while the frame only turns it, and the quad it lands on once\n * the frame skews or stretches it. */\nfunction recordLocalRect(\n debug: DebugSink,\n affordanceId: string,\n xf: TargetTransform,\n r: { x: number; y: number; width: number; height: number },\n): void {\n if (!isRigid(xf)) {\n debug.recordHitbox(affordanceId, 'handle', { kind: 'polygon', points: targetCorners(r, xf) });\n return;\n }\n const w = localToWorld(xf, r.x + r.width / 2, r.y + r.height / 2);\n debug.recordHitbox(affordanceId, 'handle', {\n kind: 'rect',\n x: w.x - r.width / 2,\n y: w.y - r.height / 2,\n width: r.width,\n height: r.height,\n ...(xf.identity ? {} : { rotation: angleOf(xf) }),\n });\n}\n\nfunction paintRegion(\n region: AffordanceRegion,\n xf: TargetTransform,\n view: View,\n state: ChromeState,\n viewT: ReturnType<typeof viewToTransform>,\n out: DrawCommand[],\n): void {\n const paint = region.paint!;\n if (paint.kind === 'square') {\n if (region.shape.kind !== 'point') {\n // 'square' only makes sense over a point shape.\n return;\n }\n // Same frame the hit-test reads, at the paint's own size.\n const f = pointRegionFrame(region.shape, xf, view);\n const [sx, sy] = worldToScreen(f.x, f.y, viewT);\n const half = paint.sizePx / 2;\n const fill = paint.fill ? { fill: paint.fill } : {};\n const stroke = paint.stroke ? { stroke: paint.stroke } : {};\n if (f.angle === 0) {\n out.push({\n kind: 'path',\n path: { kind: 'rect', x: sx - half, y: sy - half, width: paint.sizePx, height: paint.sizePx },\n ...fill, ...stroke,\n });\n return;\n }\n const c = Math.cos(f.angle) * half, sn = Math.sin(f.angle) * half;\n const coords = new Float32Array(8);\n [[-1, -1], [1, -1], [1, 1], [-1, 1]].forEach(([u, v], i) => {\n coords[i * 2] = sx + c * u - sn * v;\n coords[i * 2 + 1] = sy + sn * u + c * v;\n });\n out.push({\n kind: 'path',\n path: { kind: 'polygon', commands: new Uint8Array([PATH_M, PATH_L, PATH_L, PATH_L, PATH_Z]), coords, fillRule: 'nonzero' },\n ...fill, ...stroke,\n });\n return;\n }\n if (paint.kind === 'annulus') {\n if (region.shape.kind !== 'annulus') return; // type mismatch — silent skip\n const s = region.shape;\n // The screen-px inset in target-local units, so it visually matches at any zoom.\n const insetLocal = pxExtentIn(paint.insetPx ?? 0, view.scale, xf);\n // Same `minBandPx` clamp the hit-test applies, so the ring you can see is\n // the ring you can grab.\n const cmd = annulusCommand(s, annulusSemiAxes(s, view, xf), insetLocal, xf, viewT, paint.fill, paint.stroke);\n if (cmd) out.push(cmd);\n return;\n }\n if (paint.kind === 'custom') {\n const ctx: CustomPaintContext = {\n world: worldOf(region, xf, view),\n local: region.shape,\n view,\n state,\n };\n for (const cmd of paint.draw(ctx)) out.push(cmd);\n return;\n }\n}\n\n/** Build a screen-space DrawCommand for an annulus region: the area\n * inside the (contracted) outer ellipse but outside the (expanded)\n * inner rect. Renders as four disjoint petal subpaths — one per rect\n * edge — so the visible fill is true `ellipse \\ rect`. A naive\n * ellipse + rect even-odd path would also fill the rect's corner\n * triangles where they poke past the contracted ellipse. Returns null\n * when the band collapses on both axes. */\nfunction annulusCommand(\n shape: Extract<AffordanceRegion['shape'], { kind: 'annulus' }>,\n semiAxes: { rx: number; ry: number },\n insetLocal: { x: number; y: number },\n xf: TargetTransform,\n viewT: ReturnType<typeof viewToTransform>,\n fill: CommandFill | undefined,\n stroke: CommandStroke | undefined,\n): DrawCommand | null {\n const rx = semiAxes.rx - insetLocal.x;\n const ry = semiAxes.ry - insetLocal.y;\n if (rx <= 0 || ry <= 0) return null;\n const cx = shape.cx;\n const cy = shape.cy;\n // Expanded inner rect (still in target-local coords).\n const rectHW = shape.innerWidth / 2 + insetLocal.x;\n const rectHH = shape.innerHeight / 2 + insetLocal.y;\n // A petal exists only when its rect edge intersects the ellipse interior — otherwise the\n // rect already extends past the ellipse on that axis.\n const haveTopBottom = rectHH < ry;\n const haveLeftRight = rectHW < rx;\n if (!haveTopBottom && !haveLeftRight) return null;\n const xT = haveTopBottom ? rx * Math.sqrt(1 - (rectHH / ry) ** 2) : 0;\n const yR = haveLeftRight ? ry * Math.sqrt(1 - (rectHW / rx) ** 2) : 0;\n\n // Line segments per petal arc; 24 is smooth at typical zooms.\n const SAMPLES = 24;\n const cmds: number[] = [];\n const localPts: { x: number; y: number }[] = [];\n\n function appendPetal(t0: number, t1: number): void {\n cmds.push(PATH_M);\n localPts.push({ x: cx + rx * Math.cos(t0), y: cy + ry * Math.sin(t0) });\n for (let i = 1; i <= SAMPLES; i++) {\n const t = t0 + (t1 - t0) * (i / SAMPLES);\n cmds.push(PATH_L);\n localPts.push({ x: cx + rx * Math.cos(t), y: cy + ry * Math.sin(t) });\n }\n // PATH_Z closes along the rect edge: both endpoints share the rect-edge coord by\n // construction, so the closing segment IS the chord.\n cmds.push(PATH_Z);\n }\n\n if (haveTopBottom) {\n // Top petal: y < cy. Arc from right endpoint over t = -π/2 to left.\n appendPetal(\n Math.atan2(-rectHH / ry, xT / rx), // in (-π/2, 0)\n Math.atan2(-rectHH / ry, -xT / rx), // in (-π, -π/2), decreasing through -π/2\n );\n // Bottom petal: y > cy. Arc through t = +π/2.\n appendPetal(\n Math.atan2(rectHH / ry, xT / rx), // in (0, π/2)\n Math.atan2(rectHH / ry, -xT / rx), // in (π/2, π), increasing through π/2\n );\n }\n if (haveLeftRight) {\n // Right petal: x > cx. Arc through t = 0.\n appendPetal(\n Math.atan2(-yR / ry, rectHW / rx), // in (-π/2, 0)\n Math.atan2(yR / ry, rectHW / rx), // in (0, π/2)\n );\n // Left petal: x < cx. Arc through t = ±π. Shift the end angle by\n // -2π so the parametric trace passes through the leftmost point\n // monotonically instead of crossing the right side.\n appendPetal(\n Math.atan2(-yR / ry, -rectHW / rx), // in (-π, -π/2)\n Math.atan2(yR / ry, -rectHW / rx) - 2 * Math.PI, // in (-3π/2, -π), decreasing through -π\n );\n }\n\n const coords = new Float32Array(localPts.length * 2);\n for (let i = 0; i < localPts.length; i++) {\n const w = localToWorld(xf, localPts[i].x, localPts[i].y);\n const [sx, sy] = worldToScreen(w.x, w.y, viewT);\n coords[i * 2] = sx;\n coords[i * 2 + 1] = sy;\n }\n return {\n kind: 'path',\n path: { kind: 'polygon', commands: new Uint8Array(cmds), coords, fillRule: 'nonzero' },\n ...(fill ? { fill } : {}),\n ...(stroke ? { stroke } : {}),\n };\n}\n\nfunction worldOf(region: AffordanceRegion, xf: TargetTransform, view: View): CustomPaintContext['world'] {\n if (region.shape.kind === 'point') {\n const w = pointRegionAnchor(region.shape, xf, view);\n return { x: w.x, y: w.y };\n }\n if (region.shape.kind === 'annulus') {\n // The outer ellipse's bounding box in world coords: each half-extent is\n // the length of that row of the frame applied to the semi-axes.\n const s = region.shape;\n const { rx, ry } = annulusSemiAxes(s, view, xf);\n const c = localToWorld(xf, s.cx, s.cy);\n const [ma, mb, mc, md] = xf.toWorld;\n const hw = Math.hypot(rx * ma, ry * mc);\n const hh = Math.hypot(rx * mb, ry * md);\n return { x: c.x - hw, y: c.y - hh, width: 2 * hw, height: 2 * hh };\n }\n // Rect: under rotation, width/height describe the AABB of the rotated rect. Affordances\n // needing exact rotated geometry can read `local` and reapply the rotation themselves.\n const r = region.shape.kind === 'outline' ? boundsOfPath(region.shape.path) : region.shape;\n const a = localToWorld(xf, r.x, r.y);\n const b = localToWorld(xf, r.x + r.width, r.y);\n const c = localToWorld(xf, r.x, r.y + r.height);\n const d = localToWorld(xf, r.x + r.width, r.y + r.height);\n const minX = Math.min(a.x, b.x, c.x, d.x);\n const minY = Math.min(a.y, b.y, c.y, d.y);\n const maxX = Math.max(a.x, b.x, c.x, d.x);\n const maxY = Math.max(a.y, b.y, c.y, d.y);\n return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n}\n\n// Hit-testing lives in `hitAffordanceRegions.ts` — see `hitRegion` there.\n"],"mappings":"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"}