@weasel-js/core 1.9.3 → 1.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (542) hide show
  1. package/CHANGELOG.md +356 -0
  2. package/README.md +2 -2
  3. package/dist/WeaselProvider.js +5 -5
  4. package/dist/affordances/composeAffordanceLayer.js +10 -20
  5. package/dist/affordances/composeAffordanceLayer.js.map +1 -1
  6. package/dist/affordances/cornerResize.js +25 -17
  7. package/dist/affordances/cornerResize.js.map +1 -1
  8. package/dist/affordances/hitAffordanceRegions.js +27 -20
  9. package/dist/affordances/hitAffordanceRegions.js.map +1 -1
  10. package/dist/affordances/rotationHandle.js +18 -18
  11. package/dist/affordances/rotationHandle.js.map +1 -1
  12. package/dist/animation/behaviors/momentum.js +93 -55
  13. package/dist/animation/behaviors/momentum.js.map +1 -1
  14. package/dist/animation/loop.js +10 -12
  15. package/dist/animation/loop.js.map +1 -1
  16. package/dist/animation/reflow.js +8 -8
  17. package/dist/animation/reflow.js.map +1 -1
  18. package/dist/animation/stagger.js +64 -56
  19. package/dist/animation/stagger.js.map +1 -1
  20. package/dist/animation/timeline/booking.js +62 -54
  21. package/dist/animation/timeline/booking.js.map +1 -1
  22. package/dist/animation/timeline/createTimeline.js +85 -76
  23. package/dist/animation/timeline/createTimeline.js.map +1 -1
  24. package/dist/animation/timeline/sampleTrack.js +13 -5
  25. package/dist/animation/timeline/sampleTrack.js.map +1 -1
  26. package/dist/animation/useAnimator.js +45 -35
  27. package/dist/animation/useAnimator.js.map +1 -1
  28. package/dist/animation/wrappers/animateLifecycle.js +31 -24
  29. package/dist/animation/wrappers/animateLifecycle.js.map +1 -1
  30. package/dist/animation/wrappers/animateOnSetPose.js +28 -26
  31. package/dist/animation/wrappers/animateOnSetPose.js.map +1 -1
  32. package/dist/animation/wrappers/instantPose.js +13 -0
  33. package/dist/animation/wrappers/instantPose.js.map +1 -0
  34. package/dist/{poseDescriptor-D-OH-tu5.d.ts → autoPoseDescriptor-Ct7yqivU.d.ts} +28 -3
  35. package/dist/barrel/chrome.js +16 -0
  36. package/dist/barrel/input.js +19 -0
  37. package/dist/barrel/interactions.js +42 -0
  38. package/dist/barrel/paint.js +18 -0
  39. package/dist/barrel/paths.js +19 -0
  40. package/dist/barrel/renderer.js +20 -0
  41. package/dist/barrel/routing.js +51 -0
  42. package/dist/barrel/text.js +18 -0
  43. package/dist/barrel/viewport.js +52 -0
  44. package/dist/builtins-DPcdghtB.d.ts +1223 -0
  45. package/dist/canvas/Canvas.js +363 -448
  46. package/dist/canvas/Canvas.js.map +1 -1
  47. package/dist/canvas/CanvasView.js +164 -165
  48. package/dist/canvas/CanvasView.js.map +1 -1
  49. package/dist/canvas/CursorCoordsHud.js +37 -32
  50. package/dist/canvas/CursorCoordsHud.js.map +1 -1
  51. package/dist/canvas/DrawCanvas.js +49 -43
  52. package/dist/canvas/DrawCanvas.js.map +1 -1
  53. package/dist/canvas/MinimapCanvas.js +26 -25
  54. package/dist/canvas/MinimapCanvas.js.map +1 -1
  55. package/dist/canvas/NodeShape.js +59 -377
  56. package/dist/canvas/NodeShape.js.map +1 -1
  57. package/dist/canvas/PickHud.js +50 -47
  58. package/dist/canvas/PickHud.js.map +1 -1
  59. package/dist/canvas/SceneCanvas/GestureDispatcherMounter.js +167 -0
  60. package/dist/canvas/SceneCanvas/GestureDispatcherMounter.js.map +1 -0
  61. package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js +13 -13
  62. package/dist/canvas/SceneCanvas/StandardActionsRegistrar.js +67 -0
  63. package/dist/canvas/SceneCanvas/StandardActionsRegistrar.js.map +1 -0
  64. package/dist/canvas/SceneCanvas/ToolKeybindingsMounter.js +16 -0
  65. package/dist/canvas/SceneCanvas/ToolKeybindingsMounter.js.map +1 -0
  66. package/dist/canvas/SceneCanvas/coordTrace.js +10 -0
  67. package/dist/canvas/SceneCanvas/coordTrace.js.map +1 -0
  68. package/dist/canvas/SceneCanvas/defaultNodeProperties.js +112 -70
  69. package/dist/canvas/SceneCanvas/defaultNodeProperties.js.map +1 -1
  70. package/dist/canvas/SceneCanvas/defaultNodeRouting.js +9 -8
  71. package/dist/canvas/SceneCanvas/defaultNodeRouting.js.map +1 -1
  72. package/dist/canvas/SceneCanvas/features.js +1 -0
  73. package/dist/canvas/SceneCanvas/features.js.map +1 -1
  74. package/dist/canvas/SceneCanvas/isDev.js +12 -0
  75. package/dist/canvas/SceneCanvas/isDev.js.map +1 -0
  76. package/dist/canvas/SceneCanvas/poseGeometry.js +29 -28
  77. package/dist/canvas/SceneCanvas/poseGeometry.js.map +1 -1
  78. package/dist/canvas/SceneCanvas/poseRegistrars.js +13 -0
  79. package/dist/canvas/SceneCanvas/poseRegistrars.js.map +1 -0
  80. package/dist/canvas/SceneCanvas/props.js +38 -0
  81. package/dist/canvas/SceneCanvas/props.js.map +1 -0
  82. package/dist/canvas/SceneCanvas/useBuiltinShapeTools.js +21 -62
  83. package/dist/canvas/SceneCanvas/useBuiltinShapeTools.js.map +1 -1
  84. package/dist/canvas/SceneCanvas/useDispatcherOverlayLayer.js.map +1 -1
  85. package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js +46 -48
  86. package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js.map +1 -1
  87. package/dist/canvas/SceneCanvas/useSceneSelectTool.js +33 -33
  88. package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
  89. package/dist/canvas/SceneCanvas.js +534 -788
  90. package/dist/canvas/SceneCanvas.js.map +1 -1
  91. package/dist/canvas/SceneViewCanvas.js +45 -43
  92. package/dist/canvas/SceneViewCanvas.js.map +1 -1
  93. package/dist/canvas/buildSceneLayer.js +91 -0
  94. package/dist/canvas/buildSceneLayer.js.map +1 -0
  95. package/dist/canvas/buildSceneTree.js +16 -19
  96. package/dist/canvas/buildSceneTree.js.map +1 -1
  97. package/dist/canvas/canvasRenderer.js +68 -52
  98. package/dist/canvas/canvasRenderer.js.map +1 -1
  99. package/dist/canvas/deps/changes.js +13 -0
  100. package/dist/canvas/deps/changes.js.map +1 -0
  101. package/dist/canvas/deps/editAnchors.js +28 -28
  102. package/dist/canvas/deps/insert.js +119 -91
  103. package/dist/canvas/deps/insert.js.map +1 -1
  104. package/dist/canvas/deps/slice.js +46 -45
  105. package/dist/canvas/deps/slice.js.map +1 -1
  106. package/dist/canvas/deps/textEdit.js +10 -9
  107. package/dist/canvas/deps/textEdit.js.map +1 -1
  108. package/dist/canvas/derivedPath.js +46 -23
  109. package/dist/canvas/derivedPath.js.map +1 -1
  110. package/dist/canvas/getNodeAtPoint.js +7 -7
  111. package/dist/canvas/getNodeAtPoint.js.map +1 -1
  112. package/dist/canvas/hud.module.js +1 -1
  113. package/dist/canvas/hud.module.js.map +1 -1
  114. package/dist/canvas/kitRequirements.js +2 -2
  115. package/dist/canvas/kitRequirements.js.map +1 -1
  116. package/dist/canvas/layerOrder.js +13 -6
  117. package/dist/canvas/layerOrder.js.map +1 -1
  118. package/dist/canvas/minimapMath.js +12 -10
  119. package/dist/canvas/minimapMath.js.map +1 -1
  120. package/dist/canvas/nodeShapes/derivedPainter.js +34 -0
  121. package/dist/canvas/nodeShapes/derivedPainter.js.map +1 -0
  122. package/dist/canvas/nodeShapes/imagePainter.js +87 -0
  123. package/dist/canvas/nodeShapes/imagePainter.js.map +1 -0
  124. package/dist/canvas/nodeShapes/memo.js +9 -0
  125. package/dist/canvas/nodeShapes/memo.js.map +1 -0
  126. package/dist/canvas/nodeShapes/nodePaint.js +57 -0
  127. package/dist/canvas/nodeShapes/nodePaint.js.map +1 -0
  128. package/dist/canvas/nodeShapes/pathPainter.js +38 -0
  129. package/dist/canvas/nodeShapes/pathPainter.js.map +1 -0
  130. package/dist/canvas/nodeShapes/poseBox.js +20 -0
  131. package/dist/canvas/nodeShapes/poseBox.js.map +1 -0
  132. package/dist/canvas/nodeShapes/rectFallbackPainter.js +38 -0
  133. package/dist/canvas/nodeShapes/rectFallbackPainter.js.map +1 -0
  134. package/dist/canvas/nodeShapes/shapePainter.js +72 -0
  135. package/dist/canvas/nodeShapes/shapePainter.js.map +1 -0
  136. package/dist/canvas/nodeShapes/textPainter.js +55 -0
  137. package/dist/canvas/nodeShapes/textPainter.js.map +1 -0
  138. package/dist/canvas/overlayRoleStrokes.js.map +1 -1
  139. package/dist/canvas/paintCull.js +10 -11
  140. package/dist/canvas/paintCull.js.map +1 -1
  141. package/dist/canvas/pickWalk.js +52 -63
  142. package/dist/canvas/pickWalk.js.map +1 -1
  143. package/dist/canvas/poseRotation.js +11 -23
  144. package/dist/canvas/poseRotation.js.map +1 -1
  145. package/dist/canvas/renderDebugSnapshot.js +5 -5
  146. package/dist/canvas/renderSceneToPixels.js +3 -1
  147. package/dist/canvas/renderSceneToPixels.js.map +1 -1
  148. package/dist/canvas/sceneAdapter.js +66 -148
  149. package/dist/canvas/sceneAdapter.js.map +1 -1
  150. package/dist/canvas/sceneClipboard.js +112 -0
  151. package/dist/canvas/sceneClipboard.js.map +1 -0
  152. package/dist/canvas/sceneSlotWiring.js +4 -4
  153. package/dist/canvas/sceneSlotWiring.js.map +1 -1
  154. package/dist/canvas/sceneViewRender.js +39 -40
  155. package/dist/canvas/sceneViewRender.js.map +1 -1
  156. package/dist/canvas/useCanvasRenderer.js +14 -6
  157. package/dist/canvas/useCanvasRenderer.js.map +1 -1
  158. package/dist/canvas/useCanvasView.js +58 -0
  159. package/dist/canvas/useCanvasView.js.map +1 -0
  160. package/dist/canvas/useFrameLoop.js +35 -26
  161. package/dist/canvas/useFrameLoop.js.map +1 -1
  162. package/dist/canvas/useSceneLayerList.js +21 -0
  163. package/dist/canvas/useSceneLayerList.js.map +1 -0
  164. package/dist/clipboard.d.ts +10 -6
  165. package/dist/clone.d.ts +4 -3
  166. package/dist/core/adapters/arrayAdapter.js +48 -39
  167. package/dist/core/adapters/arrayAdapter.js.map +1 -1
  168. package/dist/core/device/index.js +5 -4
  169. package/dist/core/device/useDeviceProfile.js +38 -44
  170. package/dist/core/device/useDeviceProfile.js.map +1 -1
  171. package/dist/core/device/watchDevicePixelRatio.js +17 -0
  172. package/dist/core/device/watchDevicePixelRatio.js.map +1 -0
  173. package/dist/core/fillInPoseFrame.js.map +1 -1
  174. package/dist/core/geometry/poseDescriptor.js +21 -19
  175. package/dist/core/geometry/poseDescriptor.js.map +1 -1
  176. package/dist/core/geometry/poseFrame.js +44 -0
  177. package/dist/core/geometry/poseFrame.js.map +1 -0
  178. package/dist/core/geometry/poseRotation.js.map +1 -1
  179. package/dist/core/geometry/regionHit.js +9 -10
  180. package/dist/core/geometry/regionHit.js.map +1 -1
  181. package/dist/core/geometry/unionBounds.js +31 -29
  182. package/dist/core/geometry/unionBounds.js.map +1 -1
  183. package/dist/core/isPlainObject.js +2 -11
  184. package/dist/core/ops/arrange.js +1 -0
  185. package/dist/core/ops/arrange.js.map +1 -1
  186. package/dist/core/ops/create.js +10 -5
  187. package/dist/core/ops/create.js.map +1 -1
  188. package/dist/core/ops/delete.js +12 -24
  189. package/dist/core/ops/delete.js.map +1 -1
  190. package/dist/core/ops/reorder/index.js +43 -40
  191. package/dist/core/ops/reorder/index.js.map +1 -1
  192. package/dist/core/ops/reorder/setChildOrder.js +34 -0
  193. package/dist/core/ops/reorder/setChildOrder.js.map +1 -0
  194. package/dist/core/paintKinds.js +5 -5
  195. package/dist/core/paintKinds.js.map +1 -1
  196. package/dist/core/scene/NodeProperties.js.map +1 -1
  197. package/dist/core/scene/appliedLog.js +37 -0
  198. package/dist/core/scene/appliedLog.js.map +1 -0
  199. package/dist/core/scene/arrivalWindow.js +101 -0
  200. package/dist/core/scene/arrivalWindow.js.map +1 -0
  201. package/dist/core/scene/derivedPath.js.map +1 -1
  202. package/dist/core/scene/historyFacade.js +84 -0
  203. package/dist/core/scene/historyFacade.js.map +1 -0
  204. package/dist/core/scene/kitOps.js +173 -0
  205. package/dist/core/scene/kitOps.js.map +1 -0
  206. package/dist/core/scene/layerStack.js +38 -0
  207. package/dist/core/scene/layerStack.js.map +1 -0
  208. package/dist/core/scene/lockGuard.js +37 -0
  209. package/dist/core/scene/lockGuard.js.map +1 -0
  210. package/dist/core/scene/nodeFnFields.js +5 -6
  211. package/dist/core/scene/nodeFnFields.js.map +1 -1
  212. package/dist/core/scene/nodeMemo.js +45 -19
  213. package/dist/core/scene/nodeMemo.js.map +1 -1
  214. package/dist/core/scene/nodeStore.js +126 -0
  215. package/dist/core/scene/nodeStore.js.map +1 -0
  216. package/dist/core/scene/pickability.js +6 -0
  217. package/dist/core/scene/pickability.js.map +1 -0
  218. package/dist/core/scene/poseFeed.js +38 -38
  219. package/dist/core/scene/poseFeed.js.map +1 -1
  220. package/dist/core/scene/poseOverrides.js +34 -38
  221. package/dist/core/scene/poseOverrides.js.map +1 -1
  222. package/dist/core/scene/renderOrder.js +84 -0
  223. package/dist/core/scene/renderOrder.js.map +1 -0
  224. package/dist/core/scene/scene.js +289 -866
  225. package/dist/core/scene/scene.js.map +1 -1
  226. package/dist/core/scene/serialization.js +68 -0
  227. package/dist/core/scene/serialization.js.map +1 -0
  228. package/dist/core/scene/treeOrder.js +13 -0
  229. package/dist/core/scene/treeOrder.js.map +1 -0
  230. package/dist/core/scene/types.js.map +1 -1
  231. package/dist/core/viewport/meanScale.js +1 -1
  232. package/dist/core/viewport/meanScale.js.map +1 -1
  233. package/dist/core/viewport/view.js +12 -12
  234. package/dist/core/viewport/view.js.map +1 -1
  235. package/dist/core/viewport/zoomBounds.js +10 -8
  236. package/dist/core/viewport/zoomBounds.js.map +1 -1
  237. package/dist/{cornerHandles-BCrDp_mH.d.ts → cornerHandles-CtCb8a3G.d.ts} +1 -1
  238. package/dist/debug/createDebugOverlayLayer.js +14 -14
  239. package/dist/debug/createDebugOverlayLayer.js.map +1 -1
  240. package/dist/debug/flag.js +2 -41
  241. package/dist/features/chrome-caps/useHoverTracking.js +22 -17
  242. package/dist/features/chrome-caps/useHoverTracking.js.map +1 -1
  243. package/dist/features/focus/gateLayer.js +3 -4
  244. package/dist/features/focus/gateLayer.js.map +1 -1
  245. package/dist/features/images/imageCache.js +19 -9
  246. package/dist/features/images/imageCache.js.map +1 -1
  247. package/dist/features/ingestion/contentHandlers.js +14 -11
  248. package/dist/features/ingestion/contentHandlers.js.map +1 -1
  249. package/dist/features/ingestion/index.js +1 -1
  250. package/dist/features/ingestion/registerKitHandlers.js +7 -7
  251. package/dist/features/ingestion/weaselJsonHandler.js +17 -14
  252. package/dist/features/ingestion/weaselJsonHandler.js.map +1 -1
  253. package/dist/features/meshPaint/lazyKind.js +201 -35
  254. package/dist/features/meshPaint/lazyKind.js.map +1 -1
  255. package/dist/features/meshPaint/meshPaint.js +82 -79
  256. package/dist/features/meshPaint/meshPaint.js.map +1 -1
  257. package/dist/features/minimap/createMinimapContribution.js +42 -42
  258. package/dist/features/minimap/layers.js +44 -62
  259. package/dist/features/minimap/layers.js.map +1 -1
  260. package/dist/features/parallax/createParallaxPlane.js +10 -14
  261. package/dist/features/parallax/createParallaxPlane.js.map +1 -1
  262. package/dist/features/paths/anchorEdits.js +19 -8
  263. package/dist/features/paths/anchorEdits.js.map +1 -1
  264. package/dist/features/paths/anchors.js +31 -26
  265. package/dist/features/paths/anchors.js.map +1 -1
  266. package/dist/features/paths/booleans.js +2 -2
  267. package/dist/features/paths/index.js +13 -11
  268. package/dist/features/paths/markerCommands.js +37 -36
  269. package/dist/features/paths/markerCommands.js.map +1 -1
  270. package/dist/features/paths/pathInWorld.js +19 -19
  271. package/dist/features/paths/pathInWorld.js.map +1 -1
  272. package/dist/features/paths/pathLayer.js.map +1 -1
  273. package/dist/features/paths/strokeToPath.js +40 -0
  274. package/dist/features/paths/strokeToPath.js.map +1 -0
  275. package/dist/features/paths/tessellate/stroke.js.map +1 -1
  276. package/dist/features/patterns/commandPaint.js +18 -0
  277. package/dist/features/patterns/commandPaint.js.map +1 -0
  278. package/dist/features/patterns/index.js +5 -17
  279. package/dist/features/patterns/index.js.map +1 -1
  280. package/dist/features/patterns/rasterizeTile.js +17 -0
  281. package/dist/features/patterns/rasterizeTile.js.map +1 -0
  282. package/dist/features/patterns/resolveSpec.js +52 -44
  283. package/dist/features/patterns/resolveSpec.js.map +1 -1
  284. package/dist/features/poseRun/usePoseRun.js +8 -3
  285. package/dist/features/poseRun/usePoseRun.js.map +1 -1
  286. package/dist/features/selection/SelectionContext.js +5 -0
  287. package/dist/features/selection/SelectionContext.js.map +1 -1
  288. package/dist/features/selection/overlay.js +18 -18
  289. package/dist/features/selection/overlay.js.map +1 -1
  290. package/dist/features/text/domOffsets.js +82 -0
  291. package/dist/features/text/domOffsets.js.map +1 -0
  292. package/dist/features/text/domRuns.js +103 -131
  293. package/dist/features/text/domRuns.js.map +1 -1
  294. package/dist/features/text/hitTest.js +5 -2
  295. package/dist/features/text/hitTest.js.map +1 -1
  296. package/dist/features/text/isTextData.js +8 -0
  297. package/dist/features/text/isTextData.js.map +1 -0
  298. package/dist/features/text/overlayDirection.js +15 -0
  299. package/dist/features/text/overlayDirection.js.map +1 -0
  300. package/dist/features/text/runs/rangeStyle.js +32 -34
  301. package/dist/features/text/runs/rangeStyle.js.map +1 -1
  302. package/dist/features/text/textCommand.js +34 -33
  303. package/dist/features/text/textCommand.js.map +1 -1
  304. package/dist/features/text/textToPath.js +40 -40
  305. package/dist/features/text/textToPath.js.map +1 -1
  306. package/dist/features/text/useSceneTextEdit.js +76 -82
  307. package/dist/features/text/useSceneTextEdit.js.map +1 -1
  308. package/dist/features/text/useTextEdit.js +184 -160
  309. package/dist/features/text/useTextEdit.js.map +1 -1
  310. package/dist/features/tiling/createTiledLayer.js +24 -22
  311. package/dist/features/tiling/createTiledLayer.js.map +1 -1
  312. package/dist/index.d.ts +11033 -7749
  313. package/dist/index.js +316 -309
  314. package/dist/input/keyState.js +42 -43
  315. package/dist/input/keyState.js.map +1 -1
  316. package/dist/input/useKeyModifiers.js +11 -0
  317. package/dist/input/useKeyModifiers.js.map +1 -0
  318. package/dist/interactions/actions/align/align.js +8 -8
  319. package/dist/interactions/actions/clipboard/clipboardOps.js +34 -32
  320. package/dist/interactions/actions/clipboard/clipboardOps.js.map +1 -1
  321. package/dist/interactions/actions/clipboard/materializePaste.js +10 -7
  322. package/dist/interactions/actions/clipboard/materializePaste.js.map +1 -1
  323. package/dist/interactions/actions/clipboard/wireFormat.js.map +1 -1
  324. package/dist/interactions/actions/clone/behaviors/cloneByAltDrag.js.map +1 -1
  325. package/dist/interactions/actions/defaultCommitAdapter.js +20 -18
  326. package/dist/interactions/actions/defaultCommitAdapter.js.map +1 -1
  327. package/dist/interactions/actions/defaults/align.js +9 -10
  328. package/dist/interactions/actions/defaults/align.js.map +1 -1
  329. package/dist/interactions/actions/defaults/anchorEditing.js +59 -81
  330. package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
  331. package/dist/interactions/actions/defaults/areaSelect.js +35 -38
  332. package/dist/interactions/actions/defaults/areaSelect.js.map +1 -1
  333. package/dist/interactions/actions/defaults/clipboard.js +26 -10
  334. package/dist/interactions/actions/defaults/clipboard.js.map +1 -1
  335. package/dist/interactions/actions/defaults/clone.js +44 -60
  336. package/dist/interactions/actions/defaults/clone.js.map +1 -1
  337. package/dist/interactions/actions/defaults/copySubtree.js +40 -0
  338. package/dist/interactions/actions/defaults/copySubtree.js.map +1 -0
  339. package/dist/interactions/actions/defaults/distribute.js +14 -13
  340. package/dist/interactions/actions/defaults/distribute.js.map +1 -1
  341. package/dist/interactions/actions/defaults/duplicate.js +17 -57
  342. package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
  343. package/dist/interactions/actions/defaults/editAnchors.js +18 -18
  344. package/dist/interactions/actions/defaults/enterTextEdit.js.map +1 -1
  345. package/dist/interactions/actions/defaults/flip.js +52 -46
  346. package/dist/interactions/actions/defaults/flip.js.map +1 -1
  347. package/dist/interactions/actions/defaults/group.js +43 -48
  348. package/dist/interactions/actions/defaults/group.js.map +1 -1
  349. package/dist/interactions/actions/defaults/ingest.js +35 -33
  350. package/dist/interactions/actions/defaults/ingest.js.map +1 -1
  351. package/dist/interactions/actions/defaults/insert.js +50 -51
  352. package/dist/interactions/actions/defaults/insert.js.map +1 -1
  353. package/dist/interactions/actions/defaults/insertPathAnchor.js +7 -7
  354. package/dist/interactions/actions/defaults/lassoSelect.js +67 -70
  355. package/dist/interactions/actions/defaults/lassoSelect.js.map +1 -1
  356. package/dist/interactions/actions/defaults/move.js +267 -249
  357. package/dist/interactions/actions/defaults/move.js.map +1 -1
  358. package/dist/interactions/actions/defaults/nudge.js +40 -68
  359. package/dist/interactions/actions/defaults/nudge.js.map +1 -1
  360. package/dist/interactions/actions/defaults/nudgeSteps.js +37 -0
  361. package/dist/interactions/actions/defaults/nudgeSteps.js.map +1 -0
  362. package/dist/interactions/actions/defaults/reorder.js +2 -0
  363. package/dist/interactions/actions/defaults/reorder.js.map +1 -1
  364. package/dist/interactions/actions/defaults/resize.js +104 -98
  365. package/dist/interactions/actions/defaults/resize.js.map +1 -1
  366. package/dist/interactions/actions/defaults/rotate.js +111 -110
  367. package/dist/interactions/actions/defaults/rotate.js.map +1 -1
  368. package/dist/interactions/actions/defaults/setFill.js +7 -7
  369. package/dist/interactions/actions/defaults/setFillOpacity.js +4 -4
  370. package/dist/interactions/actions/defaults/setStroke.js +12 -12
  371. package/dist/interactions/actions/defaults/setStrokeOpacity.js +6 -6
  372. package/dist/interactions/actions/defaults/sliceOps.js +19 -19
  373. package/dist/interactions/actions/defaults/viewportDragPan.js +37 -26
  374. package/dist/interactions/actions/defaults/viewportDragPan.js.map +1 -1
  375. package/dist/interactions/actions/edit-anchors/geometry.js +28 -23
  376. package/dist/interactions/actions/edit-anchors/geometry.js.map +1 -1
  377. package/dist/interactions/actions/flip/helpers.js +30 -17
  378. package/dist/interactions/actions/flip/helpers.js.map +1 -1
  379. package/dist/interactions/actions/hitSelection.js +8 -0
  380. package/dist/interactions/actions/hitSelection.js.map +1 -0
  381. package/dist/interactions/actions/lasso-select/behaviors/selectFromLasso.js +14 -18
  382. package/dist/interactions/actions/lasso-select/behaviors/selectFromLasso.js.map +1 -1
  383. package/dist/interactions/actions/move/behaviors/snapBackOrDelete.js +5 -5
  384. package/dist/interactions/actions/move/behaviors/snapBackOrDelete.js.map +1 -1
  385. package/dist/interactions/actions/move/behaviors/snapToContainer.js +32 -21
  386. package/dist/interactions/actions/move/behaviors/snapToContainer.js.map +1 -1
  387. package/dist/interactions/actions/move/gestureAdapter.js +32 -15
  388. package/dist/interactions/actions/move/gestureAdapter.js.map +1 -1
  389. package/dist/interactions/actions/move/landedDescendants.js +33 -0
  390. package/dist/interactions/actions/move/landedDescendants.js.map +1 -0
  391. package/dist/interactions/actions/outlines/createOutlines.js +18 -18
  392. package/dist/interactions/actions/poseDescriptorDep.js +16 -4
  393. package/dist/interactions/actions/poseDescriptorDep.js.map +1 -1
  394. package/dist/interactions/actions/poseFrame.js +11 -6
  395. package/dist/interactions/actions/poseFrame.js.map +1 -1
  396. package/dist/interactions/actions/previewOverrides.js +5 -5
  397. package/dist/interactions/actions/previewOverrides.js.map +1 -1
  398. package/dist/interactions/actions/selectionSubtree.js +69 -0
  399. package/dist/interactions/actions/selectionSubtree.js.map +1 -0
  400. package/dist/interactions/actions/useStandardActions.js +66 -62
  401. package/dist/interactions/actions/useStandardActions.js.map +1 -1
  402. package/dist/interactions/gestures/dragGesture/dragGesture.js +4 -7
  403. package/dist/interactions/gestures/dragGesture/dragGesture.js.map +1 -1
  404. package/dist/interactions/gestures/dragGesture/dragPointOf.js +23 -0
  405. package/dist/interactions/gestures/dragGesture/dragPointOf.js.map +1 -0
  406. package/dist/interactions/gestures/handleDrag/handleDrag.js +21 -26
  407. package/dist/interactions/gestures/handleDrag/handleDrag.js.map +1 -1
  408. package/dist/interactions/gestures/pointerDrag/pointerDrag.js +8 -6
  409. package/dist/interactions/gestures/pointerDrag/pointerDrag.js.map +1 -1
  410. package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js +8 -4
  411. package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js.map +1 -1
  412. package/dist/{math-Drrw4ewZ.d.ts → math-J-ACHyEg.d.ts} +1 -2
  413. package/dist/math.d.ts +2 -3
  414. package/dist/move.d.ts +12 -11
  415. package/dist/{originProjection-DEklpDc_.d.ts → originProjection-gHnOup4e.d.ts} +1 -1
  416. package/dist/{registerProgram-BMZRDgrh.d.ts → registerProgram-T9S_PfAX.d.ts} +18 -7
  417. package/dist/renderer/DrawCommand.js.map +1 -1
  418. package/dist/renderer/WeaselRenderer.js +75 -73
  419. package/dist/renderer/WeaselRenderer.js.map +1 -1
  420. package/dist/renderer/cache/GLImageCache.js +15 -5
  421. package/dist/renderer/cache/GLImageCache.js.map +1 -1
  422. package/dist/renderer/cache/GLMeshCache.js +23 -23
  423. package/dist/renderer/cache/GLMeshCache.js.map +1 -1
  424. package/dist/renderer/cache/GLTextureCache.js +18 -1
  425. package/dist/renderer/cache/GLTextureCache.js.map +1 -1
  426. package/dist/renderer/cache/cache.js.map +1 -1
  427. package/dist/renderer/cache/markerCommandCache.js.map +1 -1
  428. package/dist/renderer/cache/strokeMeshCache.js.map +1 -1
  429. package/dist/renderer/cullDrawCommands.js +11 -11
  430. package/dist/renderer/draw/batch.js +133 -0
  431. package/dist/renderer/draw/batch.js.map +1 -0
  432. package/dist/renderer/draw/clip.js +89 -0
  433. package/dist/renderer/draw/clip.js.map +1 -0
  434. package/dist/renderer/draw/customShader.js +230 -0
  435. package/dist/renderer/draw/customShader.js.map +1 -0
  436. package/dist/renderer/draw/drawTriangles.js +8 -0
  437. package/dist/renderer/draw/drawTriangles.js.map +1 -0
  438. package/dist/renderer/draw/effects.js +28 -0
  439. package/dist/renderer/draw/effects.js.map +1 -0
  440. package/dist/renderer/draw/flushBatch.js +35 -0
  441. package/dist/renderer/draw/flushBatch.js.map +1 -0
  442. package/dist/renderer/draw/gradient.js +90 -0
  443. package/dist/renderer/draw/gradient.js.map +1 -0
  444. package/dist/renderer/draw/image.js +38 -0
  445. package/dist/renderer/draw/image.js.map +1 -0
  446. package/dist/renderer/draw/mesh.js +16 -0
  447. package/dist/renderer/draw/mesh.js.map +1 -0
  448. package/dist/renderer/draw/paintBind.js +70 -0
  449. package/dist/renderer/draw/paintBind.js.map +1 -0
  450. package/dist/renderer/draw/pathPaint.js +142 -0
  451. package/dist/renderer/draw/pathPaint.js.map +1 -0
  452. package/dist/renderer/draw/scratchPass.js +83 -0
  453. package/dist/renderer/draw/scratchPass.js.map +1 -0
  454. package/dist/renderer/draw/stencil.js +28 -0
  455. package/dist/renderer/draw/stencil.js.map +1 -0
  456. package/dist/renderer/draw/text.js +167 -0
  457. package/dist/renderer/draw/text.js.map +1 -0
  458. package/dist/renderer/draw/uniforms.js +77 -0
  459. package/dist/renderer/draw/uniforms.js.map +1 -0
  460. package/dist/renderer/draw.js +60 -1116
  461. package/dist/renderer/draw.js.map +1 -1
  462. package/dist/renderer/effects/EffectTargets.js +3 -0
  463. package/dist/renderer/effects/EffectTargets.js.map +1 -1
  464. package/dist/renderer/effects/composite.js +7 -5
  465. package/dist/renderer/effects/composite.js.map +1 -1
  466. package/dist/renderer/index.js +18 -17
  467. package/dist/renderer/math/color.js +63 -83
  468. package/dist/renderer/math/color.js.map +1 -1
  469. package/dist/renderer/math/mat3.js +15 -11
  470. package/dist/renderer/math/mat3.js.map +1 -1
  471. package/dist/renderer/math/viewToMat3.js +1 -1
  472. package/dist/renderer/math/viewToMat3.js.map +1 -1
  473. package/dist/renderer/shaders/ShaderProgram.js +13 -13
  474. package/dist/renderer/shaders/ShaderProgram.js.map +1 -1
  475. package/dist/renderer/shaders/glyphPaint.js +3 -3
  476. package/dist/renderer/shaders/glyphPaint.js.map +1 -1
  477. package/dist/renderer/shaders/gradFill.js +2 -1
  478. package/dist/renderer/shaders/gradFill.js.map +1 -1
  479. package/dist/renderer/shaders/pathFill.js +2 -1
  480. package/dist/renderer/shaders/pathFill.js.map +1 -1
  481. package/dist/renderer/shaders/patternFill.js +2 -1
  482. package/dist/renderer/shaders/patternFill.js.map +1 -1
  483. package/dist/renderer/shaders/registerProgram.js.map +1 -1
  484. package/dist/renderer/strokeLengths.js.map +1 -1
  485. package/dist/renderer/textures/registerTexture.js +25 -5
  486. package/dist/renderer/textures/registerTexture.js.map +1 -1
  487. package/dist/renderer.d.ts +16 -15
  488. package/dist/renderer.js +17 -16
  489. package/dist/resize.d.ts +5 -6
  490. package/dist/resize.js +5 -5
  491. package/dist/style.css +1 -1
  492. package/dist/test-hook/install.js +12 -6
  493. package/dist/test-hook/install.js.map +1 -1
  494. package/dist/{test-seams-CuIxe0xY.d.ts → test-seams-DM2B87-g.d.ts} +4 -4
  495. package/dist/test-seams.d.ts +2 -2
  496. package/dist/tools/builtin/ellipse/useEllipseTool.js +1 -0
  497. package/dist/tools/builtin/ellipse/useEllipseTool.js.map +1 -1
  498. package/dist/tools/builtin/hand/useHandTool.js +1 -0
  499. package/dist/tools/builtin/hand/useHandTool.js.map +1 -1
  500. package/dist/tools/builtin/lasso/useLassoTool.js +11 -11
  501. package/dist/tools/builtin/lasso/useLassoTool.js.map +1 -1
  502. package/dist/tools/builtin/line/useLineTool.js +1 -0
  503. package/dist/tools/builtin/line/useLineTool.js.map +1 -1
  504. package/dist/tools/builtin/pen/usePenTool.js +153 -103
  505. package/dist/tools/builtin/pen/usePenTool.js.map +1 -1
  506. package/dist/tools/builtin/pencil/usePencilTool.js +1 -0
  507. package/dist/tools/builtin/pencil/usePencilTool.js.map +1 -1
  508. package/dist/tools/builtin/polygon/usePolygonTool.js +13 -8
  509. package/dist/tools/builtin/polygon/usePolygonTool.js.map +1 -1
  510. package/dist/tools/builtin/rect/useRectTool.js +1 -0
  511. package/dist/tools/builtin/rect/useRectTool.js.map +1 -1
  512. package/dist/tools/builtin/select/selectionContributions.js +24 -23
  513. package/dist/tools/builtin/select/selectionContributions.js.map +1 -1
  514. package/dist/tools/builtin/select/useSelectTool.js +51 -48
  515. package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
  516. package/dist/tools/builtin/shared/pointerFollower.js +17 -0
  517. package/dist/tools/builtin/shared/pointerFollower.js.map +1 -0
  518. package/dist/tools/builtin/slice/useSliceTool.js +49 -54
  519. package/dist/tools/builtin/slice/useSliceTool.js.map +1 -1
  520. package/dist/tools/builtin/star/useStarTool.js +22 -16
  521. package/dist/tools/builtin/star/useStarTool.js.map +1 -1
  522. package/dist/tools/builtin/text/useTextTool.js +1 -0
  523. package/dist/tools/builtin/text/useTextTool.js.map +1 -1
  524. package/dist/tools/useKeybindings.js +14 -30
  525. package/dist/tools/useKeybindings.js.map +1 -1
  526. package/dist/{types-B_9g6hMM.d.ts → types-B4iJRHC3.d.ts} +5 -1
  527. package/dist/{types-A37hYkeR.d.ts → types-k-5Oqh-U.d.ts} +31 -7
  528. package/dist/util/color.js +3 -3
  529. package/dist/util/color.js.map +1 -1
  530. package/dist/util/paint.js.map +1 -1
  531. package/dist/version.js +1 -1
  532. package/package.json +20 -18
  533. package/dist/autoPoseDescriptor-vCdt55xm.d.ts +0 -27
  534. package/dist/builtins-C-VgUlWI.d.ts +0 -4514
  535. package/dist/core/isPlainObject.js.map +0 -1
  536. package/dist/debug/flag.js.map +0 -1
  537. package/dist/interactions/actions/defaults/freshNodeId.js +0 -10
  538. package/dist/interactions/actions/defaults/freshNodeId.js.map +0 -1
  539. package/dist/renderer/shaders/imageFill.js +0 -14
  540. package/dist/renderer/shaders/imageFill.js.map +0 -1
  541. package/dist/tools/prefs.js +0 -62
  542. package/dist/tools/prefs.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,361 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.4
