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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (488) hide show
  1. package/CHANGELOG.md +275 -0
  2. package/dist/WeaselProvider.js +1 -1
  3. package/dist/affordances/pathAnchors.js +10 -6
  4. package/dist/affordances/pathAnchors.js.map +1 -1
  5. package/dist/animation/poseTarget.js +46 -15
  6. package/dist/animation/poseTarget.js.map +1 -1
  7. package/dist/animation/poseTransition.js +3 -3
  8. package/dist/animation/poseTransition.js.map +1 -1
  9. package/dist/animation/reflow.js +3 -6
  10. package/dist/animation/reflow.js.map +1 -1
  11. package/dist/animation/rig/bindRig.js +2 -2
  12. package/dist/animation/rig/bindRig.js.map +1 -1
  13. package/dist/animation/rig/skinBinding.js +2 -2
  14. package/dist/animation/rig/skinBinding.js.map +1 -1
  15. package/dist/barrel/canvas.js +5 -1
  16. package/dist/barrel/chrome.js +3 -1
  17. package/dist/barrel/paint.js +4 -4
  18. package/dist/barrel/paths.js +9 -4
  19. package/dist/barrel/renderer.js +3 -2
  20. package/dist/barrel/routing.js +4 -4
  21. package/dist/barrel/scene.js +7 -1
  22. package/dist/barrel/text.js +2 -2
  23. package/dist/barrel/viewport.js +6 -2
  24. package/dist/canvas/Canvas.js +2 -1
  25. package/dist/canvas/Canvas.js.map +1 -1
  26. package/dist/canvas/CanvasView.js +23 -34
  27. package/dist/canvas/CanvasView.js.map +1 -1
  28. package/dist/canvas/ModalityHud.js +1 -1
  29. package/dist/canvas/NodeShape.js +5 -1
  30. package/dist/canvas/NodeShape.js.map +1 -1
  31. package/dist/canvas/SceneCanvas/ActionsProviderIfRoot.js +1 -1
  32. package/dist/canvas/SceneCanvas/DepRegistryProviderIfRoot.js +1 -1
  33. package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js +1 -1
  34. package/dist/canvas/SceneCanvas/SceneCanvasInput.js +2 -0
  35. package/dist/canvas/SceneCanvas/SceneCanvasInput.js.map +1 -1
  36. package/dist/canvas/SceneCanvas/SurfaceDeclarations.js +23 -0
  37. package/dist/canvas/SceneCanvas/SurfaceDeclarations.js.map +1 -0
  38. package/dist/canvas/SceneCanvas/canvasPassThrough.js +2 -1
  39. package/dist/canvas/SceneCanvas/canvasPassThrough.js.map +1 -1
  40. package/dist/canvas/SceneCanvas/defaultNodeRouting.js +2 -2
  41. package/dist/canvas/SceneCanvas/defaultNodeRouting.js.map +1 -1
  42. package/dist/canvas/SceneCanvas/props.js.map +1 -1
  43. package/dist/canvas/SceneCanvas/scenePointQuery.js +27 -5
  44. package/dist/canvas/SceneCanvas/scenePointQuery.js.map +1 -1
  45. package/dist/canvas/SceneCanvas/useActionTunings.js +6 -1
  46. package/dist/canvas/SceneCanvas/useActionTunings.js.map +1 -1
  47. package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js +1 -0
  48. package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js.map +1 -1
  49. package/dist/canvas/SceneCanvas/useCanvasTools.js +10 -2
  50. package/dist/canvas/SceneCanvas/useCanvasTools.js.map +1 -1
  51. package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js +25 -19
  52. package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js.map +1 -1
  53. package/dist/canvas/SceneCanvas/useSceneCanvasHandle.js +9 -3
  54. package/dist/canvas/SceneCanvas/useSceneCanvasHandle.js.map +1 -1
  55. package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js +17 -2
  56. package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js.map +1 -1
  57. package/dist/canvas/SceneCanvas/useSceneCanvasTools.js +2 -0
  58. package/dist/canvas/SceneCanvas/useSceneCanvasTools.js.map +1 -1
  59. package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js +19 -4
  60. package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js.map +1 -1
  61. package/dist/canvas/SceneCanvas/useScenePicking.js +31 -6
  62. package/dist/canvas/SceneCanvas/useScenePicking.js.map +1 -1
  63. package/dist/canvas/SceneCanvas/useSceneSelectTool.js +4 -5
  64. package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
  65. package/dist/canvas/SceneCanvas/useSceneStandardActions.js +7 -4
  66. package/dist/canvas/SceneCanvas/useSceneStandardActions.js.map +1 -1
  67. package/dist/canvas/SceneCanvas.js +30 -12
  68. package/dist/canvas/SceneCanvas.js.map +1 -1
  69. package/dist/canvas/ViewMarker.js +35 -0
  70. package/dist/canvas/ViewMarker.js.map +1 -0
  71. package/dist/canvas/accentColor.js +26 -3
  72. package/dist/canvas/accentColor.js.map +1 -1
  73. package/dist/canvas/buildSceneTree.js +14 -20
  74. package/dist/canvas/buildSceneTree.js.map +1 -1
  75. package/dist/canvas/copiesAbove.js +1 -1
  76. package/dist/canvas/copiesAbove.js.map +1 -1
  77. package/dist/canvas/defaultDrawOne.js +11 -6
  78. package/dist/canvas/defaultDrawOne.js.map +1 -1
  79. package/dist/canvas/deps/areaSelect.js +5 -2
  80. package/dist/canvas/deps/areaSelect.js.map +1 -1
  81. package/dist/canvas/deps/booleans.js +3 -2
  82. package/dist/canvas/deps/booleans.js.map +1 -1
  83. package/dist/canvas/deps/enteredGroup.js +81 -0
  84. package/dist/canvas/deps/enteredGroup.js.map +1 -0
  85. package/dist/canvas/deps/hitTestArea.js +28 -16
  86. package/dist/canvas/deps/hitTestArea.js.map +1 -1
  87. package/dist/canvas/deps/insert.js +10 -38
  88. package/dist/canvas/deps/insert.js.map +1 -1
  89. package/dist/canvas/deps/insertSite.js +39 -0
  90. package/dist/canvas/deps/insertSite.js.map +1 -0
  91. package/dist/canvas/deps/lassoSelect.js +6 -3
  92. package/dist/canvas/deps/lassoSelect.js.map +1 -1
  93. package/dist/canvas/deps/pathEditCommits.js +3 -7
  94. package/dist/canvas/deps/pathEditCommits.js.map +1 -1
  95. package/dist/canvas/deps/pathLeaf.js +1 -1
  96. package/dist/canvas/deps/pathLeaf.js.map +1 -1
  97. package/dist/canvas/deps/slice.js +2 -1
  98. package/dist/canvas/deps/slice.js.map +1 -1
  99. package/dist/canvas/derivedPath.js +14 -6
  100. package/dist/canvas/derivedPath.js.map +1 -1
  101. package/dist/canvas/doubleClickTarget.js +9 -11
  102. package/dist/canvas/doubleClickTarget.js.map +1 -1
  103. package/dist/canvas/drawnBounds.js +26 -9
  104. package/dist/canvas/drawnBounds.js.map +1 -1
  105. package/dist/canvas/inheritedPaints.js +55 -0
  106. package/dist/canvas/inheritedPaints.js.map +1 -0
  107. package/dist/canvas/nodeShapes/derivedPainter.js +15 -16
  108. package/dist/canvas/nodeShapes/derivedPainter.js.map +1 -1
  109. package/dist/canvas/nodeShapes/nodePaint.js +7 -34
  110. package/dist/canvas/nodeShapes/nodePaint.js.map +1 -1
  111. package/dist/canvas/nodeShapes/pathPainter.js +7 -7
  112. package/dist/canvas/nodeShapes/pathPainter.js.map +1 -1
  113. package/dist/canvas/nodeShapes/rectFallbackPainter.js +4 -5
  114. package/dist/canvas/nodeShapes/rectFallbackPainter.js.map +1 -1
  115. package/dist/canvas/nodeShapes/shapePainter.js +9 -9
  116. package/dist/canvas/nodeShapes/shapePainter.js.map +1 -1
  117. package/dist/canvas/nodeShapes/textPainter.js +13 -12
  118. package/dist/canvas/nodeShapes/textPainter.js.map +1 -1
  119. package/dist/canvas/ownClip.js +24 -0
  120. package/dist/canvas/ownClip.js.map +1 -0
  121. package/dist/canvas/paintCull.js.map +1 -1
  122. package/dist/canvas/pickIndex/nodeBroadBox.js +86 -0
  123. package/dist/canvas/pickIndex/nodeBroadBox.js.map +1 -0
  124. package/dist/canvas/pickIndex/scenePickIndex.js +208 -0
  125. package/dist/canvas/pickIndex/scenePickIndex.js.map +1 -0
  126. package/dist/canvas/pickReach.js +3 -3
  127. package/dist/canvas/pickReach.js.map +1 -1
  128. package/dist/canvas/pickWalk.js +17 -91
  129. package/dist/canvas/pickWalk.js.map +1 -1
  130. package/dist/canvas/poseCascade.js +1 -1
  131. package/dist/canvas/poseCascade.js.map +1 -1
  132. package/dist/canvas/publishPreviews.js +97 -0
  133. package/dist/canvas/publishPreviews.js.map +1 -0
  134. package/dist/canvas/renderSceneToPixels.js +1 -1
  135. package/dist/canvas/renderSceneToPixels.js.map +1 -1
  136. package/dist/canvas/sceneAdapter.js +7 -5
  137. package/dist/canvas/sceneAdapter.js.map +1 -1
  138. package/dist/canvas/sceneClipboard.js +17 -10
  139. package/dist/canvas/sceneClipboard.js.map +1 -1
  140. package/dist/canvas/scenePickReads.js +37 -0
  141. package/dist/canvas/scenePickReads.js.map +1 -0
  142. package/dist/canvas/scenePickSource.js +74 -0
  143. package/dist/canvas/scenePickSource.js.map +1 -0
  144. package/dist/canvas/sceneSlotWiring.js +9 -3
  145. package/dist/canvas/sceneSlotWiring.js.map +1 -1
  146. package/dist/canvas/sceneViewRender.js +9 -4
  147. package/dist/canvas/sceneViewRender.js.map +1 -1
  148. package/dist/canvas/shownNodes.js +15 -0
  149. package/dist/canvas/shownNodes.js.map +1 -0
  150. package/dist/canvas/surfaceContribution.js +12 -8
  151. package/dist/canvas/surfaceContribution.js.map +1 -1
  152. package/dist/canvas/useCanvasView.js +4 -2
  153. package/dist/canvas/useCanvasView.js.map +1 -1
  154. package/dist/canvas/useLayerStack.js +6 -2
  155. package/dist/canvas/useLayerStack.js.map +1 -1
  156. package/dist/canvas/useSceneAdapter.js +5 -3
  157. package/dist/canvas/useSceneAdapter.js.map +1 -1
  158. package/dist/canvas/useViewAim.js +121 -0
  159. package/dist/canvas/useViewAim.js.map +1 -0
  160. package/dist/canvas/useViewNesting.js +3 -2
  161. package/dist/canvas/useViewNesting.js.map +1 -1
  162. package/dist/canvas/useViewRegistrySurface.js +4 -2
  163. package/dist/canvas/useViewRegistrySurface.js.map +1 -1
  164. package/dist/canvas/viewMarkers.js +63 -0
  165. package/dist/canvas/viewMarkers.js.map +1 -0
  166. package/dist/canvas/viewRegistry.js +27 -1
  167. package/dist/canvas/viewRegistry.js.map +1 -1
  168. package/dist/clipboard.d.ts +2 -2
  169. package/dist/{clone-C3e0OTuF.d.ts → clone-qb2e1F5p.d.ts} +2 -2
  170. package/dist/clone.d.ts +3 -3
  171. package/dist/core/isPlainObject.js +2 -2
  172. package/dist/core/layers/layerGroup.js.map +1 -1
  173. package/dist/core/pages/pageEdits.js +2 -3
  174. package/dist/core/pages/pageEdits.js.map +1 -1
  175. package/dist/core/paint/meshKindLazy.js +59 -2
  176. package/dist/core/paint/meshKindLazy.js.map +1 -1
  177. package/dist/core/scene/NodeRouting.js.map +1 -1
  178. package/dist/core/scene/arrivalWindow.js +1 -1
  179. package/dist/core/scene/arrivalWindow.js.map +1 -1
  180. package/dist/core/scene/clipNothing.js +10 -0
  181. package/dist/core/scene/clipNothing.js.map +1 -0
  182. package/dist/core/scene/declaredFieldSetters.js +44 -0
  183. package/dist/core/scene/declaredFieldSetters.js.map +1 -0
  184. package/dist/core/scene/depMemo.js +14 -3
  185. package/dist/core/scene/depMemo.js.map +1 -1
  186. package/dist/core/scene/dependents.js +9 -1
  187. package/dist/core/scene/dependents.js.map +1 -1
  188. package/dist/core/scene/derivedParts.js +10 -11
  189. package/dist/core/scene/derivedParts.js.map +1 -1
  190. package/dist/core/scene/documentOf.js +43 -0
  191. package/dist/core/scene/documentOf.js.map +1 -0
  192. package/dist/core/scene/effectivePose.js +44 -15
  193. package/dist/core/scene/effectivePose.js.map +1 -1
  194. package/dist/core/scene/fieldEdits.js +17 -14
  195. package/dist/core/scene/fieldEdits.js.map +1 -1
  196. package/dist/core/scene/fontReads.js +37 -0
  197. package/dist/core/scene/fontReads.js.map +1 -0
  198. package/dist/core/scene/historyForm.js +4 -4
  199. package/dist/core/scene/historyForm.js.map +1 -1
  200. package/dist/core/scene/inheritedPaint.js +97 -0
  201. package/dist/core/scene/inheritedPaint.js.map +1 -0
  202. package/dist/core/scene/kitOps.js +3 -23
  203. package/dist/core/scene/kitOps.js.map +1 -1
  204. package/dist/core/scene/kitRegistry.js +5 -0
  205. package/dist/core/scene/kitRegistry.js.map +1 -1
  206. package/dist/core/scene/layoutPass.js +1 -2
  207. package/dist/core/scene/layoutPass.js.map +1 -1
  208. package/dist/core/scene/nodeAsShown.js +72 -0
  209. package/dist/core/scene/nodeAsShown.js.map +1 -0
  210. package/dist/core/scene/nodeFnFields.js +2 -10
  211. package/dist/core/scene/nodeFnFields.js.map +1 -1
  212. package/dist/core/scene/nodeMemo.js +21 -8
  213. package/dist/core/scene/nodeMemo.js.map +1 -1
  214. package/dist/core/scene/nodeStore.js +2 -4
  215. package/dist/core/scene/nodeStore.js.map +1 -1
  216. package/dist/core/scene/pathEffects/blend.js.map +1 -1
  217. package/dist/core/scene/pathEffects/derive.js +10 -4
  218. package/dist/core/scene/pathEffects/derive.js.map +1 -1
  219. package/dist/core/scene/pathEffects/effects.js +3 -1
  220. package/dist/core/scene/pathEffects/effects.js.map +1 -1
  221. package/dist/core/scene/pathEffects/foundHidden.js +35 -0
  222. package/dist/core/scene/pathEffects/foundHidden.js.map +1 -0
  223. package/dist/core/scene/pathEffects/textAlong.js +95 -0
  224. package/dist/core/scene/pathEffects/textAlong.js.map +1 -0
  225. package/dist/core/scene/plyStacks.js +89 -0
  226. package/dist/core/scene/plyStacks.js.map +1 -0
  227. package/dist/core/scene/poseHold.js +42 -24
  228. package/dist/core/scene/poseHold.js.map +1 -1
  229. package/dist/core/scene/poseOverrides.js +35 -38
  230. package/dist/core/scene/poseOverrides.js.map +1 -1
  231. package/dist/core/scene/posePlies.js +59 -0
  232. package/dist/core/scene/posePlies.js.map +1 -0
  233. package/dist/core/scene/reconcileSpecs.js +6 -15
  234. package/dist/core/scene/reconcileSpecs.js.map +1 -1
  235. package/dist/core/scene/renderOrder.js +10 -4
  236. package/dist/core/scene/renderOrder.js.map +1 -1
  237. package/dist/core/scene/sameValue.js +17 -0
  238. package/dist/core/scene/sameValue.js.map +1 -0
  239. package/dist/core/scene/scene.js +3 -3
  240. package/dist/core/scene/scene.js.map +1 -1
  241. package/dist/core/scene/sceneEdits.js +8 -0
  242. package/dist/core/scene/sceneEdits.js.map +1 -1
  243. package/dist/core/scene/sceneState.js +1 -2
  244. package/dist/core/scene/sceneState.js.map +1 -1
  245. package/dist/core/scene/serialization.js +45 -9
  246. package/dist/core/scene/serialization.js.map +1 -1
  247. package/dist/core/scene/staleReplay.js +5 -2
  248. package/dist/core/scene/staleReplay.js.map +1 -1
  249. package/dist/core/scene/statedFields.js +4 -2
  250. package/dist/core/scene/statedFields.js.map +1 -1
  251. package/dist/core/scene/treeEdits.js +2 -4
  252. package/dist/core/scene/treeEdits.js.map +1 -1
  253. package/dist/core/scene/treeOrder.js +3 -1
  254. package/dist/core/scene/treeOrder.js.map +1 -1
  255. package/dist/core/scene/treeReads.js +1 -2
  256. package/dist/core/scene/treeReads.js.map +1 -1
  257. package/dist/core/scene/treeShape.js +13 -0
  258. package/dist/core/scene/treeShape.js.map +1 -0
  259. package/dist/core/scene/types.js.map +1 -1
  260. package/dist/core/scene/useScene.js +1 -1
  261. package/dist/core/scene/useScene.js.map +1 -1
  262. package/dist/{cornerHandles-BHuoMs1o.d.ts → cornerHandles-CNCb7aTY.d.ts} +3 -2
  263. package/dist/{depSchema-BpZNOv7q.d.ts → depSchema-B1RS-IJx.d.ts} +39 -18
  264. package/dist/devtools/handlers.js +1 -1
  265. package/dist/devtools/handlers.js.map +1 -1
  266. package/dist/devtools/protocol.js.map +1 -1
  267. package/dist/devtools/registry.js +4 -4
  268. package/dist/devtools/registry.js.map +1 -1
  269. package/dist/devtools-protocol.d.ts +4 -3
  270. package/dist/devtools.d.ts +3 -2
  271. package/dist/features/measure/measureContribution.js +4 -2
  272. package/dist/features/measure/measureContribution.js.map +1 -1
  273. package/dist/features/measure/measureHit.js +5 -3
  274. package/dist/features/measure/measureHit.js.map +1 -1
  275. package/dist/features/measure/measurement.js +1 -1
  276. package/dist/features/measure/measurement.js.map +1 -1
  277. package/dist/features/modes/modeLayers.js +14 -7
  278. package/dist/features/modes/modeLayers.js.map +1 -1
  279. package/dist/features/modes/modeTriggers.js +10 -6
  280. package/dist/features/modes/modeTriggers.js.map +1 -1
  281. package/dist/features/pathEffects/effectSources.js +69 -0
  282. package/dist/features/pathEffects/effectSources.js.map +1 -0
  283. package/dist/features/pathEffects/readSource.js +18 -2
  284. package/dist/features/pathEffects/readSource.js.map +1 -1
  285. package/dist/features/pathEffects/textItems.js +53 -0
  286. package/dist/features/pathEffects/textItems.js.map +1 -0
  287. package/dist/features/pathEffects/useEffectSourcesLayer.js +53 -0
  288. package/dist/features/pathEffects/useEffectSourcesLayer.js.map +1 -0
  289. package/dist/features/paths/editablePath.js +1 -1
  290. package/dist/features/paths/editablePath.js.map +1 -1
  291. package/dist/features/paths/pathEditState.js +5 -2
  292. package/dist/features/paths/pathEditState.js.map +1 -1
  293. package/dist/features/paths/pathSnap/sceneIndex.js +1 -1
  294. package/dist/features/paths/pathSnap/sceneIndex.js.map +1 -1
  295. package/dist/features/selection/enteredGroupFrame.js +65 -0
  296. package/dist/features/selection/enteredGroupFrame.js.map +1 -0
  297. package/dist/features/selection/enteredGroupScope.js +44 -0
  298. package/dist/features/selection/enteredGroupScope.js.map +1 -0
  299. package/dist/features/text/sceneTextDrafts.js +64 -0
  300. package/dist/features/text/sceneTextDrafts.js.map +1 -0
  301. package/dist/features/text/textHitAt.js +34 -3
  302. package/dist/features/text/textHitAt.js.map +1 -1
  303. package/dist/features/text/useSceneTextEdit.js +23 -19
  304. package/dist/features/text/useSceneTextEdit.js.map +1 -1
  305. package/dist/features/viewports/aim.js +105 -0
  306. package/dist/features/viewports/aim.js.map +1 -0
  307. package/dist/features/viewports/viewMarker.js +82 -0
  308. package/dist/features/viewports/viewMarker.js.map +1 -0
  309. package/dist/features/viewports/viewMarkerDrag.js +55 -0
  310. package/dist/features/viewports/viewMarkerDrag.js.map +1 -0
  311. package/dist/index.d.ts +709 -258
  312. package/dist/index.js +33 -12
  313. package/dist/interactions/actions/admitsChild.js +11 -0
  314. package/dist/interactions/actions/admitsChild.js.map +1 -0
  315. package/dist/interactions/actions/booleans/booleans.js +2 -1
  316. package/dist/interactions/actions/booleans/booleans.js.map +1 -1
  317. package/dist/interactions/actions/booleans/operands.js +6 -5
  318. package/dist/interactions/actions/booleans/operands.js.map +1 -1
  319. package/dist/interactions/actions/clipboard/action.js +27 -17
  320. package/dist/interactions/actions/clipboard/action.js.map +1 -1
  321. package/dist/interactions/actions/defaults/anchorEditing.js +44 -23
  322. package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
  323. package/dist/interactions/actions/defaults/anchorJoin.js +12 -6
  324. package/dist/interactions/actions/defaults/anchorJoin.js.map +1 -1
  325. package/dist/interactions/actions/defaults/bendSegment.js +7 -4
  326. package/dist/interactions/actions/defaults/bendSegment.js.map +1 -1
  327. package/dist/interactions/actions/defaults/cancelGesture.js +11 -8
  328. package/dist/interactions/actions/defaults/cancelGesture.js.map +1 -1
  329. package/dist/interactions/actions/defaults/contextMenu.js +33 -11
  330. package/dist/interactions/actions/defaults/contextMenu.js.map +1 -1
  331. package/dist/interactions/actions/defaults/createPaintAction.js +5 -2
  332. package/dist/interactions/actions/defaults/createPaintAction.js.map +1 -1
  333. package/dist/interactions/actions/defaults/delete.js +28 -14
  334. package/dist/interactions/actions/defaults/delete.js.map +1 -1
  335. package/dist/interactions/actions/defaults/duplicate.js +11 -8
  336. package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
  337. package/dist/interactions/actions/defaults/enterGroup.js +88 -0
  338. package/dist/interactions/actions/defaults/enterGroup.js.map +1 -0
  339. package/dist/interactions/actions/defaults/enterPathEdit.js +20 -10
  340. package/dist/interactions/actions/defaults/enterPathEdit.js.map +1 -1
  341. package/dist/interactions/actions/defaults/enterTextEdit.js +1 -1
  342. package/dist/interactions/actions/defaults/enterTextEdit.js.map +1 -1
  343. package/dist/interactions/actions/defaults/escape.js +11 -8
  344. package/dist/interactions/actions/defaults/escape.js.map +1 -1
  345. package/dist/interactions/actions/defaults/exitPathEdit.js +7 -4
  346. package/dist/interactions/actions/defaults/exitPathEdit.js.map +1 -1
  347. package/dist/interactions/actions/defaults/group.js +26 -20
  348. package/dist/interactions/actions/defaults/group.js.map +1 -1
  349. package/dist/interactions/actions/defaults/ingest.js +10 -4
  350. package/dist/interactions/actions/defaults/ingest.js.map +1 -1
  351. package/dist/interactions/actions/defaults/insertPathAnchor.js +7 -4
  352. package/dist/interactions/actions/defaults/insertPathAnchor.js.map +1 -1
  353. package/dist/interactions/actions/defaults/nudge.js +8 -2
  354. package/dist/interactions/actions/defaults/nudge.js.map +1 -1
  355. package/dist/interactions/actions/defaults/pathEditTools.js +11 -8
  356. package/dist/interactions/actions/defaults/pathEditTools.js.map +1 -1
  357. package/dist/interactions/actions/defaults/pinchZoom.js +7 -4
  358. package/dist/interactions/actions/defaults/pinchZoom.js.map +1 -1
  359. package/dist/interactions/actions/defaults/reorder.js +24 -6
  360. package/dist/interactions/actions/defaults/reorder.js.map +1 -1
  361. package/dist/interactions/actions/defaults/selectAll.js +15 -8
  362. package/dist/interactions/actions/defaults/selectAll.js.map +1 -1
  363. package/dist/interactions/actions/defaults/selectDeep.js +35 -0
  364. package/dist/interactions/actions/defaults/selectDeep.js.map +1 -0
  365. package/dist/interactions/actions/defaults/setFill.js +7 -3
  366. package/dist/interactions/actions/defaults/setFill.js.map +1 -1
  367. package/dist/interactions/actions/defaults/setFillOpacity.js +1 -1
  368. package/dist/interactions/actions/defaults/setStroke.js +12 -11
  369. package/dist/interactions/actions/defaults/setStroke.js.map +1 -1
  370. package/dist/interactions/actions/defaults/setStrokeOpacity.js +1 -1
  371. package/dist/interactions/actions/defaults/toolActivate.js +7 -1
  372. package/dist/interactions/actions/defaults/toolActivate.js.map +1 -1
  373. package/dist/interactions/actions/defaults/toolResetToDefault.js +7 -4
  374. package/dist/interactions/actions/defaults/toolResetToDefault.js.map +1 -1
  375. package/dist/interactions/actions/defaults/undoRedo.js +19 -13
  376. package/dist/interactions/actions/defaults/undoRedo.js.map +1 -1
  377. package/dist/interactions/actions/defaults/viewportWheelPan.js +16 -7
  378. package/dist/interactions/actions/defaults/viewportWheelPan.js.map +1 -1
  379. package/dist/interactions/actions/defaults/viewportZoom.js +24 -6
  380. package/dist/interactions/actions/defaults/viewportZoom.js.map +1 -1
  381. package/dist/interactions/actions/drawnBoxes.js +2 -1
  382. package/dist/interactions/actions/drawnBoxes.js.map +1 -1
  383. package/dist/interactions/actions/edit-anchors/action.js +7 -4
  384. package/dist/interactions/actions/edit-anchors/action.js.map +1 -1
  385. package/dist/interactions/actions/flip/action.js +8 -2
  386. package/dist/interactions/actions/flip/action.js.map +1 -1
  387. package/dist/interactions/actions/insert/action.js +1 -0
  388. package/dist/interactions/actions/insert/action.js.map +1 -1
  389. package/dist/interactions/actions/insert/parent.js +7 -5
  390. package/dist/interactions/actions/insert/parent.js.map +1 -1
  391. package/dist/interactions/actions/insert/placeAction.js +3 -2
  392. package/dist/interactions/actions/insert/placeAction.js.map +1 -1
  393. package/dist/interactions/actions/insert/repositionAction.js +2 -1
  394. package/dist/interactions/actions/insert/repositionAction.js.map +1 -1
  395. package/dist/interactions/actions/insert/session.js +2 -2
  396. package/dist/interactions/actions/insert/session.js.map +1 -1
  397. package/dist/interactions/actions/move/action.js +7 -4
  398. package/dist/interactions/actions/move/action.js.map +1 -1
  399. package/dist/interactions/actions/move/commit/reparent.js +1 -1
  400. package/dist/interactions/actions/move/commit/reparent.js.map +1 -1
  401. package/dist/interactions/actions/move/gestureAdapter.js +6 -7
  402. package/dist/interactions/actions/move/gestureAdapter.js.map +1 -1
  403. package/dist/interactions/actions/move/layout/exits.js +1 -1
  404. package/dist/interactions/actions/move/layout/exits.js.map +1 -1
  405. package/dist/interactions/actions/move/session.js +4 -4
  406. package/dist/interactions/actions/move/session.js.map +1 -1
  407. package/dist/interactions/actions/outlines/createOutlinesAction.js +8 -5
  408. package/dist/interactions/actions/outlines/createOutlinesAction.js.map +1 -1
  409. package/dist/interactions/actions/outlines/outlineStroke.js +5 -3
  410. package/dist/interactions/actions/outlines/outlineStroke.js.map +1 -1
  411. package/dist/interactions/actions/pathCommands/cutJoin.js +8 -5
  412. package/dist/interactions/actions/pathCommands/cutJoin.js.map +1 -1
  413. package/dist/interactions/actions/pathCommands/pathNodes.js +14 -5
  414. package/dist/interactions/actions/pathCommands/pathNodes.js.map +1 -1
  415. package/dist/interactions/actions/resize/cornerHandles.js +2 -1
  416. package/dist/interactions/actions/resize/cornerHandles.js.map +1 -1
  417. package/dist/interactions/actions/selectionSubtree.js +12 -7
  418. package/dist/interactions/actions/selectionSubtree.js.map +1 -1
  419. package/dist/interactions/actions/transform-again/action.js +8 -5
  420. package/dist/interactions/actions/transform-again/action.js.map +1 -1
  421. package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js +14 -2
  422. package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js.map +1 -1
  423. package/dist/json.d.ts +4 -0
  424. package/dist/json.js +3 -0
  425. package/dist/kitSettingsSchema.js +8 -1
  426. package/dist/kitSettingsSchema.js.map +1 -1
  427. package/dist/{math-CrJULaZI.d.ts → math-C_elShP_.d.ts} +7 -1
  428. package/dist/math.d.ts +1 -1
  429. package/dist/math.js +2 -1
  430. package/dist/move.d.ts +4 -4
  431. package/dist/{options-DBxEryYq.d.ts → options-ClEm3SEl.d.ts} +1 -1
  432. package/dist/resize.d.ts +4 -4
  433. package/dist/test-seams.d.ts +6 -6
  434. package/dist/tools/builtin/ellipse/useEllipseTool.js +1 -0
  435. package/dist/tools/builtin/ellipse/useEllipseTool.js.map +1 -1
  436. package/dist/tools/builtin/eyedropper/useEyedropperTool.js +2 -1
  437. package/dist/tools/builtin/eyedropper/useEyedropperTool.js.map +1 -1
  438. package/dist/tools/builtin/hand/dragPanContribution.js +4 -1
  439. package/dist/tools/builtin/hand/dragPanContribution.js.map +1 -1
  440. package/dist/tools/builtin/hand/useHandTool.js +7 -4
  441. package/dist/tools/builtin/hand/useHandTool.js.map +1 -1
  442. package/dist/tools/builtin/image/useImageTool.js +1 -0
  443. package/dist/tools/builtin/image/useImageTool.js.map +1 -1
  444. package/dist/tools/builtin/lasso/useLassoTool.js +1 -0
  445. package/dist/tools/builtin/lasso/useLassoTool.js.map +1 -1
  446. package/dist/tools/builtin/line/useLineTool.js +1 -0
  447. package/dist/tools/builtin/line/useLineTool.js.map +1 -1
  448. package/dist/tools/builtin/measure/useMeasureTool.js +12 -5
  449. package/dist/tools/builtin/measure/useMeasureTool.js.map +1 -1
  450. package/dist/tools/builtin/pen/usePenTool.js +14 -6
  451. package/dist/tools/builtin/pen/usePenTool.js.map +1 -1
  452. package/dist/tools/builtin/pencil/usePencilTool.js +1 -0
  453. package/dist/tools/builtin/pencil/usePencilTool.js.map +1 -1
  454. package/dist/tools/builtin/polygon/usePolygonTool.js +13 -4
  455. package/dist/tools/builtin/polygon/usePolygonTool.js.map +1 -1
  456. package/dist/tools/builtin/pressTarget.js +22 -0
  457. package/dist/tools/builtin/pressTarget.js.map +1 -0
  458. package/dist/tools/builtin/rect/useRectTool.js +1 -0
  459. package/dist/tools/builtin/rect/useRectTool.js.map +1 -1
  460. package/dist/tools/builtin/select/adapterPointQuery.js +6 -5
  461. package/dist/tools/builtin/select/adapterPointQuery.js.map +1 -1
  462. package/dist/tools/builtin/select/collapseDeferred.js +40 -0
  463. package/dist/tools/builtin/select/collapseDeferred.js.map +1 -0
  464. package/dist/tools/builtin/select/groupEntry.js +82 -0
  465. package/dist/tools/builtin/select/groupEntry.js.map +1 -0
  466. package/dist/tools/builtin/select/pressDecision.js +32 -0
  467. package/dist/tools/builtin/select/pressDecision.js.map +1 -0
  468. package/dist/tools/builtin/select/selectionContributions.js +22 -13
  469. package/dist/tools/builtin/select/selectionContributions.js.map +1 -1
  470. package/dist/tools/builtin/select/useSelectTool.js +74 -55
  471. package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
  472. package/dist/tools/builtin/slice/useSliceTool.js +22 -8
  473. package/dist/tools/builtin/slice/useSliceTool.js.map +1 -1
  474. package/dist/tools/builtin/star/useStarTool.js +13 -4
  475. package/dist/tools/builtin/star/useStarTool.js.map +1 -1
  476. package/dist/tools/builtin/sticky/stickyNode.js +4 -1
  477. package/dist/tools/builtin/sticky/stickyNode.js.map +1 -1
  478. package/dist/tools/builtin/sticky/useStickyTool.js +10 -5
  479. package/dist/tools/builtin/sticky/useStickyTool.js.map +1 -1
  480. package/dist/tools/builtin/text/useTextTool.js +3 -1
  481. package/dist/tools/builtin/text/useTextTool.js.map +1 -1
  482. package/dist/{types-7puAuVD2.d.ts → types-49uYJN3j.d.ts} +1 -1
  483. package/dist/{types-CniVctmk.d.ts → types-BEdRIhNc.d.ts} +272 -109
  484. package/dist/{types-oFeQ5VjV.d.ts → types-p2alu-R1.d.ts} +1 -1
  485. package/dist/util/paint.js +1 -3
  486. package/dist/util/paint.js.map +1 -1
  487. package/dist/version.js +1 -1
  488. package/package.json +29 -25
