@weasel-js/core 1.9.2 → 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 +393 -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 +146 -92
  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 +11010 -7700
  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 +13 -2
  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 -47
  542. package/dist/tools/prefs.js.map +0 -1
@@ -0,0 +1,84 @@
1
+ //#region src/core/scene/historyFacade.ts
2
+ function e(e) {
3
+ let { engine: t, replay: n, notify: r, refuseUntracked: i, runArrivalEdit: a } = e, o = /* @__PURE__ */ new WeakMap();
4
+ function s(t) {
5
+ let i = {
6
+ targetId: t.targetId,
7
+ forkedAtEntryId: t.forkedAtEntryId,
8
+ applyBatch(n, i) {
9
+ a(n, (n) => {
10
+ e.applying(() => t.applyBatch(n, i)), r();
11
+ });
12
+ },
13
+ undo() {
14
+ n(() => t.undo()), r();
15
+ },
16
+ redo() {
17
+ n(() => t.redo()), r();
18
+ },
19
+ canUndo: () => t.canUndo(),
20
+ canRedo: () => t.canRedo(),
21
+ entries: () => t.entries(),
22
+ seal: () => t.seal(),
23
+ getVersion: () => t.getVersion(),
24
+ subscribe: (e) => t.subscribe(e),
25
+ commit(e) {
26
+ t.commit(e), r();
27
+ },
28
+ cancel() {
29
+ n(() => t.cancel()), r();
30
+ },
31
+ suspend: () => t.suspend(),
32
+ isActive: () => t.isActive()
33
+ };
34
+ return o.set(i, t), i;
35
+ }
36
+ return {
37
+ apply(n, o) {
38
+ i("history.apply"), a([n], (i) => {
39
+ e.applying(() => i.length === 1 ? t.apply(n, o) : t.applyOps(i, o ?? n.label ?? "")), r();
40
+ });
41
+ },
42
+ applyOps(n, o) {
43
+ i("history.applyOps"), a(n, (n) => {
44
+ e.applying(() => t.applyOps(n, o)), r();
45
+ });
46
+ },
47
+ undo: e.undo,
48
+ redo: e.redo,
49
+ goto: e.goto,
50
+ clear() {
51
+ t.clear(), r();
52
+ },
53
+ recordEntry(e, n, i) {
54
+ t.recordEntry(e, n, i), r();
55
+ },
56
+ prune(e) {
57
+ t.prune(e), r();
58
+ },
59
+ switchBranch(e) {
60
+ t.switchBranch(e), r();
61
+ },
62
+ restore: e.restore,
63
+ canUndo: () => t.canUndo(),
64
+ canRedo: () => t.canRedo(),
65
+ undoDepth: () => t.undoDepth(),
66
+ redoDepth: () => t.redoDepth(),
67
+ entries: () => t.entries(),
68
+ timestampAt: (e) => t.timestampAt(e),
69
+ depthAt: (e) => t.depthAt(e),
70
+ branches: () => t.branches(),
71
+ getVersion: () => t.getVersion(),
72
+ subscribe: (e) => t.subscribe(e),
73
+ serialize: () => t.serialize(),
74
+ allForwardOps: () => t.allForwardOps(),
75
+ seal: () => t.seal(),
76
+ currentEntryId: () => t.currentEntryId(),
77
+ beginJournal: (e) => s(t.beginJournal(e)),
78
+ resumeJournal: (e) => t.resumeJournal(o.get(e) ?? e)
79
+ };
80
+ }
81
+ //#endregion
82
+ export { e as createSceneHistory };
83
+
84
+ //# sourceMappingURL=historyFacade.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"historyFacade.js","names":["createSceneHistory","history","env","rawJournals","wrapJournal","wrapped","raw","ops","all","label","notify","replay","listener","refuseUntracked","runArrivalEdit","op","opts","t","id","i","journal"],"sources":["../../../src/core/scene/historyFacade.ts"],"sourcesContent":["/**\n * `scene.history`: the `History` the kit's `undo`/`redo` actions resolve\n * their `history` dep to, and the handle a mode machine opens journals\n * against.\n *\n * Handing out the engine directly would let an action-driven undo replay ops\n * with scene-side recording still armed and without bumping the scene's\n * version, so every member that mutates routes through the scene's own\n * wrappers and one `notify()`; reads forward to the engine.\n */\nimport type { History, Journal } from '@weasel-js/history';\nimport type { Op } from 'core/ops/types';\n\n/** What the facade needs from the scene it fronts. */\nexport interface SceneHistoryEnv {\n /** The scene's own undo/redo engine. */\n engine: History;\n /** Run a step that applies ops through an engine: all-or-nothing, with\n * scene-side recording suppressed. */\n applying(step: () => void): void;\n /** Run a step that replays recorded ops: an undo, a redo, a cancel. */\n replay(step: () => void): void;\n notify(): void;\n refuseUntracked(verb: string): void;\n /** Run an op-driven edit with its arrivals settled into the same batch. */\n runArrivalEdit(ops: Op[], run: (ops: Op[]) => void): void;\n undo: History['undo'];\n redo: History['redo'];\n goto: History['goto'];\n restore: History['restore'];\n}\n\nexport function createSceneHistory(env: SceneHistoryEnv): History {\n const { engine: history, replay, notify, refuseUntracked, runArrivalEdit } = env;\n\n /** Wrapped journal → the engine's own, for `resumeJournal`: the engine keys\n * resumability off journal identity, which a wrapper would not match. */\n const rawJournals = new WeakMap<Journal, Journal>();\n\n /** Same treatment as the façade: a journal drives adapter mutation itself,\n * so its applies/undos re-enter scene mutation methods. */\n function wrapJournal(raw: Journal): Journal {\n const wrapped: Journal = {\n targetId: raw.targetId,\n forkedAtEntryId: raw.forkedAtEntryId,\n applyBatch(ops, label) {\n runArrivalEdit(ops, (all) => {\n env.applying(() => raw.applyBatch(all, label));\n notify();\n });\n },\n undo() {\n replay(() => raw.undo());\n notify();\n },\n redo() {\n replay(() => raw.redo());\n notify();\n },\n canUndo: () => raw.canUndo(),\n canRedo: () => raw.canRedo(),\n entries: () => raw.entries(),\n seal: () => raw.seal(),\n getVersion: () => raw.getVersion(),\n subscribe: (listener) => raw.subscribe(listener),\n commit(label) {\n // Flushes the session's net ops to the parent stack — no apply, but\n // the parent's undo depth changes and listeners read that.\n raw.commit(label);\n notify();\n },\n cancel() {\n replay(() => raw.cancel());\n notify();\n },\n suspend: () => raw.suspend(),\n isActive: () => raw.isActive(),\n };\n rawJournals.set(wrapped, raw);\n return wrapped;\n }\n\n return {\n // Mutating: applied ops re-enter the scene's own mutation methods.\n apply(op, label) {\n refuseUntracked('history.apply');\n runArrivalEdit([op], (all) => {\n const run = () => (all.length === 1\n ? history.apply(op, label)\n : history.applyOps(all, label ?? op.label ?? ''));\n env.applying(run);\n notify();\n });\n },\n applyOps(ops, label) {\n refuseUntracked('history.applyOps');\n runArrivalEdit(ops, (all) => {\n env.applying(() => history.applyOps(all, label));\n notify();\n });\n },\n undo: env.undo,\n redo: env.redo,\n goto: env.goto,\n // Stack-only: nothing applies, but what `canUndo()` answers changes.\n clear() {\n history.clear();\n notify();\n },\n recordEntry(ops, label, opts) {\n history.recordEntry(ops, label, opts);\n notify();\n },\n prune(t) {\n history.prune(t);\n notify();\n },\n switchBranch(id) {\n history.switchBranch(id);\n notify();\n },\n restore: env.restore,\n // Reads forward to the engine, `version` included: it counts history\n // operations, where the scene's counts every mutation.\n canUndo: () => history.canUndo(),\n canRedo: () => history.canRedo(),\n undoDepth: () => history.undoDepth(),\n redoDepth: () => history.redoDepth(),\n entries: () => history.entries(),\n timestampAt: (i) => history.timestampAt(i),\n depthAt: (t) => history.depthAt(t),\n branches: () => history.branches(),\n getVersion: () => history.getVersion(),\n subscribe: (listener) => history.subscribe(listener),\n serialize: () => history.serialize(),\n allForwardOps: () => history.allForwardOps(),\n seal: () => history.seal(),\n currentEntryId: () => history.currentEntryId(),\n beginJournal: (opts) => wrapJournal(history.beginJournal(opts)),\n resumeJournal: (journal) => history.resumeJournal(rawJournals.get(journal) ?? journal),\n };\n}\n"],"mappings":";AAgCA,SAAgBA,EAAmB,GAA+B;CAChE,IAAM,EAAE,QAAQC,GAAS,WAAQ,WAAQ,oBAAiB,sBAAmBC,GAIvEC,oBAAc,IAAI,QAA0B;CAIlD,SAASC,EAAY,GAAuB;EAC1C,IAAMC,IAAmB;GACvB,UAAUC,EAAI;GACd,iBAAiBA,EAAI;GACrB,WAAW,GAAK,GAAO;IACrB,EAAeC,IAAM,MAAQ;KAE3B,AADAL,EAAI,eAAeI,EAAI,WAAWE,GAAKC,CAAK,CAAC,GAC7CC,EAAO;IACT,CAAC;GACH;GACA,OAAO;IAEL,AADAC,QAAaL,EAAI,KAAK,CAAC,GACvBI,EAAO;GACT;GACA,OAAO;IAEL,AADAC,QAAaL,EAAI,KAAK,CAAC,GACvBI,EAAO;GACT;GACA,eAAeJ,EAAI,QAAQ;GAC3B,eAAeA,EAAI,QAAQ;GAC3B,eAAeA,EAAI,QAAQ;GAC3B,YAAYA,EAAI,KAAK;GACrB,kBAAkBA,EAAI,WAAW;GACjC,YAAY,MAAaA,EAAI,UAAUM,CAAQ;GAC/C,OAAO,GAAO;IAIZ,AADAN,EAAI,OAAOG,CAAK,GAChBC,EAAO;GACT;GACA,SAAS;IAEP,AADAC,QAAaL,EAAI,OAAO,CAAC,GACzBI,EAAO;GACT;GACA,eAAeJ,EAAI,QAAQ;GAC3B,gBAAgBA,EAAI,SAAS;EAC/B;EAEA,OADAH,EAAY,IAAIE,GAASC,CAAG,GACrBD;CACT;CAEA,OAAO;EAEL,MAAM,GAAI,GAAO;GAEf,AADAQ,EAAgB,eAAe,GAC/BC,EAAe,CAACC,CAAE,IAAI,MAAQ;IAK5B,AADAb,EAAI,eAHeM,EAAI,WAAW,IAC9BP,EAAQ,MAAMc,GAAIN,CAAK,IACvBR,EAAQ,SAASO,GAAKC,KAASM,EAAG,SAAS,EAAE,CACjC,GAChBL,EAAO;GACT,CAAC;EACH;EACA,SAAS,GAAK,GAAO;GAEnB,AADAG,EAAgB,kBAAkB,GAClCC,EAAeP,IAAM,MAAQ;IAE3B,AADAL,EAAI,eAAeD,EAAQ,SAASO,GAAKC,CAAK,CAAC,GAC/CC,EAAO;GACT,CAAC;EACH;EACA,MAAMR,EAAI;EACV,MAAMA,EAAI;EACV,MAAMA,EAAI;EAEV,QAAQ;GAEN,AADAD,EAAQ,MAAM,GACdS,EAAO;EACT;EACA,YAAY,GAAK,GAAO,GAAM;GAE5B,AADAT,EAAQ,YAAYM,GAAKE,GAAOO,CAAI,GACpCN,EAAO;EACT;EACA,MAAM,GAAG;GAEP,AADAT,EAAQ,MAAMgB,CAAC,GACfP,EAAO;EACT;EACA,aAAa,GAAI;GAEf,AADAT,EAAQ,aAAaiB,CAAE,GACvBR,EAAO;EACT;EACA,SAASR,EAAI;EAGb,eAAeD,EAAQ,QAAQ;EAC/B,eAAeA,EAAQ,QAAQ;EAC/B,iBAAiBA,EAAQ,UAAU;EACnC,iBAAiBA,EAAQ,UAAU;EACnC,eAAeA,EAAQ,QAAQ;EAC/B,cAAc,MAAMA,EAAQ,YAAYkB,CAAC;EACzC,UAAU,MAAMlB,EAAQ,QAAQgB,CAAC;EACjC,gBAAgBhB,EAAQ,SAAS;EACjC,kBAAkBA,EAAQ,WAAW;EACrC,YAAY,MAAaA,EAAQ,UAAUW,CAAQ;EACnD,iBAAiBX,EAAQ,UAAU;EACnC,qBAAqBA,EAAQ,cAAc;EAC3C,YAAYA,EAAQ,KAAK;EACzB,sBAAsBA,EAAQ,eAAe;EAC7C,eAAe,MAASG,EAAYH,EAAQ,aAAae,CAAI,CAAC;EAC9D,gBAAgB,MAAYf,EAAQ,cAAcE,EAAY,IAAIiB,CAAO,KAAKA,CAAO;CACvF;AACF"}
@@ -0,0 +1,173 @@
1
+ import { dwarn as e } from "../../debug/flag.js";
2
+ import { restoreFn as t } from "./nodeFnFields.js";
3
+ //#region src/core/scene/kitOps.ts
4
+ function n(e, t) {
5
+ t === void 0 ? delete e.parallax : e.parallax = t;
6
+ }
7
+ function r(r) {
8
+ let { state: i, requireLayerIndex: a, rebuildLayerIndex: o, invalidateOrder: s, fnFields: c } = r, { dependents: l, childDerived: u, invalidateDependents: d, invalidateChildDerivedFrom: f, trackChildDerived: p, retarget: m, requireNode: h, detach: g, attach: _, addedFns: v, restoreNodeFunctions: y } = r.store;
9
+ function b(e, t) {
10
+ r.register(e, t);
11
+ }
12
+ b("kit:add", {
13
+ apply: (n) => {
14
+ if (i.nodes.has(n.id)) throw Error(`Scene: id collision on "${n.id}"`);
15
+ let r = n.kind === "container" ? {
16
+ kind: "container",
17
+ id: n.id,
18
+ layer: n.layer,
19
+ pose: n.pose,
20
+ data: n.data,
21
+ parent: n.parent,
22
+ children: []
23
+ } : {
24
+ kind: "leaf",
25
+ id: n.id,
26
+ layer: n.layer,
27
+ pose: n.pose,
28
+ data: n.data,
29
+ parent: n.parent
30
+ };
31
+ i.nodes.set(n.id, r), _(n.id, n.parent, n.index), n.dependsOn !== void 0 && (r.dependsOn = n.dependsOn), n.pickable !== void 0 && (r.pickable = n.pickable), l.add(n.id, n.dependsOn === "children" ? [] : n.dependsOn ?? []), p(n.id, n.dependsOn);
32
+ let a = r, o = v.get(n);
33
+ for (let r of c) {
34
+ let i = o?.get(r);
35
+ if (i !== void 0) {
36
+ r.write(a, i);
37
+ continue;
38
+ }
39
+ let s = n[r.spec.opKey];
40
+ s !== void 0 && t(r, a, s, "kit:add", e);
41
+ }
42
+ d(n.id);
43
+ },
44
+ revert: (e) => {
45
+ g(e.id), i.nodes.delete(e.id), l.remove(e.id), u.delete(e.id), f(e.parent), d(e.id);
46
+ }
47
+ }), b("kit:remove", {
48
+ apply: (e) => {
49
+ for (let t of e.detached) g(t.id);
50
+ for (let t of e.nodes) i.nodes.delete(t.id), l.remove(t.id), u.delete(t.id);
51
+ for (let t of e.detached) f(t.parent);
52
+ },
53
+ revert: (e) => {
54
+ let t = new Map((e.fnKeys ?? []).map((e) => [e.id, e]));
55
+ for (let n of e.nodes) {
56
+ let e = n.kind === "container" ? {
57
+ ...n,
58
+ children: [...n.children]
59
+ } : { ...n };
60
+ y(e, t.get(n.id)), i.nodes.set(n.id, e), l.add(n.id, e.dependsOn === "children" ? [] : e.dependsOn ?? []), p(n.id, e.dependsOn);
61
+ }
62
+ for (let t of e.detached) _(t.id, t.parent, t.index);
63
+ for (let t of e.detached) f(t.parent);
64
+ }
65
+ }), b("kit:setPose", {
66
+ apply: (e) => {
67
+ h(e.id).pose = e.to, d(e.id);
68
+ },
69
+ revert: (e) => {
70
+ h(e.id).pose = e.from, d(e.id);
71
+ }
72
+ }), b("kit:setData", {
73
+ apply: (e) => {
74
+ h(e.id).data = e.to, d(e.id);
75
+ },
76
+ revert: (e) => {
77
+ h(e.id).data = e.from, d(e.id);
78
+ }
79
+ }), b("kit:setLayer", {
80
+ apply: (e) => {
81
+ a(e.to), h(e.id).layer = e.to, s(), d(e.id);
82
+ },
83
+ revert: (e) => {
84
+ h(e.id).layer = e.from, s(), d(e.id);
85
+ }
86
+ }), b("kit:setDependsOn", {
87
+ apply: (e) => {
88
+ m(e.id, e.to);
89
+ },
90
+ revert: (e) => {
91
+ m(e.id, e.from);
92
+ }
93
+ });
94
+ let x = /* @__PURE__ */ new WeakMap();
95
+ function S(n, r) {
96
+ let i = h(n.id), a = c.find((e) => e.spec.field === "layout"), o = x.get(n), s = r === "from" ? n.fromKey : n.toKey;
97
+ if (o === void 0 && s !== void 0) {
98
+ delete i.layout, t(a, i, s, "kit:setLayout", e);
99
+ return;
100
+ }
101
+ let l = o?.[r] ?? null;
102
+ l === null ? delete i.layout : a.write(i, l);
103
+ }
104
+ b("kit:setLayout", {
105
+ apply: (e) => {
106
+ S(e, "to");
107
+ },
108
+ revert: (e) => {
109
+ S(e, "from");
110
+ }
111
+ }), b("kit:move", {
112
+ apply: (e) => {
113
+ g(e.id), _(e.id, e.toParent, e.toIndex), f(e.fromParent), d(e.id);
114
+ },
115
+ revert: (e) => {
116
+ g(e.id), _(e.id, e.fromParent, e.fromIndex), f(e.toParent), d(e.id);
117
+ }
118
+ }), b("kit:setLayerVisible", {
119
+ apply: (e) => {
120
+ i.layers[a(e.layer)].visible = e.to;
121
+ },
122
+ revert: (e) => {
123
+ i.layers[a(e.layer)].visible = e.from;
124
+ }
125
+ }), b("kit:setLayerLocked", {
126
+ apply: (e) => {
127
+ i.layers[a(e.layer)].locked = e.to, e.to && r.narrowSelection();
128
+ },
129
+ revert: (e) => {
130
+ i.layers[a(e.layer)].locked = e.from;
131
+ }
132
+ }), b("kit:setLayerParallax", {
133
+ apply: (e) => {
134
+ n(i.layers[a(e.layer)], e.to);
135
+ },
136
+ revert: (e) => {
137
+ n(i.layers[a(e.layer)], e.from);
138
+ }
139
+ }), b("kit:addLayer", {
140
+ apply: (e) => {
141
+ i.layers.splice(e.index, 0, { ...e.record }), o();
142
+ },
143
+ revert: (e) => {
144
+ i.layers.splice(e.index, 1), o();
145
+ }
146
+ }), b("kit:removeLayer", {
147
+ apply: (e) => {
148
+ i.layers.splice(e.index, 1), o();
149
+ },
150
+ revert: (e) => {
151
+ i.layers.splice(e.index, 0, { ...e.record }), o();
152
+ }
153
+ }), b("kit:renameLayer", {
154
+ apply: (e) => {
155
+ i.layers[a(e.layer)].name = e.to;
156
+ },
157
+ revert: (e) => {
158
+ i.layers[a(e.layer)].name = e.from;
159
+ }
160
+ });
161
+ function C(e, t) {
162
+ let [n] = i.layers.splice(a(e), 1);
163
+ i.layers.splice(t, 0, n), o();
164
+ }
165
+ return b("kit:moveLayer", {
166
+ apply: (e) => C(e.layer, e.to),
167
+ revert: (e) => C(e.layer, e.from)
168
+ }), { layoutRefs: x };
169
+ }
170
+ //#endregion
171
+ export { r as registerKitOps };
172
+
173
+ //# sourceMappingURL=kitOps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kitOps.js","names":["setParallax","to","record","registerKitOps","env","registerKitOp","kind","handler","state","p","node","attach","dependents","trackChildDerived","target","attached","addedFns","field","fnFields","fn","key","restoreFn","dwarn","detach","childDerived","invalidateChildDerivedFrom","invalidateDependents","d","n","keys","k","clone","restoreNodeFunctions","requireLayerIndex","invalidateOrder","layoutRefs","placeLayout","requireNode","f","live","side","layout","rebuildLayerIndex","placeLayer","rec","layer","index"],"sources":["../../../src/core/scene/kitOps.ts"],"sourcesContent":["/**\n * The kit's own op kinds: `kit:add`, `kit:remove`, `kit:setPose`, and the\n * rest. They are registered like any other op; the scene's mutation methods\n * build serializable payloads and route through the same log and replay\n * machinery a consumer's ops do.\n */\nimport type { ParallaxOpts } from 'core/viewport/parallax';\nimport { dwarn } from 'debug/flag';\nimport type { LayoutStrategy } from '../../layout/types';\nimport { restoreFn, type FnBearingNode, type NodeFnField } from './nodeFnFields';\nimport type { createNodeStore } from './nodeStore';\nimport type {\n ContainerNode, LayerRecord, LeafNode, Node, NodeId, RegisteredOp, UserLayerRecord,\n} from './types';\n\n/**\n * Snapshot of every node a removal deletes, so revert restores it exactly.\n *\n * A cascade unlinks several disjoint subtrees, not one, so `detached` names\n * each node the tree has to re-link; everything else in `nodes` has a parent\n * that is also going and rides back in that parent's cloned `children`.\n * Entries are sorted by ascending sibling index — globally rather than\n * per-parent, because only same-parent order constrains re-insertion and\n * entries under different parents never interact.\n */\nexport interface RemoveSnapshot<TData, TLayer extends string, TPose> {\n nodes: Node<TData, TLayer, TPose>[];\n detached: { id: NodeId; parent: NodeId | null; index: number }[];\n /** Registry keys for the function-valued fields on `nodes`. The functions\n * themselves do not survive a persisted history, so revert re-resolves\n * them the way `kit:add` does. */\n fnKeys?: ({ id: NodeId } & Record<string, string | undefined>)[];\n}\n\n/** What a `kit:setLayout` op carries: the container, and each side's layout\n * by registry key when it has one. */\nexport interface SetLayoutPayload { id: NodeId; fromKey?: string; toKey?: string }\n\n/** What the op table needs from the scene it mutates. */\nexport interface KitOpsEnv<TData, TLayer extends string, TPose> {\n state: {\n nodes: Map<NodeId, Node<TData, TLayer, TPose>>;\n layers: LayerRecord<TLayer>[];\n };\n register(kind: string, handler: RegisteredOp<unknown>): void;\n store: ReturnType<typeof createNodeStore<TData, TLayer, TPose>>;\n requireLayerIndex(layer: TLayer): number;\n rebuildLayerIndex(): void;\n invalidateOrder(): void;\n fnFields: readonly NodeFnField[];\n /** Drop from the selection whatever a layer lock just put out of reach. */\n narrowSelection(): void;\n}\n\nfunction setParallax(record: { parallax?: ParallaxOpts }, to: ParallaxOpts | undefined): void {\n if (to === undefined) delete record.parallax;\n else record.parallax = to;\n}\n\n/** Register every kit op kind. Answers with the table a live `kit:setLayout`\n * payload's strategies are kept in, which `scene.setLayout` fills. */\nexport function registerKitOps<TData, TLayer extends string, TPose>(\n env: KitOpsEnv<TData, TLayer, TPose>,\n): {\n layoutRefs: WeakMap<object, { from: LayoutStrategy<TPose> | null; to: LayoutStrategy<TPose> | null }>;\n} {\n const { state, requireLayerIndex, rebuildLayerIndex, invalidateOrder, fnFields } = env;\n const {\n dependents, childDerived, invalidateDependents, invalidateChildDerivedFrom, trackChildDerived, retarget,\n requireNode, detach, attach, addedFns, restoreNodeFunctions,\n } = env.store;\n\n function registerKitOp<P>(kind: string, handler: RegisteredOp<P>): void {\n env.register(kind, handler as RegisteredOp<unknown>);\n }\n\n registerKitOp<{\n id: NodeId; kind: 'leaf' | 'container'; layer: TLayer; pose: TPose; data: TData;\n parent: NodeId | null; index: number;\n dependsOn?: readonly NodeId[] | 'children';\n pickable?: boolean;\n } & Record<string, unknown>>('kit:add', {\n apply: (p) => {\n if (state.nodes.has(p.id)) {\n throw new Error(`Scene: id collision on \"${p.id}\"`);\n }\n const node: Node<TData, TLayer, TPose> = p.kind === 'container'\n ? ({ kind: 'container', id: p.id, layer: p.layer, pose: p.pose, data: p.data, parent: p.parent, children: [] } as ContainerNode<TData, TLayer, TPose>)\n : ({ kind: 'leaf', id: p.id, layer: p.layer, pose: p.pose, data: p.data, parent: p.parent } as LeafNode<TData, TLayer, TPose>);\n state.nodes.set(p.id, node);\n attach(p.id, p.parent, p.index);\n if (p.dependsOn !== undefined) node.dependsOn = p.dependsOn;\n if (p.pickable !== undefined) node.pickable = p.pickable;\n dependents.add(p.id, p.dependsOn === 'children' ? [] : p.dependsOn ?? []);\n trackChildDerived(p.id, p.dependsOn);\n // Two ways a function field comes back. Redo of a live add reads\n // what `scene.add` attached, since it replays the op without a spec.\n // A restored history has only the registry key the payload carries.\n const target = node as unknown as FnBearingNode;\n const attached = addedFns.get(p);\n for (const field of fnFields) {\n const fn = attached?.get(field);\n if (fn !== undefined) {\n field.write(target, fn);\n continue;\n }\n const key = p[field.spec.opKey] as string | undefined;\n if (key !== undefined) restoreFn(field, target, key, 'kit:add', dwarn);\n }\n invalidateDependents(p.id);\n },\n revert: (p) => {\n detach(p.id);\n state.nodes.delete(p.id);\n dependents.remove(p.id);\n childDerived.delete(p.id);\n invalidateChildDerivedFrom(p.parent);\n invalidateDependents(p.id);\n },\n });\n\n // Neither half invalidates dependents: removal's closure takes every live one.\n registerKitOp<RemoveSnapshot<TData, TLayer, TPose>>('kit:remove', {\n apply: (p) => {\n for (const d of p.detached) detach(d.id);\n for (const n of p.nodes) {\n state.nodes.delete(n.id);\n dependents.remove(n.id);\n childDerived.delete(n.id);\n }\n // A `dependsOn: 'children'` container the removal did not take is now\n // hugging a smaller set.\n for (const d of p.detached) invalidateChildDerivedFrom(d.parent);\n },\n revert: (p) => {\n const keys = new Map((p.fnKeys ?? []).map((k) => [k.id, k]));\n // Order-independent: every clone carries its own parent and children.\n for (const n of p.nodes) {\n const clone: Node<TData, TLayer, TPose> = n.kind === 'container'\n ? { ...n, children: [...n.children] }\n : { ...n };\n restoreNodeFunctions(clone, keys.get(n.id));\n state.nodes.set(n.id, clone);\n dependents.add(n.id, clone.dependsOn === 'children' ? [] : clone.dependsOn ?? []);\n trackChildDerived(n.id, clone.dependsOn);\n }\n for (const d of p.detached) attach(d.id, d.parent, d.index);\n for (const d of p.detached) invalidateChildDerivedFrom(d.parent);\n },\n });\n\n registerKitOp<{ id: NodeId; from: TPose; to: TPose }>('kit:setPose', {\n apply: (p) => {\n (requireNode(p.id) as { pose: TPose }).pose = p.to;\n invalidateDependents(p.id);\n },\n revert: (p) => {\n (requireNode(p.id) as { pose: TPose }).pose = p.from;\n invalidateDependents(p.id);\n },\n });\n\n // A derivation is handed its dependencies' nodes, not only their poses, so a\n // connector reading `data` or `layer` goes stale on these two exactly as it\n // does on a move.\n registerKitOp<{ id: NodeId; from: TData; to: TData }>('kit:setData', {\n apply: (p) => {\n (requireNode(p.id) as { data: TData }).data = p.to;\n invalidateDependents(p.id);\n },\n revert: (p) => {\n (requireNode(p.id) as { data: TData }).data = p.from;\n invalidateDependents(p.id);\n },\n });\n\n registerKitOp<{ id: NodeId; from: TLayer; to: TLayer }>('kit:setLayer', {\n apply: (p) => {\n requireLayerIndex(p.to);\n (requireNode(p.id) as { layer: TLayer }).layer = p.to;\n invalidateOrder();\n invalidateDependents(p.id);\n },\n revert: (p) => {\n (requireNode(p.id) as { layer: TLayer }).layer = p.from;\n invalidateOrder();\n invalidateDependents(p.id);\n },\n });\n\n registerKitOp<{\n id: NodeId;\n from: Node<TData, TLayer, TPose>['dependsOn'];\n to: Node<TData, TLayer, TPose>['dependsOn'];\n }>('kit:setDependsOn', {\n apply: (p) => { retarget(p.id, p.to); },\n revert: (p) => { retarget(p.id, p.from); },\n });\n\n /** The strategies each live `kit:setLayout` payload swaps between. A payload\n * replayed from a persisted history has none, and resolves its keys. */\n const layoutRefs = new WeakMap<object, { from: LayoutStrategy<TPose> | null; to: LayoutStrategy<TPose> | null }>();\n function placeLayout(p: SetLayoutPayload, side: 'from' | 'to'): void {\n const node = requireNode(p.id) as unknown as FnBearingNode;\n const field = fnFields.find((f) => f.spec.field === 'layout')!;\n const live = layoutRefs.get(p);\n const key = side === 'from' ? p.fromKey : p.toKey;\n if (live === undefined && key !== undefined) {\n delete node.layout;\n restoreFn(field, node, key, 'kit:setLayout', dwarn);\n return;\n }\n const layout = live?.[side] ?? null;\n if (layout === null) delete node.layout;\n else field.write(node, layout);\n }\n registerKitOp<SetLayoutPayload>('kit:setLayout', {\n apply: (p) => { placeLayout(p, 'to'); },\n revert: (p) => { placeLayout(p, 'from'); },\n });\n\n registerKitOp<{\n id: NodeId; fromParent: NodeId | null; fromIndex: number;\n toParent: NodeId | null; toIndex: number;\n }>('kit:move', {\n apply: (p) => {\n detach(p.id);\n attach(p.id, p.toParent, p.toIndex);\n // Both ends: the container it left no longer hugs it, and the one it\n // joined now does.\n invalidateChildDerivedFrom(p.fromParent);\n invalidateDependents(p.id);\n },\n revert: (p) => {\n detach(p.id);\n attach(p.id, p.fromParent, p.fromIndex);\n invalidateChildDerivedFrom(p.toParent);\n invalidateDependents(p.id);\n },\n });\n\n registerKitOp<{ layer: TLayer; from: boolean; to: boolean }>('kit:setLayerVisible', {\n apply: (p) => { state.layers[requireLayerIndex(p.layer)].visible = p.to; },\n revert: (p) => { state.layers[requireLayerIndex(p.layer)].visible = p.from; },\n });\n\n registerKitOp<{ layer: TLayer; from: boolean; to: boolean }>('kit:setLayerLocked', {\n apply: (p) => {\n state.layers[requireLayerIndex(p.layer)].locked = p.to;\n if (p.to) env.narrowSelection();\n },\n revert: (p) => { state.layers[requireLayerIndex(p.layer)].locked = p.from; },\n });\n\n registerKitOp<{ layer: TLayer; from?: ParallaxOpts; to?: ParallaxOpts }>('kit:setLayerParallax', {\n apply: (p) => { setParallax(state.layers[requireLayerIndex(p.layer)], p.to); },\n revert: (p) => { setParallax(state.layers[requireLayerIndex(p.layer)], p.from); },\n });\n\n registerKitOp<{ record: UserLayerRecord<TLayer>; index: number }>('kit:addLayer', {\n apply: (p) => {\n state.layers.splice(p.index, 0, { ...p.record });\n rebuildLayerIndex();\n },\n revert: (p) => {\n state.layers.splice(p.index, 1);\n rebuildLayerIndex();\n },\n });\n\n registerKitOp<{ record: LayerRecord<TLayer>; index: number }>('kit:removeLayer', {\n apply: (p) => {\n state.layers.splice(p.index, 1);\n rebuildLayerIndex();\n },\n revert: (p) => {\n state.layers.splice(p.index, 0, { ...p.record } as LayerRecord<TLayer>);\n rebuildLayerIndex();\n },\n });\n\n registerKitOp<{ layer: TLayer; from: string; to: string }>('kit:renameLayer', {\n apply: (p) => {\n (state.layers[requireLayerIndex(p.layer)] as UserLayerRecord<TLayer>).name = p.to;\n },\n revert: (p) => {\n (state.layers[requireLayerIndex(p.layer)] as UserLayerRecord<TLayer>).name = p.from;\n },\n });\n\n /** Finds the layer by id, not by where the payload last left it: moves of\n * one layer coalesce, and the entry then reverts the first payload from\n * wherever the last one put the layer. */\n function placeLayer(layer: TLayer, index: number): void {\n const [rec] = state.layers.splice(requireLayerIndex(layer), 1);\n state.layers.splice(index, 0, rec);\n rebuildLayerIndex();\n }\n\n registerKitOp<{ layer: TLayer; from: number; to: number }>('kit:moveLayer', {\n apply: (p) => placeLayer(p.layer, p.to),\n revert: (p) => placeLayer(p.layer, p.from),\n });\n\n return { layoutRefs };\n}\n"],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ //#region src/core/scene/layerStack.ts
2
+ function e(e, t, n) {
3
+ for (let t = 0; t < n.length; t++) {
4
+ let r = n[t];
5
+ if (e.layerIndex.has(r.id)) throw Error(`useScene: duplicate system layer id "${r.id}"`);
6
+ e.layers.push({
7
+ kind: "system",
8
+ id: r.id,
9
+ visible: r.visible ?? !0,
10
+ locked: r.locked ?? !1,
11
+ ...r.parallax ? { parallax: r.parallax } : {}
12
+ }), e.layerIndex.set(r.id, t);
13
+ }
14
+ function r(t) {
15
+ let n = e.layerIndex.get(t);
16
+ if (n === void 0) throw Error(`Scene: unknown layer "${t}"`);
17
+ return n;
18
+ }
19
+ function i() {
20
+ t(), e.layerIndex.clear();
21
+ for (let t = 0; t < e.layers.length; t++) e.layerIndex.set(e.layers[t].id, t);
22
+ }
23
+ function a(e, t, n, i, a = "place") {
24
+ if (r(t) < r(i)) {
25
+ let r = a === "place" ? `cannot place node '${e ?? "<new>"}' on layer '${t}' under parent '${n}' on layer '${i}'` : `cannot setLayer('${e ?? "<new>"}', '${t}') — node has parent '${n}' on layer '${i}'`;
26
+ throw Error(`Scene: ${r} — a child may not render below its parent's layer`);
27
+ }
28
+ }
29
+ return {
30
+ requireLayerIndex: r,
31
+ rebuildLayerIndex: i,
32
+ assertSubtreeLayer: a
33
+ };
34
+ }
35
+ //#endregion
36
+ export { e as createLayerStack };
37
+
38
+ //# sourceMappingURL=layerStack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layerStack.js","names":["createLayerStack","i","systemLayers","spec","state","requireLayerIndex","layer","rebuildLayerIndex","invalidateOrder","assertSubtreeLayer","nodeLayer","parentLayer","action","verb","nodeId","parentId"],"sources":["../../../src/core/scene/layerStack.ts"],"sourcesContent":["/**\n * The scene's layer stack: the ordered layer records, the index from a\n * layer's id to its place in that order, and the rule tying a node's layer to\n * its parent's.\n */\nimport type { LayerRecord, NodeId, UseSceneOptions } from './types';\n\n/** What the stack keeps of a scene's state. */\nexport interface LayerStackState<TLayer extends string> {\n layers: LayerRecord<TLayer>[];\n layerIndex: Map<TLayer, number>;\n}\n\n/** Fill `state` with the declared system layers, and answer with the reads\n * and the rebuild the rest of the scene uses. Throws on a duplicate id. */\nexport function createLayerStack<TLayer extends string>(\n state: LayerStackState<TLayer>,\n /** Called whenever the layer order changes. */\n invalidateOrder: () => void,\n systemLayers: UseSceneOptions<unknown, TLayer, unknown>['systemLayers'],\n) {\n for (let i = 0; i < systemLayers.length; i++) {\n const spec = systemLayers[i];\n if (state.layerIndex.has(spec.id)) {\n throw new Error(`useScene: duplicate system layer id \"${spec.id}\"`);\n }\n state.layers.push({\n kind: 'system',\n id: spec.id,\n visible: spec.visible ?? true,\n locked: spec.locked ?? false,\n ...(spec.parallax ? { parallax: spec.parallax } : {}),\n });\n state.layerIndex.set(spec.id, i);\n }\n\n function requireLayerIndex(layer: TLayer): number {\n const i = state.layerIndex.get(layer);\n if (i === undefined) throw new Error(`Scene: unknown layer \"${layer}\"`);\n return i;\n }\n\n /** Repopulate `state.layerIndex` from the current `state.layers` order.\n * Call after any op that inserts, removes, or reorders a layer record. */\n function rebuildLayerIndex(): void {\n invalidateOrder();\n state.layerIndex.clear();\n for (let i = 0; i < state.layers.length; i++) {\n state.layerIndex.set(state.layers[i].id, i);\n }\n }\n\n /**\n * A child may sit on its parent's layer **or any layer above it** (higher\n * render index) — never below. `renderOrder()` is layer-major\n * (each node yielded in its OWN layer's pass) and `buildSceneTree` buckets by\n * each node's own layer, so a child on a higher layer than its parent renders\n * correctly on top while staying a tree child for hit-testing / move /\n * reparent (e.g. plantings tagged into a top `plantings` layer while remaining\n * children of their container). Rejecting only the *below* case keeps the\n * sensible invariant that a child never paints under its own parent.\n */\n function assertSubtreeLayer(\n nodeId: string | undefined,\n nodeLayer: TLayer,\n parentId: NodeId,\n parentLayer: TLayer,\n verb: 'place' | 'relayer' = 'place',\n ): void {\n if (requireLayerIndex(nodeLayer) < requireLayerIndex(parentLayer)) {\n const action = verb === 'place'\n ? `cannot place node '${nodeId ?? '<new>'}' on layer '${nodeLayer}' under parent '${parentId}' on layer '${parentLayer}'`\n : `cannot setLayer('${nodeId ?? '<new>'}', '${nodeLayer}') — node has parent '${parentId}' on layer '${parentLayer}'`;\n throw new Error(`Scene: ${action} — a child may not render below its parent's layer`);\n }\n }\n\n return { requireLayerIndex, rebuildLayerIndex, assertSubtreeLayer };\n}\n"],"mappings":";AAeA,SAAgBA,EACd,GAEA,GACA,GACA;CACA,KAAK,IAAIC,IAAI,GAAGA,IAAIC,EAAa,QAAQ,KAAK;EAC5C,IAAMC,IAAOD,EAAaD;EAC1B,IAAIG,EAAM,WAAW,IAAID,EAAK,EAAE,GAC9B,MAAU,MAAM,wCAAwCA,EAAK,GAAG,EAAE;EASpE,AAPAC,EAAM,OAAO,KAAK;GAChB,MAAM;GACN,IAAID,EAAK;GACT,SAASA,EAAK,WAAW;GACzB,QAAQA,EAAK,UAAU;GACvB,GAAIA,EAAK,WAAW,EAAE,UAAUA,EAAK,SAAS,IAAI,CAAC;EACrD,CAAC,GACDC,EAAM,WAAW,IAAID,EAAK,IAAIF,CAAC;CACjC;CAEA,SAASI,EAAkB,GAAuB;EAChD,IAAMJ,IAAIG,EAAM,WAAW,IAAIE,CAAK;EACpC,IAAIL,MAAM,KAAA,GAAW,MAAU,MAAM,yBAAyBK,EAAM,EAAE;EACtE,OAAOL;CACT;CAIA,SAASM,IAA0B;EAEjC,AADAC,EAAgB,GAChBJ,EAAM,WAAW,MAAM;EACvB,KAAK,IAAIH,IAAI,GAAGA,IAAIG,EAAM,OAAO,QAAQ,KACvC,EAAM,WAAW,IAAIA,EAAM,OAAOH,EAAE,CAAC,IAAIA,CAAC;CAE9C;CAYA,SAASQ,EACP,GACA,GACA,GACA,GACA,IAA4B,SACtB;EACN,IAAIJ,EAAkBK,CAAS,IAAIL,EAAkBM,CAAW,GAAG;GACjE,IAAMC,IAASC,MAAS,UACpB,sBAAsBC,KAAU,QAAQ,cAAcJ,EAAU,kBAAkBK,EAAS,cAAcJ,EAAY,KACrH,oBAAoBG,KAAU,QAAQ,MAAMJ,EAAU,wBAAwBK,EAAS,cAAcJ,EAAY;GACrH,MAAU,MAAM,UAAUC,EAAO,mDAAmD;EACtF;CACF;CAEA,OAAO;EAAE;EAAmB;EAAmB;CAAmB;AACpE"}
@@ -0,0 +1,37 @@
1
+ //#region src/core/scene/lockGuard.ts
2
+ function e(e) {
3
+ let t = 0;
4
+ function n(t) {
5
+ if (!e.layers.some((e) => e.locked)) return !1;
6
+ let n = e.nodes.get(t);
7
+ for (; n !== void 0;) {
8
+ if (e.layers[e.layerIndex.get(n.layer) ?? -1]?.locked) return !0;
9
+ n = n.parent === null ? void 0 : e.nodes.get(n.parent);
10
+ }
11
+ return !1;
12
+ }
13
+ return {
14
+ isLocked: n,
15
+ refuse(e, r) {
16
+ if (!(t > 0 || !n(e))) throw Error(`Scene: cannot ${r} "${e}" — it is on a locked layer`);
17
+ },
18
+ refuseLayer(n, r) {
19
+ if (!(t > 0 || !e.layers[e.layerIndex.get(n) ?? -1]?.locked)) throw Error(`Scene: cannot ${r} layer "${n}" — it is locked`);
20
+ },
21
+ reachable(t) {
22
+ return e.layers.some((e) => e.locked) ? t.filter((e) => !n(e)) : t;
23
+ },
24
+ lifted(e) {
25
+ t++;
26
+ try {
27
+ return e();
28
+ } finally {
29
+ t--;
30
+ }
31
+ }
32
+ };
33
+ }
34
+ //#endregion
35
+ export { e as createLockGuard };
36
+
37
+ //# sourceMappingURL=lockGuard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lockGuard.js","names":["createLockGuard","bypass","isLocked","state","l","cur","id","verb","layer","ids","fn"],"sources":["../../../src/core/scene/lockGuard.ts"],"sourcesContent":["/**\n * The scene's lock guard: what a locked layer refuses, and how a caller that\n * is restoring an already-legal state gets past it.\n */\nimport type { Node, NodeId } from './types';\n\n/** What the guard reads of a scene's state. */\nexport interface LockGuardState<TData, TLayer extends string, TPose> {\n nodes: Map<NodeId, Node<TData, TLayer, TPose>>;\n layers: { locked: boolean }[];\n layerIndex: Map<TLayer, number>;\n}\n\nexport interface LockGuard<TLayer extends string> {\n /** Whether `id` sits on a locked layer, or inside a node that does. */\n isLocked(id: NodeId): boolean;\n /** Throw when `id` is locked and the guard is not lifted. */\n refuse(id: NodeId, verb: string): void;\n /** Throw when `layer` is locked and the guard is not lifted. */\n refuseLayer(layer: TLayer, verb: string): void;\n /** `ids` less the locked ones: the scene's selection never holds a locked\n * node, whoever writes it. */\n reachable(ids: readonly NodeId[]): readonly NodeId[];\n /** Run `fn` with the guard lifted: `unlocked()`, `removeLayer`, and every\n * replay — undo, redo, a journal stepping, a rollback — which restores a\n * state that was legal when it was recorded. */\n lifted<T>(fn: () => T): T;\n}\n\nexport function createLockGuard<TData, TLayer extends string, TPose>(\n state: LockGuardState<TData, TLayer, TPose>,\n): LockGuard<TLayer> {\n let bypass = 0;\n\n function isLocked(id: NodeId): boolean {\n if (!state.layers.some((l) => l.locked)) return false;\n let cur = state.nodes.get(id);\n while (cur !== undefined) {\n if (state.layers[state.layerIndex.get(cur.layer) ?? -1]?.locked) return true;\n cur = cur.parent === null ? undefined : state.nodes.get(cur.parent);\n }\n return false;\n }\n\n return {\n isLocked,\n refuse(id, verb) {\n if (bypass > 0 || !isLocked(id)) return;\n throw new Error(`Scene: cannot ${verb} \"${id}\" — it is on a locked layer`);\n },\n refuseLayer(layer, verb) {\n if (bypass > 0 || !state.layers[state.layerIndex.get(layer) ?? -1]?.locked) return;\n throw new Error(`Scene: cannot ${verb} layer \"${layer}\" — it is locked`);\n },\n reachable(ids) {\n if (!state.layers.some((l) => l.locked)) return ids;\n return ids.filter((id) => !isLocked(id));\n },\n lifted(fn) {\n bypass++;\n try {\n return fn();\n } finally {\n bypass--;\n }\n },\n };\n}\n"],"mappings":";AA6BA,SAAgBA,EACd,GACmB;CACnB,IAAIC,IAAS;CAEb,SAASC,EAAS,GAAqB;EACrC,IAAI,CAACC,EAAM,OAAO,MAAM,MAAMC,EAAE,MAAM,GAAG,OAAO;EAChD,IAAIC,IAAMF,EAAM,MAAM,IAAIG,CAAE;EAC5B,OAAOD,MAAQ,KAAA,IAAW;GACxB,IAAIF,EAAM,OAAOA,EAAM,WAAW,IAAIE,EAAI,KAAK,KAAK,GAAG,EAAE,QAAQ,OAAO;GACxE,IAAMA,EAAI,WAAW,OAAO,KAAA,IAAYF,EAAM,MAAM,IAAIE,EAAI,MAAM;EACpE;EACA,OAAO;CACT;CAEA,OAAO;EACL;EACA,OAAO,GAAI,GAAM;GACf,IAAI,MAAS,KAAK,CAACH,EAASI,CAAE,IAC9B,MAAU,MAAM,iBAAiBC,EAAK,IAAID,EAAG,4BAA4B;EAC3E;EACA,YAAY,GAAO,GAAM;GACvB,IAAI,MAAS,KAAK,CAACH,EAAM,OAAOA,EAAM,WAAW,IAAIK,CAAK,KAAK,GAAG,EAAE,SACpE,MAAU,MAAM,iBAAiBD,EAAK,UAAUC,EAAM,iBAAiB;EACzE;EACA,UAAU,GAAK;GAEb,OADKL,EAAM,OAAO,MAAM,MAAMC,EAAE,MAAM,IAC/BK,EAAI,QAAQ,MAAO,CAACP,EAASI,CAAE,CAAC,IADSG;EAElD;EACA,OAAO,GAAI;GACT;GACA,IAAI;IACF,OAAOC,EAAG;GACZ,UAAU;IACR;GACF;EACF;CACF;AACF"}
@@ -33,16 +33,15 @@ function t(t) {
33
33
  return e.map((e) => {
34
34
  let n = t[e.field], r = /* @__PURE__ */ new Map();
35
35
  if (n) for (let [e, t] of Object.entries(n)) r.set(t, e);
36
- let i = /* @__PURE__ */ new Map(), a = (t) => !e.containersOnly || t.kind === "container";
36
+ let i = (t) => !e.containersOnly || t.kind === "container";
37
37
  return {
38
38
  spec: e,
39
39
  keyOf: (e) => r.get(e),
40
40
  fnOf: (e) => n?.[e],
41
- read: (t) => a(t) ? t[e.field] : void 0,
41
+ read: (t) => i(t) ? t[e.field] : void 0,
42
42
  write: (t, n) => {
43
- a(t) && (t[e.field] = n);
44
- },
45
- pending: i
43
+ i(t) && (t[e.field] = n);
44
+ }
46
45
  };
47
46
  });