4
+
5
+ ### Patch Changes
6
+
7
+ - 6e2447b: `absentIsDefault` now also works on a field of an object leaf in `SelectionPanel`: a field the held object omits draws the field's default and is not Mixed beside an object holding that default, while a field of an object the node does not hold stays unset. `defaultNodeProperties` and `inferredNodeProperties` set the flag on every leaf the engine reads with a fallback: a stroke's width, cap, join, and align; a text node's text, style, and vertical alignment; and the style's family, size, weight, rules, tracking, case, small caps, leading, wrap, and align. So a text node with an empty style shows 16 and sans-serif where it showed blanks.
8
+ - 9dfd8a5: A leaf may say `absentIsDefault: true`: nothing held where its value is read means its `default`. `SelectionPanel` draws that default for a node with no value at the leaf's path, and no longer reads such a node beside one holding the default as Mixed; nothing is written until an edit, which writes only the value typed. `defaultNodeProperties` sets it on `pose.rotation`, so a node whose pose has no `rotation` shows 0. `aggregateValue` takes the value an empty path reads as, and `PrefSchemaEditor` offers the flag among a leaf's attributes.
9
+ - 4c4aa40: An action leaf fires a registered action by key. `PrefAction` gains `action`, the key of an action on the `ActionsRegistry` above the surface: its id, or `id:variant`. A leaf holding one is plain data, so a schema that carries it survives JSON. `run` is now optional and is what a leaf with no `action` calls. The leaf's `name` and `icon` are what the button shows; the registered action supplies the icon and shortcut the leaf lacks. A key the registry does not hold, or no registry, leaves the button dead. `PrefAction` also gains `control`: `'button'` reads the label, `'icon'` is the icon alone.
10
+
11
+ An action says whether it is held down. `Action` gains `active`, a predicate under the same contract as `enabled`, and `evaluateActive` reads it safely. A button drawn for the action sets `aria-pressed` from it. An action with no `active` has no pressed state.
12
+
13
+ `useLeafAction` answers for an action leaf on any surface. `BarAction` is the leaf as a toolbar cell, and `BarActionButton` is the button alone. `ActionBar` draws its entries with that button, so it now shows an active action as pressed. `PrefActionButton` draws through the same hook.
14
+ - f17d213: A click counted as landing on a path anchor within 10 world units of it, whatever the zoom: at 8× a click 80 px away selected or cut the anchor, and at 0.1× the target was 1 px wide. `selectAnchor` and `cutPathAtAnchor` now measure 10 screen pixels through the view.
15
+ - 7420a37: `animateLifecycle` over `animateOnSetPose`: an animated insert wrote its start pose through the animating wrapper, which recorded an `'animate'` transform op from the final pose back to the start pose, so every animated insert left a stray undo step. The start pose is now written at once, past any wrapper that animates `setPose`.
16
+ - 41a4b4e: An op that gives a container its layout by calling `adapter.setLayout` itself now arranges the container in the same undo step, on a scene that had no layout before; only `createSetLayoutOp` did. `Op` gains `elideNoop`: an op carrying it is left out of the recorded entry when its first apply reports a no-op, which is how the scene's arrange op stays out of edits that needed no arranging.
17
+ - 50b294e: Successive `applyBatch` edits that re-arrange the same children of a layout container now coalesce into one undo entry inside `coalesceWindowMs`, as edits that arrange nothing already did. The arrange op's coalesce key was read before the op had applied, when it has none.
18
+ - f7872a9: An `arrayAdapter` configured with `getChildren` put an undone delete in the wrong place whenever a child sat earlier in the item array than the roots: with items `[kid, p, a, b]`, deleting root `a` and undoing gave roots `[a, p, b]`. `insertNode` treated the sibling ordinal the insert op hands it as an index into the flat array. It now inserts before the sibling that ordinal names.
19
+ - 9d54d0a: Core now depends on `@msb235/blits` 0.8.1, up from 0.7.0. The pin is exact, so an app that also installs blits itself gets one copy only when it names the same version.
20
+
21
+ Nothing in core's own API changes. blits 0.8.0 is a breaking release for code that calls blits directly: `seek` rebuilds a voice's state, a history store's `cut` takes a `seq`, and the deprecated `hold`, `period`, and `hex()` are gone. Its changelog has the full list.
22
+ - 0a2eba9: `resolvePatternSpec` keeps the 256 built-in pattern tiles most recently resolved or drawn and frees the rest, bitmap and GL texture both, so scrubbing a pattern's color no longer leaves a texture behind per intermediate color. A draw command still holding an evicted tile's handle rebuilds the tile when it is next drawn. `unregisterTexture(handle)` is new on `@weasel-js/core/renderer`: it drops a texture registered with `registerTexture`, and each renderer deletes its GL copy at the top of its next frame.
23
+ - 3df6f75: A canvas whose size changed and changed back between two paints no longer shows the scene stretched on a high-density display. React rewrote the `<canvas>` `width` and `height` attributes in CSS pixels, and the renderer skipped its resize because the size matched the last one it painted, leaving an 800px buffer where 1600 were expected at a device pixel ratio of 2. `<SceneCanvas>`, `<DrawCanvas>`, `<SceneViewCanvas>`, and `<MinimapCanvas>` now check the element's real buffer size on every paint.
24
+ - 719b2eb: `<SceneCanvas>` now paints a scene layer added after mount. The per-layer canvas layers (`scene:<layerId>`) were listed once, when the `layers` prop was first resolved, so nodes on a layer from a later `scene.addLayer()` never appeared, and a removed or reordered layer kept its old slot, unless the host happened to pass a new `layers` object. The canvas now rebuilds them when the scene's layer list changes.
25
+ - 095c055: Corrected the docs for `<DrawCanvas>` and `<MinimapCanvas>`, which said `className` sizes the canvas: the renderer writes `width` and `height` as inline style, so a class positions the element and cannot resize it. `CursorCoordsHud`'s doc now says it pins to the top-right of the canvas's host, not the top-left of the viewport. `PickHud` converts the pointer to world coordinates through the same `clientToWorld` helper as `CursorCoordsHud`, with no change in what it shows.
26
+ - 4b0688a: The canvas debug HUDs (`CursorCoordsHud`, `PickHud`, `ModalityHud`) no longer draw over a modal. They sat at `z-index: 10000`, above `Dialog`'s 1000, so an open dialog had the readouts painted across it. They are now at 900.
27
+ - 103f988: A canvas now comes back by itself after its WebGL context is lost and restored. `<Canvas paintInto>` panes sharing one buffer never asked the browser to restore the context, so every pane stayed blank until a reload; `WeaselRenderer` now listens for loss on the context's own canvas whether or not it sizes it. `<SceneCanvas>`, `<DrawCanvas>`, `<SceneViewCanvas>`, and `<MinimapCanvas>` also repaint as soon as the context returns, where a static one used to stay blank until something else asked for a frame. While the context is lost a paint no longer counts as landed, so `subscribeFrame` subscribers and `getPaintedVersion()` do not report a frame that drew nothing.
28
+ - c567961: A canvas whose WebGL context the renderer cannot use now says so. The reason was swallowed, so a context opened without a stencil buffer (a bare `getContext('webgl2')` on a shared buffer before any pane painted) or a shader that failed to compile left every pane blank with an empty console, and the renderer was rebuilt and failed again on every redraw. The error is now logged once per canvas with `console.warn`, and the canvas is not retried.
29
+ - 6ba9cad: Internal: `<Canvas>`'s scene-layer builders and its view state moved out of `Canvas.tsx` into modules of their own. `buildSceneLayer`, `buildSceneLayers`, and `SceneSlotConfig` are still exported from where they were. No behavior change.
30
+ - 3f18dbe: `CanvasExtensionApi` gains `subscribeResume(fn)`, which runs `fn` when the canvas's frame loop resumes after a hidden tab or an offscreen canvas suspended it. A `subscribeBeforePaint` or `subscribeFrame` subscriber that advances by the time between frames can now rebase its clock there; before, an hour in a background tab arrived as one hour-long step. Frame times also no longer run backward: a `syncPaint` paint made inside another animation-frame callback could be followed by a frame carrying an earlier timestamp.
31
+ - 8d25bd4: An uncontrolled `<CanvasView>` no longer drops a camera write that lands before React has committed the previous one. Its camera was held in React state, so two wheel-pan steps or two frames of a drag-pan glide inside one commit each read the old camera, and the second discarded the first. The camera is now readable as soon as it is written, as on `<SceneCanvas>`.
32
+ - bb078c0: A `<CanvasView>` removed while a gesture was live over it left that gesture's handle orphaned: its `onEnd` never ran, so a Space-held hand tool stayed pushed and a drag preview kept its overrides. The view now cancels what is in flight on its dispatcher when it unmounts.
33
+ - 663248a: A click no longer places the caret inside a grapheme cluster. `LaidOutCell` gains `clusterEnd`, true on the code point that closes a cluster, and `caretIndexAt` stops only after such a cell: never between a letter and its combining mark, between the parts of a joined emoji or a flag, or before a variation selector.
34
+ - c2a2a5d: A clearable enum can no longer declare a default: `PrefEnum` with `clearable: true` requires `default: undefined`, so a missing value stays the only stored form of cleared. This is a type error for a schema that declared both; drop the default. The fields every enum leaf carries are exported as `PrefEnumBase`. `PrefSchemaEditor` drops an enum's default when it is made clearable and offers no Default field for one.
35
+ - 92c8af4: `<SceneCanvas>`'s `onClick`, `onDoubleClick`, hover chrome, and tool routing named the bottom-most node under the pointer where two overlapped, and a container rather than the child inside it, while the select tool selected the one on top. All of them now resolve the node the same way the select tool does. `isPointerInteractive` is asked about that node too, so a non-interactive node underneath an interactive one no longer makes the point dead.
36
+ - 9b4fc5c: Alt-dragging a group inserted an empty container and copied none of its children, and with a group and one of its children both selected the child was copied a second time beside the original. `cloneAction` now copies each selected node with its whole subtree, the way `duplicateAction` does, keeps a copied node's `dependsOn` and derive functions, and previews the children it will copy.
37
+ - e023a51: `cloneAction` selects the copies it made, as `duplicateAction` does. After an Alt-drag the user holds the new nodes, so a second Alt-drag clones the copy rather than the originals again.
38
+ - 184be75: A closed path whose last curve ends on its first point now has one anchor there. `pathToAnchors` and `enumerateAnchors` read that point as two anchors, one holding the out handle and one the in handle, so on any closed curve (an ellipse, a pen path closed on a curve) dragging the first anchor moved one of the pair and left a straight edge to the other, its handles never mirrored, and the overlay drew two stacked squares. Both now fold the closing curve's end into the first anchor, which carries both handles. `PathAnchor` gains `closeCoordIndex` for the point's second slot in `coords`, and `translateAnchor` moves both. `countPathAnchors` still counts one per command, since it sizes the renderer's per-anchor arrays.
39
+ - 9ad839d: A `PathDrawCommand`'s `fill` and `stroke` are now `CommandFill` and `CommandStroke`: a pattern must name a registered texture, and no paint may be in `'bounds'` units, so a tile spec or a box-relative gradient on a hand-built command is a type error where it used to draw blank or in screen space. This is a breaking change to types: code that passes a node's `FillStyle` or `Stroke` straight into a command now calls `commandFill(fill, box)` or `commandStroke(stroke, box)`, which resolve both. Options that feed commands directly (`createPathLayer`'s `getFill` and `getStroke`, affordance paints, selection overlay and guide strokes, overlay role strokes) take the narrowed types. `<Canvas backgroundFill>`, `<DrawCanvas background>`, the rect fallback painter, a marker's own fill, and `diagramShape` now resolve their paint, so a tile spec or `'bounds'` gradient there paints.
40
+ - fb505bb: A node added with `pickable: false` that was deleted and restored by undo through a move or rotate commit came back taking clicks. The restore now keeps the flag.
41
+ - 0aaeebc: Internal: the root barrel is now twelve per-area modules that `index.ts` re-exports. The exported names are unchanged.
42
+ - e589003: `@weasel-js/core` re-exports the types of `@weasel-js/history` by name instead of with `export *`. The exported names are unchanged. A star re-export of another package emits no binding a consumer's bundler can enumerate, so a future runtime export of `@weasel-js/history` would have been missing from core's bundle with the typecheck green.
43
+ - ec79c3f: `@weasel-js/core`'s `sideEffects` list now names its stylesheet. A bundler that honors the field was free to drop a bare `import '@weasel-js/core/style.css'`.
44
+ - 1c401e3: Corrected docstrings that described behavior the code no longer has: `usePenTool` discards an unfinished path on a tool switch (the docs said it commits), `useHandTool` is a single binding to `viewport.dragPan`, `createRotationAffordance` paints a visible band and shows the `rotate` cursor by default, `createCornerResizeAffordance` regions bind only scratch, and `composeAffordanceLayer` picks the nearest region within an affordance.
45
+ - 158e471: A selection too small to hold its corner handles can be moved by its body again. While the target spans under four hit radii on an axis on screen, only the outward half of each corner's hit square on that axis takes a press, so a press inside a 12 px selection reaches the body and no longer resizes. A `point` affordance region opts in with the new `cornerOf` field, and `pointRegionFrame` reports the span that takes a press as `u` and `v`.
46
+ - 001aaae: `@weasel-js/registry` exports `createEmitter()`, one subscribe/notify pair with stated semantics: a pass calls the listeners held when it began, one unsubscribed before its turn is skipped, and a listener that throws is reported to `console.error` without stopping the others or throwing into the store. The reflectable registry, modes' scoping layer and decorations, and core's pose overrides, pose feed, layout reflow, parallax plane, and key state are built on it. For those stores, a listener that subscribes during a notify now waits for the next one, and a throwing listener no longer aborts the notify.
47
+ - 2fda51b: Cutting a closed path at an anchor (`openSubpathAt`, the "Cut path" action) removed the segment arriving at that anchor, so a cut triangle lost a side. The cut now leaves every segment and two coincident end anchors. `deleteAnchorsAt` also refits the closing segment when a closed subpath loses an anchor beside its seam, and no longer removes a neighbor when the same index is passed twice.
48
+ - 557c01d: `CreateDebugOverlayLayerOpts` is exported, so a consumer can name the argument `createDebugOverlayLayer` takes. This adds a type to the public surface.
49
+ - b370885: A dep registry can be listened to: `subscribe(listener)` hears a source registered or released anywhere in its tree and an owner's `changed()`, which says a source's value changed. `<SceneCanvas>` calls `changed()` when its scene or its selection changes. `ActionBar` and an action leaf's button listen, so a button whose `enabled` or `active` state reads the selection now follows it without anything else re-rendering the chrome. A dep source you publish that changes on its own calls `useDepRegistry().changed?.()` when it does. Both methods are optional on `DepRegistry`, so a hand-built registry with only `register` and `get` still fits.
50
+ - 8a21a87: Under a composing `poseComposition`, a derived path is now drawn between its dependencies' composed poses, where it was already picked. Before, the live canvas and `buildSceneViewCommands` resolved it from the stored local poses, so an edge between two nodes inside a moved container painted away from them.
51
+ - 86fd792: A scene slot's `toPose` no longer leaves a derived-path memo entry on every derived node for each function identity it has ever had. The cached paths are held against the `toPose` function and are collected with it.
52
+ - 8f23c57: `CursorCoordsHud` and `PickHud` take an optional `toWorld(clientX, clientY)` prop that replaces their own conversion through the canvas rect and view. `<Canvas>` passes one built from its `clientToWorld` prop, so on a CSS-transformed or otherwise custom-mapped canvas the world readout and the pick list now agree with what routing picks.
53
+ - eb123ad: Content far from the origin no longer pans in whole-pixel steps at high zoom. The renderer's matrices are in doubles: `GlMat3` is now `Float32Array | Float64Array`, and `viewToMat3`, every `mat3` function, and `rotateAroundAABBCenter` return a `Float64Array`. A `Float32Array` transform is still accepted everywhere. This is a type change for code that passes a kit matrix to `gl.uniformMatrix3fv` or annotates one as `Float32Array`: `mat3.toUniform(m)` is the float32 copy, and a shader command's uniform value may be a `Float64Array`. The built-in path, gradient, and pattern vertex shaders gained a `u_origin` uniform, which the renderer uses to keep a large translation out of the float32 `u_model`.
54
+ - f06caab: Moving or nudging a node whose pose is derived from an id list (`derivePose` with a `dependsOn` list) now moves the nodes it derives from, through however many derived nodes that takes; a pose written to the derived node itself was overwritten on the next read. A diagram group's box is pickable again, so dragging it, or its title, moves the group's members.
55
+ - 2b3b637: `useDragGesture`, `useDragRect`, and `useDragRadial`: `onGestureEnd` now fires once per `onGestureStart`. `end()` or `cancel()` with no gesture live does nothing, where it used to fire `onGestureEnd(false)`, and a `start()` that replaces a live gesture no longer fires `onGestureStart` a second time. A caller that relied on `onGestureEnd(false)` from an idle `end()` or `cancel()` no longer gets it.
56
+ - 55fd8a6: After a `useDragHandle` drag that ended away from its source (dropped on a zone elsewhere, dropped on nothing, or canceled), the next click on the source row did nothing: the listener that swallows the click a drag would otherwise deliver was still armed. It is now removed when the drag is canceled, and right after a release that brought no click.
57
+ - fc8068c: Zooming with the wheel while a `viewport.dragPan` drag was held was undone by the next pointer move, which rebuilt the view from the scale captured at the press. The pan now carries on from wherever another input left the camera.
58
+ - efcb538: `dragPointOf(event, element, view?)` turns a pointer event into the `DragGesturePoint` the drag gesture hooks take, for code feeding them from raw pointer handlers. `useKeyModifiers()` re-renders with the modifier keys held. `KeyState.modifiers()` now returns the same object until a modifier changes, so it works as a `useSyncExternalStore` snapshot.
59
+ - 1268f79: The renderer no longer compiles the `imageFill` program, which nothing drew with: images go through the batch program.
60
+ - 452fffc: Duplicate and Alt-drag clone point a copied node's `dependsOn` id list at the copies when every node in the list was copied with it, so a duplicated edge follows the duplicated endpoints instead of the originals. A copy with a dependency left out keeps its original list, as before.
61
+ - 01f9a06: Cmd/Ctrl+V now pastes on a `<SceneCanvas>` with the `edit` preset and without `ingest`. This changes behavior: such a canvas could copy and cut to the OS clipboard and ignored a paste. It now pastes the nodes a kit canvas copied, from the same tab or another, and nothing else. A pasted image, SVG, or file, a consumer's `ingestion.handlers`, and every OS drop still need `ingest`. `ingestion.clipboard` (`reviver`, `enabled: false`) applies to it as it does under `ingest`.
62
+
63
+ The new `clipboard.pasteEvent` action does this, bound to a `paste` event carrying `text/plain`, and `clipboardPasteEventAction` is exported. `useStandardActions` registers it only while `ingest` is excluded, since `ingest` binds the same event and accepts that payload among the rest; a canvas with both presets behaves as before. `KIT_STANDARD_ACTION_IDS` lists it, so the hook with no `exclude` now registers one fewer action than that list names.
64
+ - 41bddcc: A group draw command with `effects` ignored its own `alpha`, `colorMatrix`, and `clip`: a `LayerGroup` that set `effects` and `alpha` together drew its blur at full opacity. The group's result is now composited at its alpha, through its color matrix, and cut to its clip.
65
+ - 94abaef: A group with `effects` whose last child was a group clipped by anything but an axis-aligned rect (an ellipse, a rounded rect, a rotated rect) drew nothing at all. The effect passes now turn the stencil test off themselves instead of inheriting it from that child.
66
+ - 20c2d26: Escape cancels a drag. `startThresholdDrag` ends a live drag as a cancel when Escape is pressed, and keeps that key press from anything else listening, so a drag inside a dialog does not also close it. Everything built on it follows: `Tree`, `useReorderDragList`, and the rows of `PrefSchemaEditor`'s live preview. The editor's palette now drags through `startThresholdDrag` too, so a press on a tool that never moves makes nothing.
67
+
68
+ In `PrefSchemaEditor`, dropping a node where it already sits no longer adds a step to undo.
69
+ - f5d50b0: An even-odd path with a hole lost the hole wherever it was not a plain fill: as a group `clip` its children painted across the hole, a stroke with `align: 'inner'` or `'outer'` treated the hole as inside the shape, and a fill with `vertexColors` filled it. All three now cut the hole the way the fill does.
70
+ - b39b0f3: Move, resize, rotate, clone, nudge, and duplicate read and write each node's pose through its own descriptor (`PoseDescriptor.forNode`). Rotate measured through it and wrote through the unspecialized one, move used it only in its layout pass, and the rest never asked, so a node whose chrome was drawn from its specialized bounds was resized, translated, and copied as the default shape.
71
+ - eefcc9a: `gateLayer` dropped the wrapped layer's `effects`, `deps`, and `hitTest`, so a gated layer lost its blur pass and stopped being hittable. It now carries every member of the layer through; the hit-test answers only while the layer is visible, and the cache key includes visibility.
72
+ - f12ac6a: `useLassoTool` no longer takes an adapter: its first argument was never read, so the call is now `useLassoTool(options)`. `useHandTool` returns `Tool<null>` in place of a scratch type nothing populated. Both are breaking changes to the signatures.
73
+ - 8fca412: `useHandleDrag` ran the `onMove`, `onEnd`, and `onCancel` from the render that saw the press for the whole drag, so an `onEnd` that closed over state set by `onMove` committed the value from before the drag. Each callback is now the one from the latest render.
74
+ - a2e0462: New export `heldKeySpec(key)`, from `@weasel-js/routing` and `@weasel-js/core`: the `key-held` spec for holding a key, with every modifier optional when the key is itself a modifier and forbidden otherwise. It is general surface for any hold-a-key binding; offhand tool triggers and the labkit loupe's peek key both build their spec with it. The loupe changes for a peek key that is not a modifier: a letter no longer starts a peek while another modifier is down, the same as an offhand tool's trigger. The default peek key, Alt, behaves as before.
75
+ - 234f718: `toHex8` left a 4-digit hex color (`#f008`) unexpanded, so `getAlpha01` read its alpha as 1, `withAlpha01` returned it untouched, and `paintWithColor` kept the old paint's opacity on top of the color's own alpha. `#rgba` now expands to `#rrggbbaa`.
76
+ - 5e1cbe4: A layer hidden with `layerVisibility`, or left out of `layerOrder`, no longer receives `onUncapturedMove`. Clicks already passed through such a layer, but hover was still delivered, so a hidden HUD kept hit-testing its widgets, firing their hover events, and requesting redraws.
77
+ - 8fa3cbc: The scene walk called `drawOne` for every node on a hidden layer and then discarded the result. `buildSceneTree` no longer calls the painter for those nodes; their clips still apply to children on visible layers.
78
+ - fb297fb: An undo, redo, or `goto` whose op threw used to drop the entry from both stacks and leave the ops that had already run applied, so the edit could be neither undone nor redone. A step is now all-or-nothing: the ops that ran are inverted, the entry stays on the stack it was on, and the error still propagates. `applyOps` does the same for a batch that throws partway, and `scene.undo`, `scene.redo`, and `scene.jumpToHistoryIndex` also revert the scene writes of an op that threw partway through its own.
79
+ - 657872c: `useHoverTracking` returned a new getter on every render, though it documents a stable one, which rebuilt `SceneCanvas`'s chrome-rule callbacks each render. The getter is now stable, and it answers `null` once tracking is turned off instead of the last node hovered.
80
+ - 110142d: `useHoverTracking` tracks an element that mounts after the hook does. This is a breaking change to the hook: it no longer takes `canvasRef`, and returns `{ getHover, canvasRef }` where it returned the getter alone. Hand the returned `canvasRef` the element (it is a callback ref) and read the hovered node from `getHover`.
81
+ - 39b26f9: `releaseImage(src)` drops an image's decoded bitmap and its GL textures from the kit's image cache, which otherwise keeps every `src` it has loaded for the life of the page; reading the `src` again reloads it. `retryImage(src)` reloads an image whose load failed, which used to stay failed until a page reload.
82
+ - 3a4fa8e: Every `ImageBitmap` drawn through an image or sprites command left its GL texture allocated until the renderer was disposed, so drawing a fresh bitmap per frame (a video frame, a canvas snapshot, the pixel loupe) grew GPU memory without bound. The texture is now deleted on a later frame once its bitmap has been garbage collected.
83
+ - 71f5742: Starting a draw with `insertAction` clears the selection, and pressing Escape mid-draw left it cleared although nothing was created. A canceled insert now puts the selection back.
84
+ - e39b73c: `insert.adjustRotation` turns only the insert drag in its own input scope. The gesture in flight was held in a module variable, so with two canvases a Shift+wheel routed in one rotated a shape being dragged out in the other. It is now held on a new `insertLive` dep that `<SceneCanvas>` sources per canvas; a host that registers the insert actions without `<SceneCanvas>` sources `insertLive` itself to get the rotation.
85
+ - cf3d535: `<SceneCanvas insertPaint>`: choose the fill and stroke of a shape the kit's insert builds, without replacing its geometry. It takes `(kind, seq) => { fill?, stroke? }`; a key left out stays off the node and `null` means none. The default, exported as `paletteInsertPaint`, is the palette cycle the kit already used, so nothing changes for a canvas that passes no `insertPaint`. Additive: a new optional prop, the `InsertPaint` type, and `paletteInsertPaint`.
86
+ - 7c2e054: A key binding can name the physical key. `KeySpec` and `KeyHeldSpec` take a `code` (`KeyboardEvent.code`, one or several), tried before `key`, with either one matching; key input events carry the code. Bring to Front and Send to Back on Cmd+Alt+] and Cmd+Alt+[ use it, so they fire on layouts where Option makes those keys type something other than the US glyphs. A held key is now tracked by its code, so with both Shift keys down, releasing one no longer ends a Shift hold.
87
+ - 243984f: `useKeybindings`: changing the `disable` option after mount had no effect until the tool set also changed, so the tool keys stayed live when it turned on and stayed off when it turned back. The keys now register and unregister as `disable` changes.
88
+ - c527f50: A key or a paste that reaches the page now goes to the canvas the user last pressed or scrolled in, across every provider on the page, and to no canvas before the first such interaction. A lone canvas used to take every key from the moment it mounted, so a read-only canvas in a side panel took Space from the page around it. This changes behavior for every canvas: its shortcuts do nothing until it is pressed or scrolled in. An app whose canvas is the page passes the new `<SceneCanvas claimKeys>` (`claimKeys` on `useGestureDispatcher`) to take the keys on mount. A press gives a canvas the keys with any button and whichever channels are on; a dispatcher with no canvas element still follows the active input scope. `ActionsRegistry` gains `holdsKeys()` and `dropKeys()`, which a hand-built registry must now supply. For tests, `@weasel-js/react/testing/giveKeys` exports `giveKeys(canvas)` and `renderWithKeys`.
89
+ - b8a988c: A contribution's `requires` range no longer accepts a prerelease of the version it stops at. `satisfiesRange('2.0.0-rc.1', '^1.4.0')` returned `true`, because the prerelease orders below `2.0.0`, so a contribution written against 1.x passed its check on a release candidate of the breaking major. An exclusive upper bound (`<2`, or the one implied by `^`, `~`, and a partial version) now also excludes that version's prereleases. A space after an operator (`>= 1.2.3`) is read as npm reads it instead of reporting `invalid-range`.
90
+ - ab164dd: labkit's shared surface and `CanvasStack` take the new backing size when the device pixel ratio changes and nothing resizes, as when a window moves to a display of another density; before, they stayed blurry or oversampled until the next resize. Core exports `watchDevicePixelRatio`, the listener they use.
91
+ - 68dcc32: Two custom layers anchored on each other (`a: { after: 'b' }`, `b: { after: 'a' }`) were reported as a `dangling reference` in the console. `composeOrderedLayers` now reports them as a cycle; they still fall back to the tail.
92
+ - 20617cc: `animateLifecycle`: a node whose exit tween was canceled (the canvas unmounting, `animator.cancelAll()`, another pose animation on the same node) stayed in the scene at a half-finished exit pose. The node is now removed when its exit ends for any reason.
93
+ - 71f5742: Holding Shift on the line tool measured its 15° steps from the raw press point while the line was drawn from the start an insert behavior had moved (onto a guide, say), so a short line came out visibly off-angle. `insertAction` now constrains around the start the line is drawn from.
94
+ - 07d7cfe: `scene.loadState` called inside `scene.untracked` or `scene.batch` left the scene unable to group edits: every later `batch` recorded one undo step per op, and declared layouts stopped arranging bare edits. `loadState` now throws there, since it replaces the state those calls would revert to. It also drops selected ids the snapshot does not hold and clears every pose override, so neither carries over onto a node that reuses an id.
95
+ - d490ebc: `animator.pauseKey`, `resumeKey`, and `setTimeScaleByKey` did nothing to a `loop`, `tweenLoop`, or `stagger` holding that key, though its handle then reported itself paused. They now reach the composite's running children and a stagger's waiting items. A time scale or pause set on a loop also carries into each later iteration, where it used to last only for the one in flight.
96
+ - fc4676d: `@weasel-js/core/renderer` exports `releaseImageSource(source)`, which frees the texture every live renderer holds for an image source at once. Call it when replacing a bitmap that will not be drawn again, such as a video frame or a canvas snapshot; drawing the source later uploads it again. The pixel loupe now calls it on each refresh and on dispose, where it used to leave each replaced bitmap's texture for the garbage collector.
97
+ - 9d9eb3b: The Start / Mid / End marker pickers in `defaultNodeProperties` and `inferredNodeProperties` now list markers a consumer registers with `registerMarker`. The options were read once at module load, before any consumer code could run; they are now read from the registry when the schema is used.
98
+ - cc57bc6: A node filled with a bounds-relative mesh gradient (what `seedMeshPatch` and a stop-list conversion produce) re-baked and re-uploaded its texture on every frame of a move or resize, because the bake was cached against a copy of the paint that is rebuilt whenever the pose changes. The bake is now cached against the paint itself and reused while the node moves.
99
+ - 420ba9c: `computeFitView` and the minimap fit a rect with one zero axis along its other axis, so a scene whose only content is a horizontal or vertical line shows the whole line. It used to fall back to scale 1 at the rect's top-left; a rect with no positive axis still does.
100
+ - abe11f9: `<MinimapCanvas>` no longer breaks on a non-finite fit. One leaf whose pose had a `NaN` or infinite coordinate made `computeFitView`'s `"scene"` fit non-finite, so the minimap drew nothing; that leaf is now left out of the union. A custom `fit` function returning a zero or non-finite scale could send a non-finite point to `onMainViewChange` on a click; the fit view is now normalized like every other view.
101
+ - 316ecd0: The minimap contribution's indicator layer builds its rect with `computeIndicatorCommand`, the builder `<MinimapCanvas>` uses. In a minimap whose view is mirrored on x, the layer used to give the stroke a negative width and dash.
102
+ - fae67cb: `<MinimapCanvas>` draws its visible-window indicator at the `indicatorStyle` width and dash in CSS pixels, as documented. They were applied in world units inside the fit transform, so a 4000-unit scene in a 200px minimap got a line about 0.05px wide, effectively invisible, and a scene smaller than the minimap got a fat one. `computeIndicatorCommand` takes the minimap's view as an optional fourth argument to do the conversion.
103
+ - b4f06f1: `momentum`: undoing a flick returned the node to the point of release, a position that was never a committed state, and nothing took it back to where the drag began. The recorded transform now starts at the drag's origin. Dragging a node again while it coasted left the first flick running, writing poses under the new drag and then moving the node to where the first flick came to rest; a new drag of a coasting node now stops the coast and records it where it stands.
104
+ - 427ea6c: A `momentum` coast under the move action records one `flick` entry, where it used to record a `setPose` entry every frame and one undo stepped back a single frame. The coast is shown through the scene's pose overrides and written to the document once, when it comes to rest or a new drag catches it, through the `applyOps` dep when one is wired. `MoveAdapter` gains an optional `previewPoses`, which shows nodes at poses without writing the document; the move action's adapter provides it along with `applyOps`.
105
+ - dcc041e: `momentum`: a coasting node that something else moves or deletes, an undo included, now drops out of the coast. The coast used to go on writing its pose every frame and record a `'flick'` transform for it at rest, over whatever the other writer did. A dropped node records nothing; the rest of a multi-node flick carries on.
106
+ - 0a17152: A container dragged into a layout container, or landed by a move behavior that answers `onEnd` with ops such as `snapToContainer`, takes its children with it on a scene with absolute poses. The container used to land where the layout or behavior put it while each child kept its old pose. Each child now moves by the container's landed pose less its start pose, in the same undo step.
107
+ - f356bd0: With `coalesceWindowMs` set, two `scene.moveLayer` calls on one layer inside the window merged into one undo step whose undo pulled a different layer out of the stack: `[a, b, c]`, move `a` to 2 then to 1, undo, gave `[c, b, a]`. Undo and redo of a layer move now find the layer by id, so the step returns to the order it started from.
108
+ - 9472e9a: A move drag released before it travels `dragThresholdPx` cancels its behaviors. It used to run each behavior's `onEnd`, so a behavior could commit ops for a move that never started, with `onGestureStart` and `onGestureEnd` never fired around it.
109
+ - 928cafc: Resizing a multi-selection that held a rotated node stretched that node along the wrong axis (a horizontal drag made a quarter-turned rect taller), and the dragged corner drifted away from the pointer. `resizeAction` now scales a rotated member of a multi-selection along its own axes, as it already did inside a group, and measures the drag against the same union box the handles are drawn on.
110
+ - 95e64cd: Internal: the built-in shape painters moved out of `canvas/NodeShape.ts` into one module each. No API or behavior change.
111
+ - d01459d: Pressing an arrow key with a group selected recorded a `Nudge` undo entry and moved nothing: only the container's own pose was written, and with the default absolute poses its children each store their own position. The `nudge.*` actions now move the descendants of a selected node too, and move a child once when it is selected along with its container.
112
+ - 1eee4df: The `weasel.debug` flag is read and cached once. `@weasel-js/routing` now exports `isDebugEnabled` and `dwarn` beside `dlog`, and `@weasel-js/core` and `@weasel-js/ui` export those same functions where each used to parse `localStorage` with its own copy of the code. `@weasel-js/ui`'s `isDebugEnabled` now also takes no argument, to ask whether any namespace is on.
113
+ - cca79cf: Tool overlays follow the entry assembly the dispatcher routes by. With two tools held by hotkey, their overlays now stack newest hold first, as their bindings do, and under a `modes` registry a tool the active mode bars stops drawing its overlay as well as losing its bindings. `assembleEntries` is the one function behind both and is exported. `ContributionsApi.scopedBindings()` is removed, a breaking change for a caller of it: nothing in the kit called it, and it answered in a different order with no capability gate. `scopeBindings(assembleEntries(...))` gives the bindings the dispatcher sees.
114
+ - 5cd52f3: One hex color reader. `@weasel-js/paint` exports `parseHexBytes` (`#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa` to four 0–255 channels, `null` otherwise) and `hexToRgbBytes`, and core re-exports both; core's `parseColor` and `normalizeHex`, paint's `hexToOklchDeg`, `contrastLineColor`, and `hexToColorLiteral`, theme's `hexToRgb` and `withAlpha`, and ui's token panel all read through it. Fixed along the way: theme's `hexToRgb` (and `luminance`, `contrast`, `toLab` on top of it) read `#abc` as `#000abc` and shifted an eight-digit hex's alpha into blue; `contrastLineColor` did not read a four-digit background. Behavior change: `hexToOklchDeg`, `hexToRgb`, and `parseColor` now throw on a malformed hex where they computed from `NaN`.
115
+ - cad8bd9: `isTextData` is the kit's one test for a text node, a string `text` on its data, and is exported. The default `textEdit` dep's `isTextNode` now asks it where it used to check `data.kind === 'text'`, so it agrees with `inferredNodeRouting` and `useSceneTextEdit`: a node carrying `text` and no `kind` is text to all three.
116
+ - 190622a: Marquee select, lasso select, and `selectFromLasso` share one definition of how a hit list joins the selection. No behavior change beyond a hit list with a repeated id now selecting it once.
117
+ - 36c6ca0: The inverses of `createReorderOp`, `createMoveToIndexOp`, and a cascading `createDeleteOp` had no `name`, so a history entry built from one could not be serialized and took the older undo entries out of the snapshot with it. They are now named ops that rebuild from their args. Two additions carry them: `createSetChildOrderOp` (op name `setChildOrder`) writes a parent's whole child order and inverts to the order it replaced, and `InsertArgs` gains an optional `cascaded` list, so one `insert` op can bring back a node together with everything a delete of it took. Both are additions to the persisted history format; a snapshot written before them still loads.
118
+ - 6983df3: Editing text no longer strips a gradient or pattern fill from its runs. The edit overlay could only say a solid color, so any other run fill was dropped the moment the edit committed; the run's span now carries the fill itself and `domToRuns` hands it back.
119
+ - 399033f: Caret offsets in the text edit overlay count the breaks `domToRuns` reads: a `<br>`, and the boundary a `<div>` or `<p>` opens, each as one character. `charOffsetToDomPosition` and `domPositionToCharOffset` counted text nodes alone, so every offset after such an element was short by one per break, and `domToRuns` read no newline at a `<p>`. A position just past a trailing `<br>` comes back as an element and a child index.
120
+ - 2d1c6af: A node painted from a derived path kept its `kit:derived` painter after `scene.setDependsOn(id, undefined)` detached it, so it painted nothing where its own `data.path` should have shown; a static node that was then attached kept painting its static path. Undo and redo of the same op did the same. `findNodeShape` now re-matches when a node starts or stops deriving its path.
121
+ - e31a360: Copy and paste through a scene adapter now carries a node's derivation and a container's layout. A pasted node that derives from an id list follows the pasted copies of what it depends on; one whose dependencies were not all copied is left out of the paste rather than pasted as an invisible placeholder, and the paste says so: `commitPaste`'s ctx takes `onOmit`, `useClipboardOps` takes `onPasteOmitted`, and `<SceneCanvas ingestion={{ clipboard: { onOmitted } }}>` hears the same for an OS-clipboard paste (new types `PasteOmission` and `PasteCtx`). Snapshot items also carry `SceneRegistry` keys for their function fields, so a registered `derivePath`, `derivePose`, `clipFromPose`, or `layout` survives the JSON wire format.
122
+ - 33e0710: Pasting a node whose pose descriptor specializes per node (`PoseDescriptor.forNode`) offset it, and measured it for a drop point, through the unspecialized descriptor. `commitPaste` now reads each pasted node through its own descriptor, as moving it does.
123
+ - a8e964e: The built-in pen tool numbered its nodes from a counter that restarted with each `<SceneCanvas>` mount, so the first path drawn after a reload, a remount, or on a second canvas over the same scene was `pn-1` again and the insert threw an id collision. Pen node ids now carry a random suffix, like every other id the kit mints.
124
+ - b444e46: `usePenTool`: the segment trailing from the last anchor to the pointer, and the close hint over the first anchor (with its `pointer` cursor), never appeared, because nothing recorded where the pointer was. The pen now follows the `pointer` dep while a path is in progress, so both show wherever a canvas publishes one, as `<SceneCanvas>` does.
125
+ - e4d444f: `usePenTool`: pressing Enter after one click, or double-clicking an empty canvas with the pen idle, committed a path of a single anchor: a node that draws nothing but sits in the scene and in undo history. Finishing now drops a subpath of fewer than two anchors, and commits nothing when that was all there was.
126
+ - bdf0a97: The pen tool commits through the `insert` dep, as every other shape tool does. A finished path is inserted as kind `'path'` (`{ kind: 'path', path, closed }`), so a consumer who overrides `insert` or passes `insertNodeFactories.path` now shapes pen paths too, and the new path is selected through the `selection` dep. Joining a new path onto another path's endpoint uses the new optional `InsertDep.build`, which returns the insert ops unapplied so they share one undo step with the delete. This is a breaking change to `usePenTool`: `wrapPath` and `adapter` are gone from `UsePenToolOptions`, the hook is no longer generic, and its options are optional. A consumer's own `insert` dep needs a `'path'` case for the pen to insert anything, and `build` for the join.
127
+ - 0641010: A `spring`, `decay`, or `physics` animation canceled from its own `onTick` on the frame it came to rest still called `onDone`. It now never completes once canceled, as a `tween` already did.
128
+ - 667ca34: When a `geometry.pickEvery` override on `<SceneCanvas>` answered an id the scene does not hold, hovering or clicking that point threw `sceneToAdapter: unknown node`. The hit is now reported with `kind: 'unknown'`.
129
+ - e06409e: A node added with `pickable: false` came back clickable after undoing a delete through `<SceneCanvas>`'s adapter, and a pasted copy of it was clickable too. `sceneToAdapter`'s `insertNode`, `snapshotSelection`, and `commitPaste` now carry the flag.
130
+ - 1268f79: Every renderer program links `a_position` at location 0, so a registered paint kind whose vertex shader declares another attribute first still draws its mesh. A path drawn with per-vertex colors frees its color buffer with the draw and no longer leaves the attribute enabled on the mesh's vertex array.
131
+ - 4d559ca: `isPlainObject` lives in `@weasel-js/registry` (core still exports it), and `@weasel-js/geom` exports a scalar `clamp(v, lo, hi)`. theme, prefs, labkit, and forge read plain objects through the one realm-safe test, so a `Date` or a `Map` no longer passes where they accept a record; ui's Slider and ResizeHandle, hud's loupe, and labkit's ZoomControl use geom's clamp. When its bounds cross, ResizeHandle now holds to `min` where it held to `max`. theme and prefs gain a dependency on registry, and ui's on geom moves from dev to runtime.
132
+ - 60ac0a5: `usePolygonTool` and `useStarTool` ignored their `label` option and always showed "Polygon" and "Star". `useStarTool` also kept the `innerRatio` it was first rendered with, so a ratio driven by a slider never changed the stars drawn. Both options now take effect.
133
+ - 93780f6: A `usePoseRun` run that settled on a node locked in the meantime threw from its commit and left the node drawn at the run's last frame, away from its stored pose. The commit now skips locked nodes, and the run's overrides are dropped however the commit ends.
134
+ - 188604d: A box pose's `transform` (a `PoseLinear`: a skew or per-axis scale applied about the box center, inside the rotation) is now drawn and picked. The per-node render wrap, paint culling, painter silhouettes, `axisAlignedBounds`, `unionAABB`, `visualBoundsViaDescriptor`, `poseContainsRotated`, and region picking all read the pose through `poseFrameOf`, and `pathThroughPoseFrame` is the path form of it. `svgNodesToKitDrafts` carries a text or image node's `transform` onto the pose and `svgLeafFromKit` carries it back, so `<text transform="scale(4 1)">` imports as editable text drawn four times as wide. Selection chrome and the resize, rotate, and flip actions still work the unstretched box.
135
+ - 036d768: `pathInWorld`, `worldEditToStorage`, and `textToPath` place geometry through a pose's `transform` as well as its rotation, so anchor edits, slices, and text outlines of a skewed or stretched node land where it is drawn. Slicing keeps the transform on the pieces, and flipping a node mirrors its transform along with its rotation. `PathInWorldPose` gains the optional `transform` field.
136
+ - 9f70b3c: **Breaking change to the schema types.** A pref schema's branch node was one type, `PrefGroup`, read two ways: the prefs store and `PrefsForm` took a group's key as a path segment, and a node's property schema took it as a heading over leaves whose own keys were whole node paths. There are now two types, and a schema of one is a type error where the other is read.
137
+
138
+ `PrefGroup` keeps `children` and the nested reading: `view` > `gridDensity` is the leaf at `view.gridDensity`. The new `PrefSection` holds `members`, and its key adds nothing to a path. `NodePropertiesEntry.schema` is a `PrefSection`, and so is a heading inside a `PrefObject`'s `children`.
139
+
140
+ What to rewrite:
141
+
142
+ - A node property schema passed to `createNodeProperties().register`, `<SceneCanvas>`, or `<SelectionPanel>`: rename `children` to `members` on the root and on every group inside it. An `object` leaf keeps `children`.
143
+ - A group nested inside an `object` leaf's `children`, in any schema: rename its `children` to `members`.
144
+ - Code that walks a node property schema reads `.members`. `prefSectionLeaves(members)` returns every leaf under a section's `members` or an object leaf's `children` by its own key, and `isPrefSection` tells a section from a group.
145
+ - `prefFieldChoices(schema)` no longer takes a second argument. It reads the path rule off the schema it is given, so pass a `PrefSection` where `false` was passed.
146
+ - `SelectionPanel`'s `isGroup` is gone; use `isPrefLeaf`.
147
+
148
+ A preferences schema and a tool's `options` are unchanged unless they nest a group inside an `object` leaf.
149
+
150
+ `PrefSchemaEditor` edits the sections inside an `object` leaf: it adds one where it used to add a group, prints it with `members`, and refuses to move a group under an object leaf or a section out from under one.
151
+ - 812118e: New `@weasel-js/storage` package: the storage adapters and `RecordCache` that lived in labkit, split into one module per adapter. localStorage, sessionStorage, memory, and the no-op adapter gain `listSync`, and `openRecordsSync` opens a record cache over them without awaiting. `fallbackStorage(preferred, fallback, label)` wraps an adapter with a fallback for when it is unavailable, and `createDefaultStorage(preferred, label)` builds a page-wide default from one: `preferred`, or localStorage where it will not open, chosen once. Storage's own `indexedDbAdapter` and `defaultStorage` use the IndexedDB database `'weasel'`. `RecordCache.flush()` now resolves `true` when every queued write landed and `false` otherwise; it used to resolve to nothing. labkit re-exports the adapters, and its `indexedDbAdapter` and default storage stay on the database `'labkit'`, where every existing lab's records are.
152
+
153
+ New `@weasel-js/prefs` package: the preferences schema, moved out of core, and a store for it. `openPrefs` and `openPrefsSync` keep one record per leaf and repair values against the schema on read. `store.stored()` returns the raw stored records as a tree, orphans included, and `usePrefsValues` returns it as `stored`. Versioned migrations run on open: a malformed `$version` opens the store read-only, and a newer `$version` written by another writer stops this store from persisting. `Infinity` and `-Infinity` on number leaves are stored as the strings `'Infinity'` and `'-Infinity'`. `PrefsStore.flush()` resolves a boolean, with the same meaning as `RecordCache.flush()`. The `usePref` and `usePrefsValues` hooks, imported from `@weasel-js/prefs/react`, produce the values `PrefsForm` takes; the main entry loads no React.
154
+
155
+ Breaking: the schema types are renamed from `ToolPref*` to `Pref*` (`ToolPref` itself is `BuiltinPref`, `TOOL_PREF_KINDS` is `PREF_KINDS`, `isBuiltinToolPref` is `isBuiltinPref`) and are imported from `@weasel-js/prefs`. Neither core, ui, nor `@weasel-js/labkit/weasel-ui` re-exports them, nor ui's `isPrefLeaf`, `prefValueAtPath`, `visiblePrefSubtree`, `filterPrefSubtree`, or `prefDisplayBounds`. labkit's cross-tab BroadcastChannel is renamed, so tabs on an older and a newer labkit stop hearing each other until both reload.
156
+ - 4bbffa2: `styleAtRange` compared two class instances in a run's `fill` or `stroke`, such as two `Date`s, as equal whenever neither had own enumerable keys. They now compare by identity, so a range holding both reads `MIXED`.
157
+ - e640378: Disposing a raster session now releases the WebGL context of the canvas it created, instead of leaving it alive until garbage collection. A loop of `renderSceneToPixels` calls piled up live contexts until the browser hit its per-page cap and took the context away from the oldest one, which is a canvas on screen. A canvas from a caller's `createCanvas`, and a caller's `gl`, are left alone.
158
+ - ec5e31d: `renderSceneToPixels` and `RasterSession.render` now throw when the requested raster is larger than the WebGL context's drawing buffer, naming both sizes. A context clamps a buffer it cannot allocate, so a poster-size export came back as a full-size `RasterImage` that was transparent past the clamp, with no error. The same check covers a caller-supplied `gl` whose buffer is smaller than the render.
159
+ - 2db1963: A node added under an id a removed node had used came back with that node's `clipFromPose`, `derivePath`, `derivePose`, or `layout` when its own spec set none, and `toJSON` wrote the old key for it. `reconcileSpecs` hit this whenever a spec dropped one of those fields. `scene.add` now attaches only what its spec carries, and undo and redo give each add of an id back its own functions.
160
+ - 20cb4c6: `useAnimatedReflow` / `createReflowTransition`: a glide canceled through the animator (`cancelKey('reflow:<id>')`, `cancelAll()`) left the node frozen partway, and asking again for the same `glide` target or `settle` did nothing. Asking again now restarts it from where it stopped.
161
+ - 0a83dfb: Right-to-left text lays out in visual order everywhere once a bidi engine is registered: `registerBidi(bidi)` (from `@weasel-js/text` or `@weasel-js/core`, with `bidi` from `@weasel-js/bidi`) gives `layoutRuns` an engine to fall back on when its options carry none, so painting, picking, the caret, `measureTextBounds`, and `textToPath` all reorder.
162
+
163
+ An omitted `direction` now means `'auto'` for alignment as well as for reordering: each paragraph's first strong character decides which edge `start` and `end` are. `TextStyle.direction` and `LayoutRunsOpts.direction` accept `'auto'`, `ResolvedTextStyle.direction` defaults to it, and a text command carries `direction` and the unresolved `align`. `paragraphDirection(text, direction)` answers which way a text reads.
164
+
165
+ Breaking for a custom engine: `BidiResolver` gains `baseLevel(analysis)`, which `@weasel-js/bidi`'s `bidi` implements. `TextDrawCommand.align` and `TextPoseLayoutInput.opts.align` may now be `start`, `end`, or `justify`.
166
+
167
+ The edit overlay states the same direction in CSS, and `@weasel-js/svg` writes the direction a text actually reads in and keeps a stated `ltr` through a round trip.
168
+ - b413e19: The built-in shape painters and the effects composite program register on first use instead of when their modules load. A consumer's bundler that drops an import kept only for its side effect no longer leaves either registry empty.
169
+ - d8b791e: A custom op kind registered with `scene.registerOp` no longer coalesces just because its payload carries an `id` or `layer`: with `coalesceWindowMs` set, a relative op such as a nudge by `dx` merged into one entry and undo reverted only the first step. `RegisteredOp` gains an optional `coalesceKey(payload)`, new engine surface for any op kind whose `revert` restores an absolute state; a kind that does not declare it is always its own undo entry, so a consumer kind that relied on merging now has to declare one.
170
+ - dc856b3: `WeaselRenderer` resets the color mask, the bound vertex array, and `UNPACK_FLIP_Y_WEBGL` at the start of every frame, alongside the blend, depth, and cull state it already reset. Another renderer drawing on the same context could leave any of them set, which blanked the frame or flipped later texture uploads.
171
+ - 0b93821: Inverting the inverse of `createReorderOp` or `createMoveToIndexOp` returned an op that did nothing. It now returns the original reorder, so an undo step that fails partway and unwinds puts sibling order back.
172
+ - 8b405d0: A canvas repaints when `devicePixelRatio` changes. Moving a window to a display of another density, or zooming the page, left a static `<DrawCanvas>`, `<SceneViewCanvas>`, or `<MinimapCanvas>` at its old resolution, and a `<SceneCanvas>` blurry until something else redrew it. A canvas given its own `dpr` is unaffected.
173
+ - 6fd724f: With `reparentOnDrop` on, dragging a container onto another container moved and reparented the container but left its children at their old positions, though the drag preview showed the whole subtree moving. `moveAction` now moves the dragged node's descendants in the same undo step as the reparent.
174
+ - a145539: `moveAction` read the `nodeAtPoint` dep for `reparentOnDrop` from the context its `onEnd` was handed, and the dispatcher ends a gesture with an empty deps bag, so a drag released over a container was translated and never reparented. The action now captures the dep when the gesture starts. A drag committed with no pointer in its end context (a second finger lifting on touch) is hit-tested at the last pointer position the drag saw.
175
+ - 279d88f: Resizing, rotating, flipping, aligning, or distributing a selected group now acts on its children on a scene with absolute poses. A group there derives its pose from its children, so the pose these actions wrote to the group changed nothing on screen. Resize scales each child inside the group's box, rotate orbits each child about the group's center, flip mirrors the children about the group's box, and align and distribute carry the children by the group's shift. A group selected beside other nodes is resized with them the same way, and a `resizePolicy.expandIds` result is now honored for a multi-selection as well as a single one.
176
+ - 60edb93: Holding Shift while dragging a resize handle on a node whose pose is a polygon `Path` wrote a path with NaN coordinates, and a `pointSnap` rule on the same node was handed NaN corners and left stray `x`/`y`/`width`/`height` fields on the path. `resizeAction` now hands its constraints and point snaps the pose's bounds, and writes a snap back through the pose descriptor.
177
+ - 66f7465: `composeAffordanceLayer`: the bounds reported for a ring (`annulus`) region on a rotated target were too small, by up to 29% for a circle at 45°. That is the box a `custom` paint receives as `CustomPaintContext.world` and the box the debug overlay draws for the rotate ring, which was also turned a second time. Both are now the ring's real bounds.
178
+ - 9c10943: `DepSchema` declares the `activeTool` dep in `@weasel-js/routing` itself, where its type and the `tool.offhand` action that requires it live, so `ctx.deps.activeTool` is typed without core's augmentation. Nothing changes for a consumer naming `'@weasel-js/core'`.
179
+ - e98e95b: A run can carry no fill: `StyledRun.fill` accepts `null`, which `resolveRuns` keeps as unfilled where it used to read it as "inherit the node's". The range algebra reads and writes it (`styleAtRange` reports `null`, a `{ fill: null }` patch unfills the range, `undefined` still clears back to inheriting), the edit overlay carries it through an edit, and `@weasel-js/svg` writes it as `<tspan fill="none">` and reads that back.
180
+ - 8261c9c: `sampleTrack` at a `NaN` time returns the first key's value again, where it had started returning `NaN` (and threw for a track of values it cannot lerp). At an interior key's exact time, a track with its own `interpolate` or `interpolator` now returns a value made by that function at 0 instead of the key's own object, so changing a sampled value no longer changes the key.
181
+ - 4787e75: Internal: The scene's atomic-revert log (the kit ops an edit has applied, and the all-or-nothing wrapper that takes them back) moves out of scene.ts into appliedLog.ts. No behavior change.
182
+ - c2ec0c3: Internal: The scene's arrival window (what a live edit did to containers, and the settle that hands it to a layout handler) moves out of scene.ts into arrivalWindow.ts. No behavior change.
183
+ - 16559ef: Internal: `SceneCanvas`'s props type, coordinate trace, and its mounter and registrar components moved into their own modules. No API or behavior change.
184
+ - ba7cc48: Internal: The scene.history facade and the journal wrapper behind it move out of scene.ts into historyFacade.ts. No behavior change.
185
+ - 569a013: `scene.applyBatch` threw `Cannot read properties of undefined` for any adapter-driven op, such as `createTransformOp`, while a journal opened with `scene.history.beginJournal` was the active one, and the edit never landed. Ops routed to a journal are now bound to the adapter passed at the call, as they already were on the scene's own undo stack.
186
+ - ac8491b: `scene.toJSON()` writes `nodes` in tree preorder instead of layer-major order, so siblings on different layers come back from `loadState` / `sceneFromJSON` in the order they were saved. This changes the documented order of `nodes`: parents still come before children, but a reader painting straight from `nodes` now has to take paint order from the layers.
187
+ - 5601761: Internal: The scene's kit op table (the apply and revert of kit:add, kit:remove, kit:setPose, and the other kit kinds) moves out of scene.ts into kitOps.ts. No behavior change.
188
+ - 7123087: Internal: The scene's layer stack (the layer records' setup, the id-to-index map, and the rule tying a node's layer to its parent's) moves out of scene.ts into layerStack.ts. No behavior change.
189
+ - 1ca2395: Internal: The scene's lock guard (what a locked layer refuses, and the bypass a replay runs under) moves out of scene.ts into lockGuard.ts. No behavior change.
190
+ - 47d868f: `scene.mintId()` gives the id `scene.add` would give a spec with none, from the scene's `generateId` or the kit default. Group, duplicate, clone, paste, slice, the built-in shape tools, the pen tool, an insert factory that returns no id, and an SVG drop now take their ids from it, so a scene with `generateId` holds one id scheme. Ids no longer carry the `paste-`, `pn-`, or `kit-<kind>-` prefixes those paths used to write. `Scene` gains a required member, so a hand-built `Scene` object needs `mintId`.
191
+ - 77fe781: Internal: The scene's node store (tree links, the index of who derives from whom, and the function fields a node carries) moves out of scene.ts into nodeStore.ts. No behavior change.
192
+ - d18b041: Internal: the scene's render-order walks and their cache move out of `scene.ts` into their own module. No behavior change.
193
+ - 97bcbef: Internal: The scene's serialization (the toJSON walk, and the readers that turn a snapshot back into layer records and node specs) moves out of scene.ts into serialization.ts. No behavior change.
194
+ - a53f3da: `useSceneTextEdit` wrote the text and runs back on every commit, so opening an edit and closing it untouched left an undo step, and doing it on a node with no text gave that node `text: ''`. A commit whose text and runs match what the node already holds now writes nothing.
195
+ - cd5ff3e: `<SceneViewCanvas>` and `<MinimapCanvas>` stop painting while the canvas is scrolled out of view, as `<SceneCanvas>` and `<DrawCanvas>` already did. A minimap given the main canvas's `animator` repainted on every tick with nothing on screen to show for it. The held repaint lands when the canvas comes back. Their docs also no longer claim that `className` sizes the canvas: the renderer writes the `width` and `height` props as inline style, which a class cannot override.
196
+ - ec3c147: `SelectionContext` went on reporting a canvas's selection after that canvas unmounted. `usePublishSelection` now clears the selection it published when its component goes away, unless another publisher has written since.
197
+ - 24bdb50: `animateOnSetPose`: writing a node's pose again while its glide was still running wrote the new pose through, and the running tween's next frame put the node back on its way to the first target. The second write now cancels the glide, and its pose stands.
198
+ - 5d1ffd6: The slice tool's drag-to-place-a-point read the view and the `slice` dep from the gesture's end context, which carries none: a drag released on the first point of a pending cut did not close and commit it, and the placed point was measured at 1:1 zoom. It now uses the deps the drag started with.
199
+ - 6b42996: `snapBackOrDelete` and `snapToContainer` act on every dragged node. With several nodes dragged, a far release under `onFreeRelease: 'delete'` used to delete the first and spring the rest back; it now deletes them all. `snapToContainer` used to reparent only the first and return the rest to where they started; it now reparents each one, asking `findTarget` for that node's own slot, and a node with no slot in the snapped container lands where the drag previewed it.
200
+ - 6122f67: A drag of several nodes under `snapToContainer` previews each node in the slot it will land in; the others used to ride along with the first and jump on release. A move behavior can now return `poses`, a preview pose per dragged id, from `onMove`.
201
+ - 50cb70e: The renderer's draw module is split by owner into `renderer/draw/` (uniforms, custom shaders, effects, stencil, clip, batch, gradient, paint binding, path painters, text, the scratch pass, and images), with no behavior change; `draw.ts` keeps `dispatch` and every export it had.
202
+ - e9a28a0: A CSS-module class name in `@weasel-js/core` and `@weasel-js/ui` now depends on its stylesheet's path alone: `_editor_b9a2b2`, where it was `_editor_s6qnh_7`. The old name hashed the file's contents, so one edited rule renamed every class in the file, and a labkit stylesheet built before the edit styled none of that component.
203
+ - ea6842f: `animator.stagger` now counts its delays on the animator's clock instead of wall-clock timers. A hidden tab or `animator.pause()` no longer lets the delays run out while nothing can tick, which started every remaining item on one frame afterward, and `animator.setTimeScale()` and the stagger's own `setTimeScale()` now stretch the delays along with the children. Waiting items start on a frame, after that frame's ticks. Breaking: `UseAnimatorOptions.setTimer` and `clearTimer` are removed, since nothing reads them; a test drives a stagger through `requestFrame` and `now` like any other animation.
204
+ - 54faddb: A `stagger` whose factory returned an animation that had already finished (an empty inner `stagger`, a `loop` with `count: 0`) never completed: `isActive()` stayed true and the animator kept requesting frames. Such a child now counts as done at once.
205
+ - 0cf2bf4: `strokeToPath(path, stroke)` returns the region a stroke covers as a fillable path: width, cap, join, miter limit, dash, alignment, per-vertex widths, and marker heads all included, with self-overlaps resolved. `trianglesToPath` in `@weasel-js/geom/booleans` is the union of a triangle mesh it is built on. Both are additions.
206
+ - c5389a8: `subscribeView` on the canvas ref handle now fires on a canvas controlled by its `view` prop. Subscribers were only told about `setView` writes, which a controlled canvas does not apply, so a zoom readout or DOM pinned to world coordinates showed the mount-time camera forever while `getView()` was right. They are now called when a committed `view` prop differs from the previous one.
207
+ - a1ea9ff: `parseSvg` keeps the skew or per-axis scale of a `<text>` or `<image>` instead of warning and dropping it, and `serializeSvg` writes it back. `SvgTextNode` and `SvgImageNode` gain a `transform` field for it, a `PoseLinear`: the part of the element's transform its box, rotation, and flips cannot hold, applied about the box center inside the rotation. An image that is rotated and scaled by axis, or rotated and mirrored, now imports exactly as a box, a rotation, and a flip. Core gains `PoseLinear`, `RectPose.transform`, and `poseFrameOf` / `poseFrameInverseOf` / `poseLinearOf`, the one composition of a box pose's rotation and `transform` into a matrix; nothing in core paints or picks through it yet.
208
+ - 8d56051: A `kindOf` target predicate must now declare whether it reads the affordance hit: `TargetPredicate.readsAffordance` is required, and the exclusive-claim filter reads it and no longer guesses. Build one with the new `hitPredicate((hit, bodyTarget) => …)` or `bodyPredicate((bodyTarget) => …)`, exported from `@weasel-js/gestures` and `@weasel-js/core`. This is a breaking change: a bare function as `kindOf` is a type error, and one passed from untyped code counts as not reading the hit, so an exclusive claim bars its binding.
209
+ - d16306d: `window.__weaselTest` now passes to the next canvas when the one answering it unmounts. With two canvases on a page it answers for the earliest still mounted, where it used to disappear with the first.
210
+ - b931128: Under `?test=1`, `window.__weaselTest` kept answering for a canvas after it unmounted, so swapping one canvas for another without a page load left `getScene()` and `getActiveToolId()` reading the old one. The canvas that installed the hook now removes it when it unmounts, and the next canvas to mount installs its own.
211
+ - a200c36: The Direction leaf of a text node's properties offers Auto beside Left to right and Right to left, and reads a style with no direction as Auto, which is what the engine does with one. Before, such a style, and one holding `direction: 'auto'`, lit neither segment.
212
+ - 2bbcc5a: Double-clicking a word in the open text editor could move the edit to a different node and drop what had been typed: `useSceneTextEdit`'s `onDoubleClick` measured the click against the run it landed in and hit-tested that offset as a canvas point. A double-click inside the editor is now left to the browser's word selection.
213
+ - d3635ee: `useSceneTextEdit`'s `onDoubleClick` opened an edit on whatever node was under the pointer, so double-clicking a rectangle mounted an empty editor over it and typing wrote `text` onto a node that is not text. It now opens only a text node, passing through anything else to the text beneath. New surface: an `isText(data)` option (default: `data.text` is a string, the test `inferredNodeRouting` uses) and a returned `isTextNode(id)` that asks it by id, which is the shape `TextEditDep.isTextNode` takes.
214
+ - 2098025: A text edit is one write. `useSceneTextEdit` committed text, runs, and a rewritten node style as separate `scene.update` calls, so one styled edit took two or three undos, and after the first the node held new text beside old runs, which `createTextLayer` throws on. It now makes one update. Turning off a flag the node itself sets (Cmd+I on a word in an italic node) also wrote the node's style at once, so a canceled edit still changed the node; `useTextEdit` now holds that style until the commit and calls `setStyle` then, if at all.
215
+
216
+ New surface on `useTextEdit`: a `commit(id, { text, runs?, style? })` option that takes the whole edit in one call in place of `setText` / `setRuns` / `setStyle` (`setText` is optional when it is given), the `TextEditCommit` type it receives, and a returned `nodeStyle`, the rewritten node style an edit is holding, for a control that shows the node's style mid-edit.
217
+ - 6a4a8ad: Three fixes to `useTextEdit`. Turning underline, strikethrough, overline, or italic off for a selection in a node that sets it left the selection drawn with it until the edit committed; the overlay now drops the node's flag when the node does. Enter and Escape pressed to confirm or dismiss an IME candidate no longer end the edit. An edit started before `container` was set now mounts its overlay when the container arrives, instead of leaving the node hidden with no editor.
218
+ - 5b8578a: `useTextEdit` writes nothing when an edit closes with the text, the runs, and the node's style as it opened: neither `setText`, `setRuns`, nor `commit` is called. An open-and-close no longer strips a trailing newline from the node's text, and `useSceneTextEdit` relies on the same rule in place of its own comparison.
219
+ - d8e003d: A text node that was hovered, marqueed, or used as a clip before its font finished loading kept the pick area measured against the fallback metrics, so clicks on part of the repainted text fell through to whatever was behind it until the node was next edited. `findShapeSilhouette` now measures a text node again when a face lands.
220
+ - b3b209e: `TextStyle.tabSize` sets the distance between tab stops for a text node, in spaces (default 8). It reaches the painter, `layoutTextPose`, `measureTextBounds`, and the edit overlay's CSS `tab-size`, and `@weasel-js/svg` keeps it in `data-weasel-tab-size`. `measureText` and `layoutMarkdown` leave a hanging tab out of a wrapped line the way they leave out a hanging space.
221
+
222
+ `@weasel-js/svg` reads a `<text>` another writer left without a `direction` as `ltr`, which is what SVG means by it; text weasel wrote keeps an omitted direction as the text's own.
223
+ - 72002f8: A text node's default properties offer `tabSize` as "Tab size" under Paragraph, so the distance between tab stops can be set from a properties panel.
224
+ - 26c11b8: On the frame a `TextureHandle` uniform was first uploaded, the sampler bound before it went blank: a `kind: 'shader'` command with two texture uniforms drew its first one black, and an effect with a texture uniform read a black `u_source`. A canvas that paints on demand kept that frame. Each texture now uploads on its own unit.
225
+ - 44e1258: After a frame that threw part-way (an unparseable color, clips nested past seven), `WeaselRenderer.render` drew that frame's leftovers into the next one: translucent shapes came out twice as opaque, and a throw inside a group with `effects` left the next frame painting into an offscreen buffer, with another buffer allocated on every throwing frame. Each frame now starts from an empty batch, the canvas's own framebuffer, and a full pool of effect buffers.
226
+ - 40533db: Two `<Canvas paintInto>` tiles that share an edge now share it in device pixels at a fractional `devicePixelRatio`. The renderer rounded a tile's origin and its size separately, which at 1.25 or 1.5 could leave a one-pixel gap or overlap along the seam.
227
+ - d71fc46: `createTiledLayer` drew every copy the view could hold however far out it was zoomed, hundreds of thousands of source draws per frame at a small scale, and never returned at a view scale of 0. Past 4096 copies in one draw it now draws none.
228
+ - 4334f6d: Timeline events at the edge of a seek or a paused start now follow one rule: an event the playhead is sitting on fires, and books, when playback next advances from there, and never while paused. A timeline created with `autoplay: false` no longer fires its `t = 0` event before it is resumed, and `seek(t)` no longer swallows the event at `t`, so `seek(0)` replays the opening event as a loop wrap does. This is a behavior change for a caller that seeks onto an event.
229
+ - bb28e63: A timeline handle has `subscribePlayback(cb)`, notified on `pause`, `resume`, `seek`, `setTimeScale`, `setLoop`, a cancel, and the playhead reaching the end; `subscribe` still reports edits only. `AnimatedTimeline` listens to it, so a resume, pause, or seek made on the handle from the app's own controls moves its playhead and Play button. Anything implementing `TimelineHandle` by hand needs the new method.
230
+ - 3dff419: A tool's activation key now comes only from the `keybinding` on its definition. The built-in select, rect, ellipse, line, polygon, pencil, text, hand, and pen tools declare theirs there, with the same keys as before. Two things change for a consumer tool that reuses one of those ids: a `keybinding` it declares is honored, where it used to be ignored in favor of the built-in key, and one that declares none no longer gets the built-in key by id.
231
+ - ba4684e: An SVG image drawn at two very different sizes at once (a canvas and its minimap, or two nodes sharing one `src`) re-rasterized on every frame: each size replaced the other's raster. Once a shrunk raster has been asked to grow back, `getImageBitmap` no longer shrinks it below that size.
232
+ - 0d82f92: `View`, `ZOOM_FLOOR`, and a pure `normalizeZoom` live in `@weasel-js/geom`; routing and core re-export them under the names they had. `@weasel-js/loupe` depends on geom and no longer peers on core, so its framework-free model installs without core or React. routing gains a dependency on geom. loupe's model no longer logs core's dev warning for an invalid zoom factor; it still clamps it.
233
+ - 6befeae: A `tweenLoop` (or a `loop` of tweens) whose duration reached 0 hung the tab when it had no `count`, and ran every iteration of a counted one inside a single frame. An animation started while a frame is ticking now waits for the next frame, so a zero-duration loop runs one iteration per frame.
234
+ - 7c45605: `viewZoom` and `meanScale` returned `NaN` for a view with one flipped axis, such as a y-up `{ x: 2, y: -2 }`, so every chrome rule with a zoom threshold read false and the select tool's stroke pick tolerance was `NaN`. A view flipped on both axes reported a negative zoom. Both now take the mean of the axes' magnitudes.
235
+ - Updated dependencies [9dfd8a5]
236
+ - Updated dependencies [8e1e212]
237
+ - Updated dependencies [4c4aa40]
238
+ - Updated dependencies [41a4b4e]
239
+ - Updated dependencies [1bde2a4]
240
+ - Updated dependencies [07d96b0]
241
+ - Updated dependencies [34feefc]
242
+ - Updated dependencies [455c865]
243
+ - Updated dependencies [d99eb26]
244
+ - Updated dependencies [b48fd2c]
245
+ - Updated dependencies [663248a]
246
+ - Updated dependencies [c2a2a5d]
247
+ - Updated dependencies [9ad839d]
248
+ - Updated dependencies [bcf67ba]
249
+ - Updated dependencies [001aaae]
250
+ - Updated dependencies [a843f5a]
251
+ - Updated dependencies [b370885]
252
+ - Updated dependencies [a7c9133]
253
+ - Updated dependencies [43970d9]
254
+ - Updated dependencies [c289df7]
255
+ - Updated dependencies [259fbd3]
256
+ - Updated dependencies [63af505]
257
+ - Updated dependencies [d617bf4]
258
+ - Updated dependencies [f66ec41]
259
+ - Updated dependencies [1896646]
260
+ - Updated dependencies [ccf595e]
261
+ - Updated dependencies [546fd8a]
262
+ - Updated dependencies [310c9db]
263
+ - Updated dependencies [ad0b554]
264
+ - Updated dependencies [7da269f]
265
+ - Updated dependencies [f21325d]
266
+ - Updated dependencies [bb5c5dc]
267
+ - Updated dependencies [04caa20]
268
+ - Updated dependencies [2db1c27]
269
+ - Updated dependencies [875faf7]
270
+ - Updated dependencies [d8ba470]
271
+ - Updated dependencies [29f94d8]
272
+ - Updated dependencies [b07a91e]
273
+ - Updated dependencies [a2e0462]
274
+ - Updated dependencies [7366d0c]
275
+ - Updated dependencies [a8d8efb]
276
+ - Updated dependencies [ed78985]
277
+ - Updated dependencies [a97ba6f]
278
+ - Updated dependencies [fb297fb]
279
+ - Updated dependencies [db2818c]
280
+ - Updated dependencies [f26bf6f]
281
+ - Updated dependencies [7c2e054]
282
+ - Updated dependencies [c527f50]
283
+ - Updated dependencies [9e51015]
284
+ - Updated dependencies [cce05ca]
285
+ - Updated dependencies [7edfaf5]
286
+ - Updated dependencies [fe6006b]
287
+ - Updated dependencies [2010b82]
288
+ - Updated dependencies [40c1ec3]
289
+ - Updated dependencies [0ec7cc1]
290
+ - Updated dependencies [fa14b97]
291
+ - Updated dependencies [5a92802]
292
+ - Updated dependencies [cf170b8]
293
+ - Updated dependencies [7536ff8]
294
+ - Updated dependencies [9842451]
295
+ - Updated dependencies [b1ecc88]
296
+ - Updated dependencies [ae7346d]
297
+ - Updated dependencies [1eee4df]
298
+ - Updated dependencies [cca79cf]
299
+ - Updated dependencies [5cd52f3]
300
+ - Updated dependencies [fc0c5c7]
301
+ - Updated dependencies [747e03e]
302
+ - Updated dependencies [d0588d0]
303
+ - Updated dependencies [383771a]
304
+ - Updated dependencies [8b3f84a]
305
+ - Updated dependencies [4d559ca]
306
+ - Updated dependencies [151a441]
307
+ - Updated dependencies [a14e734]
308
+ - Updated dependencies [f6ea86c]
309
+ - Updated dependencies [80eee37]
310
+ - Updated dependencies [9f70b3c]
311
+ - Updated dependencies [047dcf3]
312
+ - Updated dependencies [3baab83]
313
+ - Updated dependencies [0c1edcb]
314
+ - Updated dependencies [7e05174]
315
+ - Updated dependencies [27818ca]
316
+ - Updated dependencies [2cc1b5a]
317
+ - Updated dependencies [3edffaf]
318
+ - Updated dependencies [812118e]
319
+ - Updated dependencies [7905beb]
320
+ - Updated dependencies [afbc295]
321
+ - Updated dependencies [9a6f7a5]
322
+ - Updated dependencies [7908e8f]
323
+ - Updated dependencies [9de4985]
324
+ - Updated dependencies [72a40c5]
325
+ - Updated dependencies [a66fcf5]
326
+ - Updated dependencies [0a83dfb]
327
+ - Updated dependencies [62d3561]
328
+ - Updated dependencies [9c10943]
329
+ - Updated dependencies [245a3a3]
330
+ - Updated dependencies [e98e95b]
331
+ - Updated dependencies [a14e734]
332
+ - Updated dependencies [d9df523]
333
+ - Updated dependencies [cae90d2]
334
+ - Updated dependencies [0cf2bf4]
335
+ - Updated dependencies [8953d33]
336
+ - Updated dependencies [7e1ecc6]
337
+ - Updated dependencies [8d56051]
338
+ - Updated dependencies [87f162f]
339
+ - Updated dependencies [b3b209e]
340
+ - Updated dependencies [3dff419]
341
+ - Updated dependencies [18fe0a0]
342
+ - Updated dependencies [611573e]
343
+ - Updated dependencies [0d82f92]
344
+ - @weasel-js/prefs@1.9.4
345
+ - @weasel-js/routing@1.9.4
346
+ - @weasel-js/history@1.9.4
347
+ - @weasel-js/text@1.9.4
348
+ - @weasel-js/font@1.9.4
349
+ - @weasel-js/paint@1.9.4
350
+ - @weasel-js/registry@1.9.4
351
+ - @weasel-js/modes@1.9.4
352
+ - @weasel-js/geom@1.9.4
353
+ - @weasel-js/select@1.9.4
354
+ - @weasel-js/gestures@1.9.4
355
+ - @weasel-js/react@1.9.4
356
+ - @weasel-js/cursor@1.9.4
357
+ - @weasel-js/quantity@1.9.4
358
+
3
359
  ## 1.9.3
