@weasel-js/core 1.11.1 → 1.13.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (488) hide show
  1. package/CHANGELOG.md +275 -0
  2. package/dist/WeaselProvider.js +1 -1
  3. package/dist/affordances/pathAnchors.js +10 -6
  4. package/dist/affordances/pathAnchors.js.map +1 -1
  5. package/dist/animation/poseTarget.js +46 -15
  6. package/dist/animation/poseTarget.js.map +1 -1
  7. package/dist/animation/poseTransition.js +3 -3
  8. package/dist/animation/poseTransition.js.map +1 -1
  9. package/dist/animation/reflow.js +3 -6
  10. package/dist/animation/reflow.js.map +1 -1
  11. package/dist/animation/rig/bindRig.js +2 -2
  12. package/dist/animation/rig/bindRig.js.map +1 -1
  13. package/dist/animation/rig/skinBinding.js +2 -2
  14. package/dist/animation/rig/skinBinding.js.map +1 -1
  15. package/dist/barrel/canvas.js +5 -1
  16. package/dist/barrel/chrome.js +3 -1
  17. package/dist/barrel/paint.js +4 -4
  18. package/dist/barrel/paths.js +9 -4
  19. package/dist/barrel/renderer.js +3 -2
  20. package/dist/barrel/routing.js +4 -4
  21. package/dist/barrel/scene.js +7 -1
  22. package/dist/barrel/text.js +2 -2
  23. package/dist/barrel/viewport.js +6 -2
  24. package/dist/canvas/Canvas.js +2 -1
  25. package/dist/canvas/Canvas.js.map +1 -1
  26. package/dist/canvas/CanvasView.js +23 -34
  27. package/dist/canvas/CanvasView.js.map +1 -1
  28. package/dist/canvas/ModalityHud.js +1 -1
  29. package/dist/canvas/NodeShape.js +5 -1
  30. package/dist/canvas/NodeShape.js.map +1 -1
  31. package/dist/canvas/SceneCanvas/ActionsProviderIfRoot.js +1 -1
  32. package/dist/canvas/SceneCanvas/DepRegistryProviderIfRoot.js +1 -1
  33. package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js +1 -1
  34. package/dist/canvas/SceneCanvas/SceneCanvasInput.js +2 -0
  35. package/dist/canvas/SceneCanvas/SceneCanvasInput.js.map +1 -1
  36. package/dist/canvas/SceneCanvas/SurfaceDeclarations.js +23 -0
  37. package/dist/canvas/SceneCanvas/SurfaceDeclarations.js.map +1 -0
  38. package/dist/canvas/SceneCanvas/canvasPassThrough.js +2 -1
  39. package/dist/canvas/SceneCanvas/canvasPassThrough.js.map +1 -1
  40. package/dist/canvas/SceneCanvas/defaultNodeRouting.js +2 -2
  41. package/dist/canvas/SceneCanvas/defaultNodeRouting.js.map +1 -1
  42. package/dist/canvas/SceneCanvas/props.js.map +1 -1
  43. package/dist/canvas/SceneCanvas/scenePointQuery.js +27 -5
  44. package/dist/canvas/SceneCanvas/scenePointQuery.js.map +1 -1
  45. package/dist/canvas/SceneCanvas/useActionTunings.js +6 -1
  46. package/dist/canvas/SceneCanvas/useActionTunings.js.map +1 -1
  47. package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js +1 -0
  48. package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js.map +1 -1
  49. package/dist/canvas/SceneCanvas/useCanvasTools.js +10 -2
  50. package/dist/canvas/SceneCanvas/useCanvasTools.js.map +1 -1
  51. package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js +25 -19
  52. package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js.map +1 -1
  53. package/dist/canvas/SceneCanvas/useSceneCanvasHandle.js +9 -3
  54. package/dist/canvas/SceneCanvas/useSceneCanvasHandle.js.map +1 -1
  55. package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js +17 -2
  56. package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js.map +1 -1
  57. package/dist/canvas/SceneCanvas/useSceneCanvasTools.js +2 -0
  58. package/dist/canvas/SceneCanvas/useSceneCanvasTools.js.map +1 -1
  59. package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js +19 -4
  60. package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js.map +1 -1
  61. package/dist/canvas/SceneCanvas/useScenePicking.js +31 -6
  62. package/dist/canvas/SceneCanvas/useScenePicking.js.map +1 -1
  63. package/dist/canvas/SceneCanvas/useSceneSelectTool.js +4 -5
  64. package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
  65. package/dist/canvas/SceneCanvas/useSceneStandardActions.js +7 -4
  66. package/dist/canvas/SceneCanvas/useSceneStandardActions.js.map +1 -1
  67. package/dist/canvas/SceneCanvas.js +30 -12
  68. package/dist/canvas/SceneCanvas.js.map +1 -1
  69. package/dist/canvas/ViewMarker.js +35 -0
  70. package/dist/canvas/ViewMarker.js.map +1 -0
  71. package/dist/canvas/accentColor.js +26 -3
  72. package/dist/canvas/accentColor.js.map +1 -1
  73. package/dist/canvas/buildSceneTree.js +14 -20
  74. package/dist/canvas/buildSceneTree.js.map +1 -1
  75. package/dist/canvas/copiesAbove.js +1 -1
  76. package/dist/canvas/copiesAbove.js.map +1 -1
  77. package/dist/canvas/defaultDrawOne.js +11 -6
  78. package/dist/canvas/defaultDrawOne.js.map +1 -1
  79. package/dist/canvas/deps/areaSelect.js +5 -2
  80. package/dist/canvas/deps/areaSelect.js.map +1 -1
  81. package/dist/canvas/deps/booleans.js +3 -2
  82. package/dist/canvas/deps/booleans.js.map +1 -1
  83. package/dist/canvas/deps/enteredGroup.js +81 -0
  84. package/dist/canvas/deps/enteredGroup.js.map +1 -0
  85. package/dist/canvas/deps/hitTestArea.js +28 -16
  86. package/dist/canvas/deps/hitTestArea.js.map +1 -1
  87. package/dist/canvas/deps/insert.js +10 -38
  88. package/dist/canvas/deps/insert.js.map +1 -1
  89. package/dist/canvas/deps/insertSite.js +39 -0
  90. package/dist/canvas/deps/insertSite.js.map +1 -0
  91. package/dist/canvas/deps/lassoSelect.js +6 -3
  92. package/dist/canvas/deps/lassoSelect.js.map +1 -1
  93. package/dist/canvas/deps/pathEditCommits.js +3 -7
  94. package/dist/canvas/deps/pathEditCommits.js.map +1 -1
  95. package/dist/canvas/deps/pathLeaf.js +1 -1
  96. package/dist/canvas/deps/pathLeaf.js.map +1 -1
  97. package/dist/canvas/deps/slice.js +2 -1
  98. package/dist/canvas/deps/slice.js.map +1 -1
  99. package/dist/canvas/derivedPath.js +14 -6
  100. package/dist/canvas/derivedPath.js.map +1 -1
  101. package/dist/canvas/doubleClickTarget.js +9 -11
  102. package/dist/canvas/doubleClickTarget.js.map +1 -1
  103. package/dist/canvas/drawnBounds.js +26 -9
  104. package/dist/canvas/drawnBounds.js.map +1 -1
  105. package/dist/canvas/inheritedPaints.js +55 -0
  106. package/dist/canvas/inheritedPaints.js.map +1 -0
  107. package/dist/canvas/nodeShapes/derivedPainter.js +15 -16
  108. package/dist/canvas/nodeShapes/derivedPainter.js.map +1 -1
  109. package/dist/canvas/nodeShapes/nodePaint.js +7 -34
  110. package/dist/canvas/nodeShapes/nodePaint.js.map +1 -1
  111. package/dist/canvas/nodeShapes/pathPainter.js +7 -7
  112. package/dist/canvas/nodeShapes/pathPainter.js.map +1 -1
  113. package/dist/canvas/nodeShapes/rectFallbackPainter.js +4 -5
  114. package/dist/canvas/nodeShapes/rectFallbackPainter.js.map +1 -1
  115. package/dist/canvas/nodeShapes/shapePainter.js +9 -9
  116. package/dist/canvas/nodeShapes/shapePainter.js.map +1 -1
  117. package/dist/canvas/nodeShapes/textPainter.js +13 -12
  118. package/dist/canvas/nodeShapes/textPainter.js.map +1 -1
  119. package/dist/canvas/ownClip.js +24 -0
  120. package/dist/canvas/ownClip.js.map +1 -0
  121. package/dist/canvas/paintCull.js.map +1 -1
  122. package/dist/canvas/pickIndex/nodeBroadBox.js +86 -0
  123. package/dist/canvas/pickIndex/nodeBroadBox.js.map +1 -0
  124. package/dist/canvas/pickIndex/scenePickIndex.js +208 -0
  125. package/dist/canvas/pickIndex/scenePickIndex.js.map +1 -0
  126. package/dist/canvas/pickReach.js +3 -3
  127. package/dist/canvas/pickReach.js.map +1 -1
  128. package/dist/canvas/pickWalk.js +17 -91
  129. package/dist/canvas/pickWalk.js.map +1 -1
  130. package/dist/canvas/poseCascade.js +1 -1
  131. package/dist/canvas/poseCascade.js.map +1 -1
  132. package/dist/canvas/publishPreviews.js +97 -0
  133. package/dist/canvas/publishPreviews.js.map +1 -0
  134. package/dist/canvas/renderSceneToPixels.js +1 -1
  135. package/dist/canvas/renderSceneToPixels.js.map +1 -1
  136. package/dist/canvas/sceneAdapter.js +7 -5
  137. package/dist/canvas/sceneAdapter.js.map +1 -1
  138. package/dist/canvas/sceneClipboard.js +17 -10
  139. package/dist/canvas/sceneClipboard.js.map +1 -1
  140. package/dist/canvas/scenePickReads.js +37 -0
  141. package/dist/canvas/scenePickReads.js.map +1 -0
  142. package/dist/canvas/scenePickSource.js +74 -0
  143. package/dist/canvas/scenePickSource.js.map +1 -0
  144. package/dist/canvas/sceneSlotWiring.js +9 -3
  145. package/dist/canvas/sceneSlotWiring.js.map +1 -1
  146. package/dist/canvas/sceneViewRender.js +9 -4
  147. package/dist/canvas/sceneViewRender.js.map +1 -1
  148. package/dist/canvas/shownNodes.js +15 -0
  149. package/dist/canvas/shownNodes.js.map +1 -0
  150. package/dist/canvas/surfaceContribution.js +12 -8
  151. package/dist/canvas/surfaceContribution.js.map +1 -1
  152. package/dist/canvas/useCanvasView.js +4 -2
  153. package/dist/canvas/useCanvasView.js.map +1 -1
  154. package/dist/canvas/useLayerStack.js +6 -2
  155. package/dist/canvas/useLayerStack.js.map +1 -1
  156. package/dist/canvas/useSceneAdapter.js +5 -3
  157. package/dist/canvas/useSceneAdapter.js.map +1 -1
  158. package/dist/canvas/useViewAim.js +121 -0
  159. package/dist/canvas/useViewAim.js.map +1 -0
  160. package/dist/canvas/useViewNesting.js +3 -2
  161. package/dist/canvas/useViewNesting.js.map +1 -1
  162. package/dist/canvas/useViewRegistrySurface.js +4 -2
  163. package/dist/canvas/useViewRegistrySurface.js.map +1 -1
  164. package/dist/canvas/viewMarkers.js +63 -0
  165. package/dist/canvas/viewMarkers.js.map +1 -0
  166. package/dist/canvas/viewRegistry.js +27 -1
  167. package/dist/canvas/viewRegistry.js.map +1 -1
  168. package/dist/clipboard.d.ts +2 -2
  169. package/dist/{clone-C3e0OTuF.d.ts → clone-qb2e1F5p.d.ts} +2 -2
  170. package/dist/clone.d.ts +3 -3
  171. package/dist/core/isPlainObject.js +2 -2
  172. package/dist/core/layers/layerGroup.js.map +1 -1
  173. package/dist/core/pages/pageEdits.js +2 -3
  174. package/dist/core/pages/pageEdits.js.map +1 -1
  175. package/dist/core/paint/meshKindLazy.js +59 -2
  176. package/dist/core/paint/meshKindLazy.js.map +1 -1
  177. package/dist/core/scene/NodeRouting.js.map +1 -1
  178. package/dist/core/scene/arrivalWindow.js +1 -1
  179. package/dist/core/scene/arrivalWindow.js.map +1 -1
  180. package/dist/core/scene/clipNothing.js +10 -0
  181. package/dist/core/scene/clipNothing.js.map +1 -0
  182. package/dist/core/scene/declaredFieldSetters.js +44 -0
  183. package/dist/core/scene/declaredFieldSetters.js.map +1 -0
  184. package/dist/core/scene/depMemo.js +14 -3
  185. package/dist/core/scene/depMemo.js.map +1 -1
  186. package/dist/core/scene/dependents.js +9 -1
  187. package/dist/core/scene/dependents.js.map +1 -1
  188. package/dist/core/scene/derivedParts.js +10 -11
  189. package/dist/core/scene/derivedParts.js.map +1 -1
  190. package/dist/core/scene/documentOf.js +43 -0
  191. package/dist/core/scene/documentOf.js.map +1 -0
  192. package/dist/core/scene/effectivePose.js +44 -15
  193. package/dist/core/scene/effectivePose.js.map +1 -1
  194. package/dist/core/scene/fieldEdits.js +17 -14
  195. package/dist/core/scene/fieldEdits.js.map +1 -1
  196. package/dist/core/scene/fontReads.js +37 -0
  197. package/dist/core/scene/fontReads.js.map +1 -0
  198. package/dist/core/scene/historyForm.js +4 -4
  199. package/dist/core/scene/historyForm.js.map +1 -1
  200. package/dist/core/scene/inheritedPaint.js +97 -0
  201. package/dist/core/scene/inheritedPaint.js.map +1 -0
  202. package/dist/core/scene/kitOps.js +3 -23
  203. package/dist/core/scene/kitOps.js.map +1 -1
  204. package/dist/core/scene/kitRegistry.js +5 -0
  205. package/dist/core/scene/kitRegistry.js.map +1 -1
  206. package/dist/core/scene/layoutPass.js +1 -2
  207. package/dist/core/scene/layoutPass.js.map +1 -1
  208. package/dist/core/scene/nodeAsShown.js +72 -0
  209. package/dist/core/scene/nodeAsShown.js.map +1 -0
  210. package/dist/core/scene/nodeFnFields.js +2 -10
  211. package/dist/core/scene/nodeFnFields.js.map +1 -1
  212. package/dist/core/scene/nodeMemo.js +21 -8
  213. package/dist/core/scene/nodeMemo.js.map +1 -1
  214. package/dist/core/scene/nodeStore.js +2 -4
  215. package/dist/core/scene/nodeStore.js.map +1 -1
  216. package/dist/core/scene/pathEffects/blend.js.map +1 -1
  217. package/dist/core/scene/pathEffects/derive.js +10 -4
  218. package/dist/core/scene/pathEffects/derive.js.map +1 -1
  219. package/dist/core/scene/pathEffects/effects.js +3 -1
  220. package/dist/core/scene/pathEffects/effects.js.map +1 -1
  221. package/dist/core/scene/pathEffects/foundHidden.js +35 -0
  222. package/dist/core/scene/pathEffects/foundHidden.js.map +1 -0
  223. package/dist/core/scene/pathEffects/textAlong.js +95 -0
  224. package/dist/core/scene/pathEffects/textAlong.js.map +1 -0
  225. package/dist/core/scene/plyStacks.js +89 -0
  226. package/dist/core/scene/plyStacks.js.map +1 -0
  227. package/dist/core/scene/poseHold.js +42 -24
  228. package/dist/core/scene/poseHold.js.map +1 -1
  229. package/dist/core/scene/poseOverrides.js +35 -38
  230. package/dist/core/scene/poseOverrides.js.map +1 -1
  231. package/dist/core/scene/posePlies.js +59 -0
  232. package/dist/core/scene/posePlies.js.map +1 -0
  233. package/dist/core/scene/reconcileSpecs.js +6 -15
  234. package/dist/core/scene/reconcileSpecs.js.map +1 -1
  235. package/dist/core/scene/renderOrder.js +10 -4
  236. package/dist/core/scene/renderOrder.js.map +1 -1
  237. package/dist/core/scene/sameValue.js +17 -0
  238. package/dist/core/scene/sameValue.js.map +1 -0
  239. package/dist/core/scene/scene.js +3 -3
  240. package/dist/core/scene/scene.js.map +1 -1
  241. package/dist/core/scene/sceneEdits.js +8 -0
  242. package/dist/core/scene/sceneEdits.js.map +1 -1
  243. package/dist/core/scene/sceneState.js +1 -2
  244. package/dist/core/scene/sceneState.js.map +1 -1
  245. package/dist/core/scene/serialization.js +45 -9
  246. package/dist/core/scene/serialization.js.map +1 -1
  247. package/dist/core/scene/staleReplay.js +5 -2
  248. package/dist/core/scene/staleReplay.js.map +1 -1
  249. package/dist/core/scene/statedFields.js +4 -2
  250. package/dist/core/scene/statedFields.js.map +1 -1
  251. package/dist/core/scene/treeEdits.js +2 -4
  252. package/dist/core/scene/treeEdits.js.map +1 -1
  253. package/dist/core/scene/treeOrder.js +3 -1
  254. package/dist/core/scene/treeOrder.js.map +1 -1
  255. package/dist/core/scene/treeReads.js +1 -2
  256. package/dist/core/scene/treeReads.js.map +1 -1
  257. package/dist/core/scene/treeShape.js +13 -0
  258. package/dist/core/scene/treeShape.js.map +1 -0
  259. package/dist/core/scene/types.js.map +1 -1
  260. package/dist/core/scene/useScene.js +1 -1
  261. package/dist/core/scene/useScene.js.map +1 -1
  262. package/dist/{cornerHandles-BHuoMs1o.d.ts → cornerHandles-CNCb7aTY.d.ts} +3 -2
  263. package/dist/{depSchema-BpZNOv7q.d.ts → depSchema-B1RS-IJx.d.ts} +39 -18
  264. package/dist/devtools/handlers.js +1 -1
  265. package/dist/devtools/handlers.js.map +1 -1
  266. package/dist/devtools/protocol.js.map +1 -1
  267. package/dist/devtools/registry.js +4 -4
  268. package/dist/devtools/registry.js.map +1 -1
  269. package/dist/devtools-protocol.d.ts +4 -3
  270. package/dist/devtools.d.ts +3 -2
  271. package/dist/features/measure/measureContribution.js +4 -2
  272. package/dist/features/measure/measureContribution.js.map +1 -1
  273. package/dist/features/measure/measureHit.js +5 -3
  274. package/dist/features/measure/measureHit.js.map +1 -1
  275. package/dist/features/measure/measurement.js +1 -1
  276. package/dist/features/measure/measurement.js.map +1 -1
  277. package/dist/features/modes/modeLayers.js +14 -7
  278. package/dist/features/modes/modeLayers.js.map +1 -1
  279. package/dist/features/modes/modeTriggers.js +10 -6
  280. package/dist/features/modes/modeTriggers.js.map +1 -1
  281. package/dist/features/pathEffects/effectSources.js +69 -0
  282. package/dist/features/pathEffects/effectSources.js.map +1 -0
  283. package/dist/features/pathEffects/readSource.js +18 -2
  284. package/dist/features/pathEffects/readSource.js.map +1 -1
  285. package/dist/features/pathEffects/textItems.js +53 -0
  286. package/dist/features/pathEffects/textItems.js.map +1 -0
  287. package/dist/features/pathEffects/useEffectSourcesLayer.js +53 -0
  288. package/dist/features/pathEffects/useEffectSourcesLayer.js.map +1 -0
  289. package/dist/features/paths/editablePath.js +1 -1
  290. package/dist/features/paths/editablePath.js.map +1 -1
  291. package/dist/features/paths/pathEditState.js +5 -2
  292. package/dist/features/paths/pathEditState.js.map +1 -1
  293. package/dist/features/paths/pathSnap/sceneIndex.js +1 -1
  294. package/dist/features/paths/pathSnap/sceneIndex.js.map +1 -1
  295. package/dist/features/selection/enteredGroupFrame.js +65 -0
  296. package/dist/features/selection/enteredGroupFrame.js.map +1 -0
  297. package/dist/features/selection/enteredGroupScope.js +44 -0
  298. package/dist/features/selection/enteredGroupScope.js.map +1 -0
  299. package/dist/features/text/sceneTextDrafts.js +64 -0
  300. package/dist/features/text/sceneTextDrafts.js.map +1 -0
  301. package/dist/features/text/textHitAt.js +34 -3
  302. package/dist/features/text/textHitAt.js.map +1 -1
  303. package/dist/features/text/useSceneTextEdit.js +23 -19
  304. package/dist/features/text/useSceneTextEdit.js.map +1 -1
  305. package/dist/features/viewports/aim.js +105 -0
  306. package/dist/features/viewports/aim.js.map +1 -0
  307. package/dist/features/viewports/viewMarker.js +82 -0
  308. package/dist/features/viewports/viewMarker.js.map +1 -0
  309. package/dist/features/viewports/viewMarkerDrag.js +55 -0
  310. package/dist/features/viewports/viewMarkerDrag.js.map +1 -0
  311. package/dist/index.d.ts +709 -258
  312. package/dist/index.js +33 -12
  313. package/dist/interactions/actions/admitsChild.js +11 -0
  314. package/dist/interactions/actions/admitsChild.js.map +1 -0
  315. package/dist/interactions/actions/booleans/booleans.js +2 -1
  316. package/dist/interactions/actions/booleans/booleans.js.map +1 -1
  317. package/dist/interactions/actions/booleans/operands.js +6 -5
  318. package/dist/interactions/actions/booleans/operands.js.map +1 -1
  319. package/dist/interactions/actions/clipboard/action.js +27 -17
  320. package/dist/interactions/actions/clipboard/action.js.map +1 -1
  321. package/dist/interactions/actions/defaults/anchorEditing.js +44 -23
  322. package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
  323. package/dist/interactions/actions/defaults/anchorJoin.js +12 -6
  324. package/dist/interactions/actions/defaults/anchorJoin.js.map +1 -1
  325. package/dist/interactions/actions/defaults/bendSegment.js +7 -4
  326. package/dist/interactions/actions/defaults/bendSegment.js.map +1 -1
  327. package/dist/interactions/actions/defaults/cancelGesture.js +11 -8
  328. package/dist/interactions/actions/defaults/cancelGesture.js.map +1 -1
  329. package/dist/interactions/actions/defaults/contextMenu.js +33 -11
  330. package/dist/interactions/actions/defaults/contextMenu.js.map +1 -1
  331. package/dist/interactions/actions/defaults/createPaintAction.js +5 -2
  332. package/dist/interactions/actions/defaults/createPaintAction.js.map +1 -1
  333. package/dist/interactions/actions/defaults/delete.js +28 -14
  334. package/dist/interactions/actions/defaults/delete.js.map +1 -1
  335. package/dist/interactions/actions/defaults/duplicate.js +11 -8
  336. package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
  337. package/dist/interactions/actions/defaults/enterGroup.js +88 -0
  338. package/dist/interactions/actions/defaults/enterGroup.js.map +1 -0
  339. package/dist/interactions/actions/defaults/enterPathEdit.js +20 -10
  340. package/dist/interactions/actions/defaults/enterPathEdit.js.map +1 -1
  341. package/dist/interactions/actions/defaults/enterTextEdit.js +1 -1
  342. package/dist/interactions/actions/defaults/enterTextEdit.js.map +1 -1
  343. package/dist/interactions/actions/defaults/escape.js +11 -8
  344. package/dist/interactions/actions/defaults/escape.js.map +1 -1
  345. package/dist/interactions/actions/defaults/exitPathEdit.js +7 -4
  346. package/dist/interactions/actions/defaults/exitPathEdit.js.map +1 -1
  347. package/dist/interactions/actions/defaults/group.js +26 -20
  348. package/dist/interactions/actions/defaults/group.js.map +1 -1
  349. package/dist/interactions/actions/defaults/ingest.js +10 -4
  350. package/dist/interactions/actions/defaults/ingest.js.map +1 -1
  351. package/dist/interactions/actions/defaults/insertPathAnchor.js +7 -4
  352. package/dist/interactions/actions/defaults/insertPathAnchor.js.map +1 -1
  353. package/dist/interactions/actions/defaults/nudge.js +8 -2
  354. package/dist/interactions/actions/defaults/nudge.js.map +1 -1
  355. package/dist/interactions/actions/defaults/pathEditTools.js +11 -8
  356. package/dist/interactions/actions/defaults/pathEditTools.js.map +1 -1
  357. package/dist/interactions/actions/defaults/pinchZoom.js +7 -4
  358. package/dist/interactions/actions/defaults/pinchZoom.js.map +1 -1
  359. package/dist/interactions/actions/defaults/reorder.js +24 -6
  360. package/dist/interactions/actions/defaults/reorder.js.map +1 -1
  361. package/dist/interactions/actions/defaults/selectAll.js +15 -8
  362. package/dist/interactions/actions/defaults/selectAll.js.map +1 -1
  363. package/dist/interactions/actions/defaults/selectDeep.js +35 -0
  364. package/dist/interactions/actions/defaults/selectDeep.js.map +1 -0
  365. package/dist/interactions/actions/defaults/setFill.js +7 -3
  366. package/dist/interactions/actions/defaults/setFill.js.map +1 -1
  367. package/dist/interactions/actions/defaults/setFillOpacity.js +1 -1
  368. package/dist/interactions/actions/defaults/setStroke.js +12 -11
  369. package/dist/interactions/actions/defaults/setStroke.js.map +1 -1
  370. package/dist/interactions/actions/defaults/setStrokeOpacity.js +1 -1
  371. package/dist/interactions/actions/defaults/toolActivate.js +7 -1
  372. package/dist/interactions/actions/defaults/toolActivate.js.map +1 -1
  373. package/dist/interactions/actions/defaults/toolResetToDefault.js +7 -4
  374. package/dist/interactions/actions/defaults/toolResetToDefault.js.map +1 -1
  375. package/dist/interactions/actions/defaults/undoRedo.js +19 -13
  376. package/dist/interactions/actions/defaults/undoRedo.js.map +1 -1
  377. package/dist/interactions/actions/defaults/viewportWheelPan.js +16 -7
  378. package/dist/interactions/actions/defaults/viewportWheelPan.js.map +1 -1
  379. package/dist/interactions/actions/defaults/viewportZoom.js +24 -6
  380. package/dist/interactions/actions/defaults/viewportZoom.js.map +1 -1
  381. package/dist/interactions/actions/drawnBoxes.js +2 -1
  382. package/dist/interactions/actions/drawnBoxes.js.map +1 -1
  383. package/dist/interactions/actions/edit-anchors/action.js +7 -4
  384. package/dist/interactions/actions/edit-anchors/action.js.map +1 -1
  385. package/dist/interactions/actions/flip/action.js +8 -2
  386. package/dist/interactions/actions/flip/action.js.map +1 -1
  387. package/dist/interactions/actions/insert/action.js +1 -0
  388. package/dist/interactions/actions/insert/action.js.map +1 -1
  389. package/dist/interactions/actions/insert/parent.js +7 -5
  390. package/dist/interactions/actions/insert/parent.js.map +1 -1
  391. package/dist/interactions/actions/insert/placeAction.js +3 -2
  392. package/dist/interactions/actions/insert/placeAction.js.map +1 -1
  393. package/dist/interactions/actions/insert/repositionAction.js +2 -1
  394. package/dist/interactions/actions/insert/repositionAction.js.map +1 -1
  395. package/dist/interactions/actions/insert/session.js +2 -2
  396. package/dist/interactions/actions/insert/session.js.map +1 -1
  397. package/dist/interactions/actions/move/action.js +7 -4
  398. package/dist/interactions/actions/move/action.js.map +1 -1
  399. package/dist/interactions/actions/move/commit/reparent.js +1 -1
  400. package/dist/interactions/actions/move/commit/reparent.js.map +1 -1
  401. package/dist/interactions/actions/move/gestureAdapter.js +6 -7
  402. package/dist/interactions/actions/move/gestureAdapter.js.map +1 -1
  403. package/dist/interactions/actions/move/layout/exits.js +1 -1
  404. package/dist/interactions/actions/move/layout/exits.js.map +1 -1
  405. package/dist/interactions/actions/move/session.js +4 -4
  406. package/dist/interactions/actions/move/session.js.map +1 -1
  407. package/dist/interactions/actions/outlines/createOutlinesAction.js +8 -5
  408. package/dist/interactions/actions/outlines/createOutlinesAction.js.map +1 -1
  409. package/dist/interactions/actions/outlines/outlineStroke.js +5 -3
  410. package/dist/interactions/actions/outlines/outlineStroke.js.map +1 -1
  411. package/dist/interactions/actions/pathCommands/cutJoin.js +8 -5
  412. package/dist/interactions/actions/pathCommands/cutJoin.js.map +1 -1
  413. package/dist/interactions/actions/pathCommands/pathNodes.js +14 -5
  414. package/dist/interactions/actions/pathCommands/pathNodes.js.map +1 -1
  415. package/dist/interactions/actions/resize/cornerHandles.js +2 -1
  416. package/dist/interactions/actions/resize/cornerHandles.js.map +1 -1
  417. package/dist/interactions/actions/selectionSubtree.js +12 -7
  418. package/dist/interactions/actions/selectionSubtree.js.map +1 -1
  419. package/dist/interactions/actions/transform-again/action.js +8 -5
  420. package/dist/interactions/actions/transform-again/action.js.map +1 -1
  421. package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js +14 -2
  422. package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js.map +1 -1
  423. package/dist/json.d.ts +4 -0
  424. package/dist/json.js +3 -0
  425. package/dist/kitSettingsSchema.js +8 -1
  426. package/dist/kitSettingsSchema.js.map +1 -1
  427. package/dist/{math-CrJULaZI.d.ts → math-C_elShP_.d.ts} +7 -1
  428. package/dist/math.d.ts +1 -1
  429. package/dist/math.js +2 -1
  430. package/dist/move.d.ts +4 -4
  431. package/dist/{options-DBxEryYq.d.ts → options-ClEm3SEl.d.ts} +1 -1
  432. package/dist/resize.d.ts +4 -4
  433. package/dist/test-seams.d.ts +6 -6
  434. package/dist/tools/builtin/ellipse/useEllipseTool.js +1 -0
  435. package/dist/tools/builtin/ellipse/useEllipseTool.js.map +1 -1
  436. package/dist/tools/builtin/eyedropper/useEyedropperTool.js +2 -1
  437. package/dist/tools/builtin/eyedropper/useEyedropperTool.js.map +1 -1
  438. package/dist/tools/builtin/hand/dragPanContribution.js +4 -1
  439. package/dist/tools/builtin/hand/dragPanContribution.js.map +1 -1
  440. package/dist/tools/builtin/hand/useHandTool.js +7 -4
  441. package/dist/tools/builtin/hand/useHandTool.js.map +1 -1
  442. package/dist/tools/builtin/image/useImageTool.js +1 -0
  443. package/dist/tools/builtin/image/useImageTool.js.map +1 -1
  444. package/dist/tools/builtin/lasso/useLassoTool.js +1 -0
  445. package/dist/tools/builtin/lasso/useLassoTool.js.map +1 -1
  446. package/dist/tools/builtin/line/useLineTool.js +1 -0
  447. package/dist/tools/builtin/line/useLineTool.js.map +1 -1
  448. package/dist/tools/builtin/measure/useMeasureTool.js +12 -5
  449. package/dist/tools/builtin/measure/useMeasureTool.js.map +1 -1
  450. package/dist/tools/builtin/pen/usePenTool.js +14 -6
  451. package/dist/tools/builtin/pen/usePenTool.js.map +1 -1
  452. package/dist/tools/builtin/pencil/usePencilTool.js +1 -0
  453. package/dist/tools/builtin/pencil/usePencilTool.js.map +1 -1
  454. package/dist/tools/builtin/polygon/usePolygonTool.js +13 -4
  455. package/dist/tools/builtin/polygon/usePolygonTool.js.map +1 -1
  456. package/dist/tools/builtin/pressTarget.js +22 -0
  457. package/dist/tools/builtin/pressTarget.js.map +1 -0
  458. package/dist/tools/builtin/rect/useRectTool.js +1 -0
  459. package/dist/tools/builtin/rect/useRectTool.js.map +1 -1
  460. package/dist/tools/builtin/select/adapterPointQuery.js +6 -5
  461. package/dist/tools/builtin/select/adapterPointQuery.js.map +1 -1
  462. package/dist/tools/builtin/select/collapseDeferred.js +40 -0
  463. package/dist/tools/builtin/select/collapseDeferred.js.map +1 -0
  464. package/dist/tools/builtin/select/groupEntry.js +82 -0
  465. package/dist/tools/builtin/select/groupEntry.js.map +1 -0
  466. package/dist/tools/builtin/select/pressDecision.js +32 -0
  467. package/dist/tools/builtin/select/pressDecision.js.map +1 -0
  468. package/dist/tools/builtin/select/selectionContributions.js +22 -13
  469. package/dist/tools/builtin/select/selectionContributions.js.map +1 -1
  470. package/dist/tools/builtin/select/useSelectTool.js +74 -55
  471. package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
  472. package/dist/tools/builtin/slice/useSliceTool.js +22 -8
  473. package/dist/tools/builtin/slice/useSliceTool.js.map +1 -1
  474. package/dist/tools/builtin/star/useStarTool.js +13 -4
  475. package/dist/tools/builtin/star/useStarTool.js.map +1 -1
  476. package/dist/tools/builtin/sticky/stickyNode.js +4 -1
  477. package/dist/tools/builtin/sticky/stickyNode.js.map +1 -1
  478. package/dist/tools/builtin/sticky/useStickyTool.js +10 -5
  479. package/dist/tools/builtin/sticky/useStickyTool.js.map +1 -1
  480. package/dist/tools/builtin/text/useTextTool.js +3 -1
  481. package/dist/tools/builtin/text/useTextTool.js.map +1 -1
  482. package/dist/{types-7puAuVD2.d.ts → types-49uYJN3j.d.ts} +1 -1
  483. package/dist/{types-CniVctmk.d.ts → types-BEdRIhNc.d.ts} +272 -109
  484. package/dist/{types-oFeQ5VjV.d.ts → types-p2alu-R1.d.ts} +1 -1
  485. package/dist/util/paint.js +1 -3
  486. package/dist/util/paint.js.map +1 -1
  487. package/dist/version.js +1 -1
  488. package/package.json +29 -25