48
47
  }
@@ -52,7 +51,7 @@ function n(e, t, n, r, i) {
52
51
  i("scene", `${r}: ${e.spec.opKey} "${n}" not in this scene's registry — node "${t.id}" restored without its ${e.spec.lost}. Register a function with this key in the registry option to restore it.`);
53
52
  return;
54
53
  }
55
- e.write(t, a), e.pending.set(t.id, a);
54
+ e.write(t, a);
56
55
  }
57
56
  function r(e, t, n, r) {
58
57
  let i = {};
@@ -1 +1 @@
1
- {"version":3,"file":"nodeFnFields.js","names":["NODE_FN_FIELDS","createNodeFnFields","table","registry","spec","reverse","key","fn","pending","applies","node","restoreFn","field","site","fnKeysOf","out","fields","which","fnFieldsOfNode","src"],"sources":["../../../src/core/scene/nodeFnFields.ts"],"sourcesContent":["/**\n * The registry-keyed function fields a node can carry, as a table.\n *\n * `clipFromPose`, `derivePath` and `derivePose` are live functions on a node,\n * and a function travels through neither a serializable op payload nor a JSON\n * snapshot. Each one therefore needs the same six things: a function→key\n * reverse map, a side-channel cache the `kit:add` redo path re-attaches from,\n * a name in the op payload, a name in the snapshot, a registry lookup on\n * restore, and a prune when history evicts the entry that could reach it.\n *\n * The first two fields were written out twice, and `types.ts` said in a\n * comment that a third should be the point it stopped being copied per field.\n * This is that point.\n */\nimport type { NodeId, SceneRegistry } from './types';\n\n/** A node's function field, opaque here: the table moves these around by\n * reference and never calls one. Each field's real signature lives on `Node`.\n * A container's `layout` is an object rather than a function, and rides the\n * same table for the same reason — it travels by reference, not as data. */\nexport type NodeFn = ((...args: never[]) => unknown) | object;\n\n/** A node as this module reads it — a bag with the three fields on it. The\n * full `Node` type would make the table generic in `TData`/`TLayer` for no\n * gain; nothing here looks at either. */\nexport type FnBearingNode = { id: NodeId; kind: 'leaf' | 'container' } & {\n [K in NodeFnFieldName]?: NodeFn;\n};\n\nexport type NodeFnFieldName = 'clipFromPose' | 'derivePath' | 'derivePose' | 'layout';\n\nexport interface NodeFnFieldSpec {\n /** The field's name on `Node` — and, deliberately, its name in\n * `SceneRegistry` too, so one string indexes both. */\n readonly field: NodeFnFieldName;\n /** Its key in the `kit:add` and `kit:remove` op payloads. `clipFromPose`'s\n * is the short one because a persisted history already carries that name. */\n readonly opKey: string;\n /** Its key in a `SerializedNode`. */\n readonly jsonKey: string;\n /** True for a field only a `ContainerNode` can carry. */\n readonly containersOnly: boolean;\n /** What a node loses when its key is not in the registry, for the warning. */\n readonly lost: string;\n}\n\nexport const NODE_FN_FIELDS: readonly NodeFnFieldSpec[] = [\n {\n field: 'clipFromPose',\n opKey: 'clipKey',\n jsonKey: 'clipFromPoseKey',\n containersOnly: true,\n lost: 'clip',\n },\n {\n field: 'derivePath',\n opKey: 'derivePathKey',\n jsonKey: 'derivePathKey',\n containersOnly: false,\n lost: 'derived path',\n },\n {\n field: 'derivePose',\n opKey: 'derivePoseKey',\n jsonKey: 'derivePoseKey',\n containersOnly: false,\n lost: 'derived pose',\n },\n {\n field: 'layout',\n opKey: 'layoutKey',\n jsonKey: 'layoutKey',\n containersOnly: true,\n lost: 'layout',\n },\n];\n\n/** One field's per-scene state. */\nexport interface NodeFnField {\n readonly spec: NodeFnFieldSpec;\n /** `fn → registry key`, for writing a payload or a snapshot. */\n keyOf(fn: NodeFn): string | undefined;\n /** `registry key → fn`, for restoring one. */\n fnOf(key: string): NodeFn | undefined;\n /** The function on `node`, or `undefined` — including when the field is\n * container-only and `node` is a leaf. */\n read(node: FnBearingNode): NodeFn | undefined;\n /** Attach `fn` to `node`, ignoring a container-only field on a leaf. */\n write(node: FnBearingNode, fn: NodeFn): void;\n /** The redo side-channel: `kit:add` replays without the original spec, so\n * the function it should re-attach is cached here by node id. */\n readonly pending: Map<NodeId, NodeFn>;\n}\n\nexport function createNodeFnFields<TPose>(registry: SceneRegistry<TPose>): readonly NodeFnField[] {\n return NODE_FN_FIELDS.map((spec) => {\n const table = registry[spec.field] as Readonly<Record<string, NodeFn>> | undefined;\n const reverse = new Map<NodeFn, string>();\n if (table) for (const [key, fn] of Object.entries(table)) reverse.set(fn, key);\n const pending = new Map<NodeId, NodeFn>();\n const applies = (node: FnBearingNode): boolean =>\n !spec.containersOnly || node.kind === 'container';\n return {\n spec,\n keyOf: (fn) => reverse.get(fn),\n fnOf: (key) => table?.[key],\n read: (node) => (applies(node) ? node[spec.field] : undefined),\n write: (node, fn) => {\n if (applies(node)) node[spec.field] = fn;\n },\n pending,\n };\n });\n}\n\n/**\n * Attach the function `key` names to `node`, seeding the redo cache so later\n * undo/redo cycles behave like the live path. Warns and leaves the field\n * unset when the key is not in this scene's registry — a snapshot outliving\n * the code that registered its functions must still load.\n */\nexport function restoreFn(\n field: NodeFnField,\n node: FnBearingNode,\n key: string,\n site: string,\n warn: (channel: string, message: string) => void,\n): void {\n const fn = field.fnOf(key);\n if (fn === undefined) {\n warn(\n 'scene',\n `${site}: ${field.spec.opKey} \"${key}\" not in this scene's registry — node \"${node.id}\" ` +\n `restored without its ${field.spec.lost}. Register a function with this key in the ` +\n `registry option to restore it.`,\n );\n return;\n }\n field.write(node, fn);\n field.pending.set(node.id, fn);\n}\n\n/** The `{ clipKey?, derivePathKey?, … }` an op payload or a snapshot carries\n * for `node`. Empty when the node has no keyed function on it. */\nexport function fnKeysOf(\n fields: readonly NodeFnField[],\n node: FnBearingNode,\n which: 'opKey' | 'jsonKey',\n onUnregistered?: (field: NodeFnField) => never,\n): Record<string, string> {\n const out: Record<string, string> = {};\n for (const field of fields) {\n const fn = field.read(node);\n if (fn === undefined) continue;\n const key = field.keyOf(fn);\n if (key === undefined) {\n onUnregistered?.(field);\n continue;\n }\n out[field.spec[which]] = key;\n }\n return out;\n}\n\n/**\n * The function fields to carry from a live node into an `AddNodeSpec`. An\n * insert that drops them brings an edge back as a static path that never\n * follows its endpoints again, and a clipped container back with nothing\n * clipped.\n */\nexport function fnFieldsOfNode<T extends { kind: 'leaf' | 'container' }>(\n node: T,\n): Partial<Pick<T, Extract<NodeFnFieldName, keyof T>>> {\n const src = node as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n for (const spec of NODE_FN_FIELDS) {\n if (spec.containersOnly && node.kind !== 'container') continue;\n const fn = src[spec.field];\n if (fn !== undefined) out[spec.field] = fn;\n }\n return out as Partial<Pick<T, Extract<NodeFnFieldName, keyof T>>>;\n}\n"],"mappings":";AA8CA,IAAaA,IAA6C;CACxD;EACE,OAAO;EACP,OAAO;EACP,SAAS;EACT,gBAAgB;EAChB,MAAM;CACR;CACA;EACE,OAAO;EACP,OAAO;EACP,SAAS;EACT,gBAAgB;EAChB,MAAM;CACR;CACA;EACE,OAAO;EACP,OAAO;EACP,SAAS;EACT,gBAAgB;EAChB,MAAM;CACR;CACA;EACE,OAAO;EACP,OAAO;EACP,SAAS;EACT,gBAAgB;EAChB,MAAM;CACR;AACF;AAmBA,SAAgBC,EAA0B,GAAwD;CAChG,OAAOD,EAAe,KAAK,MAAS;EAClC,IAAME,IAAQC,EAASC,EAAK,QACtBC,oBAAU,IAAI,IAAoB;EACxC,IAAIH,GAAO,KAAK,IAAM,CAACI,GAAKC,MAAO,OAAO,QAAQL,CAAK,GAAG,EAAQ,IAAIK,GAAID,CAAG;EAC7E,IAAME,oBAAU,IAAI,IAAoB,GAClCC,KAAW,MACf,CAACL,EAAK,kBAAkBM,EAAK,SAAS;EACxC,OAAO;GACL;GACA,QAAQ,MAAOL,EAAQ,IAAIE,CAAE;GAC7B,OAAO,MAAQL,IAAQI;GACvB,OAAO,MAAUG,EAAQC,CAAI,IAAIA,EAAKN,EAAK,SAAS,KAAA;GACpD,QAAQ,GAAM,MAAO;IACnB,AAAIK,EAAQC,CAAI,MAAG,EAAKN,EAAK,SAASG;GACxC;GACA;EACF;CACF,CAAC;AACH;AAQA,SAAgBI,EACd,GACA,GACA,GACA,GACA,GACM;CACN,IAAMJ,IAAKK,EAAM,KAAKN,CAAG;CACzB,IAAIC,MAAO,KAAA,GAAW;EACpB,EACE,SACA,GAAGM,EAAK,IAAID,EAAM,KAAK,MAAM,IAAIN,EAAI,yCAAyCI,EAAK,GAAG,yBAC5DE,EAAM,KAAK,KAAK,0EAE5C;EACA;CACF;CAEA,AADAA,EAAM,MAAMF,GAAMH,CAAE,GACpBK,EAAM,QAAQ,IAAIF,EAAK,IAAIH,CAAE;AAC/B;AAIA,SAAgBO,EACd,GACA,GACA,GACA,GACwB;CACxB,IAAMC,IAA8B,CAAC;CACrC,KAAK,IAAMH,KAASI,GAAQ;EAC1B,IAAMT,IAAKK,EAAM,KAAKF,CAAI;EAC1B,IAAIH,MAAO,KAAA,GAAW;EACtB,IAAMD,IAAMM,EAAM,MAAML,CAAE;EAC1B,IAAID,MAAQ,KAAA,GAAW;GACrB,IAAiBM,CAAK;GACtB;EACF;EACA,EAAIA,EAAM,KAAKK,MAAUX;CAC3B;CACA,OAAOS;AACT;AAQA,SAAgBG,EACd,GACqD;CACrD,IAAMC,IAAMT,GACNK,IAA+B,CAAC;CACtC,KAAK,IAAMX,KAAQJ,GAAgB;EACjC,IAAII,EAAK,kBAAkBM,EAAK,SAAS,aAAa;EACtD,IAAMH,IAAKY,EAAIf,EAAK;EACpB,AAAIG,MAAO,KAAA,MAAW,EAAIH,EAAK,SAASG;CAC1C;CACA,OAAOQ;AACT"}
1
+ {"version":3,"file":"nodeFnFields.js","names":["NODE_FN_FIELDS","createNodeFnFields","table","registry","spec","reverse","key","fn","applies","node","restoreFn","field","site","fnKeysOf","out","fields","which","fnFieldsOfNode","src"],"sources":["../../../src/core/scene/nodeFnFields.ts"],"sourcesContent":["/**\n * The registry-keyed function fields a node can carry, as a table.\n *\n * `clipFromPose`, `derivePath` and `derivePose` are live functions on a node,\n * and a function travels through neither a serializable op payload nor a JSON\n * snapshot. Each one therefore needs the same five things: a function→key\n * reverse map, a side channel the `kit:add` redo path re-attaches from, a\n * name in the op payload, a name in the snapshot, and a registry lookup on\n * restore.\n *\n * The first two fields were written out twice, and `types.ts` said in a\n * comment that a third should be the point it stopped being copied per field.\n * This is that point.\n */\nimport type { NodeId, SceneRegistry } from './types';\n\n/** A node's function field, opaque here: the table moves these around by\n * reference and never calls one. Each field's real signature lives on `Node`.\n * A container's `layout` is an object rather than a function, and rides the\n * same table for the same reason — it travels by reference, not as data. */\nexport type NodeFn = ((...args: never[]) => unknown) | object;\n\n/** A node as this module reads it — a bag with the three fields on it. The\n * full `Node` type would make the table generic in `TData`/`TLayer` for no\n * gain; nothing here looks at either. */\nexport type FnBearingNode = { id: NodeId; kind: 'leaf' | 'container' } & {\n [K in NodeFnFieldName]?: NodeFn;\n};\n\nexport type NodeFnFieldName = 'clipFromPose' | 'derivePath' | 'derivePose' | 'layout';\n\nexport interface NodeFnFieldSpec {\n /** The field's name on `Node` — and, deliberately, its name in\n * `SceneRegistry` too, so one string indexes both. */\n readonly field: NodeFnFieldName;\n /** Its key in the `kit:add` and `kit:remove` op payloads. `clipFromPose`'s\n * is the short one because a persisted history already carries that name. */\n readonly opKey: string;\n /** Its key in a `SerializedNode`. */\n readonly jsonKey: string;\n /** True for a field only a `ContainerNode` can carry. */\n readonly containersOnly: boolean;\n /** What a node loses when its key is not in the registry, for the warning. */\n readonly lost: string;\n}\n\nexport const NODE_FN_FIELDS: readonly NodeFnFieldSpec[] = [\n {\n field: 'clipFromPose',\n opKey: 'clipKey',\n jsonKey: 'clipFromPoseKey',\n containersOnly: true,\n lost: 'clip',\n },\n {\n field: 'derivePath',\n opKey: 'derivePathKey',\n jsonKey: 'derivePathKey',\n containersOnly: false,\n lost: 'derived path',\n },\n {\n field: 'derivePose',\n opKey: 'derivePoseKey',\n jsonKey: 'derivePoseKey',\n containersOnly: false,\n lost: 'derived pose',\n },\n {\n field: 'layout',\n opKey: 'layoutKey',\n jsonKey: 'layoutKey',\n containersOnly: true,\n lost: 'layout',\n },\n];\n\n/** One field's per-scene state. */\nexport interface NodeFnField {\n readonly spec: NodeFnFieldSpec;\n /** `fn → registry key`, for writing a payload or a snapshot. */\n keyOf(fn: NodeFn): string | undefined;\n /** `registry key → fn`, for restoring one. */\n fnOf(key: string): NodeFn | undefined;\n /** The function on `node`, or `undefined` — including when the field is\n * container-only and `node` is a leaf. */\n read(node: FnBearingNode): NodeFn | undefined;\n /** Attach `fn` to `node`, ignoring a container-only field on a leaf. */\n write(node: FnBearingNode, fn: NodeFn): void;\n}\n\nexport function createNodeFnFields<TPose>(registry: SceneRegistry<TPose>): readonly NodeFnField[] {\n return NODE_FN_FIELDS.map((spec) => {\n const table = registry[spec.field] as Readonly<Record<string, NodeFn>> | undefined;\n const reverse = new Map<NodeFn, string>();\n if (table) for (const [key, fn] of Object.entries(table)) reverse.set(fn, key);\n const applies = (node: FnBearingNode): boolean =>\n !spec.containersOnly || node.kind === 'container';\n return {\n spec,\n keyOf: (fn) => reverse.get(fn),\n fnOf: (key) => table?.[key],\n read: (node) => (applies(node) ? node[spec.field] : undefined),\n write: (node, fn) => {\n if (applies(node)) node[spec.field] = fn;\n },\n };\n });\n}\n\n/**\n * Attach the function `key` names to `node`. Warns and leaves the field\n * unset when the key is not in this scene's registry — a snapshot outliving\n * the code that registered its functions must still load.\n */\nexport function restoreFn(\n field: NodeFnField,\n node: FnBearingNode,\n key: string,\n site: string,\n warn: (channel: string, message: string) => void,\n): void {\n const fn = field.fnOf(key);\n if (fn === undefined) {\n warn(\n 'scene',\n `${site}: ${field.spec.opKey} \"${key}\" not in this scene's registry — node \"${node.id}\" ` +\n `restored without its ${field.spec.lost}. Register a function with this key in the ` +\n `registry option to restore it.`,\n );\n return;\n }\n field.write(node, fn);\n}\n\n/** The `{ clipKey?, derivePathKey?, … }` an op payload or a snapshot carries\n * for `node`. Empty when the node has no keyed function on it. */\nexport function fnKeysOf(\n fields: readonly NodeFnField[],\n node: FnBearingNode,\n which: 'opKey' | 'jsonKey',\n onUnregistered?: (field: NodeFnField) => never,\n): Record<string, string> {\n const out: Record<string, string> = {};\n for (const field of fields) {\n const fn = field.read(node);\n if (fn === undefined) continue;\n const key = field.keyOf(fn);\n if (key === undefined) {\n onUnregistered?.(field);\n continue;\n }\n out[field.spec[which]] = key;\n }\n return out;\n}\n\n/**\n * The function fields to carry from a live node into an `AddNodeSpec`. An\n * insert that drops them brings an edge back as a static path that never\n * follows its endpoints again, and a clipped container back with nothing\n * clipped.\n */\nexport function fnFieldsOfNode<T extends { kind: 'leaf' | 'container' }>(\n node: T,\n): Partial<Pick<T, Extract<NodeFnFieldName, keyof T>>> {\n const src = node as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n for (const spec of NODE_FN_FIELDS) {\n if (spec.containersOnly && node.kind !== 'container') continue;\n const fn = src[spec.field];\n if (fn !== undefined) out[spec.field] = fn;\n }\n return out as Partial<Pick<T, Extract<NodeFnFieldName, keyof T>>>;\n}\n"],"mappings":";AA8CA,IAAaA,IAA6C;CACxD;EACE,OAAO;EACP,OAAO;EACP,SAAS;EACT,gBAAgB;EAChB,MAAM;CACR;CACA;EACE,OAAO;EACP,OAAO;EACP,SAAS;EACT,gBAAgB;EAChB,MAAM;CACR;CACA;EACE,OAAO;EACP,OAAO;EACP,SAAS;EACT,gBAAgB;EAChB,MAAM;CACR;CACA;EACE,OAAO;EACP,OAAO;EACP,SAAS;EACT,gBAAgB;EAChB,MAAM;CACR;AACF;AAgBA,SAAgBC,EAA0B,GAAwD;CAChG,OAAOD,EAAe,KAAK,MAAS;EAClC,IAAME,IAAQC,EAASC,EAAK,QACtBC,oBAAU,IAAI,IAAoB;EACxC,IAAIH,GAAO,KAAK,IAAM,CAACI,GAAKC,MAAO,OAAO,QAAQL,CAAK,GAAG,EAAQ,IAAIK,GAAID,CAAG;EAC7E,IAAME,KAAW,MACf,CAACJ,EAAK,kBAAkBK,EAAK,SAAS;EACxC,OAAO;GACL;GACA,QAAQ,MAAOJ,EAAQ,IAAIE,CAAE;GAC7B,OAAO,MAAQL,IAAQI;GACvB,OAAO,MAAUE,EAAQC,CAAI,IAAIA,EAAKL,EAAK,SAAS,KAAA;GACpD,QAAQ,GAAM,MAAO;IACnB,AAAII,EAAQC,CAAI,MAAG,EAAKL,EAAK,SAASG;GACxC;EACF;CACF,CAAC;AACH;AAOA,SAAgBG,EACd,GACA,GACA,GACA,GACA,GACM;CACN,IAAMH,IAAKI,EAAM,KAAKL,CAAG;CACzB,IAAIC,MAAO,KAAA,GAAW;EACpB,EACE,SACA,GAAGK,EAAK,IAAID,EAAM,KAAK,MAAM,IAAIL,EAAI,yCAAyCG,EAAK,GAAG,yBAC5DE,EAAM,KAAK,KAAK,0EAE5C;EACA;CACF;CACA,EAAM,MAAMF,GAAMF,CAAE;AACtB;AAIA,SAAgBM,EACd,GACA,GACA,GACA,GACwB;CACxB,IAAMC,IAA8B,CAAC;CACrC,KAAK,IAAMH,KAASI,GAAQ;EAC1B,IAAMR,IAAKI,EAAM,KAAKF,CAAI;EAC1B,IAAIF,MAAO,KAAA,GAAW;EACtB,IAAMD,IAAMK,EAAM,MAAMJ,CAAE;EAC1B,IAAID,MAAQ,KAAA,GAAW;GACrB,IAAiBK,CAAK;GACtB;EACF;EACA,EAAIA,EAAM,KAAKK,MAAUV;CAC3B;CACA,OAAOQ;AACT;AAQA,SAAgBG,EACd,GACqD;CACrD,IAAMC,IAAMT,GACNK,IAA+B,CAAC;CACtC,KAAK,IAAMV,KAAQJ,GAAgB;EACjC,IAAII,EAAK,kBAAkBK,EAAK,SAAS,aAAa;EACtD,IAAMF,IAAKW,EAAId,EAAK;EACpB,AAAIG,MAAO,KAAA,MAAW,EAAIH,EAAK,SAASG;CAC1C;CACA,OAAOO;AACT"}
@@ -1,28 +1,54 @@
1
1
  //#region src/core/scene/nodeMemo.ts
2
- var e = /* @__PURE__ */ new WeakMap(), t = 0;
3
- function n(n, r, i, a) {
4
- let o = e.get(n);
5
- o === void 0 ? (o = {
6
- generation: t,
7
- slots: /* @__PURE__ */ new Map()
8
- }, e.set(n, o)) : o.generation !== t && (o.generation = t, o.slots.clear());
9
- let s = n.data, c = o.slots.get(r);
10
- if (c !== void 0 && c.pose === i && c.data === s) return c.value;
11
- let l = a();
12
- return c === void 0 ? o.slots.set(r, {
2
+ function e() {
3
+ return /* @__PURE__ */ new WeakMap();
4
+ }
5
+ var t = /* @__PURE__ */ new WeakMap(), n = 0;
6
+ function r(e, r, a, o) {
7
+ if (typeof r != "string") return i(e, r, a, o);
8
+ let s = t.get(e);
9
+ s === void 0 ? (s = {
10
+ generation: n,
11
+ slots: /* @__PURE__ */ new Map(),
12
+ drops: 0
13
+ }, t.set(e, s)) : s.generation !== n && (s.generation = n, s.slots.clear());
14
+ let c = e.data, l = s.slots.get(r);
15
+ if (l !== void 0 && l.pose === a && l.data === c) return l.value;
16
+ let u = o();
17
+ return l === void 0 ? s.slots.set(r, {
18
+ pose: a,
19
+ data: c,
20
+ value: u
21
+ }) : (l.pose = a, l.data = c, l.value = u), u;
22
+ }
23
+ function i(e, r, i, a) {
24
+ let o = r, s = e.data, c = t.get(e)?.drops ?? 0, l = o.get(e);
25
+ if (l !== void 0 && l.generation === n && l.pose === i && l.data === s && (i === void 0 || l.drops === c)) return l.value;
26
+ let u = a(), d = t.get(e)?.drops ?? 0;
27
+ return l === void 0 ? o.set(e, {
13
28
  pose: i,
14
29
  data: s,
15
- value: l
16
- }) : (c.pose = i, c.data = s, c.value = l), l;
30
+ value: u,
31
+ generation: n,
32
+ drops: d
33
+ }) : (l.pose = i, l.data = s, l.value = u, l.generation = n, l.drops = d), u;
17
34
  }
18
- function r() {
19
- t++;
35
+ function a() {
36
+ n++;
20
37
  }
21
- function i(t) {
22
- let n = e.get(t);
23
- if (n !== void 0) for (let [e, t] of n.slots) t.pose !== void 0 && n.slots.delete(e);
38
+ function o(e) {
39
+ let r = t.get(e);
40
+ if (r === void 0) {
41
+ t.set(e, {
42
+ generation: n,
43
+ slots: /* @__PURE__ */ new Map(),
44
+ drops: 1
45
+ });
46
+ return;
47
+ }
48
+ r.drops++;
49
+ for (let [e, t] of r.slots) t.pose !== void 0 && r.slots.delete(e);
24
50
  }
25
51
  //#endregion
26
- export { r as bumpNodeMemoGeneration, i as dropPoseKeyedMemoSlots, n as nodeMemo };
52
+ export { a as bumpNodeMemoGeneration, e as createNodeMemoScope, o as dropPoseKeyedMemoSlots, r as nodeMemo };
27
53
 
28
54
  //# sourceMappingURL=nodeMemo.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"nodeMemo.js","names":["MEMO","generation","nodeMemo","record","node","data","hit","slot","pose","value","derive","bumpNodeMemoGeneration","dropPoseKeyedMemoSlots","entry"],"sources":["../../../src/core/scene/nodeMemo.ts"],"sourcesContent":["/**\n * `nodeMemo` — memoize anything derived from a node, keyed on the node's\n * identity plus the `pose` / `data` references the scene swaps on every edit.\n *\n * **Why this key works.** The scene mutates node objects in place but\n * *replaces* `pose` and `data` with new references — see the `kit:setPose` /\n * `kit:setData` ops in `core/scene/scene.ts`, which do `node.pose = p.to` /\n * `node.data = p.to`. So the node object is a stable identity, and those two\n * references are an exact freshness signal for anything derived per node: no\n * deep compare, no dirty flag, no subscription. The whole memo is three\n * reference comparisons.\n *\n * The assumption it rests on is the other side of that contract: nobody\n * mutates `node.data` (or `node.pose`) in place without replacing the\n * reference. A consumer who violates it sees a stale derivation until the next\n * real edit.\n *\n * **What the key does not cover** is ambient state that `derive` reads — a\n * registry, a font set, a decode cache. Those change what `derive` would\n * return with no change to the node at all, which is the one failure a\n * `(pose, data)` key cannot see. {@link bumpNodeMemoGeneration} is the escape\n * hatch: whoever owns the ambient state bumps, and every slot on every node is\n * dropped. Derivations that read ambient state with *no* such signal (an async\n * image decode landing, say) must not be memoized here at all.\n *\n * Consumers: `canvas/NodeShape.ts` memoizes the painter match and the\n * silhouette path this way.\n */\n\n/** One memoized derivation: the references it was derived against, plus the\n * value. Presence in the slot map is what \"computed\" means — a `null` or\n * `undefined` value is a real answer and stays cached. */\ninterface MemoSlot {\n /** Reference-compared against the caller's `pose` argument. */\n pose: unknown;\n /** Reference-compared against `node.data` at call time. */\n data: unknown;\n value: unknown;\n}\n\ninterface NodeMemoRecord {\n /** `generation` at the time the slots were filled. */\n generation: number;\n slots: Map<string, MemoSlot>;\n}\n\nconst MEMO = new WeakMap<object, NodeMemoRecord>();\n\n/** Bumped whenever ambient state a derivation reads has changed. Entries\n * stamped with an older value are dropped rather than served stale. */\nlet generation = 0;\n\n/** A node, as far as the memo is concerned: an object identity that may carry\n * a `data` reference. Deliberately not `Node<…>` — the memo doesn't read\n * anything else, and typing it loosely keeps `core/scene` from depending on\n * the full node shape for a three-comparison cache. */\ntype MemoizableNode = { data?: unknown };\n\n/**\n * Return the value `derive` produces for `node`, recomputing only when the\n * node's `data` reference, the given `pose` reference, or the global\n * generation has changed since the last call for this `slot`.\n *\n * `slot` names the derivation. A node has more than one derived value — the\n * painter match, the silhouette, draw commands, text layout — and they\n * invalidate on the same signals but must not overwrite each other.\n *\n * `pose` is a **parameter rather than a read off the node**, because callers\n * legitimately derive against a pose that isn't `node.pose`: a preview or\n * ghost pose mid-drag. Those miss and recompute, as they should, and must not\n * poison the entry for the node's real pose. Pass `undefined` for derivations\n * that don't depend on the pose at all (the painter match is one) — it\n * compares equal to itself, leaving `data` as the only key.\n *\n * `data` is read off the node rather than passed: no caller has had a reason\n * to derive against data the node doesn't hold.\n */\nexport function nodeMemo<T>(\n node: MemoizableNode,\n slot: string,\n pose: unknown,\n derive: () => T,\n): T {\n let record = MEMO.get(node);\n if (record === undefined) {\n record = { generation, slots: new Map() };\n MEMO.set(node, record);\n } else if (record.generation !== generation) {\n record.generation = generation;\n record.slots.clear();\n }\n\n const data = node.data;\n const hit = record.slots.get(slot);\n if (hit !== undefined && hit.pose === pose && hit.data === data) {\n return hit.value as T;\n }\n\n const value = derive();\n // Reuse the slot object when there is one: this runs per node per frame, and\n // the entry churn is the only allocation the memo would otherwise add.\n if (hit !== undefined) {\n hit.pose = pose;\n hit.data = data;\n hit.value = value;\n } else {\n record.slots.set(slot, { pose, data, value });\n }\n return value;\n}\n\n/**\n * Drop every memoized slot on every node.\n *\n * For owners of ambient state that changes what a `derive` would return: the\n * shape-painter registry calls this when a painter is registered or disposed,\n * since a cache keyed only on `(pose, data)` would keep answering with the\n * painter that matched before.\n *\n * Cheap — it bumps a counter; records are re-stamped lazily on next access,\n * and unreachable nodes stay collectable via the `WeakMap`.\n */\nexport function bumpNodeMemoGeneration(): void {\n generation++;\n}\n\n/**\n * Drop the slots of `node` that were keyed on a pose, keeping the ones that\n * weren't (the painter match passes `undefined`, and re-matching it is a\n * registry scan).\n *\n * For per-frame pose overrides, which mutate one buffer in place rather than\n * replacing the reference: the `(pose, data)` key cannot see that, so the\n * override's `commit()` calls this for each node it holds.\n * {@link bumpNodeMemoGeneration} is the wrong tool there — it would drop every\n * static node's slots too, every frame.\n */\nexport function dropPoseKeyedMemoSlots(node: MemoizableNode): void {\n const record = MEMO.get(node);\n if (record === undefined) return;\n for (const [slot, entry] of record.slots) {\n if (entry.pose !== undefined) record.slots.delete(slot);\n }\n}\n"],"mappings":";AA8CA,IAAMA,oBAAO,IAAI,QAAgC,GAI7CC,IAAa;AA2BjB,SAAgBC,EACd,GACA,GACA,GACA,GACG;CACH,IAAIC,IAASH,EAAK,IAAII,CAAI;CAC1B,AAAID,MAAW,KAAA,KACb,IAAS;EAAE;EAAY,uBAAO,IAAI,IAAI;CAAE,GACxCH,EAAK,IAAII,GAAMD,CAAM,KACZA,EAAO,eAAeF,MAC/B,EAAO,aAAaA,GACpBE,EAAO,MAAM,MAAM;CAGrB,IAAME,IAAOD,EAAK,MACZE,IAAMH,EAAO,MAAM,IAAII,CAAI;CACjC,IAAID,MAAQ,KAAA,KAAaA,EAAI,SAASE,KAAQF,EAAI,SAASD,GACzD,OAAOC,EAAI;CAGb,IAAMG,IAAQC,EAAO;CAUrB,OAPIJ,MAAQ,KAAA,IAKVH,EAAO,MAAM,IAAII,GAAM;EAAE;EAAM;EAAM;CAAM,CAAC,KAJ5C,EAAI,OAAOC,GACX,EAAI,OAAOH,GACX,EAAI,QAAQI,IAIPA;AACT;AAaA,SAAgBE,IAA+B;CAC7C;AACF;AAaA,SAAgBC,EAAuB,GAA4B;CACjE,IAAMT,IAASH,EAAK,IAAII,CAAI;CAC5B,IAAID,MAAW,KAAA,GACf,KAAK,IAAM,CAACI,GAAMM,MAAUV,EAAO,OACjC,AAAIU,EAAM,SAAS,KAAA,KAAWV,EAAO,MAAM,OAAOI,CAAI;AAE1D"}
1
+ {"version":3,"file":"nodeMemo.js","names":["createNodeMemoScope","MEMO","generation","nodeMemo","slot","scopedNodeMemo","node","pose","derive","record","data","hit","value","entries","scope","drops","dropsNow","bumpNodeMemoGeneration","dropPoseKeyedMemoSlots","entry"],"sources":["../../../src/core/scene/nodeMemo.ts"],"sourcesContent":["/**\n * `nodeMemo` — memoize anything derived from a node, keyed on the node's\n * identity plus the `pose` / `data` references the scene swaps on every edit.\n *\n * **Why this key works.** The scene mutates node objects in place but\n * *replaces* `pose` and `data` with new references — see the `kit:setPose` /\n * `kit:setData` ops in `core/scene/scene.ts`, which do `node.pose = p.to` /\n * `node.data = p.to`. So the node object is a stable identity, and those two\n * references are an exact freshness signal for anything derived per node: no\n * deep compare, no dirty flag, no subscription. The whole memo is three\n * reference comparisons.\n *\n * The assumption it rests on is the other side of that contract: nobody\n * mutates `node.data` (or `node.pose`) in place without replacing the\n * reference. A consumer who violates it sees a stale derivation until the next\n * real edit.\n *\n * **What the key does not cover** is ambient state that `derive` reads — a\n * registry, a font set, a decode cache. Those change what `derive` would\n * return with no change to the node at all, which is the one failure a\n * `(pose, data)` key cannot see. {@link bumpNodeMemoGeneration} is the escape\n * hatch: whoever owns the ambient state bumps, and every slot on every node is\n * dropped. Derivations that read ambient state with *no* such signal (an async\n * image decode landing, say) must not be memoized here at all.\n *\n * Consumers: `canvas/NodeShape.ts` memoizes the painter match and the\n * silhouette path this way.\n */\n\n/** One memoized derivation: the references it was derived against, plus the\n * value. Presence in the slot map is what \"computed\" means — a `null` or\n * `undefined` value is a real answer and stays cached. */\ninterface MemoSlot {\n /** Reference-compared against the caller's `pose` argument. */\n pose: unknown;\n /** Reference-compared against `node.data` at call time. */\n data: unknown;\n value: unknown;\n}\n\ninterface NodeMemoRecord {\n /** `generation` at the time the slots were filled. */\n generation: number;\n slots: Map<string, MemoSlot>;\n /** How many times {@link dropPoseKeyedMemoSlots} has run for this node,\n * which is how a drop reaches the scopes it cannot enumerate. */\n drops: number;\n}\n\n/** A scoped entry also carries what a named slot's record holds for it. */\ninterface ScopedSlot extends MemoSlot {\n generation: number;\n drops: number;\n}\n\ndeclare const scopeBrand: unique symbol;\n\n/**\n * A private place to memoize one derivation, in place of a slot name.\n *\n * A named slot lives on the node, so it lasts as long as the node does. That\n * is wrong for a derivation that belongs to some shorter-lived reader — one\n * `toPose` function among many over the life of a scene. A scope is held by\n * that reader, and its entries go when the reader does.\n */\nexport type NodeMemoScope = { readonly [scopeBrand]: true };\n\nexport function createNodeMemoScope(): NodeMemoScope {\n return new WeakMap<object, ScopedSlot>() as unknown as NodeMemoScope;\n}\n\nconst MEMO = new WeakMap<object, NodeMemoRecord>();\n\n/** Bumped whenever ambient state a derivation reads has changed. Entries\n * stamped with an older value are dropped rather than served stale. */\nlet generation = 0;\n\n/** A node, as far as the memo is concerned: an object identity that may carry\n * a `data` reference. Deliberately not `Node<…>` — the memo doesn't read\n * anything else, and typing it loosely keeps `core/scene` from depending on\n * the full node shape for a three-comparison cache. */\ntype MemoizableNode = { data?: unknown };\n\n/**\n * Return the value `derive` produces for `node`, recomputing only when the\n * node's `data` reference, the given `pose` reference, or the global\n * generation has changed since the last call for this `slot`.\n *\n * `slot` names the derivation. A node has more than one derived value — the\n * painter match, the silhouette, draw commands, text layout — and they\n * invalidate on the same signals but must not overwrite each other.\n *\n * `pose` is a **parameter rather than a read off the node**, because callers\n * legitimately derive against a pose that isn't `node.pose`: a preview or\n * ghost pose mid-drag. Those miss and recompute, as they should, and must not\n * poison the entry for the node's real pose. Pass `undefined` for derivations\n * that don't depend on the pose at all (the painter match is one) — it\n * compares equal to itself, leaving `data` as the only key.\n *\n * `data` is read off the node rather than passed: no caller has had a reason\n * to derive against data the node doesn't hold.\n */\nexport function nodeMemo<T>(\n node: MemoizableNode,\n slot: string | NodeMemoScope,\n pose: unknown,\n derive: () => T,\n): T {\n if (typeof slot !== 'string') return scopedNodeMemo(node, slot, pose, derive);\n let record = MEMO.get(node);\n if (record === undefined) {\n record = { generation, slots: new Map(), drops: 0 };\n MEMO.set(node, record);\n } else if (record.generation !== generation) {\n record.generation = generation;\n record.slots.clear();\n }\n\n const data = node.data;\n const hit = record.slots.get(slot);\n if (hit !== undefined && hit.pose === pose && hit.data === data) {\n return hit.value as T;\n }\n\n const value = derive();\n // Reuse the slot object when there is one: this runs per node per frame, and\n // the entry churn is the only allocation the memo would otherwise add.\n if (hit !== undefined) {\n hit.pose = pose;\n hit.data = data;\n hit.value = value;\n } else {\n record.slots.set(slot, { pose, data, value });\n }\n return value;\n}\n\nfunction scopedNodeMemo<T>(\n node: MemoizableNode,\n scope: NodeMemoScope,\n pose: unknown,\n derive: () => T,\n): T {\n const entries = scope as unknown as WeakMap<object, ScopedSlot>;\n const data = node.data;\n const drops = MEMO.get(node)?.drops ?? 0;\n const hit = entries.get(node);\n if (\n hit !== undefined && hit.generation === generation && hit.pose === pose && hit.data === data\n && (pose === undefined || hit.drops === drops)\n ) {\n return hit.value as T;\n }\n const value = derive();\n // Read again: a derivation that hit a cycle drops this node's slots itself.\n const dropsNow = MEMO.get(node)?.drops ?? 0;\n if (hit !== undefined) {\n hit.pose = pose;\n hit.data = data;\n hit.value = value;\n hit.generation = generation;\n hit.drops = dropsNow;\n } else {\n entries.set(node, { pose, data, value, generation, drops: dropsNow });\n }\n return value;\n}\n\n/** How many named slots `node` holds. For tests of what is kept on a node. */\nexport function nodeMemoSlotCount(node: MemoizableNode): number {\n const record = MEMO.get(node);\n return record === undefined || record.generation !== generation ? 0 : record.slots.size;\n}\n\n/**\n * Drop every memoized slot on every node.\n *\n * For owners of ambient state that changes what a `derive` would return: the\n * shape-painter registry calls this when a painter is registered or disposed,\n * since a cache keyed only on `(pose, data)` would keep answering with the\n * painter that matched before.\n *\n * Cheap — it bumps a counter; records are re-stamped lazily on next access,\n * and unreachable nodes stay collectable via the `WeakMap`.\n */\nexport function bumpNodeMemoGeneration(): void {\n generation++;\n}\n\n/**\n * Drop the slots of `node` that were keyed on a pose, keeping the ones that\n * weren't (the painter match passes `undefined`, and re-matching it is a\n * registry scan).\n *\n * For per-frame pose overrides, which mutate one buffer in place rather than\n * replacing the reference: the `(pose, data)` key cannot see that, so the\n * override's `commit()` calls this for each node it holds.\n * {@link bumpNodeMemoGeneration} is the wrong tool there — it would drop every\n * static node's slots too, every frame.\n */\nexport function dropPoseKeyedMemoSlots(node: MemoizableNode): void {\n const record = MEMO.get(node);\n if (record === undefined) {\n MEMO.set(node, { generation, slots: new Map(), drops: 1 });\n return;\n }\n record.drops++;\n for (const [slot, entry] of record.slots) {\n if (entry.pose !== undefined) record.slots.delete(slot);\n }\n}\n"],"mappings":";AAmEA,SAAgBA,IAAqC;CACnD,uBAAO,IAAI,QAA4B;AACzC;AAEA,IAAMC,oBAAO,IAAI,QAAgC,GAI7CC,IAAa;AA2BjB,SAAgBC,EACd,GACA,GACA,GACA,GACG;CACH,IAAI,OAAOC,KAAS,UAAU,OAAOC,EAAeC,GAAMF,GAAMG,GAAMC,CAAM;CAC5E,IAAIC,IAASR,EAAK,IAAIK,CAAI;CAC1B,AAAIG,MAAW,KAAA,KACb,IAAS;EAAE;EAAY,uBAAO,IAAI,IAAI;EAAG,OAAO;CAAE,GAClDR,EAAK,IAAIK,GAAMG,CAAM,KACZA,EAAO,eAAeP,MAC/B,EAAO,aAAaA,GACpBO,EAAO,MAAM,MAAM;CAGrB,IAAMC,IAAOJ,EAAK,MACZK,IAAMF,EAAO,MAAM,IAAIL,CAAI;CACjC,IAAIO,MAAQ,KAAA,KAAaA,EAAI,SAASJ,KAAQI,EAAI,SAASD,GACzD,OAAOC,EAAI;CAGb,IAAMC,IAAQJ,EAAO;CAUrB,OAPIG,MAAQ,KAAA,IAKVF,EAAO,MAAM,IAAIL,GAAM;EAAE;EAAM;EAAM;CAAM,CAAC,KAJ5C,EAAI,OAAOG,GACX,EAAI,OAAOG,GACX,EAAI,QAAQE,IAIPA;AACT;AAEA,SAASP,EACP,GACA,GACA,GACA,GACG;CACH,IAAMQ,IAAUC,GACVJ,IAAOJ,EAAK,MACZS,IAAQd,EAAK,IAAIK,CAAI,CAAC,EAAE,SAAS,GACjCK,IAAME,EAAQ,IAAIP,CAAI;CAC5B,IACEK,MAAQ,KAAA,KAAaA,EAAI,eAAeT,KAAcS,EAAI,SAASJ,KAAQI,EAAI,SAASD,MACpFH,MAAS,KAAA,KAAaI,EAAI,UAAUI,IAExC,OAAOJ,EAAI;CAEb,IAAMC,IAAQJ,EAAO,GAEfQ,IAAWf,EAAK,IAAIK,CAAI,CAAC,EAAE,SAAS;CAU1C,OATIK,MAAQ,KAAA,IAOVE,EAAQ,IAAIP,GAAM;EAAE;EAAM;EAAM;EAAO;EAAY,OAAOU;CAAS,CAAC,KANpE,EAAI,OAAOT,GACX,EAAI,OAAOG,GACX,EAAI,QAAQE,GACZ,EAAI,aAAaV,GACjB,EAAI,QAAQc,IAIPJ;AACT;AAmBA,SAAgBK,IAA+B;CAC7C;AACF;AAaA,SAAgBC,EAAuB,GAA4B;CACjE,IAAMT,IAASR,EAAK,IAAIK,CAAI;CAC5B,IAAIG,MAAW,KAAA,GAAW;EACxB,EAAK,IAAIH,GAAM;GAAE;GAAY,uBAAO,IAAI,IAAI;GAAG,OAAO;EAAE,CAAC;EACzD;CACF;CACA,EAAO;CACP,KAAK,IAAM,CAACF,GAAMe,MAAUV,EAAO,OACjC,AAAIU,EAAM,SAAS,KAAA,KAAWV,EAAO,MAAM,OAAOL,CAAI;AAE1D"}