4
360
 
5
361
  ### Patch Changes
package/README.md CHANGED
@@ -47,10 +47,10 @@ A bare `<SceneCanvas>` only renders. It keeps a selection that nothing sets from
47
47
  | `resize` | resize handles |
48
48
  | `rotate` | the rotation handle |
49
49
  | `transform` | `resize` and `rotate` |
50
- | `edit` | undo/redo, delete, duplicate, group/ungroup, nudge, select-all, Escape, cut/copy/paste, fill and stroke, and their keys |
50
+ | `edit` | undo/redo, delete, duplicate, group/ungroup, nudge, select-all, Escape, cut/copy/paste, fill and stroke, and their keys; Cmd/Ctrl+V pastes nodes a kit canvas copied, and nothing else |
51
51
  | `arrange` | align, distribute, reorder, flip |
52
52
  | `paths` | pathfinder operations, path editing and anchor editing |
53
- | `ingest` | dropped and pasted content |
53
+ | `ingest` | dropped and pasted content: images, SVG, files, and consumer content handlers, on Cmd/Ctrl+V or an OS drop |
54
54
  | `draw` | every preset above |
55
55
 
56
56
  `defaultTools` adds built-in tools on top, and each brings the actions it binds: `defaultTools={['rect']}` brings `insert`.