package/CHANGELOG.md CHANGED
@@ -1,5 +1,280 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.13.0-rc.0
4
+
5
+ ### Patch Changes
6
+
7
+ - 38d5ad1: Depend on `@msb235/blits` 0.12.0, which has no breaking changes from 0.11.0. Its fixes make a single `fn` voice over many subjects, the shape weasel's animator uses, cheaper per frame.
8
+ - 7edd3be: Canceling an in-place edit of text something derives from, such as the hidden text of a Text Along Path, now leaves no undo entry and keeps the redo stack. Its keystrokes still reach the curved copy as you type, but they are shown through a data override rather than written into the node, so the document holds none of them; a commit records one entry from the text before the edit to the text after it. A write made to the node during the edit, such as a fill change, shows through the draft and is kept, and undoing it after a cancel brings back no draft text.
9
+ - cbb16ca: `kit:shape` paints a container that has a `shape` again, and its interior is grabbable by its fill. The previous release stopped it, so a node that draws itself and has children, such as a skeleton bone with child bones, drew nothing. A container whose `path` is its clip, as an SVG `<g>` imports, still paints nothing of its own.
10
+ - 5f28e9c: A path effect that hides its sources no longer treats a source that was already hidden when the effect was made as one it hid. Release leaves that source hidden, and Expand to Paths, Delete and Cut leave it in the scene; Set Path moving off it leaves it hidden too. The effect records those sources by position in a new `foundHidden` field beside `hideSources`, and a saved effect without one reads as before: it hid them all. `sourcesHiddenBy(scene, id)` in core answers which sources an effect holds hidden, and `sourcesOf(node)` the ids a node names in its `dependsOn`.
11
+ - e70d2cd: A node is a mark or a group, and the two now behave differently:
12
+
13
+ - A group never paints, whatever its data: a `shape` or `path` on a group draws nothing, and only its members draw. A mark with children paints itself and its children. A group is hit anywhere in its box, even with no member under the press or after its last member is deleted.
14
+ - Ungroup dissolves only groups; a mark with children is left whole.
15
+ - The area hit test's `includeContainers` option is now `includeGroups` (breaking for a custom `hitTestArea` dep). Marquee and lasso take a mark and its children, and a group only when asked for.
16
+ - A mark's drawn bounds include its own ink as well as its children's.
17
+ - Any node may clip, repeat (`copies`), lay out and set units for its children, and `'inherit'` fills and strokes read from any parent.
18
+ - A drop or an insert lands in a mark only when the `canDropInto` dep admits it; with no dep, a group admits and a mark does not. An insert asks `canDropInto` with an empty list, since the new node is not minted yet.
19
+ - Diagram bodies, containers, pools and lanes are marks, so they paint again and take drops through `diagramCanDropInto`. A stencil body still takes nothing, and a press inside an unfilled lane hits the lane.
20
+ - In a diagram scene, `kind === 'group'` no longer identifies a body or a region. A region is a node where `diagramContainerOf(node)` is not null. A diagram group box is one where `diagramGroupOf(node)` is not null. A body is a node where `diagramNodeOf(node)` is not null and neither of those is. `isDiagramBody(node)` asks that, and `isBodyOrRegion(node)` asks for either.
21
+ - ed89d42: A node clips its children only when it declares a clip. Breaking: a group no longer clips its children to its outline by default, and nothing else does either. A node clips its children to its `clipFromPose` when it has one, or to its outline when it sets the new `clip: 'outline'` field. Its own paint, under its children or over them, is not clipped by its own clip. That outline is the silhouette of whichever painter claims the node, the rect fallback's box included, read by the new `clipOutline(node, pose)`. `clip` is saved and loaded, copied, pasted and undone like the node's other fields, and `NodeClip` names its type.
22
+
23
+ Scenes saved at version 1 keep their look: a version 1 container that loads as a group, as it does by default, gets `clip: 'outline'` when it has no clip function or a load skips its clip key. One that `classifyV1` makes a mark gets no implicit clip, since the loader cannot know which painter will claim it; set `clip` on it where it should clip. Version 2 scenes saved by builds between the version 2 format and this change are read as written, so a group there that relied on the old default clip draws its children unclipped.
24
+
25
+ - `clipNothing` and symmetry's `SYMMETRY_CLIP_KEY` stay registered so saves naming them load; diagram bodies and symmetry groups no longer set a clip, and `@weasel-js/diagram`'s `BodyNodeSpec` drops its `clipFromPose` field.
26
+ - Baking a symmetry group carries each copied node's pose through the copy's transform, its clip with it, and copies a mark's children and every node's stated fields (`pickable`, `hidden`, `units`, `clip`). A mirrored copy of a clipping group used to clip its content away.
27
+ - `statedFields(node)` and `StatedNodeFields` are exported: the plain optional fields a copy of a node carries.
28
+ - `@weasel-js/svg`: an imported `<g clip-path>` is a group with `clip: 'outline'` (`SvgSceneDraft` carries `clip` on a group). The export writes a clip only for a node that declares one, clipped to the same `clipOutline` the canvas uses; a group holding `data.path` without `clip` exports unclipped. A mark with children now exports as a `<g>` holding its own element and then its children, which were left out before; its declared clip and copies apply to its children only. `SvgKitTreeNode` takes `clip`.
29
+ - `@weasel-js/diagram-formats`: a page export writes a diagram box as its outline followed by its rows, with no special case for diagram nodes.
30
+ - `specsFromSerialized` is exported, with `SpecsFromSerializedOptions`. Its `unknownKeys: 'skip'` loads a node whose registry key is unknown without that function instead of throwing, and `onSkip` hears each key left out as a `SkippedKey`. `sceneFromJSON` takes both options, and `scene.loadState(json, opts)` takes them as a second argument.
31
+ - 04b862d: Scene node kinds are now `mark` and `group`, replacing `leaf` and `container`, and any node may have children. `ContainerNode` and `LeafNode` are now `GroupNode` and `MarkNode`; `AddNodeSpec.kind` is `'mark' | 'group'`, and `isLeaf(node)` and `hasChildren(node)` ask whether a node has children. This breaks code that names the old kinds. Scenes save as version 2, and a version 1 save still loads: its leaves become marks and its containers become groups, unless the `classifyV1` option of `sceneFromJSON(json, { classifyV1 })` says otherwise. `V1Classifier` and `nodeKindOf` are exported from `@weasel-js/core`. `@weasel-js/svg`'s `SvgSceneDraft.kind` changed from `'container' | 'leaf'` to `'group' | 'mark'`.
32
+ - 7e7549c: A group now selects as one item:
33
+
34
+ - A press on a group's member selects the group, and so does a right-click, whose menu is the group's own. A drag from a member moves the group.
35
+ - A double-click on a member enters the group, drawn with a dashed frame; a press then selects among its members, and a further double-click goes one level deeper. Escape leaves one level. Both work under any tool. While a group is entered only what is inside it can be pressed, marqueed, lassoed or selected with Select All. Under the select tool a press outside the entered group's box, with or without modifiers, leaves every group that box is outside of and selects nothing; that leave is its own binding, `group.leave.press`, which `bindingOverrides` can rebind or remove. Under a drawing tool the press draws, and what is drawn or pasted lands in the entered group: core now sources the `insertInto` dep from it, under any source registered after it, and `sceneToAdapter`'s new `pasteInto` option says where a paste lands. A double-click on a member that is not a group still reaches text and path editing, and an open mode takes Escape before the group does.
36
+ - Cmd-click (Ctrl on Windows and Linux) selects the node under the pointer past every group. It no longer extends the selection: Shift-click does that, and Cmd-Shift-click adds the node under the pointer past every group.
37
+ - The entered group is session state beside the selection: undo does not move it. `<SceneCanvas>` publishes it as the optional `enteredGroup` dep, and an absent dep means no group is entered. `<SceneCanvas enteredGroup>` takes an `EnteredGroupApi` made with `useEnteredGroup(scene)`, as `selection` is passed, for an app that reads or drives it; without it the canvas keeps its own. `leaveAll()` leaves every level at once.
38
+ - `createEnteredGroupScope({ entered, scene })` returns a `NodeScope` (from `@weasel-js/modes`, which `ScopingDim` now extends) for the entered group, everything outside it dimmed to `OUT_OF_SCOPE_ALPHA`. Compose its `alphaFor` into `<SceneCanvas alphaFor>` and pass it in `redrawOn` to dim what is outside the group.
39
+ - The entered-group frame takes its color from the `--wzl-entered-group` override hook on the canvas or any element above it, and repaints when a theme, mode or class attribute changes there or the system color scheme flips.
40
+ - A contribution's dep source may read the source it displaces: `ContributionDeps` entries take the `below` argument `DepSource` does. The symmetry contribution's `insertInto` falls through to it when no symmetry group is active.
41
+ - `workspaceTintLayer` takes a `fallback` tint, shown when the mode has none.
42
+
43
+ Breaking: isolation mode is gone. `ISOLATION` is no longer exported from `@weasel-js/modes` and `DEFAULT_MODES` no longer holds it, and `<SceneCanvas isolatedGroup>` is removed: the entered group is the one way of working inside a group. In WeaselDraw a double-click on a group's member now enters the group, dims everything outside it, tints the workspace, and names the group in the breadcrumb. Edits made inside it are each their own undo step, where isolation by default collected them into one entry on leaving, and path and text editing open inside the group without leaving it.
44
+ - d5a9a80: A path effect whose source has an `'inherit'` fill or stroke paints that source in what it inherits from its containers, and follows a container's paint when it changes; it used to read as none. `DerivedDep` gains an optional `inherited`, which every dependency the scene builds carries, so any derivation can read the paint a dependency shows. `diagram:mark` resolves an `'inherit'` fill and stroke too.
45
+ - 3519c96: Two more readers resolve an `'inherit'` fill or stroke against a node's containers. The preview ghost of a gesture paints a member with the paint its containers are taking mid-gesture, so a container's fill or stroke scrubbed in a paint control reaches its inheriting members at once rather than at the commit: the paint actions preview those members with the container, and the ghost reads every previewed container above a member. And culling measures an inherited stroke as the stroke it takes instead of declining to bound the node: `defaultPaintBounds` and `PaintBoundsFn` take an optional fourth `ctx` with `inherited`, which `<SceneCanvas>` and the headless render fill in from the scene; `withInheritedPaintBounds` does the same for a consumer's own cull. A `drawOne` wrapped by `withInheritedPaints` now keeps a `ctx.inherited` its caller passes.
46
+ - 773f9c8: Several writers can now override one node at once. Each entry set on `scene.overrides` is a ply over the ones set before it: for each field the most recently claimed ply that gives it wins, `alpha` multiplies through every ply, and `data` runs each ply's function on what the plies under it show. Releasing a ply shows what lies under it. A ply's `path` is drawn in the box of the `pose` it gives beside it, so a pose laid over it by another writer, such as a move over a skinned node, carries the path along; a path given with no pose is drawn where it is. So a gesture's published preview no longer gives way where another writer holds the node: the data a move previews for a transform follower shows while the move holds the follower's pose, and an edge whose live route is eased shows the drag's reroute beside the route, so what derives from either follows every frame.
47
+
48
+ `has` and `clear` take an optional `entry`, to check or release one writer's ply: `has(id, entry)`, `clear(id, entry)`. `clear(id)` still drops every ply of the node; each keeps its place, so a writer that sets its entry again goes back where it stood. `get(id)` answers the top ply. A hand-written `PoseOverrides` must honor `entry` on both: a writer releasing its own ply must not clear another's. This is a behavior change for a writer that set an entry over another's to replace it, or that checked `get(id) === entry` to know its own was still installed: the earlier entry now stays and shows again when the later one goes, and `get` answers only the top one. A hold's `show` no longer leaves an id another writer holds to that writer; it lays its frame over it. `PoseHold.others()` answers each node's topmost pose that is not the hold's own, where it answered the pose of whichever single entry the node held.
49
+
50
+ `PoseTransitionScene.overrides` now needs `read` where it needed `has`: the undo glide skips only a node something else poses, not one showing a data draft. A container's descendants likewise ride along with its pose animation unless another writer poses them. Rigs and pose targets put their override back when the table drops it, and a pose target retargeted mid-run keeps running when its own write reflows a layout. Labkit's `LensActionApi` gains `unfollow`, which Pinned and Close call to let go of the follow hold alone.
51
+ - 39c72a4: What derives from a node now follows an anchor drag on it every frame, rather than at the release: a Text Along Path laid along a path or a blend from it, and the same for every other action that previews through its handle. `<SceneCanvas>` publishes each in-flight handle's `previewPose` and `previewData` to the scene's overrides through the new `publishPreviews(scene, dispatcher)`, and releases them once the handle's commit has landed, so a cancel leaves the document and its history untouched. A consumer driving a dispatcher of its own, a 3D host among them, can call it too.
52
+
53
+ A pose override can now carry `data(stored)`, the data a node is shown with, worked out from what it stores. Painting, picking, drawn bounds and the effective reading of every derivation read it through the new `nodeAsShown(overrides, node)`; the document reading, `scene.get`, history and export do not. `PoseOverride` and `PoseOverrides` take the scene's data type as a second parameter, and `PoseHold` gains `show(frame)`, which publishes data beside poses and leaves an id another writer holds to that writer; a hold now puts back an entry the scene dropped when its node was removed, once the node is back.
54
+
55
+ Commands and exports read the document through the new `documentOf(scene)`, the scene with its overrides empty: Expand to Paths, Slice, `drawnBoxes` and what it serves (Align, Flip, resize and rotate, snap targets), and the diagram page export no longer bake in an in-flight drag's pose or an open edit's draft, and `documentDepOf` takes the reading node as `derivedDepOf` does. `withPath` keeps returning the same object for the same node and path, so a gesture previewing through it keeps its previews' identity between frames.
56
+ - 8c39126: Picks consult a BVH index kept beside the scene, so a marquee, a lasso or a click visits the nodes near the query instead of every node. `@weasel-js/geom` adds the tree. Three contracts tighten for the index to be sound. `NodeInk.outset` must not depend on the view scale: screen-space reach goes in `outsetPx`. In kernel3d, `world.bounds` must be a function of the node alone. Two picks change behavior: marquee and lasso now test a derived node at its derived path, and the point pick now tests a node carrying an override `path` against that path, which is what the renderer paints, instead of its pose box.
57
+ - 1322310: A node's declared clip can now be changed after it is added: `scene.setClip(id, 'outline')` declares it and `scene.setClip(id, null)` clears it, as one undo step, refused on a locked node like `setUnits`. `reconcileSpecs` writes a changed `clip` in place through it, where before it removed the node and added it again. The two setters are typed together as `SceneDeclaredFieldSetters`, which `Scene` extends. `setUnits` no longer records an entry when given a scope equal to the one declared. This adds API.
58
+ - bbee4df: `startThresholdDrag` takes `captureAt: 'threshold'`, which leaves the pointer uncaptured until the press is a drag, so a press released short of the threshold still clicks what it was on; the capture it takes is released when the drag ends or is canceled. `modifierStateOf`, which reads an event's modifier keys as a `ModifierState`, is exported from routing and core, for `keysHeld` to read a press.
59
+ - Updated dependencies [7e5f9e3]
60
+ - Updated dependencies [38d5ad1]
61
+ - Updated dependencies [04b862d]
62
+ - Updated dependencies [7e7549c]
63
+ - Updated dependencies [c7516cd]
64
+ - Updated dependencies [1f2a0b3]
65
+ - Updated dependencies [8c39126]
66
+ - Updated dependencies [3eaceb1]
67
+ - Updated dependencies [86f67ad]
68
+ - Updated dependencies [769eef8]
69
+ - Updated dependencies [352502d]
70
+ - Updated dependencies [bbee4df]
71
+ - Updated dependencies [aba2016]
72
+ - Updated dependencies [4c113da]
73
+ - @weasel-js/geom@1.13.0-rc.0
74
+ - @weasel-js/animator@1.13.0-rc.0
75
+ - @weasel-js/quantity@1.13.0-rc.0
76
+ - @weasel-js/modes@1.13.0-rc.0
77
+ - @weasel-js/routing@1.13.0-rc.0
78
+ - @weasel-js/prefs@1.13.0-rc.0
79
+ - @weasel-js/text-dom@1.13.0-rc.0
80
+ - @weasel-js/renderer@1.13.0-rc.0
81
+ - @weasel-js/stroke@1.13.0-rc.0
82
+ - @weasel-js/text@1.13.0-rc.0
83
+ - @weasel-js/gestures@1.13.0-rc.0
84
+ - @weasel-js/cursor@1.13.0-rc.0
85
+ - @weasel-js/font@1.13.0-rc.0
86
+ - @weasel-js/history@1.13.0-rc.0
87
+ - @weasel-js/icons@1.13.0-rc.0
88
+ - @weasel-js/paint@1.13.0-rc.0
89
+ - @weasel-js/react@1.13.0-rc.0
90
+ - @weasel-js/registry@1.13.0-rc.0
91
+ - @weasel-js/select@1.13.0-rc.0
92
+ - @weasel-js/simulation@1.13.0-rc.0
93
+
94
+ ## 1.12.0
95
+
96
+ ### Minor Changes
97
+
98
+ - 13e597d: Text can follow a path. `@weasel-js/geom` adds a `'path'` track shape (`pathShape`), a `side` override on `trackRun`,
99
+ and `placeAlongRun`, which turns item lengths into places along a run, in `RunOrder` `'reading'` or `'track'`.
100
+ `TrackShape` gains a required `closed` member, so a shape implemented outside geom has to add it.
101
+ `runPlaces` and `runLength` moved there from `@weasel-js/ui`, which re-exports `runPlaces` by name, so its curved rows
102
+ come from geom's placement. `measurePath` takes `keepCorners`. `@weasel-js/text` adds `textClusters`, a line's
103
+ grapheme clusters as outlines with their advances. `@weasel-js/core` adds the `textAlong` path effect; every effect
104
+ takes `hideSources`, effect sources can carry a list of items, curved text is derived again when a font loads, a
105
+ hidden source is edited through its effect, and Delete, Cut and Expand now remove an effect's hidden sources with
106
+ it. A committed in-place text edit is labeled "Edit text" in history.
107
+ `@weasel-js/path-tools` adds Text Along Path (Alt+Shift+X), Release and Set Path. `useTextEdit` in
108
+ `@weasel-js/text-dom` gains `onDraft`.
109
+
110
+ <!-- bump-approved: minor: maintainer — 1.12.0 release requested 2026-10-22 -->
111
+
112
+ ### Patch Changes
113
+
114
+ - 0996ce6: `captureGesture` captures the next click, double click, drag, right click or keystroke as a gesture
115
+ spec, for a binding editor that rebinds pointer input as well as keys. It tells a click from a drag
116
+ with the dispatcher's own recognizer and drag threshold, writes the platform's primary modifier as
117
+ `mod`, and takes every event of what it captures from the page; Escape or an abort resolves `null`.
118
+ `captureKey` is now `captureGesture` limited to a keystroke.
119
+ - 3716e73: The dev-time route-conflict check and devtools' registry listing now read contribution bindings under
120
+ their binding overrides, as dispatch does: a binding rebound onto a taken key is reported, and an
121
+ unbound one is no longer listed or reported. The conflict check runs again when the overrides change.
122
+ `overriddenBindings` returns a binding list under the overrides in force, and `findScopedConflicts`
123
+ takes them as `overrides`.
124
+ - b571ad6: A node's `fill: 'inherit'` or `stroke: 'inherit'` now takes its nearest enclosing container's fill or stroke whole. If that container inherits too, the lookup keeps going up, and at the root it is none. A container's fill and stroke are only a source: the built-in `kit:shape` and `kit:path` painters no longer paint a container's own fill or stroke, and its interior is no longer hit for picking.
125
+
126
+ The scene's draw wrapper passes the resolved paint to painters as `ctx.inherited` on `NodePaintCtx`, and pickers pass it on `NodeInkCtx`. A painter's memo has to key on it: `nodeMemo` takes an extra reference-compared key for this, and the paint stays the same object while its sources are unchanged. **A custom painter that does not read `ctx.inherited` paints an `'inherit'` field as none.** Pass the field and `ctx?.inherited?.fill` to `resolveFill` (or `resolveStroke`) to pick it up. `diagram:shape` reads it. Readers outside a painter can use `inheritedPaintIn(scene, node)` and `withInheritedPaint(data, inherited)`, and `paintAbove(scene, node)` says what inheriting would paint.
127
+
128
+ SVG import turns a `<g>`'s fill and stroke into its container's, and a child that sets none of them gets `'inherit'`. A gradient, or a child faded further than its group, is still copied onto the child. Export writes a container's paint on its `<g>` and leaves the attribute off a child that inherits a solid one.
129
+
130
+ `SelectionPanel` adds an Inherit choice beside None for a node's fill and stroke, showing the paint it would take. `PaintField` and `PaintInput` offer it when given `inherit` (`{ paint, onInherit }`); their `value` may be `'inherit'`, and `onChange` is unchanged.
131
+ - 1cc3d07: Pressing a node inside a selected group no longer selects that node on the press. A drag that starts
132
+ there moves the group, and a click that ends without a drag selects the node, as a press on one member
133
+ of a multi-selection already did. It used to select the member on the press, so the drag moved the
134
+ member alone and left the rest of the group behind.
135
+
136
+ A group here is a container whose pose derives from its children (`dependsOn: 'children'` with a
137
+ `derivePose`), which is what the `group` action makes in a scene that composes poses by identity. A
138
+ selected container with a pose of its own, such as a diagram container, still lets a press pick the
139
+ member out of it.
140
+ - 78f29de: Lenses in weaselforge. Alt+L opens the Loupe keymap, and `l` adds a lens: a bubble showing the patch
141
+ of a story under its reticle, magnified, or rendered with a comparison such as the opposite mode, an
142
+ arg or a DPR, as Swap, Split or Difference. A lens shows a Live second render or, from the dev
143
+ server, Pixels captured in headless Chromium; Playwright is a new optional peer of
144
+ `@weasel-js/forge`, and without it a lens offers Live only. Token, Contrast and pixel-grid readouts
145
+ sit in the lens chrome, and Alt+Slash opens a panel listing every key binding the workshop
146
+ registers.
147
+
148
+ Everything below is additive.
149
+
150
+ forge: the shell config takes `capture.allowOrigins`, naming what a capture page may load besides
151
+ the dev server, and `loupe.modeGlobal`, the global an opposite-mode comparison flips. A global
152
+ declaration may give `opposite(value, globals)`, the value that comparison flips it to.
153
+ `<TrialHost>` takes a `copy` prop, which makes the host a second copy of a trial's story, under
154
+ globals of its own. `@weasel-js/forge/shell` exports `followEdits`, and `@weasel-js/forge/frame`
155
+ exports `mountCapture`, which mounts a capture page.
156
+
157
+ labkit: the loupe entry gains `<LensOverlay>` (whose `renderReadouts` is handed `LensReadoutProps`),
158
+ `<LensActions>`, the lens scene and its ops, the `LensSource` contract, `createLoupeKeymap`,
159
+ `probeUnderLens` and `LENS_REST_MS`. A lab has an `overlay` chrome region, rendered over the trials
160
+ inside the lab surface, and `useLabEntries` reads the ambient entries installed on the lab, its
161
+ keymaps among them. Each lab surface gains a `'top'` canvas layer for one tenant above `'over'`,
162
+ made only while a tenant asks for it; a surface owner offers it by publishing `top` and `claimTop`
163
+ in its `SurfaceCanvases`. `surface.scope()` takes a list of elements as well as one
164
+ (`SurfaceScope`).
165
+
166
+ Elsewhere: `@weasel-js/core/json` exports `stableStringify` for Node; paint exports `contrastRatio`
167
+ and `wcagLevels`; svg exports `specificity`, `compareSpecificity` and `splitTopLevel`; an action's
168
+ `enabled` may return a reason as text (`ActionUnavailable` in routing, re-exported by core), which
169
+ the command palette shows as written.
170
+ - 92a0f2d: A keymap's leader triggered without a key can now hold its keymap open:
171
+ `trigger('keymap.<id>', { hold: true })` keeps it open through any number of
172
+ inputs until Escape, where a plain `trigger` still opens it for one input.
173
+ `KEYMAP_HOLD` is the `heldBy` such a level reports.
174
+ - c697535: Every binding the kit ships now declares an id, so a binding override names it by that id rather
175
+ than by its owner and position: `undo.key`, `rect.drag`, `nudge.left.big`, `selection.rotate.handle`,
176
+ `tool.activate.rect`. The scheme is `<owner>.<role>`, the owner being the action for an action's own
177
+ binding and the tool or entry for a contribution's (`docs/taxonomy.md`, "binding override"). An
178
+ override written against a positional id such as `select#3` no longer reaches its binding.
179
+
180
+ An action's `defaultBinding` that was a bare spec is now a one-entry `{ spec, opts: { id } }` array;
181
+ code reading `defaultBinding` as a spec reads it through `actionBindings`.
182
+ - c2fb65c: Keymaps behind a leader key. `keymapContribution` builds a keymap, whose bindings are live only while
183
+ it is open, and the leader that opens it: tap the leader for one input, or hold it to keep the keymap
184
+ open. Escape always closes it; a key it does not bind is ignored, or passed on under the new
185
+ `gestures.keymapUnbound` kit setting. `<KeymapHint>` lists an open keymap's keys, and `formatShortcut`
186
+ draws a key inside one as `Loupe ▸ 2`.
187
+
188
+ Bindings can now be rebound or removed by id, on actions and contributions alike:
189
+ `ActionsRegistry.overrideBindings` and `<SceneCanvas bindingOverrides>`, written as route strings or
190
+ with the new `key()`, `held()` and `unbound()` helpers. `captureKey` reads the next keystroke for a
191
+ binding editor.
192
+
193
+ `modeShortcutSpec` now reads through `key()`, so a mode shortcut written `'Space'` or `'Esc'` names
194
+ the key it says; before, either matched nothing.
195
+
196
+ Additive: `ActionsRegistry` gains `overrideBindings` and `bindingOverrides`, so a hand-written
197
+ registry must implement them.
198
+ - 24f2495: An absent fill or stroke now means none everywhere, read through `resolveFill` and `resolveStroke`. This breaks three things that used to fill in a default:
199
+
200
+ - **A text node built without a `fill` now draws no glyphs.** Write `fill: { paint: solid('#000') }`, or `DEFAULT_TEXT_FILL`, now exported from `@weasel-js/text` and `@weasel-js/core`. `DEFAULT_TEXT_STYLE.fill` is `null`. The text insert, the sticky note and SVG import write black themselves.
201
+ - **A derived part with no `fill` or `stroke` no longer takes its node's.** Write `INHERIT` (`'inherit'`) for that; `DerivedPart` allows it, and `resolvePartPaint` is the one reading of a part's paint.
202
+ - **`diagramShape` drops its `defaultFill` and `defaultStroke` options.** An outline paints what its data names; `diagramScene` already writes the default style onto every node. SVG import writes SVG's initial black onto text that names no fill, and SVG export writes `fill="none"` for text that has none.
203
+
204
+ `setFill` and `setStroke` write None as a record with no `paint` rather than `null`, so a stroke keeps its width and dash; a `paint: null` passed when the gesture starts used to be read as no paint given and painted the default color. The selection panel shows a fill or stroke paint as a paint, None or Mixed, never unset; `nestedPaintValue` is that reading. Booleans, Outline Stroke and the character bar read a fill with no paint as unfilled. Core re-exports `resolveFill`, `resolveStroke`, `INHERIT` and their types.
205
+ - d9fd036: `@weasel-js/pdf` rasterizes a group with effects over the group's bounds grown by how far its effects reach, rather than over the whole page, so a small blurred badge no longer costs a full-page bitmap. An `Effect` can now say how far it reaches with the new optional `reach`, in device pixels; `blur` sets it, and `effectsReach` (`@weasel-js/renderer`) adds up a stack's. An effect without one, such as `vignette`, still covers the whole page. The renderer's retained surfaces use the same bound, and `commandsBounds` takes the scale the commands will be drawn at.
206
+
207
+ `screen`-unit paint inside a rasterized group now lands in the PDF where the vector path puts it, in page points rather than raster pixels. `WeaselRenderer.render` takes `screen` in its options, naming what screen space is when it is not the drawing surface, and `renderSceneToPixels` and a raster session's `render` pass it through as `screen`.
208
+ - f461572: `DiagramView` now draws a node's perimeter port labels. A node's `portLabels` are children of its
209
+ container and sit outside its box, and a container clips its children to its painter's
210
+ silhouette, so every one of them was clipped away. `buildBody` now gives the container a `clipFromPose` that clips nothing.
211
+
212
+ Core exports that clip as `clipNothing` (from `@weasel-js/core` and `@weasel-js/core/math`),
213
+ for any container whose children may draw outside it. Every scene's registry carries it under
214
+ `CLIP_NOTHING`, so a container using it saves and loads with no registry entry of the consumer's.
215
+ A symmetry group now uses it too, and still saves under `SYMMETRY_CLIP_KEY`.
216
+ - 3bf5edb: Add `resolveFill` and `resolveStroke`, the one answer to what a fill or stroke field paints: absent, `null` and a record with no `paint` are none, a paint with alpha 0 is still a paint, and `INHERIT` (`'inherit'`) takes whatever the caller passes as inherited. `fillHasInk` and `strokeHasInk` now go through them, so `fillHasInk({ paint: null })` is `false` where it used to be `true`.
217
+ - 92b8df8: A node with no `data.fill` now paints no fill. It used to paint gray (`#888`) in the built-in
218
+ painters and in `svgLeafFromKit`'s export, so a container from Cmd+G drew a gray box behind its
219
+ members, and a shape inserted without a fill showed up gray. This changes what existing scenes look
220
+ like: a node that relied on the gray default needs an explicit `fill`. `DEFAULT_SHAPE_FILL` is
221
+ removed, since nothing falls back to it any more, and `resolveNodeFill` takes no fallback argument.
222
+
223
+ The rect fallback painter no longer matches containers. A container without `clipFromPose` used to
224
+ take that painter's box as its silhouette and clip its members to it, which cut off the outer half of
225
+ their strokes, since a group's derived pose leaves stroke width out. A container now paints
226
+ nothing, even a `data.fill` it declares, and clips nothing; one that should clip says so with
227
+ `clipFromPose`, and a background behind a group's members is a leaf among them.
228
+ - 91a1d84: `useVisibleRaf`, `defaultStorage` and the deprecated `cornerPoint` now carry a description, which
229
+ editors show on hover and the package pages show beside the name.
230
+ - eec50d7: Views answer what they show: `SceneCanvasApi.aimOf` and `setAim`, and `<CanvasView aim onAimChange>` for a view that holds its aim and derives its camera from it. `<ViewMarker>`, or a contribution's `markers`, draws one view's aim inside another's frame, and dragging the marker moves that view; `actions={null}` turns marker dragging off. kernel3d answers a frustum aim with `aimOf3d`.
231
+
232
+ `CanvasViewProps` is now a union: `aim` cannot be passed together with `view`, `defaultView`, `onViewChange` or `viewBounds`. Code that writes an interface extending `CanvasViewProps` needs a type alias instead. A `Partial<CanvasViewProps>` spread into `<CanvasView>` no longer type-checks, and `Omit<CanvasViewProps, …>` does not distribute over the union; type either over one of the named halves, `CanvasViewCommonProps` with `CameraHeldViewProps` or `AimHeldViewProps`.
233
+
234
+ `SceneCanvasApi` has two new required members, `aimOf` and `setAim`, so a hand-written fake of it needs both.
235
+ - Updated dependencies [dbd6711]
236
+ - Updated dependencies [f6db995]
237
+ - Updated dependencies [0996ce6]
238
+ - Updated dependencies [3716e73]
239
+ - Updated dependencies [b571ad6]
240
+ - Updated dependencies [13e597d]
241
+ - Updated dependencies [400b393]
242
+ - Updated dependencies [40d87f7]
243
+ - Updated dependencies [78f29de]
244
+ - Updated dependencies [2cf7da4]
245
+ - Updated dependencies [92a0f2d]
246
+ - Updated dependencies [448065a]
247
+ - Updated dependencies [c697535]
248
+ - Updated dependencies [c2fb65c]
249
+ - Updated dependencies [2c52cc5]
250
+ - Updated dependencies [94ebd35]
251
+ - Updated dependencies [24f2495]
252
+ - Updated dependencies [929736f]
253
+ - Updated dependencies [d9fd036]
254
+ - Updated dependencies [3bf5edb]
255
+ - Updated dependencies [e9304f4]
256
+ - Updated dependencies [91a1d84]
257
+ - @weasel-js/geom@1.12.0
258
+ - @weasel-js/routing@1.12.0
259
+ - @weasel-js/paint@1.12.0
260
+ - @weasel-js/text@1.12.0
261
+ - @weasel-js/text-dom@1.12.0
262
+ - @weasel-js/stroke@1.12.0
263
+ - @weasel-js/gestures@1.12.0
264
+ - @weasel-js/renderer@1.12.0
265
+ - @weasel-js/react@1.12.0
266
+ - @weasel-js/animator@1.12.0
267
+ - @weasel-js/prefs@1.12.0
268
+ - @weasel-js/simulation@1.12.0
269
+ - @weasel-js/cursor@1.12.0
270
+ - @weasel-js/font@1.12.0
271
+ - @weasel-js/history@1.12.0
272
+ - @weasel-js/icons@1.12.0
273
+ - @weasel-js/modes@1.12.0
274
+ - @weasel-js/quantity@1.12.0
275
+ - @weasel-js/registry@1.12.0
276
+ - @weasel-js/select@1.12.0
277
+
3
278
  ## 1.11.1