@@ -1 +1 @@
1
- {"version":3,"file":"treeEdits.js","names":[],"sources":["../../../src/core/scene/treeEdits.ts"],"sourcesContent":["/**\n * The scene's edits to its tree: adding and removing nodes, reparenting, and\n * reordering siblings. Each checks its arguments, then records one kit op.\n */\nimport type { ArrivalWindow } from './arrivalWindow';\nimport type { RemoveSnapshot } from './kitOps';\nimport type { createLayerStack } from './layerStack';\nimport type { LockGuard } from './lockGuard';\nimport { fnKeysOf, type FnBearingNode, type NodeFnField } from './nodeFnFields';\nimport type { createNodeStore } from './nodeStore';\nimport type { Recorder } from './recorder';\nimport { SceneMembershipError } from './staleReplay';\nimport { statedFields } from './statedFields';\nimport type { AddNodeSpec, Node, NodeId, Scene } from './types';\n\nexport interface TreeEditsEnv<TData, TLayer extends string, TPose> {\n nodes: ReadonlyMap<NodeId, Node<TData, TLayer, TPose>>;\n generateId(): NodeId;\n store: Pick<\n ReturnType<typeof createNodeStore<TData, TLayer, TPose>>,\n 'requireNode' | 'siblingsOf' | 'descendants' | 'removalClosure' | 'patchNodeFunctions'\n >;\n locks: Pick<LockGuard<TLayer>, 'refuse' | 'refuseLayer' | 'lifted'>;\n layers: Pick<ReturnType<typeof createLayerStack<TLayer>>, 'requireLayerIndex' | 'assertSubtreeLayer'>;\n fnFields: readonly NodeFnField[];\n /** Run a registered op without recording it. */\n runOp(kind: string, payload: unknown): void;\n recorder: Pick<Recorder, 'record' | 'edit'>;\n arrivals: Pick<ArrivalWindow<TPose>, 'noteArrival' | 'noteDeparture' | 'noteChange'>;\n}\n\nexport interface TreeEdits<TData, TLayer extends string, TPose>\n extends Pick<Scene<TData, TLayer, TPose>, 'add' | 'remove' | 'removalClosure' | 'removeMany' | 'move' | 'reorder'> {\n /** Place nodes as the document a scene starts from, or is loaded with:\n * nothing is recorded, and no lock refuses. Specs list parents before\n * children, the order `toJSON()` emits. */\n construct(specs: readonly AddNodeSpec<TData, TLayer, TPose>[]): void;\n}\n\n/** What `kit:remove` carries for the nodes `ids`, read before any of them\n * goes. Throws on an id that is not in the scene. */\nfunction removeSnapshot<TData, TLayer extends string, TPose>(\n ids: readonly NodeId[],\n removing: ReadonlySet<NodeId>,\n store: Pick<TreeEditsEnv<TData, TLayer, TPose>['store'], 'requireNode' | 'siblingsOf'>,\n fnFields: readonly NodeFnField[],\n): RemoveSnapshot<TData, TLayer, TPose> {\n const nodes: Node<TData, TLayer, TPose>[] = ids.map((nid) => {\n const n = store.requireNode(nid);\n return n.kind === 'container' ? { ...n, children: [...n.children] } : { ...n };\n });\n // The rest is reachable through a snapshotted parent's cloned `children`.\n const detached = nodes\n .filter((n) => n.parent === null || !removing.has(n.parent))\n .map((n) => ({ id: n.id, parent: n.parent, index: store.siblingsOf(n.parent).indexOf(n.id) }))\n .sort((l, r) => l.index - r.index);\n const fnKeys = nodes.flatMap((n) => {\n const keys = fnKeysOf(fnFields, n as unknown as FnBearingNode, 'opKey');\n return Object.keys(keys).length === 0 ? [] : [{ id: n.id, ...keys }];\n });\n return { nodes, detached, ...(fnKeys.length > 0 ? { fnKeys } : {}) };\n}\n\nexport function createTreeEdits<TData, TLayer extends string, TPose>(\n env: TreeEditsEnv<TData, TLayer, TPose>,\n): TreeEdits<TData, TLayer, TPose> {\n const { nodes, fnFields } = env;\n const { requireNode, siblingsOf, descendants, removalClosure, patchNodeFunctions } = env.store;\n const { refuse, refuseLayer } = env.locks;\n const { requireLayerIndex, assertSubtreeLayer } = env.layers;\n const { record, edit } = env.recorder;\n const { noteArrival, noteDeparture, noteChange } = env.arrivals;\n\n /** Check that `spec` can be placed, and answer with the `kit:add` payload\n * that places it. Throws on an id collision, an unknown or locked layer,\n * a parent that is not a container or is locked, and a layer below the\n * parent's. */\n function addPayload(spec: AddNodeSpec<TData, TLayer, TPose>) {\n const id = spec.id ?? env.generateId();\n if (nodes.has(id)) throw new SceneMembershipError(`Scene: id collision on \"${id}\"`);\n requireLayerIndex(spec.layer);\n refuseLayer(spec.layer, 'add to');\n const parent = spec.parent ?? null;\n if (parent !== null) {\n const p = requireNode(parent);\n if (p.kind !== 'container') {\n throw new Error(`Scene: parent \"${parent}\" is not a container`);\n }\n assertSubtreeLayer(spec.id, spec.layer, parent, p.layer);\n refuse(parent, 'add under');\n }\n return {\n id, kind: spec.kind, layer: spec.layer, pose: spec.pose, data: spec.data,\n parent, index: spec.index ?? siblingsOf(parent).length,\n ...(spec.dependsOn !== undefined ? { dependsOn: spec.dependsOn } : {}),\n ...statedFields(spec),\n ...fnKeysOf(fnFields, { ...spec, id, kind: spec.kind } as unknown as FnBearingNode, 'opKey'),\n };\n }\n\n function removeMany(rootIds: readonly NodeId[]): void {\n if (rootIds.length === 0) return;\n // Every id resolves against the pre-removal tree, so an id another one\n // cascades away is absorbed rather than being a second, failing removal.\n const { ids, removing } = removalClosure(rootIds);\n for (const nid of ids) refuse(nid, 'remove');\n const payload = removeSnapshot(ids, removing, env.store, fnFields);\n const { detached } = payload;\n edit('remove', detached.some((d) => d.parent !== null), () => {\n for (const d of detached) noteDeparture(d.id, d.parent, null);\n record('kit:remove', payload, 'remove');\n });\n }\n\n return {\n construct(specs) {\n env.locks.lifted(() => {\n for (const spec of specs) {\n const payload = addPayload(spec);\n env.runOp('kit:add', payload);\n patchNodeFunctions(spec, payload.id, null);\n }\n });\n },\n\n add(spec) {\n const payload = addPayload(spec);\n const label = `add ${spec.kind}`;\n return edit(label, payload.parent !== null, () => {\n noteArrival(payload.id, payload.parent, null);\n record('kit:add', payload, label);\n patchNodeFunctions(spec, payload.id, payload);\n return payload.id;\n });\n },\n\n remove: (id) => removeMany([id]),\n\n removalClosure: (rootIds) => removalClosure(rootIds).ids,\n\n removeMany,\n\n move(id, parent, index) {\n const node = requireNode(id);\n refuse(id, 'move');\n if (parent !== null) {\n refuse(parent, 'move nodes into');\n const p = requireNode(parent);\n if (p.kind !== 'container') {\n throw new Error(`Scene: parent \"${parent}\" is not a container`);\n }\n assertSubtreeLayer(id, node.layer, parent, p.layer);\n if (parent === id) throw new Error('Scene: cannot parent a node to itself');\n const desc: NodeId[] = [];\n descendants(id, desc);\n if (desc.includes(parent)) {\n throw new Error('Scene: cannot parent a node to its own descendant');\n }\n }\n const fromParent = node.parent;\n edit('move', parent !== null || fromParent !== null, () => {\n const fromIndex = siblingsOf(fromParent).indexOf(id);\n const toIndex = index ?? siblingsOf(parent).length;\n noteArrival(id, parent, fromParent);\n noteDeparture(id, fromParent, parent);\n noteChange(fromParent);\n record('kit:move', { id, fromParent, fromIndex, toParent: parent, toIndex }, 'move');\n });\n },\n\n reorder(id, index) {\n const node = requireNode(id);\n refuse(id, 'reorder');\n const parent = node.parent;\n edit('reorder', parent !== null, () => {\n noteChange(parent);\n const fromIndex = siblingsOf(parent).indexOf(id);\n record('kit:move', { id, fromParent: parent, fromIndex, toParent: parent, toIndex: index }, 'reorder');\n });\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"treeEdits.js","names":[],"sources":["../../../src/core/scene/treeEdits.ts"],"sourcesContent":["/**\n * The scene's edits to its tree: adding and removing nodes, reparenting, and\n * reordering siblings. Each checks its arguments, then records one kit op.\n */\nimport type { ArrivalWindow } from './arrivalWindow';\nimport type { RemoveSnapshot } from './kitOps';\nimport type { createLayerStack } from './layerStack';\nimport type { LockGuard } from './lockGuard';\nimport { fnKeysOf, type FnBearingNode, type NodeFnField } from './nodeFnFields';\nimport type { createNodeStore } from './nodeStore';\nimport type { Recorder } from './recorder';\nimport { SceneMembershipError } from './staleReplay';\nimport { statedFields } from './statedFields';\nimport type { AddNodeSpec, Node, NodeId, Scene } from './types';\n\nexport interface TreeEditsEnv<TData, TLayer extends string, TPose> {\n nodes: ReadonlyMap<NodeId, Node<TData, TLayer, TPose>>;\n generateId(): NodeId;\n store: Pick<\n ReturnType<typeof createNodeStore<TData, TLayer, TPose>>,\n 'requireNode' | 'siblingsOf' | 'descendants' | 'removalClosure' | 'patchNodeFunctions'\n >;\n locks: Pick<LockGuard<TLayer>, 'refuse' | 'refuseLayer' | 'lifted'>;\n layers: Pick<ReturnType<typeof createLayerStack<TLayer>>, 'requireLayerIndex' | 'assertSubtreeLayer'>;\n fnFields: readonly NodeFnField[];\n /** Run a registered op without recording it. */\n runOp(kind: string, payload: unknown): void;\n recorder: Pick<Recorder, 'record' | 'edit'>;\n arrivals: Pick<ArrivalWindow<TPose>, 'noteArrival' | 'noteDeparture' | 'noteChange'>;\n}\n\nexport interface TreeEdits<TData, TLayer extends string, TPose>\n extends Pick<Scene<TData, TLayer, TPose>, 'add' | 'remove' | 'removalClosure' | 'removeMany' | 'move' | 'reorder'> {\n /** Place nodes as the document a scene starts from, or is loaded with:\n * nothing is recorded, and no lock refuses. Specs list parents before\n * children, the order `toJSON()` emits. */\n construct(specs: readonly AddNodeSpec<TData, TLayer, TPose>[]): void;\n}\n\n/** What `kit:remove` carries for the nodes `ids`, read before any of them\n * goes. Throws on an id that is not in the scene. */\nfunction removeSnapshot<TData, TLayer extends string, TPose>(\n ids: readonly NodeId[],\n removing: ReadonlySet<NodeId>,\n store: Pick<TreeEditsEnv<TData, TLayer, TPose>['store'], 'requireNode' | 'siblingsOf'>,\n fnFields: readonly NodeFnField[],\n): RemoveSnapshot<TData, TLayer, TPose> {\n const nodes: Node<TData, TLayer, TPose>[] = ids.map((nid) => {\n const n = store.requireNode(nid);\n return n.children ? { ...n, children: [...n.children] } : { ...n };\n });\n // The rest is reachable through a snapshotted parent's cloned `children`.\n const detached = nodes\n .filter((n) => n.parent === null || !removing.has(n.parent))\n .map((n) => ({ id: n.id, parent: n.parent, index: store.siblingsOf(n.parent).indexOf(n.id) }))\n .sort((l, r) => l.index - r.index);\n const fnKeys = nodes.flatMap((n) => {\n const keys = fnKeysOf(fnFields, n as unknown as FnBearingNode, 'opKey');\n return Object.keys(keys).length === 0 ? [] : [{ id: n.id, ...keys }];\n });\n return { nodes, detached, ...(fnKeys.length > 0 ? { fnKeys } : {}) };\n}\n\nexport function createTreeEdits<TData, TLayer extends string, TPose>(\n env: TreeEditsEnv<TData, TLayer, TPose>,\n): TreeEdits<TData, TLayer, TPose> {\n const { nodes, fnFields } = env;\n const { requireNode, siblingsOf, descendants, removalClosure, patchNodeFunctions } = env.store;\n const { refuse, refuseLayer } = env.locks;\n const { requireLayerIndex, assertSubtreeLayer } = env.layers;\n const { record, edit } = env.recorder;\n const { noteArrival, noteDeparture, noteChange } = env.arrivals;\n\n /** Check that `spec` can be placed, and answer with the `kit:add` payload\n * that places it. Throws on an id collision, an unknown or locked layer,\n * a locked parent, and a layer below the\n * parent's. */\n function addPayload(spec: AddNodeSpec<TData, TLayer, TPose>) {\n const id = spec.id ?? env.generateId();\n if (nodes.has(id)) throw new SceneMembershipError(`Scene: id collision on \"${id}\"`);\n requireLayerIndex(spec.layer);\n refuseLayer(spec.layer, 'add to');\n const parent = spec.parent ?? null;\n if (parent !== null) {\n const p = requireNode(parent);\n assertSubtreeLayer(spec.id, spec.layer, parent, p.layer);\n refuse(parent, 'add under');\n }\n return {\n id, kind: spec.kind, layer: spec.layer, pose: spec.pose, data: spec.data,\n parent, index: spec.index ?? siblingsOf(parent).length,\n ...(spec.dependsOn !== undefined ? { dependsOn: spec.dependsOn } : {}),\n ...statedFields(spec),\n ...fnKeysOf(fnFields, { ...spec, id, kind: spec.kind } as unknown as FnBearingNode, 'opKey'),\n };\n }\n\n function removeMany(rootIds: readonly NodeId[]): void {\n if (rootIds.length === 0) return;\n // Every id resolves against the pre-removal tree, so an id another one\n // cascades away is absorbed rather than being a second, failing removal.\n const { ids, removing } = removalClosure(rootIds);\n for (const nid of ids) refuse(nid, 'remove');\n const payload = removeSnapshot(ids, removing, env.store, fnFields);\n const { detached } = payload;\n edit('remove', detached.some((d) => d.parent !== null), () => {\n for (const d of detached) noteDeparture(d.id, d.parent, null);\n record('kit:remove', payload, 'remove');\n });\n }\n\n return {\n construct(specs) {\n env.locks.lifted(() => {\n for (const spec of specs) {\n const payload = addPayload(spec);\n env.runOp('kit:add', payload);\n patchNodeFunctions(spec, payload.id, null);\n }\n });\n },\n\n add(spec) {\n const payload = addPayload(spec);\n const label = `add ${spec.kind}`;\n return edit(label, payload.parent !== null, () => {\n noteArrival(payload.id, payload.parent, null);\n record('kit:add', payload, label);\n patchNodeFunctions(spec, payload.id, payload);\n return payload.id;\n });\n },\n\n remove: (id) => removeMany([id]),\n\n removalClosure: (rootIds) => removalClosure(rootIds).ids,\n\n removeMany,\n\n move(id, parent, index) {\n const node = requireNode(id);\n refuse(id, 'move');\n if (parent !== null) {\n refuse(parent, 'move nodes into');\n const p = requireNode(parent);\n assertSubtreeLayer(id, node.layer, parent, p.layer);\n if (parent === id) throw new Error('Scene: cannot parent a node to itself');\n const desc: NodeId[] = [];\n descendants(id, desc);\n if (desc.includes(parent)) {\n throw new Error('Scene: cannot parent a node to its own descendant');\n }\n }\n const fromParent = node.parent;\n edit('move', parent !== null || fromParent !== null, () => {\n const fromIndex = siblingsOf(fromParent).indexOf(id);\n const toIndex = index ?? siblingsOf(parent).length;\n noteArrival(id, parent, fromParent);\n noteDeparture(id, fromParent, parent);\n noteChange(fromParent);\n record('kit:move', { id, fromParent, fromIndex, toParent: parent, toIndex }, 'move');\n });\n },\n\n reorder(id, index) {\n const node = requireNode(id);\n refuse(id, 'reorder');\n const parent = node.parent;\n edit('reorder', parent !== null, () => {\n noteChange(parent);\n const fromIndex = siblingsOf(parent).indexOf(id);\n record('kit:move', { id, fromParent: parent, fromIndex, toParent: parent, toIndex: index }, 'reorder');\n });\n },\n };\n}\n"],"mappings":"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"}
@@ -1,3 +1,5 @@
1
+ import { hasChildren } from "./treeShape.js";
2
+
1
3
  //#region src/core/scene/treeOrder.ts
2
4
  /** Every node reachable from `roots`, in tree preorder: a parent before its
3
5
  * children, siblings in sibling order, whatever layer each sits on. */
@@ -8,7 +10,7 @@ function treePreorder(roots, nodes) {
8
10
  const node = nodes.get(stack.pop());
9
11
  if (!node) continue;
10
12
  out.push(node);
11
- if (node.kind === "container") for (let i = node.children.length - 1; i >= 0; i--) stack.push(node.children[i]);
13
+ if (hasChildren(node)) for (let i = node.children.length - 1; i >= 0; i--) stack.push(node.children[i]);
12
14
  }
13
15
  return out;
14
16
  }
@@ -1 +1 @@
1
- {"version":3,"file":"treeOrder.js","names":[],"sources":["../../../src/core/scene/treeOrder.ts"],"sourcesContent":["import type { Node, NodeId } from './types';\n\n/** Every node reachable from `roots`, in tree preorder: a parent before its\n * children, siblings in sibling order, whatever layer each sits on. */\nexport function treePreorder<TData, TLayer extends string, TPose>(\n roots: readonly NodeId[],\n nodes: ReadonlyMap<NodeId, Node<TData, TLayer, TPose>>,\n): Node<TData, TLayer, TPose>[] {\n const out: Node<TData, TLayer, TPose>[] = [];\n const stack: NodeId[] = [...roots].reverse();\n while (stack.length > 0) {\n const node = nodes.get(stack.pop()!);\n if (!node) continue;\n out.push(node);\n if (node.kind === 'container') {\n for (let i = node.children.length - 1; i >= 0; i--) stack.push(node.children[i]);\n }\n }\n return out;\n}\n"],"mappings":";;;AAIA,SAAgB,aACd,OACA,OAC8B;CAC9B,MAAM,MAAoC,CAAC;CAC3C,MAAM,QAAkB,CAAC,GAAG,KAAK,CAAC,CAAC,QAAQ;CAC3C,OAAO,MAAM,SAAS,GAAG;EACvB,MAAM,OAAO,MAAM,IAAI,MAAM,IAAI,CAAE;EACnC,IAAI,CAAC,MAAM;EACX,IAAI,KAAK,IAAI;EACb,IAAI,KAAK,SAAS,aAChB,KAAK,IAAI,IAAI,KAAK,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK,MAAM,KAAK,KAAK,SAAS,EAAE;CAEnF;CACA,OAAO;AACT"}
1
+ {"version":3,"file":"treeOrder.js","names":[],"sources":["../../../src/core/scene/treeOrder.ts"],"sourcesContent":["import type { Node, NodeId } from './types';\nimport { hasChildren } from './treeShape';\n\n/** Every node reachable from `roots`, in tree preorder: a parent before its\n * children, siblings in sibling order, whatever layer each sits on. */\nexport function treePreorder<TData, TLayer extends string, TPose>(\n roots: readonly NodeId[],\n nodes: ReadonlyMap<NodeId, Node<TData, TLayer, TPose>>,\n): Node<TData, TLayer, TPose>[] {\n const out: Node<TData, TLayer, TPose>[] = [];\n const stack: NodeId[] = [...roots].reverse();\n while (stack.length > 0) {\n const node = nodes.get(stack.pop()!);\n if (!node) continue;\n out.push(node);\n if (hasChildren(node)) {\n for (let i = node.children.length - 1; i >= 0; i--) stack.push(node.children[i]);\n }\n }\n return out;\n}\n"],"mappings":";;;;;AAKA,SAAgB,aACd,OACA,OAC8B;CAC9B,MAAM,MAAoC,CAAC;CAC3C,MAAM,QAAkB,CAAC,GAAG,KAAK,CAAC,CAAC,QAAQ;CAC3C,OAAO,MAAM,SAAS,GAAG;EACvB,MAAM,OAAO,MAAM,IAAI,MAAM,IAAI,CAAE;EACnC,IAAI,CAAC,MAAM;EACX,IAAI,KAAK,IAAI;EACb,IAAI,YAAY,IAAI,GAClB,KAAK,IAAI,IAAI,KAAK,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK,MAAM,KAAK,KAAK,SAAS,EAAE;CAEnF;CACA,OAAO;AACT"}
@@ -15,8 +15,7 @@ function createTreeReads(nodes) {
15
15
  return token;
16
16
  },
17
17
  childrenOf(id) {
18
- const n = nodes.get(id);
19
- return n && n.kind === "container" ? n.children : [];
18
+ return nodes.get(id)?.children ?? [];
20
19
  },
21
20
  ancestorsOf(id) {
22
21
  const out = [];
@@ -1 +1 @@
1
- {"version":3,"file":"treeReads.js","names":[],"sources":["../../../src/core/scene/treeReads.ts"],"sourcesContent":["/** The scene's reads of one node and its place in the tree. */\nimport type { Node, NodeId, Scene } from './types';\n\nexport function createTreeReads<TData, TLayer extends string, TPose>(\n nodes: ReadonlyMap<NodeId, Node<TData, TLayer, TPose>>,\n): Pick<Scene<TData, TLayer, TPose>, 'get' | 'incarnation' | 'childrenOf' | 'ancestorsOf'> {\n // Keyed by node object: every entry into `nodes` is a fresh object and\n // every edit mutates it in place, which is exactly an incarnation's lifetime.\n const incarnations = new WeakMap<object, number>();\n let nextIncarnation = 1;\n\n return {\n get: (id) => nodes.get(id),\n\n incarnation(id) {\n const node = nodes.get(id);\n if (!node) return undefined;\n let token = incarnations.get(node);\n if (token === undefined) {\n token = nextIncarnation++;\n incarnations.set(node, token);\n }\n return token;\n },\n\n childrenOf(id) {\n const n = nodes.get(id);\n return n && n.kind === 'container' ? n.children : [];\n },\n\n ancestorsOf(id) {\n const out: NodeId[] = [];\n let cur = nodes.get(id)?.parent ?? null;\n while (cur !== null) {\n out.push(cur);\n cur = nodes.get(cur)?.parent ?? null;\n }\n return out;\n },\n };\n}\n"],"mappings":";AAGA,SAAgB,gBACd,OACyF;CAGzF,MAAM,+BAAe,IAAI,QAAwB;CACjD,IAAI,kBAAkB;CAEtB,OAAO;EACL,MAAM,OAAO,MAAM,IAAI,EAAE;EAEzB,YAAY,IAAI;GACd,MAAM,OAAO,MAAM,IAAI,EAAE;GACzB,IAAI,CAAC,MAAM,OAAO;GAClB,IAAI,QAAQ,aAAa,IAAI,IAAI;GACjC,IAAI,UAAU,QAAW;IACvB,QAAQ;IACR,aAAa,IAAI,MAAM,KAAK;GAC9B;GACA,OAAO;EACT;EAEA,WAAW,IAAI;GACb,MAAM,IAAI,MAAM,IAAI,EAAE;GACtB,OAAO,KAAK,EAAE,SAAS,cAAc,EAAE,WAAW,CAAC;EACrD;EAEA,YAAY,IAAI;GACd,MAAM,MAAgB,CAAC;GACvB,IAAI,MAAM,MAAM,IAAI,EAAE,CAAC,EAAE,UAAU;GACnC,OAAO,QAAQ,MAAM;IACnB,IAAI,KAAK,GAAG;IACZ,MAAM,MAAM,IAAI,GAAG,CAAC,EAAE,UAAU;GAClC;GACA,OAAO;EACT;CACF;AACF"}
1
+ {"version":3,"file":"treeReads.js","names":[],"sources":["../../../src/core/scene/treeReads.ts"],"sourcesContent":["/** The scene's reads of one node and its place in the tree. */\nimport type { Node, NodeId, Scene } from './types';\n\nexport function createTreeReads<TData, TLayer extends string, TPose>(\n nodes: ReadonlyMap<NodeId, Node<TData, TLayer, TPose>>,\n): Pick<Scene<TData, TLayer, TPose>, 'get' | 'incarnation' | 'childrenOf' | 'ancestorsOf'> {\n // Keyed by node object: every entry into `nodes` is a fresh object and\n // every edit mutates it in place, which is exactly an incarnation's lifetime.\n const incarnations = new WeakMap<object, number>();\n let nextIncarnation = 1;\n\n return {\n get: (id) => nodes.get(id),\n\n incarnation(id) {\n const node = nodes.get(id);\n if (!node) return undefined;\n let token = incarnations.get(node);\n if (token === undefined) {\n token = nextIncarnation++;\n incarnations.set(node, token);\n }\n return token;\n },\n\n childrenOf(id) {\n const n = nodes.get(id);\n return n?.children ?? [];\n },\n\n ancestorsOf(id) {\n const out: NodeId[] = [];\n let cur = nodes.get(id)?.parent ?? null;\n while (cur !== null) {\n out.push(cur);\n cur = nodes.get(cur)?.parent ?? null;\n }\n return out;\n },\n };\n}\n"],"mappings":";AAGA,SAAgB,gBACd,OACyF;CAGzF,MAAM,+BAAe,IAAI,QAAwB;CACjD,IAAI,kBAAkB;CAEtB,OAAO;EACL,MAAM,OAAO,MAAM,IAAI,EAAE;EAEzB,YAAY,IAAI;GACd,MAAM,OAAO,MAAM,IAAI,EAAE;GACzB,IAAI,CAAC,MAAM,OAAO;GAClB,IAAI,QAAQ,aAAa,IAAI,IAAI;GACjC,IAAI,UAAU,QAAW;IACvB,QAAQ;IACR,aAAa,IAAI,MAAM,KAAK;GAC9B;GACA,OAAO;EACT;EAEA,WAAW,IAAI;GAEb,OADU,MAAM,IAAI,EACb,CAAA,EAAG,YAAY,CAAC;EACzB;EAEA,YAAY,IAAI;GACd,MAAM,MAAgB,CAAC;GACvB,IAAI,MAAM,MAAM,IAAI,EAAE,CAAC,EAAE,UAAU;GACnC,OAAO,QAAQ,MAAM;IACnB,IAAI,KAAK,GAAG;IACZ,MAAM,MAAM,IAAI,GAAG,CAAC,EAAE,UAAU;GAClC;GACA,OAAO;EACT;CACF;AACF"}
@@ -0,0 +1,13 @@
1
+ //#region src/core/scene/treeShape.ts
2
+ /** True for a node with no children: absent and empty are the same thing. */
3
+ function isLeaf(node) {
4
+ return node.children === void 0 || node.children.length === 0;
5
+ }
6
+ /** True for a node with at least one child; narrows `children` to present. */
7
+ function hasChildren(node) {
8
+ return node.children !== void 0 && node.children.length > 0;
9
+ }
10
+
11
+ //#endregion
12
+ export { hasChildren, isLeaf };
13
+ //# sourceMappingURL=treeShape.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"treeShape.js","names":[],"sources":["../../../src/core/scene/treeShape.ts"],"sourcesContent":["import type { NodeId } from '@weasel-js/routing';\n\n/** True for a node with no children: absent and empty are the same thing. */\nexport function isLeaf(node: { children?: readonly NodeId[] }): boolean {\n return node.children === undefined || node.children.length === 0;\n}\n\n/** True for a node with at least one child; narrows `children` to present. */\nexport function hasChildren<T extends { children?: readonly NodeId[] }>(\n node: T,\n): node is T & { children: NodeId[] } {\n return node.children !== undefined && node.children.length > 0;\n}\n"],"mappings":";;AAGA,SAAgB,OAAO,MAAiD;CACtE,OAAO,KAAK,aAAa,UAAa,KAAK,SAAS,WAAW;AACjE;;AAGA,SAAgB,YACd,MACoC;CACpC,OAAO,KAAK,aAAa,UAAa,KAAK,SAAS,SAAS;AAC/D"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","names":[],"sources":["../../../src/core/scene/types.ts"],"sourcesContent":["import type { History, SerializedHistory } from '@weasel-js/history';\nimport type { SerializedSceneHistory } from './historyForm';\nimport type { ParallaxOpts } from '../viewport/parallax';\nimport type { UnitScope } from '@weasel-js/quantity';\nimport type { LayoutStrategy } from '../../layout/types';\nimport type { AddLayerSpec, LayerRecord, SystemLayerRecord, SystemLayerSpec, UserLayerRecord } from './layerTypes';\nimport type { LayoutFrame, LayoutMove } from './layoutTypes';\nimport type { PoseComposition } from './composeFrame';\nimport type { PoseRemoval, PoseWrite, PoseWriteOrigin, RemovedNode, ScenePoseReports } from './poseWrites';\nimport type { PoseHold, PoseOverride, PoseOverrides } from './poseOverrideTypes';\nimport type { SceneChanges } from './sceneChanges';\nimport type { SerializedLayer, SerializedNode, SerializedScene } from './serializedTypes';\nimport type { UseSceneOptions } from './useSceneOptions';\nimport type { SceneDocValues } from './docValues';\nimport type { SceneEditPasses } from './editPass';\nimport type { RectPose } from '../geometry/rectPose';\nexport type { RectPose, UseSceneOptions };\nexport type { AddLayerSpec, LayerRecord, SystemLayerRecord, SystemLayerSpec, UserLayerRecord };\nexport type { PoseRemoval, PoseWrite, PoseWriteOrigin, RemovedNode, ScenePoseReports };\nexport type { LayoutFrame, LayoutMove, PoseHold, PoseOverride, PoseOverrides, SerializedLayer, SerializedNode, SerializedScene };\n\n/**\n * # SceneNode — the thing in the scene\n *\n * Everything the user sees on the canvas — a shape, a group, an annotation,\n * a tile — is a node. Containers and leaves are both nodes; the kit has no\n * other concept of \"scene element.\"\n *\n * Every node carries three independent slots, plus its tree position:\n *\n * - **`data: TData`** — app-defined payload. The kit never inspects it.\n * - **`pose: TPose`** — local transform, relative to the node's direct\n * parent (or world, for root nodes).\n * - **`layer: TLayer`** — a string tag associating the node with a\n * visual `RenderLayer` at draw time. Separate from `LayerRecord` (the\n * per-layer visible/locked metadata held by the `Scene`).\n *\n * Nodes are addressed by `NodeId` everywhere outside the scene tree. Don't\n * hold references to a `Node` object across scene updates; look up by id\n * when you need the latest.\n *\n * The kit-internal name is `Node`; the public re-export is `SceneNode`\n * (avoids collision with DOM `Node` at call sites).\n */\n\n/** Opaque branded id. Treat as opaque outside the kit. */\nimport type { NodeId } from '@weasel-js/routing';\nexport type { NodeId };\n\n/** Brand a string as a NodeId. */\nexport const asNodeId = (s: string): NodeId => s as NodeId;\n\nimport type { DerivedDep, Bivariant, DerivePathFn, DerivePoseFn, ClipFromPoseFn, CopiesFromPoseFn } from './nodeFnTypes';\nexport type { DerivedDep, Bivariant, DerivePathFn, DerivePoseFn, ClipFromPoseFn, CopiesFromPoseFn };\n\ninterface NodeBase<TData, TLayer extends string, TPose> {\n id: NodeId;\n layer: TLayer;\n pose: TPose;\n data: TData;\n parent: NodeId | null;\n /** Nodes whose poses this node's geometry is computed from. Fixed at add\n * time. Absent or empty means the node's geometry is authored, which is the\n * normal case.\n *\n * `'children'` means \"my own children, in child order\" — a container that\n * hugs its contents, which a fixed id list cannot express because\n * reparenting would have to maintain it. The two forms differ in lifetime\n * as well as in membership: deleting a node deletes everything that names\n * it in `dependsOn`, but a container outlives the children it derives\n * from — an emptied group is still a group. */\n dependsOn?: readonly NodeId[] | 'children';\n /** Whether the hit-test walk can return this node. Default `true`; `false`\n * makes it transparent to picking, so a click on it lands on whatever is\n * behind — normally its own container.\n *\n * For content a container owns rather than content the user selects on its\n * own: the label inside a box, a body's rows. It does not hide the node\n * from anything else — it still paints, still clips, and a consumer can\n * still select it by id. */\n pickable?: boolean;\n /** Not drawn, with its whole subtree: picking, area selection, select-all,\n * snapping and the selection pass over it, and an SVG export writes it with\n * `display=\"none\"`. Absent means shown; `Scene.isHidden` folds in its\n * ancestors' flags and its layer's visibility. */\n hidden?: boolean;\n /** Out of reach, with its whole subtree: still drawn, but picking and area\n * selection pass over it, the selection drops it, and every edit to it\n * throws, as on a locked layer. Absent means unlocked; `Scene.isLocked`\n * folds in its ancestors' and its layer's. */\n locked?: boolean;\n /** Computes this node's path from its dependencies, in `dependsOn` order —\n * each one the node and the pose it is painted at.\n * A dependency that has been removed arrives as `undefined`.\n * Returning `null` means \"nothing to draw right now\". Re-evaluated when a\n * dependency's world pose changes, never authored. */\n derivePath?: DerivePathFn<TPose, TData, TLayer>;\n /** Computes this node's pose from its dependencies' poses, the same way\n * `derivePath` computes its path — same `dependsOn` list, same\n * registry-keyed serialization.\n *\n * Where a derived path is resolved at paint time and reaches only the\n * painter, a derived pose is what the node *is* at: it feeds bounds,\n * hit-testing, selection chrome, snapping and layout, and every reader\n * gets it through `effectivePose`. A pose override still wins over it.\n *\n * Returning `null` means \"I have nothing to derive from right now\", and\n * the node falls back to its authored `pose`. `setPose` on a derived node\n * still writes that authored pose; it is simply not what anything reads. */\n derivePose?: DerivePoseFn<TPose, TData, TLayer>;\n}\n\n/** A node with no children — a shape, a label, an image. */\nexport interface LeafNode<TData, TLayer extends string, TPose = RectPose>\n extends NodeBase<TData, TLayer, TPose> {\n kind: 'leaf';\n}\n\n/** A node with an ordered list of children. This is the real group: what\n * Cmd+G creates, what SVG `<g>` round-trips to. A container has its own pose,\n * which its children's poses are relative to, and may optionally clip them. */\nexport interface ContainerNode<TData, TLayer extends string, TPose = RectPose>\n extends NodeBase<TData, TLayer, TPose> {\n kind: 'container';\n children: NodeId[];\n /** Optional clip-path source. Re-evaluated each render. Returning `null`\n * means \"no clip for this container right now\"; an empty / zero-area path\n * means \"clip everything out\" (children render nowhere). */\n clipFromPose?: ClipFromPoseFn<TPose>;\n /** Draws the whole subtree again under each affine it returns, on top of\n * the original, which still draws once as authored. The copies are paint,\n * not nodes: nothing stores them, so they follow every edit to the\n * children, and a pick that lands on one answers with the child it copies.\n * A copy is a copy of the clipped content — the clip travels with it.\n * Re-evaluated each render; `null` or `[]` draws no copies. */\n copiesFromPose?: CopiesFromPoseFn<TPose, TData, TLayer>;\n /** How this container arranges its children. Whenever the container's\n * child set or order changes, its bounds change size, or a child's pose is\n * written, the scene hands the change to the strategy — `depart` for children that left it,\n * `arrive` for children that joined it, `childPoses` otherwise — and records the poses it writes in the same\n * undo entry as the change. A drag reads it too: it is the layout `move`\n * reflows and drops against. */\n layout?: LayoutStrategy<TPose>;\n /** The units its children are shown, typed and snapped in (their poses stay\n * in world units), and what one base unit is worth in the enclosing scope's.\n * It covers the children, not the container; `sceneUnitScopes` reads the chain. */\n units?: UnitScope;\n}\n\n/** A node in the scene tree: either a leaf or a container. Re-exported\n * publicly as `SceneNode`, to avoid colliding with the DOM's `Node`. */\nexport type Node<TData, TLayer extends string, TPose = RectPose> =\n | LeafNode<TData, TLayer, TPose>\n | ContainerNode<TData, TLayer, TPose>;\n\n/** What `Scene.add` needs to mint a node. Everything except the id is\n * required; the id is generated unless one is supplied. */\nexport interface AddNodeSpec<TData, TLayer extends string, TPose = RectPose> {\n kind: 'leaf' | 'container';\n layer: TLayer;\n pose: TPose;\n data: TData;\n parent?: NodeId | null;\n index?: number;\n /** Explicit id wins over the Scene's `generateId` and the kit default. */\n id?: NodeId;\n /** Mirrors `SceneNode.pickable`. Omit for the default, which is pickable. */\n pickable?: boolean;\n /** Mirrors `SceneNode.hidden`. */\n hidden?: boolean;\n /** Mirrors `SceneNode.locked`. */\n locked?: boolean;\n /** Only meaningful when `kind === 'container'`. Attach a clip-path function\n * to the node; ignored for leaves. Mirrors `ContainerNode.clipFromPose`. */\n clipFromPose?: ClipFromPoseFn<TPose>;\n /** Mirrors `ContainerNode.copiesFromPose`; ignored for leaves. */\n copiesFromPose?: CopiesFromPoseFn<TPose, TData, TLayer>;\n /** Mirrors `SceneNode.dependsOn`. */\n dependsOn?: readonly NodeId[] | 'children';\n /** Mirrors `SceneNode.derivePath`. Taken as a live function; its registry key is\n * looked up from it, never passed in. */\n derivePath?: DerivePathFn<TPose, TData, TLayer>;\n /** Mirrors `SceneNode.derivePose`, on the same terms as `derivePath`. */\n derivePose?: DerivePoseFn<TPose, TData, TLayer>;\n /** Mirrors `ContainerNode.layout`; ignored for leaves. */\n layout?: LayoutStrategy<TPose>;\n /** Mirrors `ContainerNode.units`; ignored for leaves. */\n units?: UnitScope;\n}\n\n/** A custom scene mutation registered with `Scene.registerOp`: how to apply\n * it and how to undo it. The pair is what makes it participate in history. */\nexport interface RegisteredOp<P> {\n apply: (payload: P) => void;\n revert: (payload: P) => void;\n /** Opts this kind into coalescing under `UseSceneOptions.coalesceWindowMs`:\n * two entries of this kind that return the same key inside the window\n * merge, and undoing the merged entry calls `revert` with the first\n * payload only. Declare it only when `revert` restores an absolute state\n * (a stored \"from\" value), never for a relative payload such as a delta.\n * Omitted, or returning `undefined`, the op is always its own entry. */\n coalesceKey?: (payload: P) => string | undefined;\n}\n\n/** Per-scene registry mapping string keys to live function references.\n * Passed to `createScene({ ..., registry })` and `sceneFromJSON(json, { registry })`.\n * Each function-field type has its own keyed map. */\nexport interface SceneRegistry<TPose, TData = unknown, TLayer extends string = string> {\n /** Maps registry keys to `clipFromPose` factory functions for container nodes. */\n clipFromPose?: Readonly<Record<string, ClipFromPoseFn<TPose>>>;\n /** Maps registry keys to `copiesFromPose` functions for container nodes. */\n copiesFromPose?: Readonly<Record<string, CopiesFromPoseFn<TPose, TData, TLayer>>>;\n /** Maps registry keys to `derivePath` functions for nodes with `dependsOn`. */\n derivePath?: Readonly<Record<string, DerivePathFn<TPose, TData, TLayer>>>;\n /** Maps registry keys to `derivePose` functions for nodes with `dependsOn`. */\n derivePose?: Readonly<Record<string, DerivePoseFn<TPose, TData, TLayer>>>;\n /** Maps registry keys to container layout strategies. */\n layout?: Readonly<Record<string, LayoutStrategy<TPose>>>;\n // A new function field is a row in `NODE_FN_FIELDS` (core/scene/nodeFnFields.ts)\n // plus its entry here; the field name is the registry key by construction.\n}\n\n/**\n * Decides what one edit's changes to containers do to their children.\n * `arrivals` maps each container to the ids that joined it — by an `add`\n * under it or a `move` into it, whatever op or call made them — in the order\n * they arrived. `changed` names every container whose child set or order\n * changed, whose pose changed size, or one of whose children was posed,\n * arrivals included. Both are read once the edit's other mutations have landed.\n *\n * Return the poses to write, in each node's stored frame; they become part of\n * the same edit, and so of the same undo step. An empty map writes nothing.\n * Return `null` to refuse: the whole edit is reverted (see\n * `SceneArrivalRefused`).\n *\n * Runs only for live edits. Undo, redo and a restored history replay the\n * arrangement that was decided the first time. With none installed, a scene\n * holding declared layouts (`ContainerNode.layout`) runs its own.\n */\nexport type SceneArrivalHandler<TPose> = (\n arrivals: ReadonlyMap<NodeId, readonly NodeId[]>,\n changed: ReadonlySet<NodeId>,\n /** The nodes that left each container in the edit, by the container they\n * left — removed, or moved out. */\n departures: ReadonlyMap<NodeId, readonly NodeId[]>,\n) => ReadonlyMap<NodeId, TPose> | null;\n\n/**\n * The kit-owned scene tree: nodes, layers, and the undo history over both.\n *\n * A scene is logical, not visual — it says what exists and where, and nothing\n * about how it is painted. Every mutating method is undoable, and reads are\n * snapshots rather than live views. Nodes are addressed by `NodeId`; hold ids\n * across mutations, not node objects.\n *\n * Three type parameters keep it domain-agnostic: `TData` is the app's payload,\n * which the kit never inspects; `TPose` is the transform shape, `RectPose` by\n * default; `TLayer` is the union of layer names.\n */\nexport interface Scene<TData, TLayer extends string, TPose = RectPose>\n extends ScenePoseReports<TPose, Node<TData, TLayer, TPose>>, SceneChanges, SceneDocValues, SceneEditPasses {\n // Reads\n readonly nodes: ReadonlyMap<NodeId, Node<TData, TLayer, TPose>>;\n readonly roots: readonly NodeId[];\n readonly layers: readonly LayerRecord<TLayer>[];\n /** The registry this scene resolves node functions against — the consumer's\n * entries over the kit's. */\n readonly registry: SceneRegistry<TPose, TData, TLayer>;\n /** See {@link UseSceneOptions.composition}. */\n readonly composition: PoseComposition<TPose>;\n get(id: NodeId): Node<TData, TLayer, TPose> | undefined;\n /** A token for the node `id` names right now, for telling one node from a\n * later one that reuses its id. It holds for as long as the node stays in\n * the scene, whatever edits it takes, and is different every time the id\n * enters the scene — by `add`, by an undo or redo that brings it back, or\n * by `loadState`. `undefined` when `id` is not in the scene. Compare tokens\n * for equality only; their order means nothing. */\n incarnation(id: NodeId): number | undefined;\n childrenOf(id: NodeId): readonly NodeId[];\n ancestorsOf(id: NodeId): readonly NodeId[];\n renderOrder(): Iterable<NodeId>;\n /** The same layer-major sequence as {@link Scene.renderOrder}, as the nodes\n * themselves. Prefer this wherever the ids are only going to be resolved\n * back to nodes. Cached until a structural edit, so repeat calls hand back\n * the same array — a snapshot, not a live view, and not yours to mutate. */\n renderOrderNodes(): readonly Node<TData, TLayer, TPose>[];\n /** The nodes on one layer, in render order. Cached alongside\n * {@link Scene.renderOrderNodes} until a structural edit. A snapshot, not\n * a live view, and not yours to mutate. An unknown layer id yields an\n * empty array. */\n nodesOnLayer(layer: TLayer): readonly Node<TData, TLayer, TPose>[];\n\n /** A fresh id from this scene's `generateId`, or the kit default: the id\n * `add` would give a spec with none. For code that has to name a node\n * before it exists, such as one building an insert op. */\n mintId(): NodeId;\n\n // Mutations (all auto-undoable)\n add(spec: AddNodeSpec<TData, TLayer, TPose>): NodeId;\n /** Delete `id`, its **entire subtree**, and **everything that derives from**\n * any of those nodes — a node listing one of them in `dependsOn` goes too,\n * along with its own subtree, transitively. A dependent can live anywhere in\n * the tree, so this deletes nodes the caller never named and may unlink\n * several disjoint subtrees at once. Recorded as one undoable step; `undo()`\n * restores every one of them where it was, child order intact. */\n remove(id: NodeId): void;\n /** {@link remove} over several roots at once, as a **single** undoable step.\n * Ids resolve against the tree as it stands at the call, so an id that\n * another one would cascade away is absorbed rather than removed twice —\n * which is what makes it safe to pass a whole selection. Throws if any id is\n * not in the scene; an empty list does nothing and records no step. */\n removeMany(ids: readonly NodeId[]): void;\n /** Every node {@link removeMany} would take if given `ids`: each id, its\n * subtree, everything deriving from any of those, and those nodes' subtrees\n * in turn. Ids not in the scene come back unchanged.\n *\n * A set, not a restore order: a dependent can be reached before the parent\n * it sits under, which is also in the closure. */\n removalClosure(ids: readonly NodeId[]): readonly NodeId[];\n update(id: NodeId, patch: { data: TData }): void;\n setPose(id: NodeId, pose: TPose): void;\n /** Retag `id` to `layer`. On a **container this cascades**: every descendant\n * is moved to the same layer, recorded as a **single** undo step.\n *\n * Invariants:\n * - **Layer floor** — a child may not render below its parent, so retagging\n * to a layer *below* the node's parent throws. Retagging to the parent's\n * layer or any higher one is allowed; a node with no parent is\n * unconstrained.\n * - **No-op elision** — setting the layer a node already has does nothing\n * and pushes **no** history entry. */\n setLayer(id: NodeId, layer: TLayer): void;\n /** Point `id` at a different set of dependencies — retargeting an edge's end\n * onto another node, or switching a container between an id list and\n * `'children'`. Recorded as one undoable step.\n *\n * Order is significant: a derivation reads its dependencies positionally,\n * so `[b, a]` is not `[a, b]`. Ids need not be in the scene — a declared\n * dependency that appears later invalidates the node when it does, the same\n * as one declared at `add`. Passing `undefined` drops the declaration, after\n * which nothing cascades the node away.\n *\n * **No-op elision** — declaring what the node already declares does nothing\n * and pushes no history entry. */\n setDependsOn(id: NodeId, dependsOn: readonly NodeId[] | 'children' | undefined): void;\n /** Give container `id` a different layout — a strategy, its key in\n * `SceneRegistry.layout`, or `null` for none — and re-arrange its children\n * under it, as one undoable step. Throws for a leaf, or a key the registry\n * does not hold. A registered layout is recorded by its key, so a\n * serialized history restores it; an unregistered one lives only as long\n * as the session.\n *\n * **No-op elision** — setting the layout a container already has does\n * nothing and pushes no history entry. */\n setLayout(id: NodeId, layout: LayoutStrategy<TPose> | string | null): void;\n /** Reparent `id` under `parent` (or to a root when `parent` is `null`) at\n * `index` within the new sibling list, appending when `index` is omitted.\n * Siblings are reindexed. Recorded as one undoable step.\n *\n * Rejected (throws) when:\n * - `parent` exists but is a **leaf**, not a container;\n * - the move would form a **cycle** — `parent` is `id` itself or one of\n * `id`'s own descendants;\n * - it would drop `id` **below its new parent's layer** (child may not\n * render below its parent).\n *\n * `move(id, null)` — detaching to a root — is always allowed regardless of\n * layer, since a root has no parent to render beneath. */\n move(id: NodeId, parent: NodeId | null, index?: number): void;\n /** Shift `id` to `index` within its **current** parent's child list. Unlike\n * {@link move}, the parent never changes — only sibling order. */\n reorder(id: NodeId, index: number): void;\n /** Show or hide a layer. Hiding drops its nodes from the selection. */\n setLayerVisible(layer: TLayer, visible: boolean): void;\n /** Lock or unlock a layer. A locked layer still paints, but its nodes are\n * out of reach: picking and area selection pass over them, the selection\n * drops them, and every node mutation on them throws. Locking is never\n * blocked by the lock, and neither are the other layer operations. */\n setLayerLocked(layer: TLayer, locked: boolean): void;\n /** Set or clear (`undefined`) a layer's parallax — see\n * `LayerRecord.parallax`. Undoable; an animation tweening it writes inside\n * {@link Scene.untracked} so no frame becomes an undo step. */\n setLayerParallax(layer: TLayer, parallax: ParallaxOpts | undefined): void;\n /** Whether `id` is locked: it or an ancestor carries `locked`, or sits on a\n * locked layer. A lock covers its whole subtree, whatever layers the\n * descendants are tagged to. An id not in the scene is not locked. */\n isLocked(id: NodeId): boolean;\n /** Whether `id` is not drawn: its own layer is hidden, or it or an ancestor\n * carries `hidden`. A hidden layer hides only the nodes tagged to it. An\n * id not in the scene is not hidden. */\n isHidden(id: NodeId): boolean;\n /** Hide or show node `id` and its subtree — see `SceneNode.hidden`. One\n * undo step; hiding drops the subtree from the selection. Never blocked by\n * a lock, and setting what the node already has records nothing. */\n setHidden(id: NodeId, hidden: boolean): void;\n /** Lock or unlock node `id` and its subtree — see `SceneNode.locked`. One\n * undo step; locking drops the subtree from the selection. Never blocked\n * by a lock, and setting what the node already has records nothing. */\n setLocked(id: NodeId, locked: boolean): void;\n /** Run `fn` with the lock guard lifted, for a programmatic edit that has to\n * reach a locked node. Undo and redo never need it. */\n unlocked<T>(fn: () => T): T;\n addLayer(spec: AddLayerSpec<TLayer>): void;\n /** Drop a user layer and every node tagged to it, as one undoable step.\n * Removal cascades, so this also deletes nodes **on other layers** that\n * derive from a node on this one. */\n removeLayer(layer: TLayer): void;\n renameLayer(layer: TLayer, name: string): void;\n moveLayer(layer: TLayer, index: number): void;\n\n // Custom op seam\n registerOp<P>(kind: string, handler: RegisteredOp<P>): void;\n recordOp<P>(op: { kind: string; payload: P }): void;\n\n /** Install (or clear with `null`) the active-journal accessor after scene\n * construction, for a journal source built with `scene.history` as a\n * dependency. Overrides any `getActiveJournal` set in `UseSceneOptions`. */\n setActiveJournalAccessor(\n fn: (() => import('@weasel-js/history').Journal | null) | null,\n ): void;\n\n /** Install what the scene does when nodes join a container, replacing any\n * handler installed before; `null` removes it. Returns a function that\n * removes this handler, and does nothing once another has replaced it.\n * See {@link SceneArrivalHandler}. */\n setArrivalHandler(handler: SceneArrivalHandler<TPose> | null): () => void;\n\n /** Apply a batch of ops against `adapter` as one undo entry.\n *\n * The entry holds the scene writes the ops made, as a direct call records\n * them, and beside them any op that wrote no scene state, bound to\n * `adapter`. Undo restores those writes; it does not call an op's\n * `invert()` or the adapter again. **What an op states is not recorded**:\n * one applied over a document that already holds its result, as after an\n * `untracked` preview, records a write from that pose to itself, and undo\n * goes nowhere. Put the document back first.\n *\n * The entry lands in the open `batch` when there is one, else on the\n * active journal, else on the scene's own history, where it can coalesce\n * under `coalesceWindowMs` with one whose ops' keys match.\n *\n * Returns false when the arrival handler refused the batch: it is reverted\n * and unrecorded. Inside `batch` it is true; that batch may still refuse. */\n applyBatch(ops: import('core/ops/types').Op[], label: string, adapter: unknown): boolean;\n\n // Selection\n\n /** The transient set of active ids — \"operate on these N as a unit\".\n * Shared by every view over this scene unless a view supplies its own\n * (see `CanvasView.selection`). Not document content: it never appears\n * in `toJSON`. It does ride on history entries, so undo and redo put\n * back the selection an edit was made under; changing it is never an\n * undo step of its own. Removing a node deselects it. */\n getSelection(): readonly NodeId[];\n setSelection(ids: readonly NodeId[]): void;\n\n // Ephemeral presentation state\n\n /** Per-node pose / alpha overrides the render and hit-test paths read\n * through. Like {@link Scene.getSelection} this is not document content:\n * writes are never recorded, never serialized, and do not bump\n * {@link Scene.getVersion}. See {@link PoseOverrides}. */\n readonly overrides: PoseOverrides<TPose>;\n\n // History\n /** The scene's undo/redo engine, in the shape `@weasel-js/history` defines.\n * Mutating members route through\n * the scene's own wrappers, so an undo driven through this bumps\n * {@link Scene.getVersion} and notifies subscribers exactly as\n * {@link Scene.undo} does; `getVersion` / `subscribe` on it report the\n * engine's history version, not the scene's. */\n readonly history: History;\n undo(): boolean;\n redo(): boolean;\n canUndo(): boolean;\n canRedo(): boolean;\n batch<T>(label: string, fn: () => T, options?: import('./recorder').SceneBatchOptions): T;\n /** The layout container `id` declares, or `null` for a leaf or a\n * container that declares none. */\n layoutOf(id: NodeId): LayoutStrategy<TPose> | null;\n /** Declare the unit scope of container `id`, or clear it with `null`. */\n setUnits(id: NodeId, units: UnitScope | null): void;\n /** Run `fn` with every mutation applied but none recorded — for writes\n * that are not edits, such as a simulation stepping poses each frame.\n * Undo and redo move between recorded states and never restore an\n * untracked write; the next recorded change starts from whatever the\n * untracked writes left, so undoing it lands there. A `batch` inside\n * records nothing; inside a `batch`, these writes stay out of its entry.\n * Notifies once, reverts `fn`'s writes if it throws, and throws on\n * `applyBatch` and `history.apply` / `applyOps`, which exist to record. */\n untracked<T>(fn: () => T): T;\n /** Read-only snapshot of every history entry currently reachable from\n * the present state. Oldest applied first, then redoable entries in\n * the order they'd be re-applied. Each entry id is stable. */\n historyEntries(): readonly { id: string; label: string }[];\n /** Index of the \"current state\". Equals the count of applied entries;\n * `0` means \"nothing applied\" (initial). */\n historyIndex(): number;\n /** Jump to the given history index, clamped to [0, total]. Returns true\n * if any movement occurred. */\n jumpToHistoryIndex(index: number): boolean;\n\n // History persistence\n /** Snapshot the undo/redo history in a JSON-serializable form (the\n * engine's `SerializedHistory`, marked with the scene's recorded form).\n * An entry holds the scene's own ops, whichever way the edit was made.\n * An op handed to `applyBatch` that wrote no scene state is held as\n * itself, and an entry with a nameless one is dropped. Payload\n * JSON-safety (e.g. typed arrays inside poses) is the caller's concern.\n * Do not call mid-`batch` — the open batch's ops are not yet recorded. */\n serializeHistory(): SerializedSceneHistory;\n /** Replace the undo/redo history from a `serializeHistory()` snapshot.\n * Call on a scene whose node/layer state already matches the snapshot's\n * head state (i.e. right after `loadState` from the paired scene\n * snapshot); node state is NOT mutated. Ops re-registered via\n * `registerOp` before this call round-trip; unknown kinds become no-op\n * placeholders; external ops rebuild via the global op-factory registry\n * and replay against the `setHistoryAdapter` accessor. Restored entries\n * never coalesce with new ones. Notifies once. A snapshot that is not in\n * the current recorded form (see `SCENE_HISTORY_FORM`) is dropped:\n * the history is left as it was. Do not call mid-`batch`:\n * the stacks are replaced underneath the open batch, whose eventual\n * flush would graft onto (and evict against) the restored stacks. */\n restoreHistory(snapshot: SerializedHistory): void;\n /** Install (or clear with `null`) the accessor for the adapter that\n * RESTORED external ops apply against on undo/redo: the ops handed to\n * `applyBatch` that wrote no scene state, such as a selection change,\n * rebuilt from a `restoreHistory` snapshot. Resolved lazily at each\n * apply, so wiring order relative to `restoreHistory` doesn't matter.\n * Live `applyBatch` entries are unaffected (they bind their call-site\n * adapter). If unset when a restored op applies, the op is a debug-warned\n * no-op. */\n setHistoryAdapter(fn: (() => unknown) | null): void;\n\n // Serialization\n /** Snapshot the current scene state to a JSON-serializable shape.\n * History (undo/redo stacks) is NOT captured. Function fields like\n * `ContainerNode.clipFromPose` are translated to string keys via the\n * scene's registry; throws if any function field has no matching key. */\n toJSON(): SerializedScene<TData, TLayer, TPose>;\n /** Replace this scene's entire node + layer state in place from a snapshot\n * produced by `toJSON()`. Unlike `sceneFromJSON`, the existing Scene\n * instance is preserved — holders such as `<SceneCanvas>` keep their\n * reference. History (undo/redo) is cleared, matching `sceneFromJSON`.\n * Bumps `getVersion()` and notifies subscribers exactly once. Selected\n * ids the snapshot does not hold leave the selection, and every pose\n * override is cleared.\n *\n * Throws inside any other edit — `batch`, `untracked`, `applyBatch`, an\n * undo — which would revert to a state this replaces. Throws on an unsupported version or unknown registry/layer ids; on a\n * malformed snapshot the scene is left empty or partially populated (callers should treat a\n * `loadState` throw as fatal and reload). Snapshots from `toJSON()` are\n * always well-formed. */\n loadState(json: SerializedScene<TData, TLayer, TPose>): void;\n\n}\n"],"mappings":";;AAkDA,IAAa,YAAY,MAAsB"}
1
+ {"version":3,"file":"types.js","names":[],"sources":["../../../src/core/scene/types.ts"],"sourcesContent":["import type { History, SerializedHistory } from '@weasel-js/history';\nimport type { SerializedSceneHistory } from './historyForm';\nimport type { ParallaxOpts } from '../viewport/parallax';\nimport type { UnitScope } from '@weasel-js/quantity';\nimport type { LayoutStrategy } from '../../layout/types';\nimport type { AddLayerSpec, LayerRecord, SystemLayerRecord, SystemLayerSpec, UserLayerRecord } from './layerTypes';\nimport type { LayoutFrame, LayoutMove } from './layoutTypes';\nimport type { PoseComposition } from './composeFrame';\nimport type { PoseRemoval, PoseWrite, PoseWriteOrigin, RemovedNode, ScenePoseReports } from './poseWrites';\nimport type { PoseHold, PoseOverride, PoseOverrides, ShownOverride } from './poseOverrideTypes';\nimport type { SceneChanges } from './sceneChanges';\nimport type {\n SerializedLayer, SerializedNode, SerializedNodeV1, SerializedScene, SerializedSceneV1,\n} from './serializedTypes';\nimport type { UseSceneOptions } from './useSceneOptions';\nimport type { SceneDocValues } from './docValues';\nimport type { SceneDeclaredFieldSetters } from './declaredFieldSetters';\nimport type { SceneEditPasses } from './editPass';\nimport type { SceneArrivalHandler } from './arrivalTypes';\nimport type { RegisteredOp } from './registeredOp';\nimport type { SpecsFromSerializedOptions } from './serialization';\nimport type { RectPose } from '../geometry/rectPose';\nexport type { RectPose, RegisteredOp, SceneArrivalHandler, UseSceneOptions };\nexport type { AddLayerSpec, LayerRecord, SystemLayerRecord, SystemLayerSpec, UserLayerRecord };\nexport type { PoseRemoval, PoseWrite, PoseWriteOrigin, RemovedNode, ScenePoseReports };\nexport type { LayoutFrame, LayoutMove, PoseHold, PoseOverride, PoseOverrides, ShownOverride, SerializedLayer, SerializedNode, SerializedNodeV1, SerializedScene, SerializedSceneV1 };\n\n/**\n * # SceneNode — the thing in the scene\n *\n * Everything the user sees on the canvas — a shape, a group, an annotation,\n * a tile — is a node. Marks and groups are both nodes; the kit has no\n * other concept of \"scene element.\"\n *\n * Every node carries three independent slots, plus its tree position:\n *\n * - **`data: TData`** — app-defined payload. The kit never inspects it.\n * - **`pose: TPose`** — local transform, relative to the node's direct\n * parent (or world, for root nodes).\n * - **`layer: TLayer`** — a string tag associating the node with a\n * visual `RenderLayer` at draw time. Separate from `LayerRecord` (the\n * per-layer visible/locked metadata held by the `Scene`).\n *\n * Nodes are addressed by `NodeId` everywhere outside the scene tree. Don't\n * hold references to a `Node` object across scene updates; look up by id\n * when you need the latest.\n *\n * The kit-internal name is `Node`; the public re-export is `SceneNode`\n * (avoids collision with DOM `Node` at call sites).\n */\n\n/** Opaque branded id. Treat as opaque outside the kit. */\nimport type { NodeId } from '@weasel-js/routing';\nexport type { NodeId };\n\n/** Brand a string as a NodeId. */\nexport const asNodeId = (s: string): NodeId => s as NodeId;\n\nimport type { DerivedDep, Bivariant, DerivePathFn, DerivePoseFn, ClipFromPoseFn, CopiesFromPoseFn } from './nodeFnTypes';\nexport type { DerivedDep, Bivariant, DerivePathFn, DerivePoseFn, ClipFromPoseFn, CopiesFromPoseFn };\n\n/** A clip a node declares by name rather than by function: see `SceneNode.clip`. */\nexport type NodeClip = 'outline';\n\ninterface NodeBase<TData, TLayer extends string, TPose> {\n id: NodeId;\n layer: TLayer;\n pose: TPose;\n data: TData;\n parent: NodeId | null;\n /** Nodes whose poses this node's geometry is computed from. Fixed at add\n * time. Absent or empty means the node's geometry is authored, which is the\n * normal case.\n *\n * `'children'` means \"my own children, in child order\" — a container that\n * hugs its contents, which a fixed id list cannot express because\n * reparenting would have to maintain it. The two forms differ in lifetime\n * as well as in membership: deleting a node deletes everything that names\n * it in `dependsOn`, but a container outlives the children it derives\n * from — an emptied group is still a group. */\n dependsOn?: readonly NodeId[] | 'children';\n /** Whether the hit-test walk can return this node. Default `true`; `false`\n * makes it transparent to picking, so a click on it lands on whatever is\n * behind — normally its own container.\n *\n * For content a container owns rather than content the user selects on its\n * own: the label inside a box, a body's rows. It does not hide the node\n * from anything else — it still paints, still clips, and a consumer can\n * still select it by id. */\n pickable?: boolean;\n /** Not drawn, with its whole subtree: picking, area selection, select-all,\n * snapping and the selection pass over it, and an SVG export writes it with\n * `display=\"none\"`. Absent means shown; `Scene.isHidden` folds in its\n * ancestors' flags and its layer's visibility. */\n hidden?: boolean;\n /** Out of reach, with its whole subtree: still drawn, but picking and area\n * selection pass over it, the selection drops it, and every edit to it\n * throws, as on a locked layer. Absent means unlocked; `Scene.isLocked`\n * folds in its ancestors' and its layer's. */\n locked?: boolean;\n /** Computes this node's path from its dependencies, in `dependsOn` order —\n * each one the node and the pose it is painted at.\n * A dependency that has been removed arrives as `undefined`.\n * Returning `null` means \"nothing to draw right now\". Re-evaluated when a\n * dependency's world pose changes, never authored. */\n derivePath?: DerivePathFn<TPose, TData, TLayer>;\n /** Computes this node's pose from its dependencies' poses, the same way\n * `derivePath` computes its path — same `dependsOn` list, same\n * registry-keyed serialization.\n *\n * Where a derived path is resolved at paint time and reaches only the\n * painter, a derived pose is what the node *is* at: it feeds bounds,\n * hit-testing, selection chrome, snapping and layout, and every reader\n * gets it through `effectivePose`. A pose override still wins over it.\n *\n * Returning `null` means \"I have nothing to derive from right now\", and\n * the node falls back to its authored `pose`. `setPose` on a derived node\n * still writes that authored pose; it is simply not what anything reads. */\n derivePose?: DerivePoseFn<TPose, TData, TLayer>;\n /** Ordered child ids. Absent until the node gets its first child. */\n children?: NodeId[];\n /** Optional clip-path source. Re-evaluated each render. Returning `null`\n * means \"no clip for this node right now\"; an empty / zero-area path\n * means \"clip everything out\" (children render nowhere). Wins over `clip`. */\n clipFromPose?: ClipFromPoseFn<TPose>;\n /** `'outline'` clips the children to this node's own outline: a mark's\n * painter silhouette, or for a group the silhouette of its `data.path`.\n * Absent, and with no `clipFromPose`, the children are not clipped. */\n clip?: NodeClip;\n /** Draws the whole subtree again under each affine it returns, on top of\n * the original, which still draws once as authored. The copies are paint,\n * not nodes: nothing stores them, so they follow every edit to the\n * children, and a pick that lands on one answers with the child it copies.\n * A copy is a copy of the clipped content — the clip travels with it.\n * Re-evaluated each render; `null` or `[]` draws no copies. */\n copiesFromPose?: CopiesFromPoseFn<TPose, TData, TLayer>;\n /** How this node arranges its children. Whenever the node's\n * child set or order changes, its bounds change size, or a child's pose is\n * written, the scene hands the change to the strategy — `depart` for children that left it,\n * `arrive` for children that joined it, `childPoses` otherwise — and records the poses it writes in the same\n * undo entry as the change. A drag reads it too: it is the layout `move`\n * reflows and drops against. */\n layout?: LayoutStrategy<TPose>;\n /** The units its children are shown, typed and snapped in (their poses stay\n * in world units), and what one base unit is worth in the enclosing scope's.\n * It covers the children, not the node; `sceneUnitScopes` reads the chain. */\n units?: UnitScope;\n}\n\n/** A node that is drawn and picked as one thing — a shape, a label, an image.\n * It may still hold children, which are what it is made of. */\nexport interface MarkNode<TData, TLayer extends string, TPose = RectPose>\n extends NodeBase<TData, TLayer, TPose> {\n kind: 'mark';\n}\n\n/** A node the user gathers things into: what Cmd+G creates, what SVG `<g>`\n * round-trips to. An emptied group is still a group. */\nexport interface GroupNode<TData, TLayer extends string, TPose = RectPose>\n extends NodeBase<TData, TLayer, TPose> {\n kind: 'group';\n}\n\n/** A node in the scene tree: either a mark or a group. Re-exported\n * publicly as `SceneNode`, to avoid colliding with the DOM's `Node`. */\nexport type Node<TData, TLayer extends string, TPose = RectPose> =\n | MarkNode<TData, TLayer, TPose>\n | GroupNode<TData, TLayer, TPose>;\n\n/** What `Scene.add` needs to mint a node. Everything except the id is\n * required; the id is generated unless one is supplied. */\nexport interface AddNodeSpec<TData, TLayer extends string, TPose = RectPose> {\n kind: 'mark' | 'group';\n layer: TLayer;\n pose: TPose;\n data: TData;\n parent?: NodeId | null;\n index?: number;\n /** Explicit id wins over the Scene's `generateId` and the kit default. */\n id?: NodeId;\n /** Mirrors `SceneNode.pickable`. Omit for the default, which is pickable. */\n pickable?: boolean;\n /** Mirrors `SceneNode.hidden`. */\n hidden?: boolean;\n /** Mirrors `SceneNode.locked`. */\n locked?: boolean;\n /** Attach a clip-path function to the node; mirrors `SceneNode.clipFromPose`. */\n clipFromPose?: ClipFromPoseFn<TPose>;\n /** Mirrors `SceneNode.clip`. */\n clip?: NodeClip;\n /** Mirrors `SceneNode.copiesFromPose`. */\n copiesFromPose?: CopiesFromPoseFn<TPose, TData, TLayer>;\n /** Mirrors `SceneNode.dependsOn`. */\n dependsOn?: readonly NodeId[] | 'children';\n /** Mirrors `SceneNode.derivePath`. Taken as a live function; its registry key is\n * looked up from it, never passed in. */\n derivePath?: DerivePathFn<TPose, TData, TLayer>;\n /** Mirrors `SceneNode.derivePose`, on the same terms as `derivePath`. */\n derivePose?: DerivePoseFn<TPose, TData, TLayer>;\n /** Mirrors `SceneNode.layout`. */\n layout?: LayoutStrategy<TPose>;\n /** Mirrors `SceneNode.units`. */\n units?: UnitScope;\n}\n\n/** Per-scene registry mapping string keys to live function references.\n * Passed to `createScene({ ..., registry })` and `sceneFromJSON(json, { registry })`.\n * Each function-field type has its own keyed map. */\nexport interface SceneRegistry<TPose, TData = unknown, TLayer extends string = string> {\n /** Maps registry keys to `clipFromPose` factory functions for nodes. */\n clipFromPose?: Readonly<Record<string, ClipFromPoseFn<TPose>>>;\n /** Maps registry keys to `copiesFromPose` functions for nodes. */\n copiesFromPose?: Readonly<Record<string, CopiesFromPoseFn<TPose, TData, TLayer>>>;\n /** Maps registry keys to `derivePath` functions for nodes with `dependsOn`. */\n derivePath?: Readonly<Record<string, DerivePathFn<TPose, TData, TLayer>>>;\n /** Maps registry keys to `derivePose` functions for nodes with `dependsOn`. */\n derivePose?: Readonly<Record<string, DerivePoseFn<TPose, TData, TLayer>>>;\n /** Maps registry keys to layout strategies. */\n layout?: Readonly<Record<string, LayoutStrategy<TPose>>>;\n // A new function field is a row in `NODE_FN_FIELDS` (core/scene/nodeFnFields.ts)\n // plus its entry here; the field name is the registry key by construction.\n}\n\n/**\n * The kit-owned scene tree: nodes, layers, and the undo history over both.\n *\n * A scene is logical, not visual — it says what exists and where, and nothing\n * about how it is painted. Every mutating method is undoable, and reads are\n * snapshots rather than live views. Nodes are addressed by `NodeId`; hold ids\n * across mutations, not node objects.\n *\n * Three type parameters keep it domain-agnostic: `TData` is the app's payload,\n * which the kit never inspects; `TPose` is the transform shape, `RectPose` by\n * default; `TLayer` is the union of layer names.\n */\nexport interface Scene<TData, TLayer extends string, TPose = RectPose>\n extends ScenePoseReports<TPose, Node<TData, TLayer, TPose>>, SceneChanges, SceneDocValues, SceneEditPasses, SceneDeclaredFieldSetters {\n // Reads\n readonly nodes: ReadonlyMap<NodeId, Node<TData, TLayer, TPose>>;\n readonly roots: readonly NodeId[];\n readonly layers: readonly LayerRecord<TLayer>[];\n /** The registry this scene resolves node functions against — the consumer's\n * entries over the kit's. */\n readonly registry: SceneRegistry<TPose, TData, TLayer>;\n /** See {@link UseSceneOptions.composition}. */\n readonly composition: PoseComposition<TPose>;\n get(id: NodeId): Node<TData, TLayer, TPose> | undefined;\n /** A token for the node `id` names right now, for telling one node from a\n * later one that reuses its id. It holds for as long as the node stays in\n * the scene, whatever edits it takes, and is different every time the id\n * enters the scene — by `add`, by an undo or redo that brings it back, or\n * by `loadState`. `undefined` when `id` is not in the scene. Compare tokens\n * for equality only; their order means nothing. */\n incarnation(id: NodeId): number | undefined;\n childrenOf(id: NodeId): readonly NodeId[];\n ancestorsOf(id: NodeId): readonly NodeId[];\n renderOrder(): Iterable<NodeId>;\n /** The same layer-major sequence as {@link Scene.renderOrder}, as the nodes\n * themselves. Prefer this wherever the ids are only going to be resolved\n * back to nodes. Cached until a structural edit, so repeat calls hand back\n * the same array — a snapshot, not a live view, and not yours to mutate. */\n renderOrderNodes(): readonly Node<TData, TLayer, TPose>[];\n /** The nodes on one layer, in render order. Cached alongside\n * {@link Scene.renderOrderNodes} until a structural edit. A snapshot, not\n * a live view, and not yours to mutate. An unknown layer id yields an\n * empty array. */\n nodesOnLayer(layer: TLayer): readonly Node<TData, TLayer, TPose>[];\n\n /** A fresh id from this scene's `generateId`, or the kit default: the id\n * `add` would give a spec with none. For code that has to name a node\n * before it exists, such as one building an insert op. */\n mintId(): NodeId;\n\n // Mutations (all auto-undoable)\n add(spec: AddNodeSpec<TData, TLayer, TPose>): NodeId;\n /** Delete `id`, its **entire subtree**, and **everything that derives from**\n * any of those nodes — a node listing one of them in `dependsOn` goes too,\n * along with its own subtree, transitively. A dependent can live anywhere in\n * the tree, so this deletes nodes the caller never named and may unlink\n * several disjoint subtrees at once. Recorded as one undoable step; `undo()`\n * restores every one of them where it was, child order intact. */\n remove(id: NodeId): void;\n /** {@link remove} over several roots at once, as a **single** undoable step.\n * Ids resolve against the tree as it stands at the call, so an id that\n * another one would cascade away is absorbed rather than removed twice —\n * which is what makes it safe to pass a whole selection. Throws if any id is\n * not in the scene; an empty list does nothing and records no step. */\n removeMany(ids: readonly NodeId[]): void;\n /** Every node {@link removeMany} would take if given `ids`: each id, its\n * subtree, everything deriving from any of those, and those nodes' subtrees\n * in turn. Ids not in the scene come back unchanged.\n *\n * A set, not a restore order: a dependent can be reached before the parent\n * it sits under, which is also in the closure. */\n removalClosure(ids: readonly NodeId[]): readonly NodeId[];\n update(id: NodeId, patch: { data: TData }): void;\n setPose(id: NodeId, pose: TPose): void;\n /** Retag `id` to `layer`. On a **container this cascades**: every descendant\n * is moved to the same layer, recorded as a **single** undo step.\n *\n * Invariants:\n * - **Layer floor** — a child may not render below its parent, so retagging\n * to a layer *below* the node's parent throws. Retagging to the parent's\n * layer or any higher one is allowed; a node with no parent is\n * unconstrained.\n * - **No-op elision** — setting the layer a node already has does nothing\n * and pushes **no** history entry. */\n setLayer(id: NodeId, layer: TLayer): void;\n /** Point `id` at a different set of dependencies — retargeting an edge's end\n * onto another node, or switching a container between an id list and\n * `'children'`. Recorded as one undoable step.\n *\n * Order is significant: a derivation reads its dependencies positionally,\n * so `[b, a]` is not `[a, b]`. Ids need not be in the scene — a declared\n * dependency that appears later invalidates the node when it does, the same\n * as one declared at `add`. Passing `undefined` drops the declaration, after\n * which nothing cascades the node away.\n *\n * **No-op elision** — declaring what the node already declares does nothing\n * and pushes no history entry. */\n setDependsOn(id: NodeId, dependsOn: readonly NodeId[] | 'children' | undefined): void;\n /** Give container `id` a different layout — a strategy, its key in\n * `SceneRegistry.layout`, or `null` for none — and re-arrange its children\n * under it, as one undoable step. Throws for a mark, or a key the registry\n * does not hold. A registered layout is recorded by its key, so a\n * serialized history restores it; an unregistered one lives only as long\n * as the session.\n *\n * **No-op elision** — setting the layout a container already has does\n * nothing and pushes no history entry. */\n setLayout(id: NodeId, layout: LayoutStrategy<TPose> | string | null): void;\n /** Reparent `id` under `parent` (or to a root when `parent` is `null`) at\n * `index` within the new sibling list, appending when `index` is omitted.\n * Siblings are reindexed. Recorded as one undoable step.\n *\n * Rejected (throws) when:\n * - the move would form a **cycle** — `parent` is `id` itself or one of\n * `id`'s own descendants;\n * - it would drop `id` **below its new parent's layer** (child may not\n * render below its parent).\n *\n * `move(id, null)` — detaching to a root — is always allowed regardless of\n * layer, since a root has no parent to render beneath. */\n move(id: NodeId, parent: NodeId | null, index?: number): void;\n /** Shift `id` to `index` within its **current** parent's child list. Unlike\n * {@link move}, the parent never changes — only sibling order. */\n reorder(id: NodeId, index: number): void;\n /** Show or hide a layer. Hiding drops its nodes from the selection. */\n setLayerVisible(layer: TLayer, visible: boolean): void;\n /** Lock or unlock a layer. A locked layer still paints, but its nodes are\n * out of reach: picking and area selection pass over them, the selection\n * drops them, and every node mutation on them throws. Locking is never\n * blocked by the lock, and neither are the other layer operations. */\n setLayerLocked(layer: TLayer, locked: boolean): void;\n /** Set or clear (`undefined`) a layer's parallax — see\n * `LayerRecord.parallax`. Undoable; an animation tweening it writes inside\n * {@link Scene.untracked} so no frame becomes an undo step. */\n setLayerParallax(layer: TLayer, parallax: ParallaxOpts | undefined): void;\n /** Whether `id` is locked: it or an ancestor carries `locked`, or sits on a\n * locked layer. A lock covers its whole subtree, whatever layers the\n * descendants are tagged to. An id not in the scene is not locked. */\n isLocked(id: NodeId): boolean;\n /** Whether `id` is not drawn: its own layer is hidden, or it or an ancestor\n * carries `hidden`. A hidden layer hides only the nodes tagged to it. An\n * id not in the scene is not hidden. */\n isHidden(id: NodeId): boolean;\n /** Hide or show node `id` and its subtree — see `SceneNode.hidden`. One\n * undo step; hiding drops the subtree from the selection. Never blocked by\n * a lock, and setting what the node already has records nothing. */\n setHidden(id: NodeId, hidden: boolean): void;\n /** Lock or unlock node `id` and its subtree — see `SceneNode.locked`. One\n * undo step; locking drops the subtree from the selection. Never blocked\n * by a lock, and setting what the node already has records nothing. */\n setLocked(id: NodeId, locked: boolean): void;\n /** Run `fn` with the lock guard lifted, for a programmatic edit that has to\n * reach a locked node. Undo and redo never need it. */\n unlocked<T>(fn: () => T): T;\n addLayer(spec: AddLayerSpec<TLayer>): void;\n /** Drop a user layer and every node tagged to it, as one undoable step.\n * Removal cascades, so this also deletes nodes **on other layers** that\n * derive from a node on this one. */\n removeLayer(layer: TLayer): void;\n renameLayer(layer: TLayer, name: string): void;\n moveLayer(layer: TLayer, index: number): void;\n\n // Custom op seam\n registerOp<P>(kind: string, handler: RegisteredOp<P>): void;\n recordOp<P>(op: { kind: string; payload: P }): void;\n\n /** Install (or clear with `null`) the active-journal accessor after scene\n * construction, for a journal source built with `scene.history` as a\n * dependency. Overrides any `getActiveJournal` set in `UseSceneOptions`. */\n setActiveJournalAccessor(\n fn: (() => import('@weasel-js/history').Journal | null) | null,\n ): void;\n\n /** Install what the scene does when nodes join a container, replacing any\n * handler installed before; `null` removes it. Returns a function that\n * removes this handler, and does nothing once another has replaced it.\n * See {@link SceneArrivalHandler}. */\n setArrivalHandler(handler: SceneArrivalHandler<TPose> | null): () => void;\n\n /** Apply a batch of ops against `adapter` as one undo entry.\n *\n * The entry holds the scene writes the ops made, as a direct call records\n * them, and beside them any op that wrote no scene state, bound to\n * `adapter`. Undo restores those writes; it does not call an op's\n * `invert()` or the adapter again. **What an op states is not recorded**:\n * one applied over a document that already holds its result, as after an\n * `untracked` preview, records a write from that pose to itself, and undo\n * goes nowhere. Put the document back first.\n *\n * The entry lands in the open `batch` when there is one, else on the\n * active journal, else on the scene's own history, where it can coalesce\n * under `coalesceWindowMs` with one whose ops' keys match.\n *\n * Returns false when the arrival handler refused the batch: it is reverted\n * and unrecorded. Inside `batch` it is true; that batch may still refuse. */\n applyBatch(ops: import('core/ops/types').Op[], label: string, adapter: unknown): boolean;\n\n // Selection\n\n /** The transient set of active ids — \"operate on these N as a unit\".\n * Shared by every view over this scene unless a view supplies its own\n * (see `CanvasView.selection`). Not document content: it never appears\n * in `toJSON`. It does ride on history entries, so undo and redo put\n * back the selection an edit was made under; changing it is never an\n * undo step of its own. Removing a node deselects it. */\n getSelection(): readonly NodeId[];\n setSelection(ids: readonly NodeId[]): void;\n\n // Ephemeral presentation state\n\n /** Per-node pose / alpha / path / data overrides the render and hit-test paths read\n * through. Like {@link Scene.getSelection} this is not document content:\n * writes are never recorded, never serialized, and do not bump\n * {@link Scene.getVersion}. See {@link PoseOverrides}. */\n readonly overrides: PoseOverrides<TPose, TData>;\n\n // History\n /** The scene's undo/redo engine, in the shape `@weasel-js/history` defines.\n * Mutating members route through\n * the scene's own wrappers, so an undo driven through this bumps\n * {@link Scene.getVersion} and notifies subscribers exactly as\n * {@link Scene.undo} does; `getVersion` / `subscribe` on it report the\n * engine's history version, not the scene's. */\n readonly history: History;\n undo(): boolean;\n redo(): boolean;\n canUndo(): boolean;\n canRedo(): boolean;\n batch<T>(label: string, fn: () => T, options?: import('./recorder').SceneBatchOptions): T;\n /** The layout group `id` declares, or `null` for a mark or a group with none. */\n layoutOf(id: NodeId): LayoutStrategy<TPose> | null;\n /** Run `fn` with every mutation applied but none recorded — for writes\n * that are not edits, such as a simulation stepping poses each frame.\n * Undo and redo move between recorded states and never restore an\n * untracked write; the next recorded change starts from whatever the\n * untracked writes left, so undoing it lands there. A `batch` inside\n * records nothing; inside a `batch`, these writes stay out of its entry.\n * Notifies once, reverts `fn`'s writes if it throws, and throws on\n * `applyBatch` and `history.apply` / `applyOps`, which exist to record. */\n untracked<T>(fn: () => T): T;\n /** Read-only snapshot of every history entry currently reachable from\n * the present state. Oldest applied first, then redoable entries in\n * the order they'd be re-applied. Each entry id is stable. */\n historyEntries(): readonly { id: string; label: string }[];\n /** Index of the \"current state\". Equals the count of applied entries;\n * `0` means \"nothing applied\" (initial). */\n historyIndex(): number;\n /** Jump to the given history index, clamped to [0, total]. Returns true\n * if any movement occurred. */\n jumpToHistoryIndex(index: number): boolean;\n\n // History persistence\n /** Snapshot the undo/redo history in a JSON-serializable form (the\n * engine's `SerializedHistory`, marked with the scene's recorded form).\n * An entry holds the scene's own ops, whichever way the edit was made.\n * An op handed to `applyBatch` that wrote no scene state is held as\n * itself, and an entry with a nameless one is dropped. Payload\n * JSON-safety (e.g. typed arrays inside poses) is the caller's concern.\n * Do not call mid-`batch` — the open batch's ops are not yet recorded. */\n serializeHistory(): SerializedSceneHistory;\n /** Replace the undo/redo history from a `serializeHistory()` snapshot.\n * Call on a scene whose node/layer state already matches the snapshot's\n * head state (i.e. right after `loadState` from the paired scene\n * snapshot); node state is NOT mutated. Ops re-registered via\n * `registerOp` before this call round-trip; unknown kinds become no-op\n * placeholders; external ops rebuild via the global op-factory registry\n * and replay against the `setHistoryAdapter` accessor. Restored entries\n * never coalesce with new ones. Notifies once. A snapshot that is not in\n * the current recorded form (see `SCENE_HISTORY_FORM`) is dropped:\n * the history is left as it was. Do not call mid-`batch`:\n * the stacks are replaced underneath the open batch, whose eventual\n * flush would graft onto (and evict against) the restored stacks. */\n restoreHistory(snapshot: SerializedHistory): void;\n /** Install (or clear with `null`) the accessor for the adapter that\n * RESTORED external ops apply against on undo/redo: the ops handed to\n * `applyBatch` that wrote no scene state, such as a selection change,\n * rebuilt from a `restoreHistory` snapshot. Resolved lazily at each\n * apply, so wiring order relative to `restoreHistory` doesn't matter.\n * Live `applyBatch` entries are unaffected (they bind their call-site\n * adapter). If unset when a restored op applies, the op is a debug-warned\n * no-op. */\n setHistoryAdapter(fn: (() => unknown) | null): void;\n\n // Serialization\n /** Snapshot the current scene state to a JSON-serializable shape.\n * History (undo/redo stacks) is NOT captured. Function fields like\n * `GroupNode.clipFromPose` are translated to string keys via the\n * scene's registry; throws if any function field has no matching key. */\n toJSON(): SerializedScene<TData, TLayer, TPose>;\n /** Replace this scene's entire node + layer state in place from a snapshot\n * produced by `toJSON()`. Unlike `sceneFromJSON`, the existing Scene\n * instance is preserved — holders such as `<SceneCanvas>` keep their\n * reference. History (undo/redo) is cleared, matching `sceneFromJSON`.\n * Bumps `getVersion()` and notifies subscribers exactly once. Selected\n * ids the snapshot does not hold leave the selection, and every pose\n * override is cleared.\n *\n * Throws inside any other edit — `batch`, `untracked`, `applyBatch`, an\n * undo — which would revert to a state this replaces. Throws on an unsupported version or unknown registry/layer ids; on a\n * malformed snapshot the scene is left empty or partially populated (callers should treat a\n * `loadState` throw as fatal and reload). Snapshots from `toJSON()` are\n * always well-formed. `opts` reads the snapshot as `specsFromSerialized` does. */\n loadState(\n json: SerializedScene<TData, TLayer, TPose> | SerializedSceneV1<TData, TLayer, TPose>,\n opts?: SpecsFromSerializedOptions<TData, TLayer, TPose>,\n ): void;\n\n}\n"],"mappings":";;AAwDA,IAAa,YAAY,MAAsB"}
@@ -17,7 +17,7 @@ function buildScene(options) {
17
17
  historyLimit: options.historyLimit,
18
18
  generateId: options.generateId,
19
19
  initial: options.items.map((item) => ({
20
- kind: "leaf",
20
+ kind: "mark",
21
21
  layer: DEFAULT_LAYER,
22
22
  pose: item,
23
23
  data: item,
@@ -1 +1 @@
1
- {"version":3,"file":"useScene.js","names":[],"sources":["../../../src/core/scene/useScene.ts"],"sourcesContent":["import { useRef, useSyncExternalStore } from 'react';\nimport { createScene } from './scene';\nimport type { NodeId, Scene, UseSceneOptions } from './types';\nimport { asNodeId } from './types';\n\n/** Trivial-form options: a flat list of items, no layers, no parenting. The\n * full `useScene` form is reachable any time by adding `systemLayers`.\n *\n * `historyLimit` and `generateId` are accepted because they're orthogonal to\n * the \"what's in the scene\" question. `ops` is intentionally omitted — if\n * you need custom ops, your app state already lives outside the \"flat list\n * of rects\" frame and you should reach for the full `UseSceneOptions`. */\nexport interface UseSceneTrivialOptions<TItem extends { id: string }> {\n items: readonly TItem[];\n historyLimit?: number;\n generateId?: () => NodeId;\n /** Re-render the host on every scene mutation. Default `true`. See\n * {@link UseSceneOptions.subscribe}. */\n subscribe?: boolean;\n}\n\n/** A subscribe function that never notifies. `useSyncExternalStore` still runs\n * (hooks are unconditional) but the host is never re-rendered by the store. */\nconst NEVER_SUBSCRIBE = () => () => {};\n\nconst DEFAULT_LAYER = 'default' as const;\ntype DefaultLayer = typeof DEFAULT_LAYER;\n\nfunction isTrivialOptions<TItem extends { id: string }>(\n options: unknown,\n): options is UseSceneTrivialOptions<TItem> {\n return (\n !!options\n && typeof options === 'object'\n && 'items' in (options as Record<string, unknown>)\n && !('systemLayers' in (options as Record<string, unknown>))\n );\n}\n\nfunction buildScene(options: unknown): Scene<unknown, string, unknown> {\n if (!isTrivialOptions(options)) {\n return createScene(options as UseSceneOptions<unknown, string, unknown>);\n }\n return createScene<unknown, DefaultLayer, unknown>({\n systemLayers: [{ id: DEFAULT_LAYER }],\n historyLimit: options.historyLimit,\n generateId: options.generateId,\n initial: options.items.map((item) => ({\n kind: 'leaf',\n layer: DEFAULT_LAYER,\n pose: item,\n data: item,\n id: asNodeId(item.id),\n })),\n });\n}\n\n/** React hook returning a kit-owned `Scene`. The Scene is constructed once\n * per host component and tracked via `useSyncExternalStore`, so React re-\n * renders on every Scene mutation (including undo/redo) unless\n * `subscribe: false` opts out.\n *\n * Two call shapes:\n * - **Trivial**: `useScene({ items })` — one auto-registered system layer\n * named `'default'`, each item added as a leaf with `pose === data === item`.\n * Cheapest path for \"a flat list of rects.\"\n * - **Full**: `useScene({ systemLayers, initial?, ops?, ... })` — explicit\n * layers, parenting, and custom op registration. */\nexport function useScene<TItem extends { id: string }>(\n options: UseSceneTrivialOptions<TItem>,\n): Scene<TItem, DefaultLayer, TItem>;\n/** The full call shape: explicit layers, initial nodes, custom ops, and\n * history configuration. */\nexport function useScene<TData, TLayer extends string, TPose = import('./composePose').RectPose>(\n options: UseSceneOptions<TData, TLayer, TPose>,\n): Scene<TData, TLayer, TPose>;\n/** Create and subscribe to a scene. */\nexport function useScene(options: unknown): unknown {\n const sceneRef = useRef<Scene<unknown, string, unknown> | null>(null);\n if (sceneRef.current === null) sceneRef.current = buildScene(options);\n const scene = sceneRef.current;\n const subscribed = (options as { subscribe?: boolean } | null)?.subscribe !== false;\n useSyncExternalStore(\n subscribed ? scene.subscribe : NEVER_SUBSCRIBE,\n scene.getVersion,\n scene.getVersion,\n );\n return scene;\n}\n"],"mappings":";;;;;;;AAuBA,IAAM,8BAA8B,CAAC;AAErC,IAAM,gBAAgB;AAGtB,SAAS,iBACP,SAC0C;CAC1C,OACE,CAAC,CAAC,WACC,OAAO,YAAY,YACnB,WAAY,WACZ,EAAE,kBAAmB;AAE5B;AAEA,SAAS,WAAW,SAAmD;CACrE,IAAI,CAAC,iBAAiB,OAAO,GAC3B,OAAO,YAAY,OAAoD;CAEzE,OAAO,YAA4C;EACjD,cAAc,CAAC,EAAE,IAAI,cAAc,CAAC;EACpC,cAAc,QAAQ;EACtB,YAAY,QAAQ;EACpB,SAAS,QAAQ,MAAM,KAAK,UAAU;GACpC,MAAM;GACN,OAAO;GACP,MAAM;GACN,MAAM;GACN,IAAI,SAAS,KAAK,EAAE;EACtB,EAAE;CACJ,CAAC;AACH;;AAsBA,SAAgB,SAAS,SAA2B;CAClD,MAAM,WAAW,OAA+C,IAAI;CACpE,IAAI,SAAS,YAAY,MAAM,SAAS,UAAU,WAAW,OAAO;CACpE,MAAM,QAAQ,SAAS;CACvB,MAAM,aAAc,SAA4C,cAAc;CAC9E,qBACE,aAAa,MAAM,YAAY,iBAC/B,MAAM,YACN,MAAM,UACR;CACA,OAAO;AACT"}
1
+ {"version":3,"file":"useScene.js","names":[],"sources":["../../../src/core/scene/useScene.ts"],"sourcesContent":["import { useRef, useSyncExternalStore } from 'react';\nimport { createScene } from './scene';\nimport type { NodeId, Scene, UseSceneOptions } from './types';\nimport { asNodeId } from './types';\n\n/** Trivial-form options: a flat list of items, no layers, no parenting. The\n * full `useScene` form is reachable any time by adding `systemLayers`.\n *\n * `historyLimit` and `generateId` are accepted because they're orthogonal to\n * the \"what's in the scene\" question. `ops` is intentionally omitted — if\n * you need custom ops, your app state already lives outside the \"flat list\n * of rects\" frame and you should reach for the full `UseSceneOptions`. */\nexport interface UseSceneTrivialOptions<TItem extends { id: string }> {\n items: readonly TItem[];\n historyLimit?: number;\n generateId?: () => NodeId;\n /** Re-render the host on every scene mutation. Default `true`. See\n * {@link UseSceneOptions.subscribe}. */\n subscribe?: boolean;\n}\n\n/** A subscribe function that never notifies. `useSyncExternalStore` still runs\n * (hooks are unconditional) but the host is never re-rendered by the store. */\nconst NEVER_SUBSCRIBE = () => () => {};\n\nconst DEFAULT_LAYER = 'default' as const;\ntype DefaultLayer = typeof DEFAULT_LAYER;\n\nfunction isTrivialOptions<TItem extends { id: string }>(\n options: unknown,\n): options is UseSceneTrivialOptions<TItem> {\n return (\n !!options\n && typeof options === 'object'\n && 'items' in (options as Record<string, unknown>)\n && !('systemLayers' in (options as Record<string, unknown>))\n );\n}\n\nfunction buildScene(options: unknown): Scene<unknown, string, unknown> {\n if (!isTrivialOptions(options)) {\n return createScene(options as UseSceneOptions<unknown, string, unknown>);\n }\n return createScene<unknown, DefaultLayer, unknown>({\n systemLayers: [{ id: DEFAULT_LAYER }],\n historyLimit: options.historyLimit,\n generateId: options.generateId,\n initial: options.items.map((item) => ({\n kind: 'mark',\n layer: DEFAULT_LAYER,\n pose: item,\n data: item,\n id: asNodeId(item.id),\n })),\n });\n}\n\n/** React hook returning a kit-owned `Scene`. The Scene is constructed once\n * per host component and tracked via `useSyncExternalStore`, so React re-\n * renders on every Scene mutation (including undo/redo) unless\n * `subscribe: false` opts out.\n *\n * Two call shapes:\n * - **Trivial**: `useScene({ items })` — one auto-registered system layer\n * named `'default'`, each item added as a leaf with `pose === data === item`.\n * Cheapest path for \"a flat list of rects.\"\n * - **Full**: `useScene({ systemLayers, initial?, ops?, ... })` — explicit\n * layers, parenting, and custom op registration. */\nexport function useScene<TItem extends { id: string }>(\n options: UseSceneTrivialOptions<TItem>,\n): Scene<TItem, DefaultLayer, TItem>;\n/** The full call shape: explicit layers, initial nodes, custom ops, and\n * history configuration. */\nexport function useScene<TData, TLayer extends string, TPose = import('./composePose').RectPose>(\n options: UseSceneOptions<TData, TLayer, TPose>,\n): Scene<TData, TLayer, TPose>;\n/** Create and subscribe to a scene. */\nexport function useScene(options: unknown): unknown {\n const sceneRef = useRef<Scene<unknown, string, unknown> | null>(null);\n if (sceneRef.current === null) sceneRef.current = buildScene(options);\n const scene = sceneRef.current;\n const subscribed = (options as { subscribe?: boolean } | null)?.subscribe !== false;\n useSyncExternalStore(\n subscribed ? scene.subscribe : NEVER_SUBSCRIBE,\n scene.getVersion,\n scene.getVersion,\n );\n return scene;\n}\n"],"mappings":";;;;;;;AAuBA,IAAM,8BAA8B,CAAC;AAErC,IAAM,gBAAgB;AAGtB,SAAS,iBACP,SAC0C;CAC1C,OACE,CAAC,CAAC,WACC,OAAO,YAAY,YACnB,WAAY,WACZ,EAAE,kBAAmB;AAE5B;AAEA,SAAS,WAAW,SAAmD;CACrE,IAAI,CAAC,iBAAiB,OAAO,GAC3B,OAAO,YAAY,OAAoD;CAEzE,OAAO,YAA4C;EACjD,cAAc,CAAC,EAAE,IAAI,cAAc,CAAC;EACpC,cAAc,QAAQ;EACtB,YAAY,QAAQ;EACpB,SAAS,QAAQ,MAAM,KAAK,UAAU;GACpC,MAAM;GACN,OAAO;GACP,MAAM;GACN,MAAM;GACN,IAAI,SAAS,KAAK,EAAE;EACtB,EAAE;CACJ,CAAC;AACH;;AAsBA,SAAgB,SAAS,SAA2B;CAClD,MAAM,WAAW,OAA+C,IAAI;CACpE,IAAI,SAAS,YAAY,MAAM,SAAS,UAAU,WAAW,OAAO;CACpE,MAAM,QAAQ,SAAS;CACvB,MAAM,aAAc,SAA4C,cAAc;CAC9E,qBACE,aAAa,MAAM,YAAY,iBAC/B,MAAM,YACN,MAAM,UACR;CACA,OAAO;AACT"}
@@ -1,4 +1,4 @@
1
- import { B as BoundsConstraint } from './clone-C3e0OTuF.js';
1
+ import { B as BoundsConstraint } from './clone-qb2e1F5p.js';
2
2
  import { FramedBox, Affine } from '@weasel-js/geom';
3
3
  import { ResizeAnchor } from '@weasel-js/routing';
4
4
 
@@ -139,7 +139,8 @@ declare function handlePoint(bounds: FramedBox, entry: Pick<ResizeHandleAnchor,
139
139
  x: number;
140
140
  y: number;
141
141
  };
142
- /** @deprecated Use {@link handlePoint}, which also places edge handles. */
142
+ /** A corner handle's position for the given bounds, in the bounds' frame.
143
+ * @deprecated Use {@link handlePoint}, which also places edge handles. */
143
144
  declare const cornerPoint: (bounds: FramedBox, entry: Pick<CornerAnchor, 'xEdge' | 'yEdge'>) => {
144
145
  x: number;
145
146
  y: number;
@@ -1,4 +1,5 @@
1
- import { TileKind, Paint, GradStop, TilePaint, Fill, Stroke, CommandFill, CommandStroke, CommandPaint } from '@weasel-js/paint';
1
+ import * as _weasel_js_paint from '@weasel-js/paint';
2
+ import { TileKind, Paint, GradStop, TilePaint, Fill, Stroke, Inherit, CommandFill, CommandStroke, CommandPaint } from '@weasel-js/paint';
2
3
  import { Reflection } from '@weasel-js/registry';
3
4
  import { ComponentType } from 'react';
4
5
  import { IconRef } from '@weasel-js/icons';
@@ -6,18 +7,18 @@ import { PaintDrawKind, DrawCommand, Effect, CompositeOp, BlendFactors } from '@
6
7
  import * as _weasel_js_routing_react from '@weasel-js/routing/react';
7
8
  import * as _weasel_js_geom from '@weasel-js/geom';
8
9
  import { FramedBox, View, Path, Affine, Rect, Scale2, PolygonPath, PathFillRule, AnchorSet, PointOrigin } from '@weasel-js/geom';
10
+ import { I as InheritedPaint, S as Scene, R as RectPose } from './types-BEdRIhNc.js';
9
11
  import { Op, History } from '@weasel-js/history';
10
12
  import { NodeId, ModifierState, OverlayRole, ResizeAnchor, ActionDeps, OngoingHandle, OngoingOverlay, SelectionApi, IngestItem, Point2, DragSample, DebugSink, LongPressState } from '@weasel-js/routing';
11
13
  import { TextNodeSource, TextOutlinesError, TextStyle, StyledRun, TextVerticalAlign } from '@weasel-js/text';
12
14
  import { CursorSpec } from '@weasel-js/cursor';
13
15
  import { ClaimableGesture } from '@weasel-js/gestures';
14
16
  import { SnapTier, ProbeRole } from '@weasel-js/geom/nd';
15
- import { S as Scene, R as RectPose } from './types-CniVctmk.js';
16
- import { a as PlaneMap, L as LayoutStrategy, b as LayoutDropTargetMode, R as ReflowTransition } from './types-7puAuVD2.js';
17
+ import { a as PlaneMap, L as LayoutStrategy, b as LayoutDropTargetMode, R as ReflowTransition } from './types-49uYJN3j.js';
17
18
  import { O as OriginProjection } from './originProjection-gY0jb4Wb.js';
18
19
  import { EasingSpec, InterpolatorFactory, Target, Animator, TargetAxes } from '@weasel-js/animator';
19
- import { I as InsertAdapter, P as PasteOmission, R as RegionPickView } from './types-oFeQ5VjV.js';
20
- import { B as BoundsConstraint } from './clone-C3e0OTuF.js';
20
+ import { I as InsertAdapter, P as PasteOmission, R as RegionPickView } from './types-p2alu-R1.js';
21
+ import { B as BoundsConstraint } from './clone-qb2e1F5p.js';
21
22
  import { P as PoseDescriptor } from './poseDescriptor-DN2yfnq_.js';
22
23
 
23
24
  /** Pixel dimensions of the canvas viewport. */
@@ -209,13 +210,12 @@ interface PathPaint {
209
210
  stroke?: Stroke;
210
211
  }
211
212
  /** {@link PathPaint} of a node that keeps its paint in `data.fill` and
212
- * `data.stroke`, as the built-in path painter draws it: a stroke with no
213
- * `paint` is none, and an undeclared fill is the default shape fill unless
214
- * the node is stroked. */
213
+ * `data.stroke`, as the built-in path painter draws it: a stroke or a fill
214
+ * with no `paint` is none, and an `'inherit'` one is `inherited`'s. */
215
215
  declare function pathPaintOf(data: {
216
- fill?: Fill | null;
217
- stroke?: Stroke | null;
218
- }): PathPaint;
216
+ fill?: Fill | Inherit | null;
217
+ stroke?: Stroke | Inherit | null;
218
+ }, inherited?: InheritedPaint): PathPaint;
219
219
 
220
220
  /**
221
221
  * Create Outlines: replace each selected text node with path nodes carrying
@@ -747,7 +747,7 @@ interface LayerHit<TScratch = unknown> extends AffordanceBinding<TScratch> {
747
747
  * separated in the render order is drawn as one bracket per run, with a
748
748
  * warning.
749
749
  *
750
- * This is a render-stack bracket, not a scene `ContainerNode`.
750
+ * This is a render-stack bracket, not a scene `GroupNode`.
751
751
  */
752
752
  interface LayerGroup {
753
753
  /** Names the group in warnings; not a layer id and never drawn. */
@@ -1450,7 +1450,7 @@ type InsertNodeFactory = (bounds: {
1450
1450
  /**
1451
1451
  * The paint a kit-built shape is inserted with. `kind` is the shape being
1452
1452
  * built and `seq` counts this canvas's inserts from 1. A key left out stays
1453
- * off the node, so its painter's default applies; `null` is "none". A pencil
1453
+ * off the node, which paints none of it, the same as `null`. A pencil
1454
1454
  * trail too short to fit is built as a rect and asks as one. A line and an
1455
1455
  * open pen path are inserted unfilled: a fill given to either is dropped.
1456
1456
  * `fillRule` is the rule a path that carries one is built with, where the
@@ -1474,8 +1474,8 @@ interface TextNodeData {
1474
1474
  text: string;
1475
1475
  style?: TextStyle;
1476
1476
  runs?: readonly StyledRun[];
1477
- fill?: Fill | null;
1478
- stroke?: Stroke | null;
1477
+ fill?: Fill | Inherit | null;
1478
+ stroke?: Stroke | Inherit | null;
1479
1479
  verticalAlign?: TextVerticalAlign;
1480
1480
  /** Paint for the whole pose box, drawn under the glyphs. `fill` is the
1481
1481
  * glyphs' own paint, so a box behind them needs a key of its own. */
@@ -1508,11 +1508,11 @@ interface MinSize {
1508
1508
  declare function minSizeOf(data: unknown): MinSize | null;
1509
1509
 
1510
1510
  /** How a sticky note looks as it is made, named as the text node it becomes
1511
- * names them. Every field but `fill` has a default, in {@link STICKY_DEFAULTS}. */
1511
+ * names them. Every field has a default, in {@link STICKY_DEFAULTS}. */
1512
1512
  interface StickyStyle {
1513
1513
  /** The note itself, painted over its whole box. */
1514
1514
  background?: Fill;
1515
- /** The text's paint. Absent, the text style's default. */
1515
+ /** The text's paint. */
1516
1516
  fill?: Fill;
1517
1517
  /** World units from the note's edge to its text, on every side. */
1518
1518
  padding?: number;
@@ -1538,6 +1538,7 @@ declare const STICKY_DEFAULTS: {
1538
1538
  readonly color: "#fff1a6ff";
1539
1539
  };
1540
1540
  };
1541
+ readonly fill: _weasel_js_paint.PaintedFill;
1541
1542
  readonly padding: 12;
1542
1543
  readonly style: {
1543
1544
  readonly fontSize: 18;
@@ -2112,6 +2113,22 @@ interface EditAnchorsDep {
2112
2113
  setSelectedMark?(mark: PathEditMark | null): void;
2113
2114
  }
2114
2115
 
2116
+ /** The group a press resolves inside of, held beside the selection; `<SceneCanvas enteredGroup>` takes one. */
2117
+ interface EnteredGroupApi {
2118
+ get(): NodeId | null;
2119
+ enter(id: NodeId): void;
2120
+ /** Moves to the nearest group above the current one, or to none. */
2121
+ leave(): void;
2122
+ /** Leaves every entered group at once. */
2123
+ leaveAll(): void;
2124
+ subscribe(fn: () => void): () => void;
2125
+ }
2126
+ type SceneReads = Pick<Scene<unknown, string, unknown>, 'get' | 'ancestorsOf' | 'subscribe'>;
2127
+ /** An entered-group store over `scene`, with nothing entered. */
2128
+ declare function createEnteredGroup(scene: SceneReads): EnteredGroupApi;
2129
+ /** One entered-group store for the life of the scene. */
2130
+ declare function useEnteredGroup(scene: SceneReads): EnteredGroupApi;
2131
+
2115
2132
  /** Context handed to every content handler for one ingest event. */
2116
2133
  interface IngestCtx {
2117
2134
  /** World-space arrival point (drop / pointed imperative ingest); `null`
@@ -2624,6 +2641,10 @@ declare module '@weasel-js/routing' {
2624
2641
  interface DepSchema {
2625
2642
  /** Kit selection state — ids of currently selected nodes. */
2626
2643
  selection: SelectionApi;
2644
+ /** The group the user has entered, or null — session state beside the
2645
+ * selection, not scene data and not undoable. Optional: absent means
2646
+ * inside no group. */
2647
+ enteredGroup?: EnteredGroupApi;
2627
2648
  /** Current viewport — camera position + scale. */
2628
2649
  view: ViewApi;
2629
2650
  /**
@@ -2800,4 +2821,4 @@ declare module '@weasel-js/routing' {
2800
2821
  }
2801
2822
  }
2802
2823
 
2803
- export { type SnapCollect as $, type AnchorMarkerShape as A, type PathEditTarget as B, type ContentHandlerEntry as C, type Dims as D, type EditAnchorsDep as E, type FitViewToBoundsOptions as F, type GestureLifecycleOptions as G, type HandleLocks as H, type InertiaConfig as I, type FinishedEditFrame as J, type PathSymmetryDep as K, type LayerHit as L, type BooleanOpOptions as M, type BooleansAdapter as N, type BooleanOp as O, type PathEditMark as P, type PathPaint as Q, type RenderLayer as R, type SnapSource as S, type TransformFollower as T, type BooleanOpResult as U, type ViewAnimationOptions as V, type BooleanStrokes as W, type SnapMods as X, type SourceCandidate as Y, type SnapProbeAt as Z, _resetPaintKindsForTests as _, type PathEditExtension as a, type SnapEdges as a$, type SnapMark as a0, type SnapDep as a1, type SnapOutcome as a2, type SnapReadoutStyle as a3, type AngleCandidate as a4, type AngleCollect as a5, type AnimateToBoundsOptions as a6, type AreaSelectDep as a7, type BooleanOpPreview as a8, type BooleanPreviewPiece as a9, type NodeAtPointDep as aA, type OutlinePathSpec as aB, PATH_OVERLAY_ROLES as aC, type PaintKind as aD, type PaintKindEditorProps as aE, type PaintKindFamily as aF, type PaintKindLoader as aG, type PanBounds as aH, type PathEditDrag as aI, type PathEditDragStart as aJ, type PathEditFinishCtx as aK, type PathEditHandleMove as aL, type PathEditMarksCtx as aM, type PathEditOther as aN, type PathEditPart as aO, type PathEditStore as aP, type PathForm as aQ, type PathOverlay as aR, type PathOverlayOptions as aS, type PathOverlayStyle as aT, type PathRepresentation as aU, type PathTransformFinishCtx as aV, type PreviewPathsDep as aW, type ResizePolicy as aX, STICKY_DEFAULTS as aY, type SnapBegin as aZ, type SnapEdge as a_, type CanDropIntoDep as aa, type ClipboardDep as ab, type ClipboardIngestCtx as ac, type CreateOutlinesResult as ad, type CustomPaintContext as ae, DEFAULT_LAYER_RETAIN as af, type DecayLoopConfig as ag, type FinishedPathEdit as ah, GEOMETRY_BOUND_DATA as ai, type HandleLock as aj, type HitTestView as ak, type IngestCtx as al, type IngestionDep as am, type InsertDep as an, type InsertEnds as ao, type InsertExtras as ap, type InsertIntoDep as aq, type InsertLive as ar, type LassoSelectDep as as, type LayerCommandCache as at, type LayerDrawFailure as au, type LayerDrawSpan as av, type LayerRetainPolicy as aw, type LayoutDep as ax, type Locus as ay, type MinSize as az, type SnapGesture as b, type SnapMarks as b0, type SnapReadoutDep as b1, type SvgUnpacker as b2, type TextEditDep as b3, type TextNodeData as b4, type TransformPreviewNode as b5, type TransientPath as b6, VIEW_ANIMATION_KEY as b7, type ViewAnimationApi as b8, type ViewApi as b9, registerPaintKindLoader as bA, seedTile as bB, stickyData as bC, stickyNodeFactory as bD, subscribeToSources as bE, switchGradientKind as bF, textBoxOf as bG, useDecayLoop as bH, useViewAnimation as bI, useViewAnimationOn as bJ, viewAxes as bK, withoutGeometryBoundData as bL, type ViewChannel as ba, applyBooleanOp as bb, applyCreateOutlines as bc, asPaint as bd, createPathOverlay as be, createSnapOutcomeStore as bf, depsUnchanged as bg, drawLayers as bh, drawOneLayer as bi, fitViewToBounds as bj, getPaintKind as bk, isLayerPainted as bl, isLayerVisible as bm, listGradientKinds as bn, listPaintKinds as bo, minSizeOf as bp, paintKindOf as bq, paintKindRegistry as br, paintedLayers as bs, paintsLayer as bt, paletteInsertPaint as bu, pathPaintOf as bv, patternFamily as bw, previewBooleanOp as bx, registerContentHandler as by, registerPaintKind as bz, type SnapConstrain as c, type SnapResizeBox as d, type SnapGeometry as e, type SnapOutcomeStore as f, type SnapSession as g, type ViewportDims as h, type RedrawSource as i, type EditAnchorsParams as j, type SliceDep as k, type StickyStyle as l, type ChromeState as m, type LayerGroup as n, type GeometryProjection as o, type InsertNodeFactory as p, type InsertPaint as q, type SvgIngestOptions as r, type SceneCanvasSnap as s, type Affordance as t, type AffordanceRegion as u, type AffordanceBinding as v, type CommonAffordanceScratch as w, type TextOverflow as x, type CreateOutlinesAdapter as y, type PaintKindEntry as z };
2824
+ export { type SnapProbeAt as $, type AnchorMarkerShape as A, type PaintKindEntry as B, type ContentHandlerEntry as C, type Dims as D, type EditAnchorsDep as E, type FitViewToBoundsOptions as F, type GestureLifecycleOptions as G, type HandleLocks as H, type InertiaConfig as I, type PathEditTarget as J, type FinishedEditFrame as K, type LayerHit as L, type PathSymmetryDep as M, type BooleanOpOptions as N, type BooleansAdapter as O, type PathEditMark as P, type BooleanOp as Q, type RenderLayer as R, type SnapSource as S, type TransformFollower as T, type PathPaint as U, type ViewAnimationOptions as V, type BooleanOpResult as W, type BooleanStrokes as X, type SnapMods as Y, type SourceCandidate as Z, _resetPaintKindsForTests as _, type PathEditExtension as a, type SnapEdge as a$, type SnapCollect as a0, type SnapMark as a1, type SnapDep as a2, type SnapOutcome as a3, type SnapReadoutStyle as a4, type AngleCandidate as a5, type AngleCollect as a6, type AnimateToBoundsOptions as a7, type AreaSelectDep as a8, type BooleanOpPreview as a9, type MinSize as aA, type NodeAtPointDep as aB, type OutlinePathSpec as aC, PATH_OVERLAY_ROLES as aD, type PaintKind as aE, type PaintKindEditorProps as aF, type PaintKindFamily as aG, type PaintKindLoader as aH, type PanBounds as aI, type PathEditDrag as aJ, type PathEditDragStart as aK, type PathEditFinishCtx as aL, type PathEditHandleMove as aM, type PathEditMarksCtx as aN, type PathEditOther as aO, type PathEditPart as aP, type PathEditStore as aQ, type PathForm as aR, type PathOverlay as aS, type PathOverlayOptions as aT, type PathOverlayStyle as aU, type PathRepresentation as aV, type PathTransformFinishCtx as aW, type PreviewPathsDep as aX, type ResizePolicy as aY, STICKY_DEFAULTS as aZ, type SnapBegin as a_, type BooleanPreviewPiece as aa, type CanDropIntoDep as ab, type ClipboardDep as ac, type ClipboardIngestCtx as ad, type CreateOutlinesResult as ae, type CustomPaintContext as af, DEFAULT_LAYER_RETAIN as ag, type DecayLoopConfig as ah, type FinishedPathEdit as ai, GEOMETRY_BOUND_DATA as aj, type HandleLock as ak, type HitTestView as al, type IngestCtx as am, type IngestionDep as an, type InsertDep as ao, type InsertEnds as ap, type InsertExtras as aq, type InsertIntoDep as ar, type InsertLive as as, type LassoSelectDep as at, type LayerCommandCache as au, type LayerDrawFailure as av, type LayerDrawSpan as aw, type LayerRetainPolicy as ax, type LayoutDep as ay, type Locus as az, type SnapGesture as b, type SnapEdges as b0, type SnapMarks as b1, type SnapReadoutDep as b2, type SvgUnpacker as b3, type TextEditDep as b4, type TextNodeData as b5, type TransformPreviewNode as b6, type TransientPath as b7, VIEW_ANIMATION_KEY as b8, type ViewAnimationApi as b9, registerContentHandler as bA, registerPaintKind as bB, registerPaintKindLoader as bC, seedTile as bD, stickyData as bE, stickyNodeFactory as bF, subscribeToSources as bG, switchGradientKind as bH, textBoxOf as bI, useDecayLoop as bJ, useEnteredGroup as bK, useViewAnimation as bL, useViewAnimationOn as bM, viewAxes as bN, withoutGeometryBoundData as bO, type ViewApi as ba, type ViewChannel as bb, applyBooleanOp as bc, applyCreateOutlines as bd, asPaint as be, createEnteredGroup as bf, createPathOverlay as bg, createSnapOutcomeStore as bh, depsUnchanged as bi, drawLayers as bj, drawOneLayer as bk, fitViewToBounds as bl, getPaintKind as bm, isLayerPainted as bn, isLayerVisible as bo, listGradientKinds as bp, listPaintKinds as bq, minSizeOf as br, paintKindOf as bs, paintKindRegistry as bt, paintedLayers as bu, paintsLayer as bv, paletteInsertPaint as bw, pathPaintOf as bx, patternFamily as by, previewBooleanOp as bz, type SnapConstrain as c, type SnapResizeBox as d, type SnapGeometry as e, type SnapOutcomeStore as f, type SnapSession as g, type ViewportDims as h, type RedrawSource as i, type EditAnchorsParams as j, type SliceDep as k, type StickyStyle as l, type EnteredGroupApi as m, type ChromeState as n, type LayerGroup as o, type GeometryProjection as p, type InsertNodeFactory as q, type InsertPaint as r, type SvgIngestOptions as s, type SceneCanvasSnap as t, type Affordance as u, type AffordanceRegion as v, type AffordanceBinding as w, type CommonAffordanceScratch as x, type TextOverflow as y, type CreateOutlinesAdapter as z };
@@ -12,7 +12,7 @@ function summaryOf(node) {
12
12
  id: node.id,
13
13
  kind: node.kind,
14
14
  layer: node.layer,
15
- childCount: node.kind === "container" ? node.children.length : 0,
15
+ childCount: node.children?.length ?? 0,
16
16
  hidden: node.hidden === true,
17
17
  locked: node.locked === true,
18
18
  pickable: node.pickable !== false
@@ -1 +1 @@
1
- {"version":3,"file":"handlers.js","names":[],"sources":["../../src/devtools/handlers.ts"],"sourcesContent":["/** The answer to each devtools request, for one root. The session owns the override a steering request changes. */\nimport type { Node, NodeId, Scene } from 'core/scene/types';\nimport type { DebugConfig } from '../debug/types';\nimport type { DevtoolsRoot } from './root';\nimport type {\n DevtoolsNodeDetail, DevtoolsNodeSummary, DevtoolsOverlays, DevtoolsRequest, DevtoolsResult,\n DevtoolsRootState, DevtoolsTraceSlice,\n} from './protocol';\nimport { serialize } from './serialize';\nimport { historySlice } from './history';\n\ntype AnyScene = Scene<unknown, string, unknown>;\ntype AnyNode = Node<unknown, string, unknown>;\n\nfunction sceneOf(root: DevtoolsRoot): AnyScene {\n const scene = root.source.scene();\n if (!scene) throw new Error('scene not mounted yet');\n return scene;\n}\n\nfunction summaryOf(node: AnyNode): DevtoolsNodeSummary {\n return {\n id: node.id,\n kind: node.kind,\n layer: node.layer,\n childCount: node.kind === 'container' ? node.children.length : 0,\n hidden: node.hidden === true,\n locked: node.locked === true,\n pickable: node.pickable !== false,\n };\n}\n\nfunction nodeOf(scene: AnyScene, id: string): AnyNode {\n const node = scene.get(id as NodeId);\n if (!node) throw new Error(`no node \"${id}\"`);\n return node;\n}\n\nexport function childrenOf(root: DevtoolsRoot, nodeId: string | undefined): DevtoolsNodeSummary[] {\n const scene = sceneOf(root);\n const ids = nodeId === undefined ? scene.roots : (nodeOf(scene, nodeId), scene.childrenOf(nodeId as NodeId));\n return ids.map((id) => summaryOf(nodeOf(scene, id)));\n}\n\nexport function nodeDetail(root: DevtoolsRoot, nodeId: string): DevtoolsNodeDetail {\n const node = nodeOf(sceneOf(root), nodeId);\n return {\n ...summaryOf(node),\n parent: node.parent ?? null,\n pose: serialize(node.pose),\n data: serialize(node.data),\n };\n}\n\n/** The on/off features of a debug config, leaving out its theme and strokes. */\nexport function overlayFlags(config: DebugConfig | null): DevtoolsOverlays {\n const flags: DevtoolsOverlays = {};\n if (!config) return flags;\n for (const [key, value] of Object.entries(config)) {\n if (typeof value === 'boolean') (flags as Record<string, boolean>)[key] = value;\n }\n return flags;\n}\n\nexport function rootState(root: DevtoolsRoot): DevtoolsRootState {\n const { source } = root;\n const probes: DevtoolsRootState['probes'] = {};\n for (const [name, read] of root.probes) {\n try {\n probes[name] = serialize(read());\n } catch (err) {\n probes[name] = `[threw: ${(err as Error).message}]`;\n }\n }\n return {\n selection: [...source.selection()],\n view: serialize(source.view()),\n activeTool: source.activeTool(),\n tools: [...source.tools()],\n overlays: overlayFlags(source.overlays()),\n probes,\n };\n}\n\n/** Trace records are plain data by contract, and nest deeper than a node's data is shown. */\nconst RECORD = { depth: 12 };\n\nexport function traceSlice(root: DevtoolsRoot, cursor: number, coordCursor: number | undefined): DevtoolsTraceSlice {\n const { trace, coords } = root.source;\n const slice = trace.since(cursor);\n const out: DevtoolsTraceSlice = {\n entries: slice.entries.map((e) => serialize(e, RECORD)),\n cursor: slice.cursor,\n live: serialize(trace.live(), RECORD),\n };\n if (coordCursor !== undefined) {\n const c = coords.since(coordCursor);\n out.coords = { entries: c.entries.map((e) => serialize(e, RECORD)), cursor: c.cursor };\n }\n return out;\n}\n\n/** Answer `req` about `root`. `steer` lays a change over the panel's override. Throws on a bad request. */\nexport function answer(\n root: DevtoolsRoot,\n req: DevtoolsRequest,\n steer: (change: { overlays?: DevtoolsOverlays; highlight?: readonly string[] }) => void,\n): DevtoolsResult {\n switch (req.type) {\n case 'get-children': return childrenOf(root, req.nodeId);\n case 'get-node': return nodeDetail(root, req.nodeId);\n case 'get-state': return rootState(root);\n case 'get-trace': return traceSlice(root, req.cursor, req.coordCursor);\n case 'get-frame': return serialize(root.source.frame());\n case 'get-registry':\n if (!root.source.registry) throw new Error('this root serves no registry');\n return root.source.registry();\n case 'get-history': return historySlice(root, req.cursor, req.checkpoints);\n case 'select':\n root.source.select(req.ids);\n return null;\n case 'set-tool':\n if (req.toolId !== null && !root.source.tools().includes(req.toolId)) throw new Error(`no tool \"${req.toolId}\"`);\n root.source.setTool(req.toolId);\n return null;\n case 'highlight':\n steer({ highlight: [...req.ids] });\n return null;\n case 'set-overlays':\n steer({ overlays: req.overlays });\n return null;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"handlers.js","names":[],"sources":["../../src/devtools/handlers.ts"],"sourcesContent":["/** The answer to each devtools request, for one root. The session owns the override a steering request changes. */\nimport type { Node, NodeId, Scene } from 'core/scene/types';\nimport type { DebugConfig } from '../debug/types';\nimport type { DevtoolsRoot } from './root';\nimport type {\n DevtoolsNodeDetail, DevtoolsNodeSummary, DevtoolsOverlays, DevtoolsRequest, DevtoolsResult,\n DevtoolsRootState, DevtoolsTraceSlice,\n} from './protocol';\nimport { serialize } from './serialize';\nimport { historySlice } from './history';\n\ntype AnyScene = Scene<unknown, string, unknown>;\ntype AnyNode = Node<unknown, string, unknown>;\n\nfunction sceneOf(root: DevtoolsRoot): AnyScene {\n const scene = root.source.scene();\n if (!scene) throw new Error('scene not mounted yet');\n return scene;\n}\n\nfunction summaryOf(node: AnyNode): DevtoolsNodeSummary {\n return {\n id: node.id,\n kind: node.kind,\n layer: node.layer,\n childCount: node.children?.length ?? 0,\n hidden: node.hidden === true,\n locked: node.locked === true,\n pickable: node.pickable !== false,\n };\n}\n\nfunction nodeOf(scene: AnyScene, id: string): AnyNode {\n const node = scene.get(id as NodeId);\n if (!node) throw new Error(`no node \"${id}\"`);\n return node;\n}\n\nexport function childrenOf(root: DevtoolsRoot, nodeId: string | undefined): DevtoolsNodeSummary[] {\n const scene = sceneOf(root);\n const ids = nodeId === undefined ? scene.roots : (nodeOf(scene, nodeId), scene.childrenOf(nodeId as NodeId));\n return ids.map((id) => summaryOf(nodeOf(scene, id)));\n}\n\nexport function nodeDetail(root: DevtoolsRoot, nodeId: string): DevtoolsNodeDetail {\n const node = nodeOf(sceneOf(root), nodeId);\n return {\n ...summaryOf(node),\n parent: node.parent ?? null,\n pose: serialize(node.pose),\n data: serialize(node.data),\n };\n}\n\n/** The on/off features of a debug config, leaving out its theme and strokes. */\nexport function overlayFlags(config: DebugConfig | null): DevtoolsOverlays {\n const flags: DevtoolsOverlays = {};\n if (!config) return flags;\n for (const [key, value] of Object.entries(config)) {\n if (typeof value === 'boolean') (flags as Record<string, boolean>)[key] = value;\n }\n return flags;\n}\n\nexport function rootState(root: DevtoolsRoot): DevtoolsRootState {\n const { source } = root;\n const probes: DevtoolsRootState['probes'] = {};\n for (const [name, read] of root.probes) {\n try {\n probes[name] = serialize(read());\n } catch (err) {\n probes[name] = `[threw: ${(err as Error).message}]`;\n }\n }\n return {\n selection: [...source.selection()],\n view: serialize(source.view()),\n activeTool: source.activeTool(),\n tools: [...source.tools()],\n overlays: overlayFlags(source.overlays()),\n probes,\n };\n}\n\n/** Trace records are plain data by contract, and nest deeper than a node's data is shown. */\nconst RECORD = { depth: 12 };\n\nexport function traceSlice(root: DevtoolsRoot, cursor: number, coordCursor: number | undefined): DevtoolsTraceSlice {\n const { trace, coords } = root.source;\n const slice = trace.since(cursor);\n const out: DevtoolsTraceSlice = {\n entries: slice.entries.map((e) => serialize(e, RECORD)),\n cursor: slice.cursor,\n live: serialize(trace.live(), RECORD),\n };\n if (coordCursor !== undefined) {\n const c = coords.since(coordCursor);\n out.coords = { entries: c.entries.map((e) => serialize(e, RECORD)), cursor: c.cursor };\n }\n return out;\n}\n\n/** Answer `req` about `root`. `steer` lays a change over the panel's override. Throws on a bad request. */\nexport function answer(\n root: DevtoolsRoot,\n req: DevtoolsRequest,\n steer: (change: { overlays?: DevtoolsOverlays; highlight?: readonly string[] }) => void,\n): DevtoolsResult {\n switch (req.type) {\n case 'get-children': return childrenOf(root, req.nodeId);\n case 'get-node': return nodeDetail(root, req.nodeId);\n case 'get-state': return rootState(root);\n case 'get-trace': return traceSlice(root, req.cursor, req.coordCursor);\n case 'get-frame': return serialize(root.source.frame());\n case 'get-registry':\n if (!root.source.registry) throw new Error('this root serves no registry');\n return root.source.registry();\n case 'get-history': return historySlice(root, req.cursor, req.checkpoints);\n case 'select':\n root.source.select(req.ids);\n return null;\n case 'set-tool':\n if (req.toolId !== null && !root.source.tools().includes(req.toolId)) throw new Error(`no tool \"${req.toolId}\"`);\n root.source.setTool(req.toolId);\n return null;\n case 'highlight':\n steer({ highlight: [...req.ids] });\n return null;\n case 'set-overlays':\n steer({ overlays: req.overlays });\n return null;\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.js","names":[],"sources":["../../src/devtools/protocol.ts"],"sourcesContent":["/**\n * The messages a devtools panel and a page running weasel exchange. Plain\n * data only, so the same messages cross a direct call or `window.postMessage`.\n */\nimport type { DebugFeature } from '../debug/types';\nimport type { SerializedValue } from './serialize';\n\n/** Bumped on any change a panel built against the previous number would misread. */\nexport const DEVTOOLS_PROTOCOL = 1;\n\n/** The areas of a root a `changed` notice reports. */\nexport type DevtoolsArea = 'scene' | 'selection' | 'view' | 'tool' | 'trace' | 'registry' | 'history';\n\n/** One mounted `<SceneCanvas>`. */\nexport interface DevtoolsRootInfo {\n id: string;\n /** The canvas's `aria-label`, else its DOM id, else its root id. */\n label: string;\n dimension: '2d' | '3d';\n}\n\n/** Debug overlay features a panel turns on or off over the canvas's own `debug` prop. */\nexport type DevtoolsOverlays = Partial<Record<DebugFeature, boolean>>;\n\n/** A request a panel sends about one root. */\nexport type DevtoolsRequest = { protocol: number; requestId: number; rootId: string } & (\n | { type: 'get-children'; nodeId?: string }\n | { type: 'get-node'; nodeId: string }\n | { type: 'get-state' }\n /** Coord records come back only when `coordCursor` is given. */\n | { type: 'get-trace'; cursor: number; coordCursor?: number }\n | { type: 'get-frame' }\n | { type: 'get-registry' }\n /** The stack's entry labels come back only when `checkpoints` is true. */\n | { type: 'get-history'; cursor: number; checkpoints?: boolean }\n | { type: 'select'; ids: string[] }\n | { type: 'highlight'; ids: string[] }\n | { type: 'set-tool'; toolId: string | null }\n | { type: 'set-overlays'; overlays: DevtoolsOverlays }\n);\n\n/** Every message a panel sends the page. */\nexport type PanelMessage =\n | { type: 'yo'; protocol: number }\n /** The panel is closing: drop its overlays and highlight, and stop tracing for it. */\n | { type: 'bye'; protocol: number }\n | DevtoolsRequest;\n\n/** Every message the page sends a panel. */\nexport type PageMessage =\n | { type: 'sup'; protocol: number; kitVersion: string; roots: DevtoolsRootInfo[] }\n | { type: 'root-added'; protocol: number; root: DevtoolsRootInfo }\n | { type: 'root-removed'; protocol: number; rootId: string }\n /** Each changed area with its new version. At most one per root per frame. */\n | { type: 'changed'; protocol: number; rootId: string; areas: Partial<Record<DevtoolsArea, number>> }\n | { type: 'result'; protocol: number; requestId: number; result: DevtoolsResult }\n /** `requestId` is null for a message that was not a request. */\n | { type: 'error'; protocol: number; requestId: number | null; message: string };\n\n/** One child row of the scene tree. `nodeId` absent asks for the top level. */\nexport interface DevtoolsNodeSummary {\n id: string;\n kind: 'leaf' | 'container';\n layer: string;\n childCount: number;\n hidden: boolean;\n locked: boolean;\n pickable: boolean;\n}\n\n/** A node as `get-node` returns it: its row in the tree, plus its parent, pose and data. */\nexport interface DevtoolsNodeDetail extends DevtoolsNodeSummary {\n parent: string | null;\n pose: SerializedValue;\n data: SerializedValue;\n}\n\n/** What `get-state` returns about a root. */\nexport interface DevtoolsRootState {\n selection: string[];\n view: SerializedValue;\n activeTool: string | null;\n tools: string[];\n /** The debug overlay features in effect: the canvas's own, with the panel's over them. */\n overlays: DevtoolsOverlays;\n /** Probes registered through the test hook, by name. */\n probes: Record<string, SerializedValue>;\n}\n\n/** What `get-trace` returns: the records after the cursor, and the cursor to send next. */\nexport interface DevtoolsTraceSlice {\n entries: SerializedValue[];\n cursor: number;\n /** What a press at the pointer would do, or null when the pointer is off the canvas. */\n live: SerializedValue;\n coords?: { entries: SerializedValue[]; cursor: number };\n}\n\n/** One gesture-to-action binding, as a tool, an always-on entry or an action declares it. */\nexport interface DevtoolsBinding {\n actionId: string;\n /** The `GestureSpec` as written. */\n spec: SerializedValue;\n /** The spec in the route grammar, one string per route it expands to. */\n routes: string[];\n /** The binding's `BindingOpts`: params, roles, views. */\n opts: SerializedValue;\n}\n\n/** A tool or other contribution the canvas's dispatcher assembles bindings from. */\nexport interface DevtoolsToolEntry {\n id: string;\n /** A switchable tool, an always-on entry, or one a `features` preset brought. */\n slot: 'tool' | 'ambient' | 'contribution';\n label: string;\n /** The icon's qualified name. */\n icon: string | null;\n group: string | null;\n shortcut: string | null;\n /** The key that holds it engaged while pressed. */\n hotkey: string | null;\n hidden: boolean;\n cursor: SerializedValue;\n eligibility: SerializedValue;\n bindings: DevtoolsBinding[];\n /** Ids of the actions it brings with it. */\n actions: string[];\n /** Names of the deps it registers. */\n deps: string[];\n hasOverlay: boolean;\n}\n\n/** A registered action. `enabled` and `active` are asked when the registry is read. */\nexport interface DevtoolsActionEntry {\n id: string;\n label: string;\n icon: string | null;\n group: string | null;\n tags: string[];\n shortcut: string | null;\n /** Which binding scope its own bindings ride. */\n scope: 'hotkey' | 'ambient';\n invoker: 'immediate' | 'ongoing' | null;\n requires: string[];\n variants: { key: string; label: string; icon: string | null; params: SerializedValue }[];\n /** `true`, or the reason it is disabled. */\n enabled: true | string;\n /** Null for an action with no pressed state. */\n active: boolean | null;\n hasPreview: boolean;\n /** The eligibility rule, or null for always eligible. */\n eligible: SerializedValue;\n bindings: DevtoolsBinding[];\n}\n\n/** A dep name the canvas's registry answers. */\nexport interface DevtoolsDepEntry {\n name: string;\n /** How many scopes above the canvas's own its live source sits: 0 for the canvas's. */\n depth: number;\n /** How many sources are stacked under the name there, the live one included. */\n stacked: number;\n}\n\n/** What `get-registry` returns: what one canvas has registered. */\nexport interface DevtoolsRegistry {\n tools: DevtoolsToolEntry[];\n actions: DevtoolsActionEntry[];\n deps: DevtoolsDepEntry[];\n /** The node routing kinds a binding's `kind:` target can name, in classify order. */\n routingKinds: string[];\n}\n\n/** One op of a recorded history step. */\nexport interface DevtoolsHistoryOp {\n /** The op's registered name, `'kit:setPose'` and the like; `'?'` for an op with none. */\n kind: string;\n /** The node ids its args address. */\n targets: string[];\n summary: SerializedValue;\n}\n\n/** One change to a scene's history: an edit recorded, merged, undone, redone or jumped across. */\nexport interface DevtoolsHistoryRecord {\n ts: number;\n step: 'push' | 'coalesce' | 'undo' | 'redo' | 'goto' | 'branch' | 'prune' | 'clear' | 'restore';\n /** The entries the step pushed, merged into or walked across, in the order it touched them. */\n entries: { id: number; label: string; opCount: number; ops: DevtoolsHistoryOp[] }[];\n /** Where the history stood after the step: entries applied, and entries in all. */\n position: { index: number; total: number };\n}\n\n/** What `get-history` returns: the steps after the cursor, the cursor to send next, and where the history stands. */\nexport interface DevtoolsHistorySlice {\n entries: DevtoolsHistoryRecord[];\n cursor: number;\n position: { index: number; total: number; canUndo: boolean; canRedo: boolean };\n /** Every entry on the stacks, oldest applied first, then the redoable ones. */\n checkpoints?: { id: string; label: string }[];\n}\n\n/** The body of a `result` reply; which one depends on the request it answers. */\nexport type DevtoolsResult =\n | DevtoolsNodeSummary[]\n | DevtoolsNodeDetail\n | DevtoolsRootState\n | DevtoolsTraceSlice\n | DevtoolsRegistry\n | DevtoolsHistorySlice\n | SerializedValue\n | null;\n"],"mappings":";;AAQA,IAAa,oBAAoB"}
1
+ {"version":3,"file":"protocol.js","names":[],"sources":["../../src/devtools/protocol.ts"],"sourcesContent":["/**\n * The messages a devtools panel and a page running weasel exchange. Plain\n * data only, so the same messages cross a direct call or `window.postMessage`.\n */\nimport type { DebugFeature } from '../debug/types';\nimport type { SerializedValue } from './serialize';\n\n/** Bumped on any change a panel built against the previous number would misread. */\nexport const DEVTOOLS_PROTOCOL = 1;\n\n/** The areas of a root a `changed` notice reports. */\nexport type DevtoolsArea = 'scene' | 'selection' | 'view' | 'tool' | 'trace' | 'registry' | 'history';\n\n/** One mounted `<SceneCanvas>`. */\nexport interface DevtoolsRootInfo {\n id: string;\n /** The canvas's `aria-label`, else its DOM id, else its root id. */\n label: string;\n dimension: '2d' | '3d';\n}\n\n/** Debug overlay features a panel turns on or off over the canvas's own `debug` prop. */\nexport type DevtoolsOverlays = Partial<Record<DebugFeature, boolean>>;\n\n/** A request a panel sends about one root. */\nexport type DevtoolsRequest = { protocol: number; requestId: number; rootId: string } & (\n | { type: 'get-children'; nodeId?: string }\n | { type: 'get-node'; nodeId: string }\n | { type: 'get-state' }\n /** Coord records come back only when `coordCursor` is given. */\n | { type: 'get-trace'; cursor: number; coordCursor?: number }\n | { type: 'get-frame' }\n | { type: 'get-registry' }\n /** The stack's entry labels come back only when `checkpoints` is true. */\n | { type: 'get-history'; cursor: number; checkpoints?: boolean }\n | { type: 'select'; ids: string[] }\n | { type: 'highlight'; ids: string[] }\n | { type: 'set-tool'; toolId: string | null }\n | { type: 'set-overlays'; overlays: DevtoolsOverlays }\n);\n\n/** Every message a panel sends the page. */\nexport type PanelMessage =\n | { type: 'yo'; protocol: number }\n /** The panel is closing: drop its overlays and highlight, and stop tracing for it. */\n | { type: 'bye'; protocol: number }\n | DevtoolsRequest;\n\n/** Every message the page sends a panel. */\nexport type PageMessage =\n | { type: 'sup'; protocol: number; kitVersion: string; roots: DevtoolsRootInfo[] }\n | { type: 'root-added'; protocol: number; root: DevtoolsRootInfo }\n | { type: 'root-removed'; protocol: number; rootId: string }\n /** Each changed area with its new version. At most one per root per frame. */\n | { type: 'changed'; protocol: number; rootId: string; areas: Partial<Record<DevtoolsArea, number>> }\n | { type: 'result'; protocol: number; requestId: number; result: DevtoolsResult }\n /** `requestId` is null for a message that was not a request. */\n | { type: 'error'; protocol: number; requestId: number | null; message: string };\n\n/** One child row of the scene tree. `nodeId` absent asks for the top level. */\nexport interface DevtoolsNodeSummary {\n id: string;\n kind: 'mark' | 'group';\n layer: string;\n childCount: number;\n hidden: boolean;\n locked: boolean;\n pickable: boolean;\n}\n\n/** A node as `get-node` returns it: its row in the tree, plus its parent, pose and data. */\nexport interface DevtoolsNodeDetail extends DevtoolsNodeSummary {\n parent: string | null;\n pose: SerializedValue;\n data: SerializedValue;\n}\n\n/** What `get-state` returns about a root. */\nexport interface DevtoolsRootState {\n selection: string[];\n view: SerializedValue;\n activeTool: string | null;\n tools: string[];\n /** The debug overlay features in effect: the canvas's own, with the panel's over them. */\n overlays: DevtoolsOverlays;\n /** Probes registered through the test hook, by name. */\n probes: Record<string, SerializedValue>;\n}\n\n/** What `get-trace` returns: the records after the cursor, and the cursor to send next. */\nexport interface DevtoolsTraceSlice {\n entries: SerializedValue[];\n cursor: number;\n /** What a press at the pointer would do, or null when the pointer is off the canvas. */\n live: SerializedValue;\n coords?: { entries: SerializedValue[]; cursor: number };\n}\n\n/** One gesture-to-action binding, as a tool, an always-on entry or an action declares it. */\nexport interface DevtoolsBinding {\n actionId: string;\n /** The `GestureSpec` as written. */\n spec: SerializedValue;\n /** The spec in the route grammar, one string per route it expands to. */\n routes: string[];\n /** The binding's `BindingOpts`: params, roles, views. */\n opts: SerializedValue;\n}\n\n/** A tool or other contribution the canvas's dispatcher assembles bindings from. */\nexport interface DevtoolsToolEntry {\n id: string;\n /** A switchable tool, an always-on entry, or one a `features` preset brought. */\n slot: 'tool' | 'ambient' | 'contribution';\n label: string;\n /** The icon's qualified name. */\n icon: string | null;\n group: string | null;\n shortcut: string | null;\n /** The key that holds it engaged while pressed. */\n hotkey: string | null;\n hidden: boolean;\n cursor: SerializedValue;\n eligibility: SerializedValue;\n bindings: DevtoolsBinding[];\n /** Ids of the actions it brings with it. */\n actions: string[];\n /** Names of the deps it registers. */\n deps: string[];\n hasOverlay: boolean;\n}\n\n/** A registered action. `enabled` and `active` are asked when the registry is read. */\nexport interface DevtoolsActionEntry {\n id: string;\n label: string;\n icon: string | null;\n group: string | null;\n tags: string[];\n shortcut: string | null;\n /** Which binding scope its own bindings ride. */\n scope: 'hotkey' | 'ambient';\n invoker: 'immediate' | 'ongoing' | null;\n requires: string[];\n variants: { key: string; label: string; icon: string | null; params: SerializedValue }[];\n /** `true`, or the reason it is disabled. */\n enabled: true | string;\n /** Null for an action with no pressed state. */\n active: boolean | null;\n hasPreview: boolean;\n /** The eligibility rule, or null for always eligible. */\n eligible: SerializedValue;\n bindings: DevtoolsBinding[];\n}\n\n/** A dep name the canvas's registry answers. */\nexport interface DevtoolsDepEntry {\n name: string;\n /** How many scopes above the canvas's own its live source sits: 0 for the canvas's. */\n depth: number;\n /** How many sources are stacked under the name there, the live one included. */\n stacked: number;\n}\n\n/** What `get-registry` returns: what one canvas has registered. */\nexport interface DevtoolsRegistry {\n tools: DevtoolsToolEntry[];\n actions: DevtoolsActionEntry[];\n deps: DevtoolsDepEntry[];\n /** The node routing kinds a binding's `kind:` target can name, in classify order. */\n routingKinds: string[];\n}\n\n/** One op of a recorded history step. */\nexport interface DevtoolsHistoryOp {\n /** The op's registered name, `'kit:setPose'` and the like; `'?'` for an op with none. */\n kind: string;\n /** The node ids its args address. */\n targets: string[];\n summary: SerializedValue;\n}\n\n/** One change to a scene's history: an edit recorded, merged, undone, redone or jumped across. */\nexport interface DevtoolsHistoryRecord {\n ts: number;\n step: 'push' | 'coalesce' | 'undo' | 'redo' | 'goto' | 'branch' | 'prune' | 'clear' | 'restore';\n /** The entries the step pushed, merged into or walked across, in the order it touched them. */\n entries: { id: number; label: string; opCount: number; ops: DevtoolsHistoryOp[] }[];\n /** Where the history stood after the step: entries applied, and entries in all. */\n position: { index: number; total: number };\n}\n\n/** What `get-history` returns: the steps after the cursor, the cursor to send next, and where the history stands. */\nexport interface DevtoolsHistorySlice {\n entries: DevtoolsHistoryRecord[];\n cursor: number;\n position: { index: number; total: number; canUndo: boolean; canRedo: boolean };\n /** Every entry on the stacks, oldest applied first, then the redoable ones. */\n checkpoints?: { id: string; label: string }[];\n}\n\n/** The body of a `result` reply; which one depends on the request it answers. */\nexport type DevtoolsResult =\n | DevtoolsNodeSummary[]\n | DevtoolsNodeDetail\n | DevtoolsRootState\n | DevtoolsTraceSlice\n | DevtoolsRegistry\n | DevtoolsHistorySlice\n | SerializedValue\n | null;\n"],"mappings":";;AAQA,IAAa,oBAAoB"}