@@ -1,13 +1,13 @@
1
1
  import { PointerContextProvider as e } from "./features/pointer/PointerContext.js";
2
- import { SelectionContextProvider as t, SelectionContextProviderIfRoot as n } from "./features/selection/SelectionContext.js";
3
- import { PointerProviderIfRoot as r } from "./canvas/SceneCanvas/PointerProviderIfRoot.js";
4
- import { ActionsProviderIfRoot as i } from "./canvas/SceneCanvas/ActionsProviderIfRoot.js";
5
- import { DepRegistryProviderIfRoot as a } from "./canvas/SceneCanvas/DepRegistryProviderIfRoot.js";
2
+ import { PointerProviderIfRoot as t } from "./canvas/SceneCanvas/PointerProviderIfRoot.js";
3
+ import { ActionsProviderIfRoot as n } from "./canvas/SceneCanvas/ActionsProviderIfRoot.js";
4
+ import { DepRegistryProviderIfRoot as r } from "./canvas/SceneCanvas/DepRegistryProviderIfRoot.js";
5
+ import { SelectionContextProvider as i, SelectionContextProviderIfRoot as a } from "./features/selection/SelectionContext.js";
6
6
  import { ActionsProvider as o, ActiveToolContextProvider as s, ActiveToolContextProviderIfRoot as c, DepRegistryProvider as l } from "@weasel-js/routing/react";