4
279
 
5
280
  ### Patch Changes
@@ -2,8 +2,8 @@ import { PointerProviderIfRoot } from "./canvas/SceneCanvas/PointerProviderIfRoo
2
2
  import { ActionsProviderIfRoot } from "./canvas/SceneCanvas/ActionsProviderIfRoot.js";
3
3
  import { DepRegistryProviderIfRoot } from "./canvas/SceneCanvas/DepRegistryProviderIfRoot.js";
4
4
  import { IconRegistryScope } from "./iconRegistry/IconRegistryProvider.js";
5
- import { ActionsProvider, ActiveToolContextProvider, ActiveToolContextProviderIfRoot, DepRegistryProvider, PointerContextProvider, SelectionContextProvider, SelectionContextProviderIfRoot } from "@weasel-js/routing/react";
6
5
  import { jsx } from "react/jsx-runtime";
6
+ import { ActionsProvider, ActiveToolContextProvider, ActiveToolContextProviderIfRoot, DepRegistryProvider, PointerContextProvider, SelectionContextProvider, SelectionContextProviderIfRoot } from "@weasel-js/routing/react";
7
7
 
8
8
  //#region src/WeaselProvider.tsx
9
9
  /**
@@ -21,12 +21,12 @@ var PATH_ANCHOR_CHROME_ID = "path-edit.anchors";
21
21
  */