7
7
  import { jsx as u } from "react/jsx-runtime";
8
8
  //#region src/WeaselProvider.tsx
9
9
  function d({ children: d, isolate: f = !1 }) {
10
- return /* @__PURE__ */ u(f ? l : a, { children: /* @__PURE__ */ u(f ? o : i, { children: /* @__PURE__ */ u(f ? s : c, { children: /* @__PURE__ */ u(f ? t : n, { children: /* @__PURE__ */ u(f ? e : r, { children: d }) }) }) }) });
10
+ return /* @__PURE__ */ u(f ? l : r, { children: /* @__PURE__ */ u(f ? o : n, { children: /* @__PURE__ */ u(f ? s : c, { children: /* @__PURE__ */ u(f ? i : a, { children: /* @__PURE__ */ u(f ? e : t, { children: d }) }) }) }) });
11
11
  }
12
12
  //#endregion
13
13
  export { d as WeaselProvider };
@@ -60,18 +60,13 @@ function v(e, t, n, a, s) {
60
60
  return;
61
61
  }
62
62
  if (n.shape.kind === "annulus") {
63
- let c = n.shape, { rx: l, ry: u } = i(c, s, r(a)), d = [
64
- o(a, c.cx + l, c.cy),
65
- o(a, c.cx - l, c.cy),
66
- o(a, c.cx, c.cy + u),
67
- o(a, c.cx, c.cy - u)
68
- ], f = Math.min(...d.map((e) => e.x)), p = Math.min(...d.map((e) => e.y)), m = Math.max(...d.map((e) => e.x)), h = Math.max(...d.map((e) => e.y));
63
+ let c = n.shape, { rx: l, ry: u } = i(c, s, r(a)), d = o(a, c.cx, c.cy);
69
64
  e.recordHitbox(t, "handle", {
70
65
  kind: "rect",
71
- x: f,
72
- y: p,
73
- width: m - f,
74
- height: h - p,
66
+ x: d.x - l,
67
+ y: d.y - u,
68
+ width: 2 * l,
69
+ height: 2 * u,
75
70
  ...a.identity ? {} : { rotation: Math.atan2(a.sin, a.cos) }
76
71
  });
77
72
  return;
@@ -197,17 +192,12 @@ function x(e, t, n) {
197
192
  };
198
193
  }