22
22
  function createPathAnchorAffordances(getAnchorState, opts = {}) {
23
23
  const { hitRadius = 8, cursor = "pointer" } = opts;
24
- /** Selected polygon paths, paired with their node id. */
24
+ /** Selected polygon paths, and the one being edited, paired with their node id. */
25
25
  const editablePaths = (state) => {
26
26
  const anchorState = getAnchorState();
27
27
  if (!anchorState) return [];
28
28
  const out = [];
29
- for (const id of state.selection) {
29
+ for (const id of withEdited(state.selection, anchorState.editingId)) {
30
30
  const pose = anchorState.getPose(id);
31
31
  if (!pose || pose.kind !== "polygon") continue;
32
32
  out.push({
@@ -119,17 +119,16 @@ function createPathAnchorAffordances(getAnchorState, opts = {}) {
119
119
  controls
120
120
  ];
121
121
  }
122
- /** The parts path-edit extensions add to the path in edit mode while it is
123
- * selected, each pressed as its own `hitKind`. */
122
+ /** The parts path-edit extensions add to the path in edit mode, each pressed
123
+ * as its own `hitKind`. */
124
124
  function extensionParts(getAnchorState, hitRadius, cursor) {
125
125
  return {
126
126
  id: PATH_ANCHOR_CHROME_ID,
127
- regions(state) {
127
+ regions() {
128
128
  const anchorState = getAnchorState();
129
129
  const editingId = anchorState?.editingId ?? null;
130
130
  const extensions = anchorState?.extensions?.filter((e) => e.parts) ?? [];
131
131
  if (editingId === null || extensions.length === 0) return [];
132
- if (!state.selection.includes(editingId)) return [];
133
132
  const pose = anchorState.getPose(editingId);
134
133
  const node = anchorState.getNode?.(editingId);
135
134
  if (!node || !pose || pose.kind !== "polygon") return [];
@@ -157,6 +156,11 @@ function extensionParts(getAnchorState, hitRadius, cursor) {
157
156
  }
158
157
  };
159
158
  }
159
+ /** `selection`, and the path being edited after it when the selection does not hold it: a path edited through the
160
+ * effect that hides it is edited while the effect is what is selected. */
161
+ function withEdited(selection, editingId) {
162
+ return editingId === null || selection.includes(editingId) ? selection : [...selection, editingId];
163
+ }
160
164
 
161
165
  //#endregion
162
166
  export { PATH_ANCHOR_CHROME_ID, createPathAnchorAffordances };
@@ -1 +1 @@
1
- {"version":3,"file":"pathAnchors.js","names":[],"sources":["../../src/affordances/pathAnchors.ts"],"sourcesContent":["/** Path anchor, control-handle, and segment affordances, and the parts path-edit extensions add. */\n\nimport type { CursorSpec } from '@weasel-js/cursor';\nimport type { Affordance, AffordanceBinding, AffordanceRegion, CommonAffordanceScratch } from './types';\nimport type { ChromeState } from 'core/selection/chromeState';\nimport { pathToAnchors, type AnchorSet, type PolygonPath } from '@weasel-js/geom';\nimport { enumerateAnchors } from 'interactions/actions/edit-anchors/geometry';\nimport type { PathEditExtension } from 'features/paths/pathEditExtension';\n\n/**\n * Live anchor-editing state. The thunk that returns it is called on every\n * pointer event, so it must be cheap (O(1) field reads).\n */\nexport interface AnchorState {\n /** Id of the path currently in anchor-edit mode, or `null`. Control handles\n * are only hittable on the path that is being edited. */\n editingId: string | null;\n /** Current editable path for a node id. Non-polygon values are ignored. */\n getPose(id: string): unknown;\n /** The committed pose and data of a node, which extensions' parts read. */\n getNode?(id: string): { pose: unknown; data: unknown } | null;\n /** The path-edit extensions whose parts are pressed, in order. */\n extensions?: readonly PathEditExtension[];\n /** The active path-edit tool, if any. While one is set, anchors and controls\n * carry no cursor of their own, so the tool's action shows its own. */\n tool?: string | null;\n /** The path whose outline counts as a segment press, when it is not\n * `getPose`'s: a path drawn from a form, edited through the form's edit\n * path. */\n getOutline?(id: string): unknown;\n}\n\n/** Options for the path-anchor affordances. */\nexport interface PathAnchorAffordanceOptions {\n /** Hit radius (screen-px) for anchor and control points. Default 8. */\n hitRadius?: number;\n /** Cursor while hovering an anchor or control, with no path-edit tool\n * active. Defaults to `'pointer'`. */\n cursor?: CursorSpec;\n}\n\n/** What an anchor hit hands to the action that follows. */\nexport interface AnchorScratch extends CommonAffordanceScratch {\n /** Node id of the path the anchor belongs to. */\n targetId: string;\n /** Sequential anchor index in path walk order. */\n anchorIndex: number;\n /** Which of the anchor's three grabbable points this is. */\n part: 'anchor' | 'controlIn' | 'controlOut';\n}\n\n/** Chrome-caps id gating the anchor, control, and segment affordances. */\nexport const PATH_ANCHOR_CHROME_ID = 'path-edit.anchors';\n\n/**\n * Segment, extension-part, anchor, and control affordances for the selected\n * paths, bottom → top.\n *\n * Three affordances rather than one, so that a control handle beats an anchor\n * regardless of which is nearer, the way controls draw on top, and both beat\n * the segment they sit on. Within each, nearest wins. A press on the edited\n * path's outline is a `segment` hit, which the segment bend binds to and a\n * selection move turns down. Anchors, controls, and segments share one id:\n * chrome-caps gates anchor editing as a single piece of chrome. A part a\n * path-edit extension adds (`PathEditExtension.parts`) may name a chrome id of\n * its own as well, so it can be shown apart from the anchors.\n *\n * Order matters — spread this into an affordance list in the returned order.\n */\nexport function createPathAnchorAffordances(\n getAnchorState: () => AnchorState | null,\n opts: PathAnchorAffordanceOptions = {},\n): Affordance[] {\n const { hitRadius = 8, cursor = 'pointer' } = opts;\n\n /** Selected polygon paths, paired with their node id. */\n const editablePaths = (state: ChromeState): Array<{ id: string; path: PolygonPath }> => {\n const anchorState = getAnchorState();\n if (!anchorState) return [];\n const out: Array<{ id: string; path: PolygonPath }> = [];\n for (const id of state.selection) {\n const pose = anchorState.getPose(id);\n if (!pose || (pose as { kind?: string }).kind !== 'polygon') continue;\n out.push({ id, path: pose as PolygonPath });\n }\n return out;\n };\n\n const point = (\n id: string,\n targetId: string,\n x: number,\n y: number,\n scratch: AnchorScratch,\n hitKind: string,\n ): AffordanceRegion => ({\n id,\n // Anchor coordinates are already world space, pose rotation baked in.\n // Anchoring to `null` keeps the framework from applying it twice.\n targetId: null,\n shape: { kind: 'point', x, y, hitRadiusPx: hitRadius },\n hitKind,\n ...(getAnchorState()?.tool ? {} : { cursor }),\n bind: (): AffordanceBinding => ({ initialScratch: { ...scratch, targetId } }),\n });\n\n const anchors: Affordance = {\n id: PATH_ANCHOR_CHROME_ID,\n regions(state) {\n const editingId = getAnchorState()?.editingId ?? null;\n const out: AffordanceRegion[] = [];\n for (const { id, path } of editablePaths(state)) {\n // `editAnchorsAction` edits whatever path the dep says is in edit\n // mode, ignoring which one was hit — so once a path IS being edited,\n // only its own anchors may claim the press.\n if (editingId !== null && id !== editingId) continue;\n for (const a of enumerateAnchors(path)) {\n out.push(point(\n `${id}:anchor:${a.anchorIndex}`,\n id, a.x, a.y,\n { targetId: id, anchorIndex: a.anchorIndex, part: 'anchor' },\n `anchor:${a.anchorIndex}`,\n ));\n }\n }\n return out;\n },\n };\n\n const controls: Affordance = {\n id: PATH_ANCHOR_CHROME_ID,\n regions(state) {\n const anchorState = getAnchorState();\n const editingId = anchorState?.editingId ?? null;\n if (editingId === null) return [];\n const out: AffordanceRegion[] = [];\n for (const { id, path } of editablePaths(state)) {\n // Controls are grabbable only on the path actually in edit mode.\n if (id !== editingId) continue;\n for (const a of enumerateAnchors(path)) {\n if (a.controlIn) {\n out.push(point(\n `${id}:controlIn:${a.anchorIndex}`,\n id, a.controlIn.x, a.controlIn.y,\n { targetId: id, anchorIndex: a.anchorIndex, part: 'controlIn' },\n `controlIn:${a.anchorIndex}`,\n ));\n }\n if (a.controlOut) {\n out.push(point(\n `${id}:controlOut:${a.anchorIndex}`,\n id, a.controlOut.x, a.controlOut.y,\n { targetId: id, anchorIndex: a.anchorIndex, part: 'controlOut' },\n `controlOut:${a.anchorIndex}`,\n ));\n }\n }\n }\n return out;\n },\n };\n\n const segments: Affordance = {\n id: PATH_ANCHOR_CHROME_ID,\n regions(state) {\n const editingId = getAnchorState()?.editingId ?? null;\n if (editingId === null) return [];\n const edited = editablePaths(state).find(({ id }) => id === editingId);\n if (!edited) return [];\n const outline = getAnchorState()?.getOutline?.(editingId) as PolygonPath | null | undefined;\n return [{\n id: `${editingId}:segment`,\n targetId: null,\n shape: { kind: 'outline', path: outline?.kind === 'polygon' ? outline : edited.path, hitRadiusPx: hitRadius },\n hitKind: 'segment',\n bind: (): AffordanceBinding => ({ initialScratch: { targetId: editingId } }),\n }];\n },\n };\n\n return [segments, extensionParts(getAnchorState, hitRadius, cursor), anchors, controls];\n}\n\n/** The parts path-edit extensions add to the path in edit mode while it is\n * selected, each pressed as its own `hitKind`. */\nfunction extensionParts(\n getAnchorState: () => AnchorState | null,\n hitRadius: number,\n cursor: CursorSpec,\n): Affordance {\n return {\n id: PATH_ANCHOR_CHROME_ID,\n regions(state) {\n const anchorState = getAnchorState();\n const editingId = anchorState?.editingId ?? null;\n const extensions = anchorState?.extensions?.filter((e) => e.parts) ?? [];\n if (editingId === null || extensions.length === 0) return [];\n if (!(state.selection as readonly string[]).includes(editingId)) return [];\n const pose = anchorState!.getPose(editingId);\n const node = anchorState!.getNode?.(editingId);\n if (!node || !pose || (pose as { kind?: string }).kind !== 'polygon') return [];\n const target = {\n id: editingId, node, path: pose as PolygonPath, anchors: pathToAnchors(pose as PolygonPath) as AnchorSet,\n };\n return extensions.flatMap((ext) => ext.parts!(target).map((part): AffordanceRegion => ({\n id: `${editingId}:${part.hitKind}`,\n targetId: null,\n shape: {\n kind: 'point', x: part.x, y: part.y, hitRadiusPx: part.hitRadiusPx ?? hitRadius,\n ...(part.standoff ? { standoff: part.standoff } : {}),\n },\n hitKind: part.hitKind,\n ...(part.chromeId ? { chromeId: part.chromeId } : {}),\n cursor,\n bind: (): AffordanceBinding => ({ initialScratch: { targetId: editingId } }),\n })));\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"pathAnchors.js","names":[],"sources":["../../src/affordances/pathAnchors.ts"],"sourcesContent":["/** Path anchor, control-handle, and segment affordances, and the parts path-edit extensions add. */\n\nimport type { CursorSpec } from '@weasel-js/cursor';\nimport type { Affordance, AffordanceBinding, AffordanceRegion, CommonAffordanceScratch } from './types';\nimport type { ChromeState } from 'core/selection/chromeState';\nimport { pathToAnchors, type AnchorSet, type PolygonPath } from '@weasel-js/geom';\nimport { enumerateAnchors } from 'interactions/actions/edit-anchors/geometry';\nimport type { PathEditExtension } from 'features/paths/pathEditExtension';\n\n/**\n * Live anchor-editing state. The thunk that returns it is called on every\n * pointer event, so it must be cheap (O(1) field reads).\n */\nexport interface AnchorState {\n /** Id of the path currently in anchor-edit mode, or `null`. Control handles\n * are only hittable on the path that is being edited. */\n editingId: string | null;\n /** Current editable path for a node id. Non-polygon values are ignored. */\n getPose(id: string): unknown;\n /** The committed pose and data of a node, which extensions' parts read. */\n getNode?(id: string): { pose: unknown; data: unknown } | null;\n /** The path-edit extensions whose parts are pressed, in order. */\n extensions?: readonly PathEditExtension[];\n /** The active path-edit tool, if any. While one is set, anchors and controls\n * carry no cursor of their own, so the tool's action shows its own. */\n tool?: string | null;\n /** The path whose outline counts as a segment press, when it is not\n * `getPose`'s: a path drawn from a form, edited through the form's edit\n * path. */\n getOutline?(id: string): unknown;\n}\n\n/** Options for the path-anchor affordances. */\nexport interface PathAnchorAffordanceOptions {\n /** Hit radius (screen-px) for anchor and control points. Default 8. */\n hitRadius?: number;\n /** Cursor while hovering an anchor or control, with no path-edit tool\n * active. Defaults to `'pointer'`. */\n cursor?: CursorSpec;\n}\n\n/** What an anchor hit hands to the action that follows. */\nexport interface AnchorScratch extends CommonAffordanceScratch {\n /** Node id of the path the anchor belongs to. */\n targetId: string;\n /** Sequential anchor index in path walk order. */\n anchorIndex: number;\n /** Which of the anchor's three grabbable points this is. */\n part: 'anchor' | 'controlIn' | 'controlOut';\n}\n\n/** Chrome-caps id gating the anchor, control, and segment affordances. */\nexport const PATH_ANCHOR_CHROME_ID = 'path-edit.anchors';\n\n/**\n * Segment, extension-part, anchor, and control affordances for the selected\n * paths, bottom → top.\n *\n * Three affordances rather than one, so that a control handle beats an anchor\n * regardless of which is nearer, the way controls draw on top, and both beat\n * the segment they sit on. Within each, nearest wins. A press on the edited\n * path's outline is a `segment` hit, which the segment bend binds to and a\n * selection move turns down. Anchors, controls, and segments share one id:\n * chrome-caps gates anchor editing as a single piece of chrome. A part a\n * path-edit extension adds (`PathEditExtension.parts`) may name a chrome id of\n * its own as well, so it can be shown apart from the anchors.\n *\n * Order matters — spread this into an affordance list in the returned order.\n */\nexport function createPathAnchorAffordances(\n getAnchorState: () => AnchorState | null,\n opts: PathAnchorAffordanceOptions = {},\n): Affordance[] {\n const { hitRadius = 8, cursor = 'pointer' } = opts;\n\n /** Selected polygon paths, and the one being edited, paired with their node id. */\n const editablePaths = (state: ChromeState): Array<{ id: string; path: PolygonPath }> => {\n const anchorState = getAnchorState();\n if (!anchorState) return [];\n const out: Array<{ id: string; path: PolygonPath }> = [];\n for (const id of withEdited(state.selection, anchorState.editingId)) {\n const pose = anchorState.getPose(id);\n if (!pose || (pose as { kind?: string }).kind !== 'polygon') continue;\n out.push({ id, path: pose as PolygonPath });\n }\n return out;\n };\n\n const point = (\n id: string,\n targetId: string,\n x: number,\n y: number,\n scratch: AnchorScratch,\n hitKind: string,\n ): AffordanceRegion => ({\n id,\n // Anchor coordinates are already world space, pose rotation baked in.\n // Anchoring to `null` keeps the framework from applying it twice.\n targetId: null,\n shape: { kind: 'point', x, y, hitRadiusPx: hitRadius },\n hitKind,\n ...(getAnchorState()?.tool ? {} : { cursor }),\n bind: (): AffordanceBinding => ({ initialScratch: { ...scratch, targetId } }),\n });\n\n const anchors: Affordance = {\n id: PATH_ANCHOR_CHROME_ID,\n regions(state) {\n const editingId = getAnchorState()?.editingId ?? null;\n const out: AffordanceRegion[] = [];\n for (const { id, path } of editablePaths(state)) {\n // `editAnchorsAction` edits whatever path the dep says is in edit\n // mode, ignoring which one was hit — so once a path IS being edited,\n // only its own anchors may claim the press.\n if (editingId !== null && id !== editingId) continue;\n for (const a of enumerateAnchors(path)) {\n out.push(point(\n `${id}:anchor:${a.anchorIndex}`,\n id, a.x, a.y,\n { targetId: id, anchorIndex: a.anchorIndex, part: 'anchor' },\n `anchor:${a.anchorIndex}`,\n ));\n }\n }\n return out;\n },\n };\n\n const controls: Affordance = {\n id: PATH_ANCHOR_CHROME_ID,\n regions(state) {\n const anchorState = getAnchorState();\n const editingId = anchorState?.editingId ?? null;\n if (editingId === null) return [];\n const out: AffordanceRegion[] = [];\n for (const { id, path } of editablePaths(state)) {\n // Controls are grabbable only on the path actually in edit mode.\n if (id !== editingId) continue;\n for (const a of enumerateAnchors(path)) {\n if (a.controlIn) {\n out.push(point(\n `${id}:controlIn:${a.anchorIndex}`,\n id, a.controlIn.x, a.controlIn.y,\n { targetId: id, anchorIndex: a.anchorIndex, part: 'controlIn' },\n `controlIn:${a.anchorIndex}`,\n ));\n }\n if (a.controlOut) {\n out.push(point(\n `${id}:controlOut:${a.anchorIndex}`,\n id, a.controlOut.x, a.controlOut.y,\n { targetId: id, anchorIndex: a.anchorIndex, part: 'controlOut' },\n `controlOut:${a.anchorIndex}`,\n ));\n }\n }\n }\n return out;\n },\n };\n\n const segments: Affordance = {\n id: PATH_ANCHOR_CHROME_ID,\n regions(state) {\n const editingId = getAnchorState()?.editingId ?? null;\n if (editingId === null) return [];\n const edited = editablePaths(state).find(({ id }) => id === editingId);\n if (!edited) return [];\n const outline = getAnchorState()?.getOutline?.(editingId) as PolygonPath | null | undefined;\n return [{\n id: `${editingId}:segment`,\n targetId: null,\n shape: { kind: 'outline', path: outline?.kind === 'polygon' ? outline : edited.path, hitRadiusPx: hitRadius },\n hitKind: 'segment',\n bind: (): AffordanceBinding => ({ initialScratch: { targetId: editingId } }),\n }];\n },\n };\n\n return [segments, extensionParts(getAnchorState, hitRadius, cursor), anchors, controls];\n}\n\n/** The parts path-edit extensions add to the path in edit mode, each pressed\n * as its own `hitKind`. */\nfunction extensionParts(\n getAnchorState: () => AnchorState | null,\n hitRadius: number,\n cursor: CursorSpec,\n): Affordance {\n return {\n id: PATH_ANCHOR_CHROME_ID,\n regions() {\n const anchorState = getAnchorState();\n const editingId = anchorState?.editingId ?? null;\n const extensions = anchorState?.extensions?.filter((e) => e.parts) ?? [];\n if (editingId === null || extensions.length === 0) return [];\n const pose = anchorState!.getPose(editingId);\n const node = anchorState!.getNode?.(editingId);\n if (!node || !pose || (pose as { kind?: string }).kind !== 'polygon') return [];\n const target = {\n id: editingId, node, path: pose as PolygonPath, anchors: pathToAnchors(pose as PolygonPath) as AnchorSet,\n };\n return extensions.flatMap((ext) => ext.parts!(target).map((part): AffordanceRegion => ({\n id: `${editingId}:${part.hitKind}`,\n targetId: null,\n shape: {\n kind: 'point', x: part.x, y: part.y, hitRadiusPx: part.hitRadiusPx ?? hitRadius,\n ...(part.standoff ? { standoff: part.standoff } : {}),\n },\n hitKind: part.hitKind,\n ...(part.chromeId ? { chromeId: part.chromeId } : {}),\n cursor,\n bind: (): AffordanceBinding => ({ initialScratch: { targetId: editingId } }),\n })));\n },\n };\n}\n\n/** `selection`, and the path being edited after it when the selection does not hold it: a path edited through the\n * effect that hides it is edited while the effect is what is selected. */\nfunction withEdited(selection: readonly string[], editingId: string | null): readonly string[] {\n return editingId === null || selection.includes(editingId) ? selection : [...selection, editingId];\n}\n"],"mappings":"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"}
@@ -57,7 +57,7 @@ function liveAxes(descriptor, get) {
57
57
  };
58
58
  }
59
59
  /** Shows a moving container's descendants through overrides of their own, and takes them back
60
- * as it rests. A descendant something else already overrides is left to it. */
60
+ * as it rests. A descendant something else already poses is left to it. */
61
61
  function riders(overrides, cascade) {
62
62
  const riding = /* @__PURE__ */ new Map();
63
63
  return (pose) => {
@@ -65,7 +65,7 @@ function riders(overrides, cascade) {
65
65
  for (const [id, beside] of pose === null ? [] : cascade.beside(pose)) {
66
66
  let entry = riding.get(id);
67
67
  if (!entry) {
68
- if (overrides.has(id)) continue;
68
+ if (overrides.read(id)?.pose !== void 0) continue;
69
69
  riding.set(id, entry = {});
70
70
  overrides.set(id, entry);
71
71
  }
@@ -74,7 +74,7 @@ function riders(overrides, cascade) {
74
74
  }
75
75
  for (const [id, entry] of riding) {
76
76
  if (seen.has(id)) continue;
77
- if (overrides.get(id) === entry) overrides.clear(id);
77
+ overrides.clear(id, entry);
78
78
  riding.delete(id);
79
79
  }
80
80
  };
@@ -86,23 +86,37 @@ function build(opts, onRest, overwritten, ride) {
86
86
  if (!axes) return null;
87
87
  const entry = {};
88
88
  let installed = false;
89
+ /** While installed, stops the run as soon as the table changes after something else wrote
90
+ * the pose: a drag laid over the run commits and then lets go, which would show this run's
91
+ * last frame until its next one. */
92
+ let unwatch = null;
93
+ const rest = () => {
94
+ installed = false;
95
+ unwatch?.();
96
+ unwatch = null;
97
+ overrides.clear(id, entry);
98
+ ride?.(null);
99
+ onRest?.();
100
+ };
89
101
  const target = createTarget({
90
102
  get: opts.get,
91
103
  set: opts.set,
92
104
  axes,
93
105
  show(pose) {
94
106
  if (target.active && overwritten?.()) target.stop({ commit: false });
95
- if (target.active) {
96
- entry.pose = pose;
97
- if (!installed) overrides.set(id, entry);
98
- installed = true;
99
- ride?.(pose);
107
+ if (!target.active) {
108
+ rest();
100
109
  return;
101
110
  }
102
- installed = false;
103
- if (overrides.get(id) === entry) overrides.clear(id);
104
- ride?.(null);
105
- onRest?.();
111
+ entry.pose = pose;
112
+ if (!overrides.has(id, entry)) overrides.set(id, entry);
113
+ installed = true;
114
+ if (overwritten && unwatch === null) unwatch = overrides.subscribe(() => {
115
+ if (!target.active || !overwritten()) return;
116
+ target.stop({ commit: false });
117
+ if (installed) rest();
118
+ });
119
+ ride?.(pose);
106
120
  },
107
121
  publish: publisherOf(overrides)
108
122
  });
@@ -122,11 +136,20 @@ function createPoseTarget(opts) {
122
136
  return target;
123
137
  }
124
138
  var sceneTargets = /* @__PURE__ */ new WeakMap();
139
+ /** Whether two poses hold the same fields, compared without allocating: a running target asks on every publish. */
125
140
  function samePose(a, b) {
126
141
  if (a === b) return true;
127
142
  if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
128
- const ka = Object.keys(a);
129
- return ka.length === Object.keys(b).length && ka.every((k) => a[k] === b[k]);
143
+ const ra = a;
144
+ const rb = b;
145
+ let count = 0;
146
+ for (const k in ra) {
147
+ if (!Object.hasOwn(ra, k)) continue;
148
+ if (!Object.hasOwn(rb, k) || ra[k] !== rb[k]) return false;
149
+ count++;
150
+ }
151
+ for (const k in rb) if (Object.hasOwn(rb, k)) count--;
152
+ return count === 0;
130
153
  }
131
154
  /**
132
155
  * The pose target of node `id` in `scene`: the same one on every call, so everything animating
@@ -154,6 +177,8 @@ function scenePoseTarget(scene, id, descriptor, cascade) {
154
177
  let last = documentPose(scene, node);
155
178
  /** The node's pose as an animation last left it, or as it was when the first one started. */
156
179
  let mine;
180
+ /** While this target's own commit is being written. */
181
+ let writing = false;
157
182
  const cache = made;
158
183
  let target = null;
159
184
  target = build({
@@ -169,12 +194,18 @@ function scenePoseTarget(scene, id, descriptor, cascade) {
169
194
  set(pose) {
170
195
  const n = scene.get(nid);
171
196
  if (!n) return;
172
- if (!samePose(n.pose, pose)) scene.untracked(() => cascade ? cascade.write(pose) : scene.setPose(nid, pose));
197
+ writing = true;
198
+ try {
199
+ if (!samePose(n.pose, pose)) scene.untracked(() => cascade ? cascade.write(pose) : scene.setPose(nid, pose));
200
+ } finally {
201
+ writing = false;
202
+ }
173
203
  mine = scene.get(nid)?.pose;
174
204
  }
175
205
  }, () => {
176
206
  if (!scene.get(nid)) cache.delete(id);
177
207
  }, () => {
208
+ if (writing) return false;
178
209
  const n = scene.get(nid);
179
210
  return !!n && mine !== void 0 && !samePose(n.pose, mine);
180
211
  }, cascade && riders(scene.overrides, cascade));
@@ -1 +1 @@
1
- {"version":3,"file":"poseTarget.js","names":[],"sources":["../../src/animation/poseTarget.ts"],"sourcesContent":["import { RECT_POSE_DESCRIPTOR, type PoseDescriptor } from '../core/geometry/poseDescriptor';\nimport { documentPose, type PoseSource } from '../core/scene/effectivePose';\nimport type { NodeId, PoseOverride, PoseOverrides } from '../core/scene/types';\nimport { axesOf } from '@weasel-js/animator';\nimport { createTarget, type Target, type TargetAxes } from '@weasel-js/animator';\n\n/** What {@link createPoseTarget} takes. */\nexport interface PoseTargetOptions<TPose> {\n /** The node whose pose it is. */\n id: string;\n /** The node's committed pose: where the document says it is. */\n get(): TPose;\n /** Commits a pose: an animation's destination as it starts, and where a canceled one stopped. */\n set(pose: TPose): void;\n /** Where the pose on screen is published while the node moves. */\n overrides: PoseOverrides<TPose>;\n /** Supplies the pose's axes through `PoseDescriptor.axes`. Without one, or where it answers\n * null, the pose has to be a number, an array of numbers, or an object of numeric fields. */\n descriptor?: PoseDescriptor<TPose>;\n}\n\n/** The part of a scene {@link scenePoseTarget} reads and writes. A `Scene` satisfies it. */\nexport interface PoseTargetScene<TPose> extends PoseSource<TPose> {\n readonly overrides: PoseOverrides<TPose>;\n setPose(id: NodeId, pose: TPose): void;\n untracked<T>(fn: () => T): T;\n}\n\n/** How `like`'s kind of pose maps to axes: the descriptor's own mapping, else its flat numeric\n * fields. Null for a pose with neither. */\nexport function poseAxes<TPose>(\n descriptor: PoseDescriptor<TPose> | undefined,\n like: TPose,\n): TargetAxes<TPose> | null {\n return descriptor?.axes?.(like) ?? (axesOf(like) as TargetAxes<TPose> | null);\n}\n\nconst publishers = new WeakMap<object, () => void>();\n\n/** One function per overrides table, so every target publishing into it shares one commit a frame. */\nfunction publisherOf<TPose>(overrides: PoseOverrides<TPose>): () => void {\n let publish = publishers.get(overrides);\n if (!publish) {\n publish = () => overrides.commit();\n publishers.set(overrides, publish);\n }\n return publish;\n}\n\n/**\n * `poseAxes` for the pose `get` answers, with `from` keeping the fields that are not axes as\n * that pose has them now. Reading them once would leave them stale in every pose a canceled\n * animation commits. Null where the pose has no axes.\n */\nexport function liveAxes<TPose>(\n descriptor: PoseDescriptor<TPose> | undefined,\n get: () => TPose,\n): TargetAxes<TPose> | null {\n let like = get();\n let axes = poseAxes(descriptor, like);\n if (!axes) return null;\n const { count, angles } = axes;\n const current = (): TargetAxes<TPose> => {\n const now = get();\n if (now !== like) {\n const next = poseAxes(descriptor, now);\n if (next && next.count === count) axes = next;\n like = now;\n }\n return axes!;\n };\n return {\n count,\n angles,\n to: (p) => current().to(p),\n from: (a) => current().from(a),\n add: axes.add && ((p, d) => {\n const now = current();\n if (now.add) return now.add(p, d);\n return now.from(now.to(p).map((n, i) => n + d[i]!));\n }),\n write: axes.write && ((p, out, at) => {\n const now = current();\n if (now.write) now.write(p, out, at);\n else now.to(p).forEach((n, i) => { out[at + i] = n; });\n }),\n };\n}\n\n/** How a container's pose target carries its descendants, for {@link scenePoseTarget}. */\nexport interface PoseCascade<TPose> {\n /** Commits `pose` to the container, moving its descendants' poses with it. */\n write(pose: TPose): void;\n /** The descendants, each at the pose to show it at while the container shows at `pose`. */\n beside(pose: TPose): Iterable<readonly [NodeId, TPose]>;\n}\n\n/** Shows a moving container's descendants through overrides of their own, and takes them back\n * as it rests. A descendant something else already overrides is left to it. */\nfunction riders<TPose>(overrides: PoseOverrides<TPose>, cascade: PoseCascade<TPose>): (pose: TPose | null) => void {\n const riding = new Map<NodeId, PoseOverride<TPose>>();\n return (pose) => {\n const seen = new Set<NodeId>();\n for (const [id, beside] of pose === null ? [] : cascade.beside(pose)) {\n let entry = riding.get(id);\n if (!entry) {\n if (overrides.has(id)) continue;\n riding.set(id, entry = {});\n overrides.set(id, entry);\n }\n entry.pose = beside;\n seen.add(id);\n }\n for (const [id, entry] of riding) {\n if (seen.has(id)) continue;\n if (overrides.get(id) === entry) overrides.clear(id);\n riding.delete(id);\n }\n };\n}\n\nfunction build<TPose>(\n opts: PoseTargetOptions<TPose>,\n onRest?: () => void,\n /** Whether something else has written the committed pose since an animation last did. */\n overwritten?: () => boolean,\n /** Told the pose on screen each time it is shown, and null as the node comes to rest. */\n ride?: (pose: TPose | null) => void,\n): Target<TPose> | null {\n const { overrides } = opts;\n const id = opts.id as NodeId;\n const axes = liveAxes(opts.descriptor, opts.get);\n if (!axes) return null;\n const entry: PoseOverride<TPose> = {};\n let installed = false;\n const target = createTarget<TPose>({\n get: opts.get,\n set: opts.set,\n axes,\n show(pose) {\n // The writer wins: what was left to travel belonged to a pose that is no longer committed.\n if (target.active && overwritten?.()) target.stop({ commit: false });\n if (target.active) {\n entry.pose = pose;\n // Installed once per run: an override laid over it since, a drag's, is not taken back.\n if (!installed) overrides.set(id, entry);\n installed = true;\n ride?.(pose);\n return;\n }\n installed = false;\n if (overrides.get(id) === entry) overrides.clear(id);\n ride?.(null);\n onRest?.();\n },\n publish: publisherOf(overrides),\n });\n return target;\n}\n\n/**\n * A {@link Target} over one node's pose. Animations on it commit their destination through\n * `set` as they start, and the pose on screen reaches the paint and pick walks as a pose\n * override: installed while the node moves and cleared when it comes to rest.\n *\n * Throws for a pose with no axes. Make one per node: two targets over one node do not add.\n * {@link scenePoseTarget} keeps that one for a scene's nodes.\n */\nexport function createPoseTarget<TPose>(opts: PoseTargetOptions<TPose>): Target<TPose> {\n const target = build(opts);\n if (!target) {\n throw new Error('createPoseTarget: this pose has no axes; give a descriptor whose `axes` maps it');\n }\n return target;\n}\n\nconst sceneTargets = new WeakMap<object, Map<string, Target<unknown>>>();\n\nfunction samePose(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false;\n const ka = Object.keys(a);\n return ka.length === Object.keys(b).length\n && ka.every((k) => (a as Record<string, unknown>)[k] === (b as Record<string, unknown>)[k]);\n}\n\n/**\n * The pose target of node `id` in `scene`: the same one on every call, so everything animating\n * the node adds. Its committed value is the node's document pose, written without recording;\n * a caller that wants the move in history records its own op first.\n *\n * Undefined for a node that is not in the scene, or whose pose has no axes. `descriptor`\n * defaults to `RECT_POSE_DESCRIPTOR`. With `cascade`, for a container whose descendants hold\n * poses of their own, a commit goes through `cascade.write` and the descendants are shown\n * traveling beside the container. Both are read the first time a node's target is made.\n */\nexport function scenePoseTarget<TPose>(\n scene: PoseTargetScene<TPose>,\n id: string,\n descriptor?: PoseDescriptor<TPose>,\n cascade?: PoseCascade<TPose>,\n): Target<TPose> | undefined {\n let made = sceneTargets.get(scene.overrides);\n if (!made) {\n made = new Map();\n sceneTargets.set(scene.overrides, made);\n }\n const nid = id as NodeId;\n const node = scene.get(nid);\n const by = descriptor ?? (RECT_POSE_DESCRIPTOR as unknown as PoseDescriptor<TPose>);\n const held = made.get(id) as Target<TPose> | undefined;\n // A pose that has changed shape at rest, a polygon given more points, needs axes made for it.\n const fits = !held || held.active || !node\n || poseAxes(by, documentPose(scene, node))?.count === held.axisCount;\n if (held && fits) return held;\n if (!node) return undefined;\n // A node removed mid-flight keeps answering with the last pose it had.\n let last = documentPose(scene, node);\n /** The node's pose as an animation last left it, or as it was when the first one started. */\n let mine: TPose | undefined;\n const cache = made;\n let target: Target<TPose> | null = null;\n target = build<TPose>({\n id,\n descriptor: by,\n overrides: scene.overrides,\n get() {\n const n = scene.get(nid);\n if (n) last = documentPose(scene, n);\n if (n && !target?.active) mine = n.pose;\n return last;\n },\n set(pose) {\n const n = scene.get(nid);\n if (!n) return;\n if (!samePose(n.pose, pose)) scene.untracked(() => (cascade ? cascade.write(pose) : scene.setPose(nid, pose)));\n mine = scene.get(nid)?.pose;\n },\n }, () => {\n if (!scene.get(nid)) cache.delete(id);\n }, () => {\n const n = scene.get(nid);\n return !!n && mine !== undefined && !samePose(n.pose, mine);\n }, cascade && riders(scene.overrides, cascade));\n if (!target) return undefined;\n made.set(id, target as Target<unknown>);\n return target;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"poseTarget.js","names":[],"sources":["../../src/animation/poseTarget.ts"],"sourcesContent":["import { RECT_POSE_DESCRIPTOR, type PoseDescriptor } from '../core/geometry/poseDescriptor';\nimport { documentPose, type PoseSource } from '../core/scene/effectivePose';\nimport type { NodeId, PoseOverride, PoseOverrides } from '../core/scene/types';\nimport { axesOf } from '@weasel-js/animator';\nimport { createTarget, type Target, type TargetAxes } from '@weasel-js/animator';\n\n/** What {@link createPoseTarget} takes. */\nexport interface PoseTargetOptions<TPose> {\n /** The node whose pose it is. */\n id: string;\n /** The node's committed pose: where the document says it is. */\n get(): TPose;\n /** Commits a pose: an animation's destination as it starts, and where a canceled one stopped. */\n set(pose: TPose): void;\n /** Where the pose on screen is published while the node moves. */\n overrides: PoseOverrides<TPose>;\n /** Supplies the pose's axes through `PoseDescriptor.axes`. Without one, or where it answers\n * null, the pose has to be a number, an array of numbers, or an object of numeric fields. */\n descriptor?: PoseDescriptor<TPose>;\n}\n\n/** The part of a scene {@link scenePoseTarget} reads and writes. A `Scene` satisfies it. */\nexport interface PoseTargetScene<TPose> extends PoseSource<TPose> {\n readonly overrides: PoseOverrides<TPose>;\n setPose(id: NodeId, pose: TPose): void;\n untracked<T>(fn: () => T): T;\n}\n\n/** How `like`'s kind of pose maps to axes: the descriptor's own mapping, else its flat numeric\n * fields. Null for a pose with neither. */\nexport function poseAxes<TPose>(\n descriptor: PoseDescriptor<TPose> | undefined,\n like: TPose,\n): TargetAxes<TPose> | null {\n return descriptor?.axes?.(like) ?? (axesOf(like) as TargetAxes<TPose> | null);\n}\n\nconst publishers = new WeakMap<object, () => void>();\n\n/** One function per overrides table, so every target publishing into it shares one commit a frame. */\nfunction publisherOf<TPose>(overrides: PoseOverrides<TPose>): () => void {\n let publish = publishers.get(overrides);\n if (!publish) {\n publish = () => overrides.commit();\n publishers.set(overrides, publish);\n }\n return publish;\n}\n\n/**\n * `poseAxes` for the pose `get` answers, with `from` keeping the fields that are not axes as\n * that pose has them now. Reading them once would leave them stale in every pose a canceled\n * animation commits. Null where the pose has no axes.\n */\nexport function liveAxes<TPose>(\n descriptor: PoseDescriptor<TPose> | undefined,\n get: () => TPose,\n): TargetAxes<TPose> | null {\n let like = get();\n let axes = poseAxes(descriptor, like);\n if (!axes) return null;\n const { count, angles } = axes;\n const current = (): TargetAxes<TPose> => {\n const now = get();\n if (now !== like) {\n const next = poseAxes(descriptor, now);\n if (next && next.count === count) axes = next;\n like = now;\n }\n return axes!;\n };\n return {\n count,\n angles,\n to: (p) => current().to(p),\n from: (a) => current().from(a),\n add: axes.add && ((p, d) => {\n const now = current();\n if (now.add) return now.add(p, d);\n return now.from(now.to(p).map((n, i) => n + d[i]!));\n }),\n write: axes.write && ((p, out, at) => {\n const now = current();\n if (now.write) now.write(p, out, at);\n else now.to(p).forEach((n, i) => { out[at + i] = n; });\n }),\n };\n}\n\n/** How a container's pose target carries its descendants, for {@link scenePoseTarget}. */\nexport interface PoseCascade<TPose> {\n /** Commits `pose` to the container, moving its descendants' poses with it. */\n write(pose: TPose): void;\n /** The descendants, each at the pose to show it at while the container shows at `pose`. */\n beside(pose: TPose): Iterable<readonly [NodeId, TPose]>;\n}\n\n/** Shows a moving container's descendants through overrides of their own, and takes them back\n * as it rests. A descendant something else already poses is left to it. */\nfunction riders<TPose>(overrides: PoseOverrides<TPose>, cascade: PoseCascade<TPose>): (pose: TPose | null) => void {\n const riding = new Map<NodeId, PoseOverride<TPose>>();\n return (pose) => {\n const seen = new Set<NodeId>();\n for (const [id, beside] of pose === null ? [] : cascade.beside(pose)) {\n let entry = riding.get(id);\n if (!entry) {\n if (overrides.read(id)?.pose !== undefined) continue;\n riding.set(id, entry = {});\n overrides.set(id, entry);\n }\n entry.pose = beside;\n seen.add(id);\n }\n for (const [id, entry] of riding) {\n if (seen.has(id)) continue;\n overrides.clear(id, entry);\n riding.delete(id);\n }\n };\n}\n\nfunction build<TPose>(\n opts: PoseTargetOptions<TPose>,\n onRest?: () => void,\n /** Whether something else has written the committed pose since an animation last did. */\n overwritten?: () => boolean,\n /** Told the pose on screen each time it is shown, and null as the node comes to rest. */\n ride?: (pose: TPose | null) => void,\n): Target<TPose> | null {\n const { overrides } = opts;\n const id = opts.id as NodeId;\n const axes = liveAxes(opts.descriptor, opts.get);\n if (!axes) return null;\n const entry: PoseOverride<TPose> = {};\n let installed = false;\n /** While installed, stops the run as soon as the table changes after something else wrote\n * the pose: a drag laid over the run commits and then lets go, which would show this run's\n * last frame until its next one. */\n let unwatch: (() => void) | null = null;\n const rest = (): void => {\n installed = false;\n unwatch?.();\n unwatch = null;\n overrides.clear(id, entry);\n ride?.(null);\n onRest?.();\n };\n const target = createTarget<TPose>({\n get: opts.get,\n set: opts.set,\n axes,\n show(pose) {\n // The writer wins: what was left to travel belonged to a pose that is no longer committed.\n if (target.active && overwritten?.()) target.stop({ commit: false });\n if (!target.active) {\n rest();\n return;\n }\n entry.pose = pose;\n // Reinstalled where it stood if the table dropped it; an override laid over it since, a drag's, is not taken back.\n if (!overrides.has(id, entry)) overrides.set(id, entry);\n installed = true;\n if (overwritten && unwatch === null) {\n unwatch = overrides.subscribe(() => {\n if (!target.active || !overwritten()) return;\n target.stop({ commit: false });\n if (installed) rest();\n });\n }\n ride?.(pose);\n },\n publish: publisherOf(overrides),\n });\n return target;\n}\n\n/**\n * A {@link Target} over one node's pose. Animations on it commit their destination through\n * `set` as they start, and the pose on screen reaches the paint and pick walks as a pose\n * override: installed while the node moves and cleared when it comes to rest.\n *\n * Throws for a pose with no axes. Make one per node: two targets over one node do not add.\n * {@link scenePoseTarget} keeps that one for a scene's nodes.\n */\nexport function createPoseTarget<TPose>(opts: PoseTargetOptions<TPose>): Target<TPose> {\n const target = build(opts);\n if (!target) {\n throw new Error('createPoseTarget: this pose has no axes; give a descriptor whose `axes` maps it');\n }\n return target;\n}\n\nconst sceneTargets = new WeakMap<object, Map<string, Target<unknown>>>();\n\n/** Whether two poses hold the same fields, compared without allocating: a running target asks on every publish. */\nfunction samePose(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false;\n const ra = a as Record<string, unknown>;\n const rb = b as Record<string, unknown>;\n let count = 0;\n for (const k in ra) {\n if (!Object.hasOwn(ra, k)) continue;\n if (!Object.hasOwn(rb, k) || ra[k] !== rb[k]) return false;\n count++;\n }\n for (const k in rb) if (Object.hasOwn(rb, k)) count--;\n return count === 0;\n}\n\n/**\n * The pose target of node `id` in `scene`: the same one on every call, so everything animating\n * the node adds. Its committed value is the node's document pose, written without recording;\n * a caller that wants the move in history records its own op first.\n *\n * Undefined for a node that is not in the scene, or whose pose has no axes. `descriptor`\n * defaults to `RECT_POSE_DESCRIPTOR`. With `cascade`, for a container whose descendants hold\n * poses of their own, a commit goes through `cascade.write` and the descendants are shown\n * traveling beside the container. Both are read the first time a node's target is made.\n */\nexport function scenePoseTarget<TPose>(\n scene: PoseTargetScene<TPose>,\n id: string,\n descriptor?: PoseDescriptor<TPose>,\n cascade?: PoseCascade<TPose>,\n): Target<TPose> | undefined {\n let made = sceneTargets.get(scene.overrides);\n if (!made) {\n made = new Map();\n sceneTargets.set(scene.overrides, made);\n }\n const nid = id as NodeId;\n const node = scene.get(nid);\n const by = descriptor ?? (RECT_POSE_DESCRIPTOR as unknown as PoseDescriptor<TPose>);\n const held = made.get(id) as Target<TPose> | undefined;\n // A pose that has changed shape at rest, a polygon given more points, needs axes made for it.\n const fits = !held || held.active || !node\n || poseAxes(by, documentPose(scene, node))?.count === held.axisCount;\n if (held && fits) return held;\n if (!node) return undefined;\n // A node removed mid-flight keeps answering with the last pose it had.\n let last = documentPose(scene, node);\n /** The node's pose as an animation last left it, or as it was when the first one started. */\n let mine: TPose | undefined;\n /** While this target's own commit is being written. */\n let writing = false;\n const cache = made;\n let target: Target<TPose> | null = null;\n target = build<TPose>({\n id,\n descriptor: by,\n overrides: scene.overrides,\n get() {\n const n = scene.get(nid);\n if (n) last = documentPose(scene, n);\n if (n && !target?.active) mine = n.pose;\n return last;\n },\n set(pose) {\n const n = scene.get(nid);\n if (!n) return;\n // The write runs the layout's reflow at once, and a reflow publishes glides to the overrides.\n writing = true;\n try {\n if (!samePose(n.pose, pose)) scene.untracked(() => (cascade ? cascade.write(pose) : scene.setPose(nid, pose)));\n } finally {\n writing = false;\n }\n mine = scene.get(nid)?.pose;\n },\n }, () => {\n if (!scene.get(nid)) cache.delete(id);\n }, () => {\n if (writing) return false;\n const n = scene.get(nid);\n return !!n && mine !== undefined && !samePose(n.pose, mine);\n }, cascade && riders(scene.overrides, cascade));\n if (!target) return undefined;\n made.set(id, target as Target<unknown>);\n return target;\n}\n"],"mappings":"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"}