199
194
  if (e.shape.kind === "annulus") {
200
- let a = e.shape, { rx: s, ry: c } = i(a, n, r(t)), l = [
201
- o(t, a.cx + s, a.cy),
202
- o(t, a.cx - s, a.cy),
203
- o(t, a.cx, a.cy + c),
204
- o(t, a.cx, a.cy - c)
205
- ], u = Math.min(...l.map((e) => e.x)), d = Math.min(...l.map((e) => e.y)), f = Math.max(...l.map((e) => e.x)), p = Math.max(...l.map((e) => e.y));
195
+ let a = e.shape, { rx: s, ry: c } = i(a, n, r(t)), l = o(t, a.cx, a.cy), u = Math.hypot(s * t.cos, c * t.sin), d = Math.hypot(s * t.sin, c * t.cos);
206
196
  return {
207
- x: u,
208
- y: d,
209
- width: f - u,
210
- height: p - d
197
+ x: l.x - u,
198
+ y: l.y - d,
199
+ width: 2 * u,
200
+ height: 2 * d
211
201
  };
212
202
  }
213
203
  let a = e.shape, c = o(t, a.x, a.y), l = o(t, a.x + a.width, a.y), u = o(t, a.x, a.y + a.height), d = o(t, a.x + a.width, a.y + a.height), f = Math.min(c.x, l.x, u.x, d.x), p = Math.min(c.y, l.y, u.y, d.y), m = Math.max(c.x, l.x, u.x, d.x), h = Math.max(c.y, l.y, u.y, d.y);