@weasel-js/core 1.13.0-rc.0 → 1.14.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 (745) hide show
  1. package/CHANGELOG.md +408 -0
  2. package/README.md +8 -12
  3. package/dist/affordances/hitAffordanceRegions.js +1 -1
  4. package/dist/affordances/hitAffordanceRegions.js.map +1 -1
  5. package/dist/affordances/pathAnchors.js.map +1 -1
  6. package/dist/animation/clipTargets.js +1 -1
  7. package/dist/animation/clipTargets.js.map +1 -1
  8. package/dist/animation/poseTarget.js +5 -32
  9. package/dist/animation/poseTarget.js.map +1 -1
  10. package/dist/{autoPoseDescriptor-CZ1eXbzY.d.ts → autoPoseDescriptor-C25QVb0W.d.ts} +1 -1
  11. package/dist/barrel/canvas.js +8 -8
  12. package/dist/barrel/chrome.js +9 -9
  13. package/dist/barrel/input.js +1 -1
  14. package/dist/barrel/interactions.js +3 -3
  15. package/dist/barrel/paint.js +2 -2
  16. package/dist/barrel/paths.js +19 -17
  17. package/dist/barrel/renderer.js +11 -7
  18. package/dist/barrel/routing.js +4 -1
  19. package/dist/barrel/scene.js +16 -12
  20. package/dist/barrel/text.js +5 -5
  21. package/dist/barrel/viewport.js +8 -8
  22. package/dist/canvas/Canvas.js +2 -2
  23. package/dist/canvas/Canvas.js.map +1 -1
  24. package/dist/canvas/CanvasView.js +20 -14
  25. package/dist/canvas/CanvasView.js.map +1 -1
  26. package/dist/canvas/GestureReason.js +64 -0
  27. package/dist/canvas/GestureReason.js.map +1 -0
  28. package/dist/canvas/GestureReason.module.js +7 -0
  29. package/dist/canvas/GestureReason.module.js.map +1 -0
  30. package/dist/canvas/LeanSceneCanvas.js +3 -1
  31. package/dist/canvas/LeanSceneCanvas.js.map +1 -1
  32. package/dist/canvas/NodeShape.js +2 -0
  33. package/dist/canvas/NodeShape.js.map +1 -1
  34. package/dist/canvas/PickHud.js +7 -3
  35. package/dist/canvas/PickHud.js.map +1 -1
  36. package/dist/canvas/SceneCanvas/CanvasKitProviders.js +1 -1
  37. package/dist/canvas/SceneCanvas/CanvasKitProviders.js.map +1 -1
  38. package/dist/canvas/SceneCanvas/DevHuds.js +2 -2
  39. package/dist/canvas/SceneCanvas/DevHuds.js.map +1 -1
  40. package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js +4 -2
  41. package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js.map +1 -1
  42. package/dist/canvas/SceneCanvas/SceneCanvasInput.js +3 -1
  43. package/dist/canvas/SceneCanvas/SceneCanvasInput.js.map +1 -1
  44. package/dist/canvas/SceneCanvas/brushOverlay.js.map +1 -1
  45. package/dist/canvas/SceneCanvas/builtinToolSets.js +17 -9
  46. package/dist/canvas/SceneCanvas/builtinToolSets.js.map +1 -1
  47. package/dist/canvas/SceneCanvas/canvasPassThrough.js +2 -1
  48. package/dist/canvas/SceneCanvas/canvasPassThrough.js.map +1 -1
  49. package/dist/canvas/SceneCanvas/defaultNodeProperties.js +75 -19
  50. package/dist/canvas/SceneCanvas/defaultNodeProperties.js.map +1 -1
  51. package/dist/canvas/SceneCanvas/defaultNodeRouting.js +7 -1
  52. package/dist/canvas/SceneCanvas/defaultNodeRouting.js.map +1 -1
  53. package/dist/canvas/SceneCanvas/defaultTextEditor.js +108 -0
  54. package/dist/canvas/SceneCanvas/defaultTextEditor.js.map +1 -0
  55. package/dist/canvas/SceneCanvas/describePresets.js +47 -0
  56. package/dist/canvas/SceneCanvas/describePresets.js.map +1 -0
  57. package/dist/canvas/SceneCanvas/features.js +57 -16
  58. package/dist/canvas/SceneCanvas/features.js.map +1 -1
  59. package/dist/canvas/SceneCanvas/mergeLayers.js +10 -1
  60. package/dist/canvas/SceneCanvas/mergeLayers.js.map +1 -1
  61. package/dist/canvas/SceneCanvas/namedPresets.js +52 -24
  62. package/dist/canvas/SceneCanvas/namedPresets.js.map +1 -1
  63. package/dist/canvas/SceneCanvas/presets/arrange.js +35 -10
  64. package/dist/canvas/SceneCanvas/presets/arrange.js.map +1 -1
  65. package/dist/canvas/SceneCanvas/presets/baseline.js +9 -0
  66. package/dist/canvas/SceneCanvas/presets/baseline.js.map +1 -0
  67. package/dist/canvas/SceneCanvas/presets/chrome.js.map +1 -1
  68. package/dist/canvas/SceneCanvas/presets/composites.js +43 -0
  69. package/dist/canvas/SceneCanvas/presets/composites.js.map +1 -0
  70. package/dist/canvas/SceneCanvas/presets/edit.js +79 -26
  71. package/dist/canvas/SceneCanvas/presets/edit.js.map +1 -1
  72. package/dist/canvas/SceneCanvas/presets/ingest.js +1 -0
  73. package/dist/canvas/SceneCanvas/presets/ingest.js.map +1 -1
  74. package/dist/canvas/SceneCanvas/presets/paths.js +58 -16
  75. package/dist/canvas/SceneCanvas/presets/paths.js.map +1 -1
  76. package/dist/canvas/SceneCanvas/presets/select.js +4 -2
  77. package/dist/canvas/SceneCanvas/presets/select.js.map +1 -1
  78. package/dist/canvas/SceneCanvas/presets/tools.js +24 -0
  79. package/dist/canvas/SceneCanvas/presets/tools.js.map +1 -0
  80. package/dist/canvas/SceneCanvas/presets/transform.js +4 -0
  81. package/dist/canvas/SceneCanvas/presets/transform.js.map +1 -1
  82. package/dist/canvas/SceneCanvas/presets/view.js +3 -1
  83. package/dist/canvas/SceneCanvas/presets/view.js.map +1 -1
  84. package/dist/canvas/SceneCanvas/props.js.map +1 -1
  85. package/dist/canvas/SceneCanvas/shapeGeneratorProperties.js +1 -1
  86. package/dist/canvas/SceneCanvas/shapeGeneratorProperties.js.map +1 -1
  87. package/dist/canvas/SceneCanvas/toolRegistry.js +5 -4
  88. package/dist/canvas/SceneCanvas/toolRegistry.js.map +1 -1
  89. package/dist/canvas/SceneCanvas/useCanvasTools.js +26 -10
  90. package/dist/canvas/SceneCanvas/useCanvasTools.js.map +1 -1
  91. package/dist/canvas/SceneCanvas/useContributionRoles.js +17 -2
  92. package/dist/canvas/SceneCanvas/useContributionRoles.js.map +1 -1
  93. package/dist/canvas/SceneCanvas/useDepartureGhostLayer.js +5 -4
  94. package/dist/canvas/SceneCanvas/useDepartureGhostLayer.js.map +1 -1
  95. package/dist/canvas/SceneCanvas/useDispatcherOverlayLayer.js +1 -1
  96. package/dist/canvas/SceneCanvas/useNodeAtPoint.js.map +1 -1
  97. package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js +9 -8
  98. package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js.map +1 -1
  99. package/dist/canvas/SceneCanvas/useSceneCanvasPathEdit.js +2 -2
  100. package/dist/canvas/SceneCanvas/useSceneCanvasPathEdit.js.map +1 -1
  101. package/dist/canvas/SceneCanvas/useSceneCanvasTools.js +4 -1
  102. package/dist/canvas/SceneCanvas/useSceneCanvasTools.js.map +1 -1
  103. package/dist/canvas/SceneCanvas/useSceneChrome.js +7 -4
  104. package/dist/canvas/SceneCanvas/useSceneChrome.js.map +1 -1
  105. package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js +9 -9
  106. package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js.map +1 -1
  107. package/dist/canvas/SceneCanvas/useScenePicking.js +4 -7
  108. package/dist/canvas/SceneCanvas/useScenePicking.js.map +1 -1
  109. package/dist/canvas/SceneCanvas/useSceneRedrawSources.js +2 -1
  110. package/dist/canvas/SceneCanvas/useSceneRedrawSources.js.map +1 -1
  111. package/dist/canvas/SceneCanvas/useSceneSelectTool.js +6 -6
  112. package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
  113. package/dist/canvas/SceneCanvas/useSceneStandardActions.js +2 -2
  114. package/dist/canvas/SceneCanvas/useSceneStandardActions.js.map +1 -1
  115. package/dist/canvas/SceneCanvas/useSelectionOverlayLayer.js +1 -1
  116. package/dist/canvas/SceneCanvas/useSelectionOverlayLayer.js.map +1 -1
  117. package/dist/canvas/SceneCanvas/useSurfaceViewInputs.js.map +1 -1
  118. package/dist/canvas/SceneCanvas/useToolKeybindings.js +2 -2
  119. package/dist/canvas/SceneCanvas/useToolKeybindings.js.map +1 -1
  120. package/dist/canvas/SceneCanvas/useViewportActions.js +5 -2
  121. package/dist/canvas/SceneCanvas/useViewportActions.js.map +1 -1
  122. package/dist/canvas/SceneCanvas/worldPointAt.js.map +1 -1
  123. package/dist/canvas/SceneCanvas.js +22 -11
  124. package/dist/canvas/SceneCanvas.js.map +1 -1
  125. package/dist/canvas/SceneViewCanvas.js +1 -0
  126. package/dist/canvas/SceneViewCanvas.js.map +1 -1
  127. package/dist/canvas/ViewMarker.js.map +1 -1
  128. package/dist/canvas/accentColor.js +14 -1
  129. package/dist/canvas/accentColor.js.map +1 -1
  130. package/dist/canvas/affordanceAt.js +2 -2
  131. package/dist/canvas/affordanceAt.js.map +1 -1
  132. package/dist/canvas/buildSceneLayer.js +1 -1
  133. package/dist/canvas/buildSceneLayer.js.map +1 -1
  134. package/dist/canvas/buildSceneTree.js +5 -3
  135. package/dist/canvas/buildSceneTree.js.map +1 -1
  136. package/dist/canvas/canvasRenderer.js +6 -2
  137. package/dist/canvas/canvasRenderer.js.map +1 -1
  138. package/dist/canvas/deps/editAnchors.js +4 -4
  139. package/dist/canvas/deps/editAnchors.js.map +1 -1
  140. package/dist/canvas/deps/enteredGroup.js +14 -5
  141. package/dist/canvas/deps/enteredGroup.js.map +1 -1
  142. package/dist/canvas/deps/ingestion.js +5 -1
  143. package/dist/canvas/deps/ingestion.js.map +1 -1
  144. package/dist/canvas/deps/insert.js.map +1 -1
  145. package/dist/canvas/deps/insertSite.js +5 -3
  146. package/dist/canvas/deps/insertSite.js.map +1 -1
  147. package/dist/canvas/deps/nodeAtPoint.js +1 -12
  148. package/dist/canvas/deps/nodeAtPoint.js.map +1 -1
  149. package/dist/canvas/deps/pathEditCommits.js +3 -3
  150. package/dist/canvas/deps/pathEditCommits.js.map +1 -1
  151. package/dist/canvas/deps/slice.js +16 -2
  152. package/dist/canvas/deps/slice.js.map +1 -1
  153. package/dist/canvas/deps/snap.js +5 -5
  154. package/dist/canvas/deps/snap.js.map +1 -1
  155. package/dist/canvas/deps/textEdit.js +5 -6
  156. package/dist/canvas/deps/textEdit.js.map +1 -1
  157. package/dist/canvas/derivedPath.js +8 -3
  158. package/dist/canvas/derivedPath.js.map +1 -1
  159. package/dist/canvas/getNodeAtPoint.js +2 -2
  160. package/dist/canvas/getNodeAtPoint.js.map +1 -1
  161. package/dist/canvas/insertPreviewExtent.js.map +1 -1
  162. package/dist/canvas/layerSlots.js.map +1 -1
  163. package/dist/canvas/nodeShapes/measurePainter.js +1 -1
  164. package/dist/canvas/nodeShapes/measurePainter.js.map +1 -1
  165. package/dist/canvas/nodeShapes/strokedBounds.js +5 -3
  166. package/dist/canvas/nodeShapes/strokedBounds.js.map +1 -1
  167. package/dist/canvas/overlayRoleStrokes.js +6 -0
  168. package/dist/canvas/overlayRoleStrokes.js.map +1 -1
  169. package/dist/canvas/pickIndex/scenePickIndex.js +10 -12
  170. package/dist/canvas/pickIndex/scenePickIndex.js.map +1 -1
  171. package/dist/canvas/sceneAdapter.js +10 -19
  172. package/dist/canvas/sceneAdapter.js.map +1 -1
  173. package/dist/canvas/sceneClipboard.js +17 -4
  174. package/dist/canvas/sceneClipboard.js.map +1 -1
  175. package/dist/canvas/sceneViewRender.js +7 -5
  176. package/dist/canvas/sceneViewRender.js.map +1 -1
  177. package/dist/canvas/surfaceContribution.js.map +1 -1
  178. package/dist/canvas/useCanvasPaint.js +12 -4
  179. package/dist/canvas/useCanvasPaint.js.map +1 -1
  180. package/dist/canvas/useHitObserver.js +1 -1
  181. package/dist/canvas/useHitObserver.js.map +1 -1
  182. package/dist/canvas/useViewAim.js +1 -1
  183. package/dist/canvas/useViewAim.js.map +1 -1
  184. package/dist/canvas/useViewHelpers.js +8 -5
  185. package/dist/canvas/useViewHelpers.js.map +1 -1
  186. package/dist/canvas/useViewNesting.js +39 -12
  187. package/dist/canvas/useViewNesting.js.map +1 -1
  188. package/dist/canvas/viewInputs.js.map +1 -1
  189. package/dist/canvas/viewMarkers.js +2 -2
  190. package/dist/canvas/viewMarkers.js.map +1 -1
  191. package/dist/canvas/viewRegistry.js +2 -2
  192. package/dist/canvas/viewRegistry.js.map +1 -1
  193. package/dist/canvas/viewSnap.js +67 -0
  194. package/dist/canvas/viewSnap.js.map +1 -0
  195. package/dist/clipboard.d.ts +6 -12
  196. package/dist/{clone-qb2e1F5p.d.ts → clone-Bz4iqGfE.d.ts} +10 -5
  197. package/dist/clone.d.ts +5 -5
  198. package/dist/core/geometry/booleanOfPaths.js +23 -0
  199. package/dist/core/geometry/booleanOfPaths.js.map +1 -0
  200. package/dist/core/geometry/pathPoseDescriptor.js +12 -9
  201. package/dist/core/geometry/pathPoseDescriptor.js.map +1 -1
  202. package/dist/core/geometry/poseDescriptor.js +12 -3
  203. package/dist/core/geometry/poseDescriptor.js.map +1 -1
  204. package/dist/core/geometry/targetFrame.js +2 -13
  205. package/dist/core/geometry/targetFrame.js.map +1 -1
  206. package/dist/core/layers/render.js +2 -1
  207. package/dist/core/layers/render.js.map +1 -1
  208. package/dist/core/layers/retain.js +5 -3
  209. package/dist/core/layers/retain.js.map +1 -1
  210. package/dist/core/ops/setPath.js +3 -0
  211. package/dist/core/ops/setPath.js.map +1 -1
  212. package/dist/core/paint/meshKindLazy.js.map +1 -1
  213. package/dist/core/paint/paintKindEntry.js.map +1 -1
  214. package/dist/core/paint/paintKinds.js +8 -1
  215. package/dist/core/paint/paintKinds.js.map +1 -1
  216. package/dist/core/scene/NodeProperties.js.map +1 -1
  217. package/dist/core/scene/NodeRouting.js.map +1 -1
  218. package/dist/core/scene/booleanGroup.js +33 -0
  219. package/dist/core/scene/booleanGroup.js.map +1 -0
  220. package/dist/core/scene/booleanOfChildren.js +53 -0
  221. package/dist/core/scene/booleanOfChildren.js.map +1 -0
  222. package/dist/core/scene/carryDescendants.js +18 -6
  223. package/dist/core/scene/carryDescendants.js.map +1 -1
  224. package/dist/core/scene/composeFrame.js +42 -5
  225. package/dist/core/scene/composeFrame.js.map +1 -1
  226. package/dist/core/scene/composePose.js +35 -16
  227. package/dist/core/scene/composePose.js.map +1 -1
  228. package/dist/core/scene/depMemo.js +36 -18
  229. package/dist/core/scene/depMemo.js.map +1 -1
  230. package/dist/core/scene/derivedParts.js +2 -23
  231. package/dist/core/scene/derivedParts.js.map +1 -1
  232. package/dist/core/scene/effectivePose.js +10 -0
  233. package/dist/core/scene/effectivePose.js.map +1 -1
  234. package/dist/core/scene/historyForm.js +5 -4
  235. package/dist/core/scene/historyForm.js.map +1 -1
  236. package/dist/core/scene/kitOps.js +5 -71
  237. package/dist/core/scene/kitOps.js.map +1 -1
  238. package/dist/core/scene/kitRegistry.js +5 -0
  239. package/dist/core/scene/kitRegistry.js.map +1 -1
  240. package/dist/core/scene/layerOps.js +82 -0
  241. package/dist/core/scene/layerOps.js.map +1 -0
  242. package/dist/core/scene/layoutPass.js +14 -12
  243. package/dist/core/scene/layoutPass.js.map +1 -1
  244. package/dist/core/scene/lockGuard.js +9 -3
  245. package/dist/core/scene/lockGuard.js.map +1 -1
  246. package/dist/core/scene/nestedHit.js +5 -5
  247. package/dist/core/scene/nestedHit.js.map +1 -1
  248. package/dist/core/scene/nodeFlags.js +8 -2
  249. package/dist/core/scene/nodeFlags.js.map +1 -1
  250. package/dist/core/scene/nodeGates.js +31 -2
  251. package/dist/core/scene/nodeGates.js.map +1 -1
  252. package/dist/core/scene/pathEffects/effects.js +22 -3
  253. package/dist/core/scene/pathEffects/effects.js.map +1 -1
  254. package/dist/core/scene/pathEffects/textAlong.js +16 -3
  255. package/dist/core/scene/pathEffects/textAlong.js.map +1 -1
  256. package/dist/core/scene/pathEffects/textAlongBands.js +65 -0
  257. package/dist/core/scene/pathEffects/textAlongBands.js.map +1 -0
  258. package/dist/core/scene/reconcileSpecs.js +1 -1
  259. package/dist/core/scene/reconcileSpecs.js.map +1 -1
  260. package/dist/core/scene/scene.js +11 -5
  261. package/dist/core/scene/scene.js.map +1 -1
  262. package/dist/core/scene/sceneEdits.js +5 -2
  263. package/dist/core/scene/sceneEdits.js.map +1 -1
  264. package/dist/core/scene/scenePoseAdapter.js +10 -1
  265. package/dist/core/scene/scenePoseAdapter.js.map +1 -1
  266. package/dist/core/scene/scrollOffsets.js +93 -0
  267. package/dist/core/scene/scrollOffsets.js.map +1 -0
  268. package/dist/core/scene/serialization.js +15 -5
  269. package/dist/core/scene/serialization.js.map +1 -1
  270. package/dist/core/scene/statedFields.js +2 -1
  271. package/dist/core/scene/statedFields.js.map +1 -1
  272. package/dist/core/scene/strategyView.js +35 -0
  273. package/dist/core/scene/strategyView.js.map +1 -0
  274. package/dist/core/scene/types.js.map +1 -1
  275. package/dist/core/scene/useScene.js +2 -0
  276. package/dist/core/scene/useScene.js.map +1 -1
  277. package/dist/core/scene/worldSave.js +87 -0
  278. package/dist/core/scene/worldSave.js.map +1 -0
  279. package/dist/core/selection/selectionTarget.js +1 -1
  280. package/dist/core/selection/selectionTarget.js.map +1 -1
  281. package/dist/{cornerHandles-CNCb7aTY.d.ts → cornerHandles-DpmQM_z5.d.ts} +1 -1
  282. package/dist/{depSchema-B1RS-IJx.d.ts → depSchema-C3BwxitB.d.ts} +130 -104
  283. package/dist/devtools-protocol.d.ts +2 -2
  284. package/dist/devtools.d.ts +2 -2
  285. package/dist/{features → domains}/chrome-caps/buildChromeCtx.js +1 -1
  286. package/dist/domains/chrome-caps/buildChromeCtx.js.map +1 -0
  287. package/dist/{features → domains}/chrome-caps/conditions.js +1 -1
  288. package/dist/domains/chrome-caps/conditions.js.map +1 -0
  289. package/dist/{features → domains}/chrome-caps/defaults.js +1 -1
  290. package/dist/domains/chrome-caps/defaults.js.map +1 -0
  291. package/dist/{features → domains}/chrome-caps/resolve.js +1 -1
  292. package/dist/domains/chrome-caps/resolve.js.map +1 -0
  293. package/dist/{features → domains}/chrome-caps/useChromeCaps.js +1 -1
  294. package/dist/domains/chrome-caps/useChromeCaps.js.map +1 -0
  295. package/dist/{features → domains}/chrome-caps/useHoverTracking.js +1 -1
  296. package/dist/domains/chrome-caps/useHoverTracking.js.map +1 -0
  297. package/dist/{features → domains}/ingestion/contentHandlers.js +20 -3
  298. package/dist/domains/ingestion/contentHandlers.js.map +1 -0
  299. package/dist/domains/ingestion/ghost.js +56 -0
  300. package/dist/domains/ingestion/ghost.js.map +1 -0
  301. package/dist/{features → domains}/ingestion/imageHandler.js +1 -1
  302. package/dist/domains/ingestion/imageHandler.js.map +1 -0
  303. package/dist/domains/ingestion/kitHandlers.js +22 -0
  304. package/dist/domains/ingestion/kitHandlers.js.map +1 -0
  305. package/dist/{features → domains}/ingestion/openFilePicker.js +1 -1
  306. package/dist/domains/ingestion/openFilePicker.js.map +1 -0
  307. package/dist/{features → domains}/ingestion/placement.js +1 -1
  308. package/dist/domains/ingestion/placement.js.map +1 -0
  309. package/dist/{features → domains}/ingestion/svgHandler.js +1 -1
  310. package/dist/domains/ingestion/svgHandler.js.map +1 -0
  311. package/dist/{features → domains}/ingestion/textHandler.js +9 -7
  312. package/dist/domains/ingestion/textHandler.js.map +1 -0
  313. package/dist/{features → domains}/ingestion/useCanvasIngestion.js +13 -15
  314. package/dist/domains/ingestion/useCanvasIngestion.js.map +1 -0
  315. package/dist/{features → domains}/ingestion/weaselJsonHandler.js +1 -1
  316. package/dist/domains/ingestion/weaselJsonHandler.js.map +1 -0
  317. package/dist/{features → domains}/longPress/longPressFeedback.js +1 -1
  318. package/dist/domains/longPress/longPressFeedback.js.map +1 -0
  319. package/dist/{features → domains}/measure/actionIds.js +1 -1
  320. package/dist/domains/measure/actionIds.js.map +1 -0
  321. package/dist/{features → domains}/measure/measureCommands.js +1 -1
  322. package/dist/domains/measure/measureCommands.js.map +1 -0
  323. package/dist/{features → domains}/measure/measureContribution.js +1 -1
  324. package/dist/domains/measure/measureContribution.js.map +1 -0
  325. package/dist/{features → domains}/measure/measureEdits.js +1 -1
  326. package/dist/domains/measure/measureEdits.js.map +1 -0
  327. package/dist/{features → domains}/measure/measureGestures.js +4 -7
  328. package/dist/domains/measure/measureGestures.js.map +1 -0
  329. package/dist/{features → domains}/measure/measureHit.js +1 -1
  330. package/dist/domains/measure/measureHit.js.map +1 -0
  331. package/dist/{features → domains}/measure/measureLayer.js +1 -1
  332. package/dist/domains/measure/measureLayer.js.map +1 -0
  333. package/dist/{features → domains}/measure/measurement.js +1 -1
  334. package/dist/domains/measure/measurement.js.map +1 -0
  335. package/dist/{features → domains}/measure/measurements.js +2 -2
  336. package/dist/domains/measure/measurements.js.map +1 -0
  337. package/dist/{features → domains}/modes/modeLayers.js +1 -1
  338. package/dist/domains/modes/modeLayers.js.map +1 -0
  339. package/dist/{features → domains}/modes/modeTriggers.js +3 -6
  340. package/dist/domains/modes/modeTriggers.js.map +1 -0
  341. package/dist/{features → domains}/pathEffects/effectSources.js +1 -1
  342. package/dist/domains/pathEffects/effectSources.js.map +1 -0
  343. package/dist/{features → domains}/pathEffects/index.js +1 -1
  344. package/dist/domains/pathEffects/index.js.map +1 -0
  345. package/dist/{features → domains}/pathEffects/readSource.js +1 -1
  346. package/dist/domains/pathEffects/readSource.js.map +1 -0
  347. package/dist/{features → domains}/pathEffects/textItems.js +11 -2
  348. package/dist/domains/pathEffects/textItems.js.map +1 -0
  349. package/dist/{features → domains}/pathEffects/useEffectSourcesLayer.js +1 -1
  350. package/dist/domains/pathEffects/useEffectSourcesLayer.js.map +1 -0
  351. package/dist/{features → domains}/paths/anchorData.js +1 -1
  352. package/dist/domains/paths/anchorData.js.map +1 -0
  353. package/dist/{features → domains}/paths/anchorMarkers.js +9 -7
  354. package/dist/domains/paths/anchorMarkers.js.map +1 -0
  355. package/dist/{features → domains}/paths/editablePath.js +1 -1
  356. package/dist/domains/paths/editablePath.js.map +1 -0
  357. package/dist/{features → domains}/paths/index.js +3 -3
  358. package/dist/{features → domains}/paths/originProjection.js +1 -1
  359. package/dist/domains/paths/originProjection.js.map +1 -0
  360. package/dist/{features → domains}/paths/pathEditExtension.js +1 -1
  361. package/dist/domains/paths/pathEditExtension.js.map +1 -0
  362. package/dist/{features → domains}/paths/pathEditState.js +1 -1
  363. package/dist/domains/paths/pathEditState.js.map +1 -0
  364. package/dist/{features → domains}/paths/pathEditingOverlayLayer.js +1 -1
  365. package/dist/domains/paths/pathEditingOverlayLayer.js.map +1 -0
  366. package/dist/{features → domains}/paths/pathLayer.js +1 -1
  367. package/dist/domains/paths/pathLayer.js.map +1 -0
  368. package/dist/{features → domains}/paths/pathRepresentation.js +1 -1
  369. package/dist/domains/paths/pathRepresentation.js.map +1 -0
  370. package/dist/{features → domains}/paths/pathSnap/glyphs.js +1 -1
  371. package/dist/domains/paths/pathSnap/glyphs.js.map +1 -0
  372. package/dist/{features → domains}/paths/pathSnap/pathSnapSource.js +1 -1
  373. package/dist/domains/paths/pathSnap/pathSnapSource.js.map +1 -0
  374. package/dist/{features → domains}/paths/pathSnap/sceneIndex.js +1 -1
  375. package/dist/domains/paths/pathSnap/sceneIndex.js.map +1 -0
  376. package/dist/{features → domains}/paths/pathSnap/snapGeometry.js +3 -2
  377. package/dist/domains/paths/pathSnap/snapGeometry.js.map +1 -0
  378. package/dist/{features → domains}/paths/penPreviewLayer.js +1 -1
  379. package/dist/domains/paths/penPreviewLayer.js.map +1 -0
  380. package/dist/{features → domains}/paths/shapeGenerator.js +6 -4
  381. package/dist/domains/paths/shapeGenerator.js.map +1 -0
  382. package/dist/{features → domains}/paths/strokeOutline.js +1 -1
  383. package/dist/domains/paths/strokeOutline.js.map +1 -0
  384. package/dist/{features → domains}/paths/useAnchorFields.js +1 -1
  385. package/dist/domains/paths/useAnchorFields.js.map +1 -0
  386. package/dist/{features → domains}/paths/usePathEdit.js +1 -1
  387. package/dist/domains/paths/usePathEdit.js.map +1 -0
  388. package/dist/{features → domains}/poseRun/usePoseRun.js +1 -1
  389. package/dist/domains/poseRun/usePoseRun.js.map +1 -0
  390. package/dist/{features → domains}/selection/enteredGroupFrame.js +49 -10
  391. package/dist/domains/selection/enteredGroupFrame.js.map +1 -0
  392. package/dist/{features → domains}/selection/enteredGroupScope.js +1 -1
  393. package/dist/domains/selection/enteredGroupScope.js.map +1 -0
  394. package/dist/domains/selection/enteredOperands.js +29 -0
  395. package/dist/domains/selection/enteredOperands.js.map +1 -0
  396. package/dist/{features → domains}/selection/handleCommands.js +1 -1
  397. package/dist/domains/selection/handleCommands.js.map +1 -0
  398. package/dist/{features → domains}/selection/overlay.js +1 -1
  399. package/dist/domains/selection/overlay.js.map +1 -0
  400. package/dist/{features → domains}/selection/selectionPose.js +1 -1
  401. package/dist/domains/selection/selectionPose.js.map +1 -0
  402. package/dist/{features → domains}/snap/angle2d.js +1 -1
  403. package/dist/domains/snap/angle2d.js.map +1 -0
  404. package/dist/{features → domains}/snap/debugCandidates.js +1 -1
  405. package/dist/domains/snap/debugCandidates.js.map +1 -0
  406. package/dist/{features → domains}/snap/gather.js +1 -1
  407. package/dist/domains/snap/gather.js.map +1 -0
  408. package/dist/{features → domains}/snap/inputSubspace.js +1 -1
  409. package/dist/domains/snap/inputSubspace.js.map +1 -0
  410. package/dist/{features → domains}/snap/lazySession.js +1 -1
  411. package/dist/domains/snap/lazySession.js.map +1 -0
  412. package/dist/{features → domains}/snap/marks.js +1 -1
  413. package/dist/domains/snap/marks.js.map +1 -0
  414. package/dist/{features → domains}/snap/outcomeStore.js +1 -1
  415. package/dist/domains/snap/outcomeStore.js.map +1 -0
  416. package/dist/{features → domains}/snap/pointSnap.js +1 -1
  417. package/dist/domains/snap/pointSnap.js.map +1 -0
  418. package/dist/{features → domains}/snap/probes2d.js +1 -1
  419. package/dist/domains/snap/probes2d.js.map +1 -0
  420. package/dist/{features → domains}/snap/readoutDraw.js +1 -1
  421. package/dist/domains/snap/readoutDraw.js.map +1 -0
  422. package/dist/{features → domains}/snap/readoutLayer.js +1 -1
  423. package/dist/domains/snap/readoutLayer.js.map +1 -0
  424. package/dist/{features → domains}/snap/registry.js +1 -1
  425. package/dist/domains/snap/registry.js.map +1 -0
  426. package/dist/{features → domains}/snap/resizeBox.js +1 -1
  427. package/dist/domains/snap/resizeBox.js.map +1 -0
  428. package/dist/{features → domains}/snap/roles.js +1 -1
  429. package/dist/domains/snap/roles.js.map +1 -0
  430. package/dist/{features → domains}/snap/rotationSource.js +1 -1
  431. package/dist/domains/snap/rotationSource.js.map +1 -0
  432. package/dist/{features → domains}/snap/sceneReads.js +24 -3
  433. package/dist/domains/snap/sceneReads.js.map +1 -0
  434. package/dist/{features → domains}/snap/sceneSession2d.js +3 -2
  435. package/dist/domains/snap/sceneSession2d.js.map +1 -0
  436. package/dist/{features → domains}/snap/screen.js +1 -1
  437. package/dist/domains/snap/screen.js.map +1 -0
  438. package/dist/{features → domains}/snap/session2d.js +3 -2
  439. package/dist/domains/snap/session2d.js.map +1 -0
  440. package/dist/{features → domains}/snap/spanSnap.js +1 -1
  441. package/dist/domains/snap/spanSnap.js.map +1 -0
  442. package/dist/{features → domains}/snap/stepAngles.js +1 -1
  443. package/dist/domains/snap/stepAngles.js.map +1 -0
  444. package/dist/{features → domains}/text/editingDrawOne.js +4 -4
  445. package/dist/domains/text/editingDrawOne.js.map +1 -0
  446. package/dist/domains/text/hostTextEditors.js +24 -0
  447. package/dist/domains/text/hostTextEditors.js.map +1 -0
  448. package/dist/{features → domains}/text/index.js +1 -1
  449. package/dist/{features → domains}/text/sceneTextDrafts.js +1 -1
  450. package/dist/domains/text/sceneTextDrafts.js.map +1 -0
  451. package/dist/{features → domains}/text/textHitAt.js +18 -2
  452. package/dist/domains/text/textHitAt.js.map +1 -0
  453. package/dist/{features → domains}/text/textLayer.js +1 -1
  454. package/dist/domains/text/textLayer.js.map +1 -0
  455. package/dist/{features → domains}/text/useSceneTextEdit.js +11 -3
  456. package/dist/domains/text/useSceneTextEdit.js.map +1 -0
  457. package/dist/{features → domains}/viewports/aim.js +1 -1
  458. package/dist/domains/viewports/aim.js.map +1 -0
  459. package/dist/{features → domains}/viewports/viewMarker.js +23 -5
  460. package/dist/domains/viewports/viewMarker.js.map +1 -0
  461. package/dist/{features → domains}/viewports/viewMarkerDrag.js +3 -3
  462. package/dist/domains/viewports/viewMarkerDrag.js.map +1 -0
  463. package/dist/{features → domains}/viewports/viewResolver.js +1 -1
  464. package/dist/domains/viewports/viewResolver.js.map +1 -0
  465. package/dist/{features → domains}/viewports/viewportLayer.js +3 -2
  466. package/dist/domains/viewports/viewportLayer.js.map +1 -0
  467. package/dist/icons.table.js +1 -1
  468. package/dist/icons.table.js.map +1 -1
  469. package/dist/icons.table.json +106 -6
  470. package/dist/index.d.ts +1445 -1053
  471. package/dist/index.js +90 -77
  472. package/dist/interactions/actions/admitsChild.js +6 -2
  473. package/dist/interactions/actions/admitsChild.js.map +1 -1
  474. package/dist/interactions/actions/align/align.js +1 -4
  475. package/dist/interactions/actions/align/align.js.map +1 -1
  476. package/dist/interactions/actions/booleans/booleanGroupAction.js +66 -0
  477. package/dist/interactions/actions/booleans/booleanGroupAction.js.map +1 -0
  478. package/dist/interactions/actions/booleans/keymap.js +52 -0
  479. package/dist/interactions/actions/booleans/keymap.js.map +1 -0
  480. package/dist/interactions/actions/booleans/operands.js +1 -1
  481. package/dist/interactions/actions/booleans/pieces.js +4 -11
  482. package/dist/interactions/actions/booleans/pieces.js.map +1 -1
  483. package/dist/interactions/actions/clipboard/action.js +3 -7
  484. package/dist/interactions/actions/clipboard/action.js.map +1 -1
  485. package/dist/interactions/actions/clipboard/clipboardOps.js +1 -1
  486. package/dist/interactions/actions/clipboard/clipboardOps.js.map +1 -1
  487. package/dist/interactions/actions/clipboard/materializePaste.js.map +1 -1
  488. package/dist/interactions/actions/defaults/anchorEditing.js +6 -6
  489. package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
  490. package/dist/interactions/actions/defaults/bendSegment.js +1 -1
  491. package/dist/interactions/actions/defaults/bendSegment.js.map +1 -1
  492. package/dist/interactions/actions/defaults/containerScroll.js +170 -0
  493. package/dist/interactions/actions/defaults/containerScroll.js.map +1 -0
  494. package/dist/interactions/actions/defaults/contextMenu.js +2 -5
  495. package/dist/interactions/actions/defaults/contextMenu.js.map +1 -1
  496. package/dist/interactions/actions/defaults/delete.js +1 -1
  497. package/dist/interactions/actions/defaults/delete.js.map +1 -1
  498. package/dist/interactions/actions/defaults/duplicate.js +1 -3
  499. package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
  500. package/dist/interactions/actions/defaults/enterGroup.js +7 -12
  501. package/dist/interactions/actions/defaults/enterGroup.js.map +1 -1
  502. package/dist/interactions/actions/defaults/enterPathEdit.js +4 -8
  503. package/dist/interactions/actions/defaults/enterPathEdit.js.map +1 -1
  504. package/dist/interactions/actions/defaults/group.js +12 -24
  505. package/dist/interactions/actions/defaults/group.js.map +1 -1
  506. package/dist/interactions/actions/defaults/ingest.js +18 -10
  507. package/dist/interactions/actions/defaults/ingest.js.map +1 -1
  508. package/dist/interactions/actions/defaults/insertPathAnchor.js +3 -3
  509. package/dist/interactions/actions/defaults/insertPathAnchor.js.map +1 -1
  510. package/dist/interactions/actions/defaults/nudgeSteps.js +3 -21
  511. package/dist/interactions/actions/defaults/nudgeSteps.js.map +1 -1
  512. package/dist/interactions/actions/defaults/pathEditMarks.js.map +1 -1
  513. package/dist/interactions/actions/defaults/selectDeep.js +2 -2
  514. package/dist/interactions/actions/defaults/selectDeep.js.map +1 -1
  515. package/dist/interactions/actions/distribute/distribute.js.map +1 -1
  516. package/dist/interactions/actions/dropVerdict.js +9 -0
  517. package/dist/interactions/actions/dropVerdict.js.map +1 -0
  518. package/dist/interactions/actions/edit-anchors/action.js +2 -2
  519. package/dist/interactions/actions/edit-anchors/action.js.map +1 -1
  520. package/dist/interactions/actions/edit-anchors/extensionDrags.js.map +1 -1
  521. package/dist/interactions/actions/edit-anchors/geometry.js +8 -6
  522. package/dist/interactions/actions/edit-anchors/geometry.js.map +1 -1
  523. package/dist/interactions/actions/edit-anchors/handleDrag.js.map +1 -1
  524. package/dist/interactions/actions/edit-anchors/preview.js +1 -1
  525. package/dist/interactions/actions/edit-anchors/preview.js.map +1 -1
  526. package/dist/interactions/actions/flip/action.js +3 -2
  527. package/dist/interactions/actions/flip/action.js.map +1 -1
  528. package/dist/interactions/actions/geometryProjection.js +8 -3
  529. package/dist/interactions/actions/geometryProjection.js.map +1 -1
  530. package/dist/interactions/actions/gestureFrames.js +1 -0
  531. package/dist/interactions/actions/gestureFrames.js.map +1 -1
  532. package/dist/interactions/actions/insert/action.js +6 -2
  533. package/dist/interactions/actions/insert/action.js.map +1 -1
  534. package/dist/interactions/actions/insert/frame.js +1 -1
  535. package/dist/interactions/actions/insert/frame.js.map +1 -1
  536. package/dist/interactions/actions/insert/placeAction.js +6 -8
  537. package/dist/interactions/actions/insert/placeAction.js.map +1 -1
  538. package/dist/interactions/actions/insert/session.js +2 -2
  539. package/dist/interactions/actions/insert/session.js.map +1 -1
  540. package/dist/interactions/actions/move/action.js +6 -15
  541. package/dist/interactions/actions/move/action.js.map +1 -1
  542. package/dist/interactions/actions/move/behaviors/layoutDrop.js +12 -2
  543. package/dist/interactions/actions/move/behaviors/layoutDrop.js.map +1 -1
  544. package/dist/interactions/actions/move/behaviors/reparentOnDrop.js +29 -10
  545. package/dist/interactions/actions/move/behaviors/reparentOnDrop.js.map +1 -1
  546. package/dist/interactions/actions/move/behaviors/snapToContainer.js +42 -6
  547. package/dist/interactions/actions/move/behaviors/snapToContainer.js.map +1 -1
  548. package/dist/interactions/actions/move/capture.js +4 -8
  549. package/dist/interactions/actions/move/capture.js.map +1 -1
  550. package/dist/interactions/actions/move/commit/index.js.map +1 -1
  551. package/dist/interactions/actions/move/commit/layoutDrop.js +4 -3
  552. package/dist/interactions/actions/move/commit/layoutDrop.js.map +1 -1
  553. package/dist/interactions/actions/move/commit/layoutRelease.js +15 -9
  554. package/dist/interactions/actions/move/commit/layoutRelease.js.map +1 -1
  555. package/dist/interactions/actions/move/commit/reparent.js +19 -13
  556. package/dist/interactions/actions/move/commit/reparent.js.map +1 -1
  557. package/dist/interactions/actions/move/delta.js +51 -5
  558. package/dist/interactions/actions/move/delta.js.map +1 -1
  559. package/dist/interactions/actions/move/frame.js +8 -4
  560. package/dist/interactions/actions/move/frame.js.map +1 -1
  561. package/dist/interactions/actions/move/layout/container.js +19 -9
  562. package/dist/interactions/actions/move/layout/container.js.map +1 -1
  563. package/dist/interactions/actions/move/layout/dragged.js +2 -2
  564. package/dist/interactions/actions/move/layout/dragged.js.map +1 -1
  565. package/dist/interactions/actions/move/layout/dropPass.js +36 -18
  566. package/dist/interactions/actions/move/layout/dropPass.js.map +1 -1
  567. package/dist/interactions/actions/move/layout/exits.js +2 -2
  568. package/dist/interactions/actions/move/layout/exits.js.map +1 -1
  569. package/dist/interactions/actions/move/layout/glide.js +5 -5
  570. package/dist/interactions/actions/move/layout/glide.js.map +1 -1
  571. package/dist/interactions/actions/move/layout/view.js +55 -0
  572. package/dist/interactions/actions/move/layout/view.js.map +1 -0
  573. package/dist/interactions/actions/move/refusal.js +52 -0
  574. package/dist/interactions/actions/move/refusal.js.map +1 -0
  575. package/dist/interactions/actions/move/session.js +9 -8
  576. package/dist/interactions/actions/move/session.js.map +1 -1
  577. package/dist/interactions/actions/move/translate.js +10 -4
  578. package/dist/interactions/actions/move/translate.js.map +1 -1
  579. package/dist/interactions/actions/moveNodes.js +21 -5
  580. package/dist/interactions/actions/moveNodes.js.map +1 -1
  581. package/dist/interactions/actions/pathCommands/pathNodes.js +3 -3
  582. package/dist/interactions/actions/pathCommands/pathNodes.js.map +1 -1
  583. package/dist/interactions/actions/planeInput.js +14 -13
  584. package/dist/interactions/actions/planeInput.js.map +1 -1
  585. package/dist/interactions/actions/poseFrame.js +21 -8
  586. package/dist/interactions/actions/poseFrame.js.map +1 -1
  587. package/dist/interactions/actions/resize/action.js +1 -1
  588. package/dist/interactions/actions/resize/action.js.map +1 -1
  589. package/dist/interactions/actions/resize/aspect.js +1 -1
  590. package/dist/interactions/actions/resize/aspect.js.map +1 -1
  591. package/dist/interactions/actions/resize/commit.js +10 -1
  592. package/dist/interactions/actions/resize/commit.js.map +1 -1
  593. package/dist/interactions/actions/resize/frame.js +3 -2
  594. package/dist/interactions/actions/resize/frame.js.map +1 -1
  595. package/dist/interactions/actions/resize/held.js +44 -0
  596. package/dist/interactions/actions/resize/held.js.map +1 -0
  597. package/dist/interactions/actions/resize/session.js +3 -1
  598. package/dist/interactions/actions/resize/session.js.map +1 -1
  599. package/dist/interactions/actions/resolveOverlays.js +9 -0
  600. package/dist/interactions/actions/resolveOverlays.js.map +1 -1
  601. package/dist/interactions/actions/resolvePreviews.js +23 -7
  602. package/dist/interactions/actions/resolvePreviews.js.map +1 -1
  603. package/dist/interactions/actions/rotate/action.js +1 -1
  604. package/dist/interactions/actions/rotate/action.js.map +1 -1
  605. package/dist/interactions/actions/rotate/frame.js +1 -1
  606. package/dist/interactions/actions/rotate/frame.js.map +1 -1
  607. package/dist/interactions/actions/rotate/session.js +2 -2
  608. package/dist/interactions/actions/rotate/session.js.map +1 -1
  609. package/dist/interactions/actions/selectionSubtree.js +2 -4
  610. package/dist/interactions/actions/selectionSubtree.js.map +1 -1
  611. package/dist/interactions/actions/spatialReference.js +4 -9
  612. package/dist/interactions/actions/spatialReference.js.map +1 -1
  613. package/dist/interactions/gestures/dragRadial/dragRadial.js.map +1 -1
  614. package/dist/interactions/gestures/dragRect/dragRect.js.map +1 -1
  615. package/dist/kitSettingsSchema.js +2 -2
  616. package/dist/kitSettingsSchema.js.map +1 -1
  617. package/dist/{math-C_elShP_.d.ts → math-B_unOnF4.d.ts} +2 -2
  618. package/dist/math.d.ts +3 -3
  619. package/dist/move.d.ts +6 -6
  620. package/dist/{options-ClEm3SEl.d.ts → options-DAjkhbcs.d.ts} +5 -5
  621. package/dist/{poseDescriptor-DN2yfnq_.d.ts → poseDescriptor-CvSxVhCd.d.ts} +17 -4
  622. package/dist/resize.d.ts +6 -6
  623. package/dist/style.css +14 -0
  624. package/dist/test-seams.d.ts +11 -15
  625. package/dist/test-seams.js +1 -1
  626. package/dist/tools/builtin/eyedropper/useEyedropperTool.js +3 -6
  627. package/dist/tools/builtin/eyedropper/useEyedropperTool.js.map +1 -1
  628. package/dist/tools/builtin/line/useLineTool.js.map +1 -1
  629. package/dist/tools/builtin/measure/useMeasureTool.js +2 -2
  630. package/dist/tools/builtin/measure/useMeasureTool.js.map +1 -1
  631. package/dist/tools/builtin/pen/penHandle.js +2 -2
  632. package/dist/tools/builtin/pen/penHandle.js.map +1 -1
  633. package/dist/tools/builtin/pen/usePenTool.js +13 -19
  634. package/dist/tools/builtin/pen/usePenTool.js.map +1 -1
  635. package/dist/tools/builtin/polygon/usePolygonTool.js +1 -1
  636. package/dist/tools/builtin/polygon/usePolygonTool.js.map +1 -1
  637. package/dist/tools/builtin/select/groupEntry.js +1 -1
  638. package/dist/tools/builtin/select/groupEntry.js.map +1 -1
  639. package/dist/tools/builtin/select/selectionContributions.js +1 -1
  640. package/dist/tools/builtin/select/selectionContributions.js.map +1 -1
  641. package/dist/tools/builtin/select/useSelectTool.js +7 -7
  642. package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
  643. package/dist/tools/builtin/slice/useSliceTool.js +5 -8
  644. package/dist/tools/builtin/slice/useSliceTool.js.map +1 -1
  645. package/dist/tools/builtin/star/useStarTool.js +1 -1
  646. package/dist/tools/builtin/star/useStarTool.js.map +1 -1
  647. package/dist/tools/index.js +2 -1
  648. package/dist/{types-49uYJN3j.d.ts → types-A_sNJYRd.d.ts} +8 -0
  649. package/dist/{types-BEdRIhNc.d.ts → types-DW14eu9B.d.ts} +131 -36
  650. package/dist/{types-p2alu-R1.d.ts → types-VCtfchp-.d.ts} +4 -6
  651. package/dist/util/color.js +12 -14
  652. package/dist/util/color.js.map +1 -1
  653. package/dist/version.js +1 -1
  654. package/package.json +25 -24
  655. package/dist/canvas/poseCascade.js +0 -65
  656. package/dist/canvas/poseCascade.js.map +0 -1
  657. package/dist/features/chrome-caps/buildChromeCtx.js.map +0 -1
  658. package/dist/features/chrome-caps/conditions.js.map +0 -1
  659. package/dist/features/chrome-caps/defaults.js.map +0 -1
  660. package/dist/features/chrome-caps/resolve.js.map +0 -1
  661. package/dist/features/chrome-caps/useChromeCaps.js.map +0 -1
  662. package/dist/features/chrome-caps/useHoverTracking.js.map +0 -1
  663. package/dist/features/ingestion/contentHandlers.js.map +0 -1
  664. package/dist/features/ingestion/imageHandler.js.map +0 -1
  665. package/dist/features/ingestion/openFilePicker.js.map +0 -1
  666. package/dist/features/ingestion/placement.js.map +0 -1
  667. package/dist/features/ingestion/registerKitHandlers.js +0 -43
  668. package/dist/features/ingestion/registerKitHandlers.js.map +0 -1
  669. package/dist/features/ingestion/svgHandler.js.map +0 -1
  670. package/dist/features/ingestion/textHandler.js.map +0 -1
  671. package/dist/features/ingestion/useCanvasIngestion.js.map +0 -1
  672. package/dist/features/ingestion/weaselJsonHandler.js.map +0 -1
  673. package/dist/features/longPress/longPressFeedback.js.map +0 -1
  674. package/dist/features/measure/actionIds.js.map +0 -1
  675. package/dist/features/measure/measureCommands.js.map +0 -1
  676. package/dist/features/measure/measureContribution.js.map +0 -1
  677. package/dist/features/measure/measureEdits.js.map +0 -1
  678. package/dist/features/measure/measureGestures.js.map +0 -1
  679. package/dist/features/measure/measureHit.js.map +0 -1
  680. package/dist/features/measure/measureLayer.js.map +0 -1
  681. package/dist/features/measure/measurement.js.map +0 -1
  682. package/dist/features/measure/measurements.js.map +0 -1
  683. package/dist/features/modes/modeLayers.js.map +0 -1
  684. package/dist/features/modes/modeTriggers.js.map +0 -1
  685. package/dist/features/pathEffects/effectSources.js.map +0 -1
  686. package/dist/features/pathEffects/index.js.map +0 -1
  687. package/dist/features/pathEffects/readSource.js.map +0 -1
  688. package/dist/features/pathEffects/textItems.js.map +0 -1
  689. package/dist/features/pathEffects/useEffectSourcesLayer.js.map +0 -1
  690. package/dist/features/paths/anchorData.js.map +0 -1
  691. package/dist/features/paths/anchorMarkers.js.map +0 -1
  692. package/dist/features/paths/editablePath.js.map +0 -1
  693. package/dist/features/paths/originProjection.js.map +0 -1
  694. package/dist/features/paths/pathEditExtension.js.map +0 -1
  695. package/dist/features/paths/pathEditState.js.map +0 -1
  696. package/dist/features/paths/pathEditingOverlayLayer.js.map +0 -1
  697. package/dist/features/paths/pathLayer.js.map +0 -1
  698. package/dist/features/paths/pathRepresentation.js.map +0 -1
  699. package/dist/features/paths/pathSnap/glyphs.js.map +0 -1
  700. package/dist/features/paths/pathSnap/pathSnapSource.js.map +0 -1
  701. package/dist/features/paths/pathSnap/sceneIndex.js.map +0 -1
  702. package/dist/features/paths/pathSnap/snapGeometry.js.map +0 -1
  703. package/dist/features/paths/penPreviewLayer.js.map +0 -1
  704. package/dist/features/paths/shapeGenerator.js.map +0 -1
  705. package/dist/features/paths/strokeOutline.js.map +0 -1
  706. package/dist/features/paths/useAnchorFields.js.map +0 -1
  707. package/dist/features/paths/usePathEdit.js.map +0 -1
  708. package/dist/features/poseRun/usePoseRun.js.map +0 -1
  709. package/dist/features/selection/enteredGroupFrame.js.map +0 -1
  710. package/dist/features/selection/enteredGroupScope.js.map +0 -1
  711. package/dist/features/selection/handleCommands.js.map +0 -1
  712. package/dist/features/selection/overlay.js.map +0 -1
  713. package/dist/features/selection/selectionPose.js.map +0 -1
  714. package/dist/features/snap/angle2d.js.map +0 -1
  715. package/dist/features/snap/debugCandidates.js.map +0 -1
  716. package/dist/features/snap/gather.js.map +0 -1
  717. package/dist/features/snap/inputSubspace.js.map +0 -1
  718. package/dist/features/snap/lazySession.js.map +0 -1
  719. package/dist/features/snap/marks.js.map +0 -1
  720. package/dist/features/snap/outcomeStore.js.map +0 -1
  721. package/dist/features/snap/pointSnap.js.map +0 -1
  722. package/dist/features/snap/probes2d.js.map +0 -1
  723. package/dist/features/snap/readoutDraw.js.map +0 -1
  724. package/dist/features/snap/readoutLayer.js.map +0 -1
  725. package/dist/features/snap/registry.js.map +0 -1
  726. package/dist/features/snap/resizeBox.js.map +0 -1
  727. package/dist/features/snap/roles.js.map +0 -1
  728. package/dist/features/snap/rotationSource.js.map +0 -1
  729. package/dist/features/snap/sceneReads.js.map +0 -1
  730. package/dist/features/snap/sceneSession2d.js.map +0 -1
  731. package/dist/features/snap/screen.js.map +0 -1
  732. package/dist/features/snap/session2d.js.map +0 -1
  733. package/dist/features/snap/spanSnap.js.map +0 -1
  734. package/dist/features/snap/stepAngles.js.map +0 -1
  735. package/dist/features/text/editingDrawOne.js.map +0 -1
  736. package/dist/features/text/sceneTextDrafts.js.map +0 -1
  737. package/dist/features/text/textHitAt.js.map +0 -1
  738. package/dist/features/text/textLayer.js.map +0 -1
  739. package/dist/features/text/useSceneTextEdit.js.map +0 -1
  740. package/dist/features/viewports/aim.js.map +0 -1
  741. package/dist/features/viewports/viewMarker.js.map +0 -1
  742. package/dist/features/viewports/viewMarkerDrag.js.map +0 -1
  743. package/dist/features/viewports/viewResolver.js.map +0 -1
  744. package/dist/features/viewports/viewportLayer.js.map +0 -1
  745. /package/dist/{features → domains}/chrome-caps/index.js +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"layerSlots.js","names":[],"sources":["../../src/canvas/layerSlots.ts"],"sourcesContent":["import type { RenderLayer } from '../core/layers/render';\nimport type { SelectionOverlayLayerOpts } from 'features/selection/overlay';\nimport type { SceneSlotConfig } from './buildSceneLayer';\nimport type { PathEditingOverlayStyle } from 'features/paths/pathEditingOverlayLayer';\n\n/** Standard slot names — render in this canonical order.\n * `cellHighlight` is not an anchor for custom layers: it sits between the\n * grid and the scene, so anchor to one of those. */\n/** @internal */\nexport const STANDARD_SLOTS = [\n 'grid',\n 'cellHighlight',\n 'scene',\n 'selectionOverlay',\n] as const;\n\n/** Names of the slots `<Canvas>` supports out of the box (excluding the implicit cell-highlight overlay). */\nexport type StandardSlotName = Exclude<(typeof STANDARD_SLOTS)[number], 'cellHighlight'>;\n\n/** Custom layer entry — any key not in `STANDARD_SLOTS`. The presence of\n * `.layer` discriminates this from a slot config. */\nexport interface CustomLayerEntry {\n layer: RenderLayer<unknown>;\n /** Insert immediately after the named standard slot or another custom-layer key. */\n after?: StandardSlotName | (string & {});\n /** Insert immediately before the named standard slot or another custom-layer key. */\n before?: StandardSlotName | (string & {});\n}\n\nexport function isCustomEntry(v: unknown): v is CustomLayerEntry {\n return !!v && typeof v === 'object' && 'layer' in (v as Record<string, unknown>);\n}\n\n/** Selection-overlay slot config — passed through to `createSelectionOverlayLayer`,\n * minus the `getSelection`/`getPose` the canvas wires automatically and the\n * `resizeHandles` it takes from the hit-test, so the two cannot disagree. */\nexport type SelectionOverlaySlotConfig<TPose> = Omit<\n SelectionOverlayLayerOpts<TPose>,\n 'getSelection' | 'getPose' | 'resizeHandles'\n> & {\n /** Override the auto-wired pose lookup (overlay-aware → adapter fallback). */\n poseById?: (id: string) => TPose | null;\n};\n\n/** Path-edit overlay slot config: how `<SceneCanvas>` draws the chrome of the\n * path whose anchors are being edited. */\nexport interface PathEditingOverlaySlotConfig {\n /** Anchor and handle marker overrides. */\n style?: PathEditingOverlayStyle;\n}\n\n/** Per-slot config union. The key narrows it in practice. */\nexport type StandardSlotConfig<TNode extends { id: string }, TPose> =\n | SceneSlotConfig<TNode, TPose>\n | SelectionOverlaySlotConfig<TPose>\n | PathEditingOverlaySlotConfig;\n\n/** What may fill one of the canvas's named layer slots: a config the canvas\n * builds the layer from, a pre-built layer, or `null` to leave the slot\n * empty. */\nexport type LayerSlotValue<TNode extends { id: string }, TPose> =\n | StandardSlotConfig<TNode, TPose>\n | CustomLayerEntry\n | null;\n\n/** The canvas's render stack, as named slots. The standard slots (grid,\n * scene, selection overlay, cell highlight) fix where a layer sits in the\n * stack; scene and selection overlay can be configured, and every one can be\n * given a layer of your own or switched off. Further keys add custom layers.\n * `TSceneSlot` is what the scene slot accepts: `<SceneCanvas>` takes a\n * partial config and fills the rest from its defaults. */\nexport type LayersMap<\n TNode extends { id: string },\n TPose,\n TSceneSlot = SceneSlotConfig<TNode, TPose>,\n> = {\n /** Grid slot, beneath the scene. Takes a pre-built layer (`{ layer }`) —\n * e.g. `createGridLayer` from `@weasel-js/guides` — or `null`. */\n grid?: CustomLayerEntry | null;\n /** Scene slot: the config `<SceneCanvas>` builds its layers from, a\n * pre-built layer that paints the scene in their place, or `null`. A\n * pre-built layer gets no ghost of a node in flight, which needs `drawOne`. */\n scene?: TSceneSlot | CustomLayerEntry | null;\n /** Selection-overlay slot: the config `<SceneCanvas>` builds the layer\n * from, a pre-built layer that paints in its place, or `null` for no\n * selection chrome. */\n selectionOverlay?: SelectionOverlaySlotConfig<TPose> | CustomLayerEntry | null;\n /** Cell-highlight overlay slot, drawn immediately after the grid. Takes a\n * pre-built layer (`{ layer }`) — e.g. `createCellHighlightLayer` from\n * `@weasel-js/guides` — or `null`. */\n cellHighlight?: CustomLayerEntry | null;\n /** `<SceneCanvas>`'s anchor-editing overlay, above the selection overlay:\n * the config it builds the layer from, a pre-built layer, or `null`. */\n pathEditingOverlay?: PathEditingOverlaySlotConfig | CustomLayerEntry | null;\n} & {\n [customKey: string]: LayerSlotValue<TNode, TPose> | TSceneSlot | undefined;\n};\n\n/** The scene slot once built: its canvas layers, bottom first. Each `key` is\n * an anchor a custom layer can name (`scene`, or `scene:<layerId>`). */\nexport interface SceneSlotLayers {\n layers: ReadonlyArray<{ key: string; layer: RenderLayer<unknown> }>;\n}\n\n/** What `<Canvas>` composes: a {@link LayersMap} with every slot already a\n * layer. `<SceneCanvas>` builds one from its `layers` prop. */\nexport type CanvasLayers = {\n grid?: CustomLayerEntry | null;\n cellHighlight?: CustomLayerEntry | null;\n scene?: SceneSlotLayers | null;\n selectionOverlay?: CustomLayerEntry | null;\n} & {\n [customKey: string]: CustomLayerEntry | SceneSlotLayers | null | undefined;\n};\n\n/** The layers of `map` that are already built, in its key order: each\n * `{ layer }` entry and each `null`. A slot config is left out, for whoever\n * builds it to put back. */\nexport function builtLayersOf(map: Record<string, unknown>): Record<string, CustomLayerEntry | null> {\n const out: Record<string, CustomLayerEntry | null> = {};\n for (const [key, value] of Object.entries(map)) {\n if (value === null || isCustomEntry(value)) out[key] = value;\n }\n return out;\n}\n"],"mappings":";;;;;AASA,IAAa,iBAAiB;CAC5B;CACA;CACA;CACA;AACF;AAeA,SAAgB,cAAc,GAAmC;CAC/D,OAAO,CAAC,CAAC,KAAK,OAAO,MAAM,YAAY,WAAY;AACrD;;;;AAuFA,SAAgB,cAAc,KAAuE;CACnG,MAAM,MAA+C,CAAC;CACtD,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,GAAG,GAC3C,IAAI,UAAU,QAAQ,cAAc,KAAK,GAAG,IAAI,OAAO;CAEzD,OAAO;AACT"}
1
+ {"version":3,"file":"layerSlots.js","names":[],"sources":["../../src/canvas/layerSlots.ts"],"sourcesContent":["import type { RenderLayer } from '../core/layers/render';\nimport type { SelectionOverlayLayerOpts } from 'domains/selection/overlay';\nimport type { SceneSlotConfig } from './buildSceneLayer';\nimport type { PathEditingOverlayStyle } from 'domains/paths/pathEditingOverlayLayer';\n\n/** Standard slot names — render in this canonical order.\n * `cellHighlight` is not an anchor for custom layers: it sits between the\n * grid and the scene, so anchor to one of those. */\n/** @internal */\nexport const STANDARD_SLOTS = [\n 'grid',\n 'cellHighlight',\n 'scene',\n 'selectionOverlay',\n] as const;\n\n/** Names of the slots `<Canvas>` supports out of the box (excluding the implicit cell-highlight overlay). */\nexport type StandardSlotName = Exclude<(typeof STANDARD_SLOTS)[number], 'cellHighlight'>;\n\n/** Custom layer entry — any key not in `STANDARD_SLOTS`. The presence of\n * `.layer` discriminates this from a slot config. */\nexport interface CustomLayerEntry {\n layer: RenderLayer<unknown>;\n /** Insert immediately after the named standard slot or another custom-layer key. */\n after?: StandardSlotName | (string & {});\n /** Insert immediately before the named standard slot or another custom-layer key. */\n before?: StandardSlotName | (string & {});\n}\n\nexport function isCustomEntry(v: unknown): v is CustomLayerEntry {\n return !!v && typeof v === 'object' && 'layer' in (v as Record<string, unknown>);\n}\n\n/** Selection-overlay slot config — passed through to `createSelectionOverlayLayer`,\n * minus the `getSelection`/`getPose` the canvas wires automatically and the\n * `resizeHandles` it takes from the hit-test, so the two cannot disagree. */\nexport type SelectionOverlaySlotConfig<TPose> = Omit<\n SelectionOverlayLayerOpts<TPose>,\n 'getSelection' | 'getPose' | 'resizeHandles'\n> & {\n /** Override the auto-wired pose lookup (overlay-aware → adapter fallback). */\n poseById?: (id: string) => TPose | null;\n};\n\n/** Path-edit overlay slot config: how `<SceneCanvas>` draws the chrome of the\n * path whose anchors are being edited. */\nexport interface PathEditingOverlaySlotConfig {\n /** Anchor and handle marker overrides. */\n style?: PathEditingOverlayStyle;\n}\n\n/** Per-slot config union. The key narrows it in practice. */\nexport type StandardSlotConfig<TNode extends { id: string }, TPose> =\n | SceneSlotConfig<TNode, TPose>\n | SelectionOverlaySlotConfig<TPose>\n | PathEditingOverlaySlotConfig;\n\n/** What may fill one of the canvas's named layer slots: a config the canvas\n * builds the layer from, a pre-built layer, or `null` to leave the slot\n * empty. */\nexport type LayerSlotValue<TNode extends { id: string }, TPose> =\n | StandardSlotConfig<TNode, TPose>\n | CustomLayerEntry\n | null;\n\n/** The canvas's render stack, as named slots. The standard slots (grid,\n * scene, selection overlay, cell highlight) fix where a layer sits in the\n * stack; scene and selection overlay can be configured, and every one can be\n * given a layer of your own or switched off. Further keys add custom layers.\n * `TSceneSlot` is what the scene slot accepts: `<SceneCanvas>` takes a\n * partial config and fills the rest from its defaults. */\nexport type LayersMap<\n TNode extends { id: string },\n TPose,\n TSceneSlot = SceneSlotConfig<TNode, TPose>,\n> = {\n /** Grid slot, beneath the scene. Takes a pre-built layer (`{ layer }`) —\n * e.g. `createGridLayer` from `@weasel-js/guides` — or `null`. */\n grid?: CustomLayerEntry | null;\n /** Scene slot: the config `<SceneCanvas>` builds its layers from, a\n * pre-built layer that paints the scene in their place, or `null`. A\n * pre-built layer gets no ghost of a node in flight, which needs `drawOne`. */\n scene?: TSceneSlot | CustomLayerEntry | null;\n /** Selection-overlay slot: the config `<SceneCanvas>` builds the layer\n * from, a pre-built layer that paints in its place, or `null` for no\n * selection chrome. */\n selectionOverlay?: SelectionOverlaySlotConfig<TPose> | CustomLayerEntry | null;\n /** Cell-highlight overlay slot, drawn immediately after the grid. Takes a\n * pre-built layer (`{ layer }`) — e.g. `createCellHighlightLayer` from\n * `@weasel-js/guides` — or `null`. */\n cellHighlight?: CustomLayerEntry | null;\n /** `<SceneCanvas>`'s anchor-editing overlay, above the selection overlay:\n * the config it builds the layer from, a pre-built layer, or `null`. */\n pathEditingOverlay?: PathEditingOverlaySlotConfig | CustomLayerEntry | null;\n} & {\n [customKey: string]: LayerSlotValue<TNode, TPose> | TSceneSlot | undefined;\n};\n\n/** The scene slot once built: its canvas layers, bottom first. Each `key` is\n * an anchor a custom layer can name (`scene`, or `scene:<layerId>`). */\nexport interface SceneSlotLayers {\n layers: ReadonlyArray<{ key: string; layer: RenderLayer<unknown> }>;\n}\n\n/** What `<Canvas>` composes: a {@link LayersMap} with every slot already a\n * layer. `<SceneCanvas>` builds one from its `layers` prop. */\nexport type CanvasLayers = {\n grid?: CustomLayerEntry | null;\n cellHighlight?: CustomLayerEntry | null;\n scene?: SceneSlotLayers | null;\n selectionOverlay?: CustomLayerEntry | null;\n} & {\n [customKey: string]: CustomLayerEntry | SceneSlotLayers | null | undefined;\n};\n\n/** The layers of `map` that are already built, in its key order: each\n * `{ layer }` entry and each `null`. A slot config is left out, for whoever\n * builds it to put back. */\nexport function builtLayersOf(map: Record<string, unknown>): Record<string, CustomLayerEntry | null> {\n const out: Record<string, CustomLayerEntry | null> = {};\n for (const [key, value] of Object.entries(map)) {\n if (value === null || isCustomEntry(value)) out[key] = value;\n }\n return out;\n}\n"],"mappings":";;;;;AASA,IAAa,iBAAiB;CAC5B;CACA;CACA;CACA;AACF;AAeA,SAAgB,cAAc,GAAmC;CAC/D,OAAO,CAAC,CAAC,KAAK,OAAO,MAAM,YAAY,WAAY;AACrD;;;;AAuFA,SAAgB,cAAc,KAAuE;CACnG,MAAM,MAA+C,CAAC;CACtD,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,GAAG,GAC3C,IAAI,UAAU,QAAQ,cAAc,KAAK,GAAG,IAAI,OAAO;CAEzD,OAAO;AACT"}
@@ -1,6 +1,6 @@
1
1
  import { paintsPose } from "./poseBox.js";
2
2
  import { OVERLAY_ROLE_STROKES } from "../overlayRoleStrokes.js";
3
- import { measureEndsOf } from "../../features/measure/measurement.js";
3
+ import { measureEndsOf } from "../../domains/measure/measurement.js";
4
4
  import { polylineFromPoints } from "@weasel-js/geom";
5
5
 
6
6
  //#region src/canvas/nodeShapes/measurePainter.ts
@@ -1 +1 @@
1
- {"version":3,"file":"measurePainter.js","names":[],"sources":["../../../src/canvas/nodeShapes/measurePainter.ts"],"sourcesContent":["import { polylineFromPoints } from '@weasel-js/geom';\nimport type { DrawCommand } from '@weasel-js/renderer';\nimport type { NodeShapeEntry } from 'canvas/NodeShape';\nimport { OVERLAY_ROLE_STROKES } from 'canvas/overlayRoleStrokes';\nimport { measureEndsOf, type MeasurePoint } from 'features/measure/measurement';\nimport { paintsPose } from './poseBox';\n\n/** Screen-px half-length of the tick across each end of a measurement. */\nexport const MEASURE_TICK_PX = 5;\n\n/**\n * A measurement's line and a tick across each end, as the measure painter\n * and the measurements layer draw it. `perPx` is world units per screen pixel, so the\n * line and its ticks keep their screen size at any zoom.\n */\nexport function measureCommands(start: MeasurePoint, end: MeasurePoint, perPx: number): DrawCommand[] {\n const stroke = { ...OVERLAY_ROLE_STROKES['measure'], width: { px: 1 } };\n const len = Math.hypot(end.x - start.x, end.y - start.y);\n // The ticks run across the line; with no length yet they stand upright.\n const nx = len > 0 ? -(end.y - start.y) / len : 0;\n const ny = len > 0 ? (end.x - start.x) / len : 1;\n const half = MEASURE_TICK_PX * perPx;\n const tick = (p: MeasurePoint): DrawCommand => ({\n kind: 'path',\n path: polylineFromPoints([{ x: p.x - nx * half, y: p.y - ny * half }, { x: p.x + nx * half, y: p.y + ny * half }]),\n stroke,\n });\n const out: DrawCommand[] = [tick(start), tick(end)];\n if (len > 0) out.unshift({ kind: 'path', path: polylineFromPoints([start, end]), stroke });\n return out;\n}\n\n/** Paints a measurement node — `data.measure`, in world units — in the\n * canvas's measure color. It has no silhouette: a measurement is not content\n * to pick. */\nexport const MEASURE_PAINTER: NodeShapeEntry = {\n id: 'kit:measure',\n matches: (node) => paintsPose(node.pose) && measureEndsOf(node) !== null,\n paint: (node, _pose, ctx) => {\n const ends = measureEndsOf(node);\n if (!ends) return [];\n const dpr = globalThis.devicePixelRatio || 1;\n return measureCommands(ends.start, ends.end, dpr / (ctx?.pixelScale ?? dpr));\n },\n};\n"],"mappings":";;;;;;;AAQA,IAAa,kBAAkB;;;;;;AAO/B,SAAgB,gBAAgB,OAAqB,KAAmB,OAA8B;CACpG,MAAM,SAAS;EAAE,GAAG,qBAAqB;EAAY,OAAO,EAAE,IAAI,EAAE;CAAE;CACtE,MAAM,MAAM,KAAK,MAAM,IAAI,IAAI,MAAM,GAAG,IAAI,IAAI,MAAM,CAAC;CAEvD,MAAM,KAAK,MAAM,IAAI,EAAE,IAAI,IAAI,MAAM,KAAK,MAAM;CAChD,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,MAAM;CAC/C,MAAM,OAAO,kBAAkB;CAC/B,MAAM,QAAQ,OAAkC;EAC9C,MAAM;EACN,MAAM,mBAAmB,CAAC;GAAE,GAAG,EAAE,IAAI,KAAK;GAAM,GAAG,EAAE,IAAI,KAAK;EAAK,GAAG;GAAE,GAAG,EAAE,IAAI,KAAK;GAAM,GAAG,EAAE,IAAI,KAAK;EAAK,CAAC,CAAC;EACjH;CACF;CACA,MAAM,MAAqB,CAAC,KAAK,KAAK,GAAG,KAAK,GAAG,CAAC;CAClD,IAAI,MAAM,GAAG,IAAI,QAAQ;EAAE,MAAM;EAAQ,MAAM,mBAAmB,CAAC,OAAO,GAAG,CAAC;EAAG;CAAO,CAAC;CACzF,OAAO;AACT;;;;AAKA,IAAa,kBAAkC;CAC7C,IAAI;CACJ,UAAU,SAAS,WAAW,KAAK,IAAI,KAAK,cAAc,IAAI,MAAM;CACpE,QAAQ,MAAM,OAAO,QAAQ;EAC3B,MAAM,OAAO,cAAc,IAAI;EAC/B,IAAI,CAAC,MAAM,OAAO,CAAC;EACnB,MAAM,MAAM,WAAW,oBAAoB;EAC3C,OAAO,gBAAgB,KAAK,OAAO,KAAK,KAAK,OAAO,KAAK,cAAc,IAAI;CAC7E;AACF"}
1
+ {"version":3,"file":"measurePainter.js","names":[],"sources":["../../../src/canvas/nodeShapes/measurePainter.ts"],"sourcesContent":["import { polylineFromPoints } from '@weasel-js/geom';\nimport type { DrawCommand } from '@weasel-js/renderer';\nimport type { NodeShapeEntry } from 'canvas/NodeShape';\nimport { OVERLAY_ROLE_STROKES } from 'canvas/overlayRoleStrokes';\nimport { measureEndsOf, type MeasurePoint } from 'domains/measure/measurement';\nimport { paintsPose } from './poseBox';\n\n/** Screen-px half-length of the tick across each end of a measurement. */\nexport const MEASURE_TICK_PX = 5;\n\n/**\n * A measurement's line and a tick across each end, as the measure painter\n * and the measurements layer draw it. `perPx` is world units per screen pixel, so the\n * line and its ticks keep their screen size at any zoom.\n */\nexport function measureCommands(start: MeasurePoint, end: MeasurePoint, perPx: number): DrawCommand[] {\n const stroke = { ...OVERLAY_ROLE_STROKES['measure'], width: { px: 1 } };\n const len = Math.hypot(end.x - start.x, end.y - start.y);\n // The ticks run across the line; with no length yet they stand upright.\n const nx = len > 0 ? -(end.y - start.y) / len : 0;\n const ny = len > 0 ? (end.x - start.x) / len : 1;\n const half = MEASURE_TICK_PX * perPx;\n const tick = (p: MeasurePoint): DrawCommand => ({\n kind: 'path',\n path: polylineFromPoints([{ x: p.x - nx * half, y: p.y - ny * half }, { x: p.x + nx * half, y: p.y + ny * half }]),\n stroke,\n });\n const out: DrawCommand[] = [tick(start), tick(end)];\n if (len > 0) out.unshift({ kind: 'path', path: polylineFromPoints([start, end]), stroke });\n return out;\n}\n\n/** Paints a measurement node — `data.measure`, in world units — in the\n * canvas's measure color. It has no silhouette: a measurement is not content\n * to pick. */\nexport const MEASURE_PAINTER: NodeShapeEntry = {\n id: 'kit:measure',\n matches: (node) => paintsPose(node.pose) && measureEndsOf(node) !== null,\n paint: (node, _pose, ctx) => {\n const ends = measureEndsOf(node);\n if (!ends) return [];\n const dpr = globalThis.devicePixelRatio || 1;\n return measureCommands(ends.start, ends.end, dpr / (ctx?.pixelScale ?? dpr));\n },\n};\n"],"mappings":";;;;;;;AAQA,IAAa,kBAAkB;;;;;;AAO/B,SAAgB,gBAAgB,OAAqB,KAAmB,OAA8B;CACpG,MAAM,SAAS;EAAE,GAAG,qBAAqB;EAAY,OAAO,EAAE,IAAI,EAAE;CAAE;CACtE,MAAM,MAAM,KAAK,MAAM,IAAI,IAAI,MAAM,GAAG,IAAI,IAAI,MAAM,CAAC;CAEvD,MAAM,KAAK,MAAM,IAAI,EAAE,IAAI,IAAI,MAAM,KAAK,MAAM;CAChD,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,MAAM;CAC/C,MAAM,OAAO,kBAAkB;CAC/B,MAAM,QAAQ,OAAkC;EAC9C,MAAM;EACN,MAAM,mBAAmB,CAAC;GAAE,GAAG,EAAE,IAAI,KAAK;GAAM,GAAG,EAAE,IAAI,KAAK;EAAK,GAAG;GAAE,GAAG,EAAE,IAAI,KAAK;GAAM,GAAG,EAAE,IAAI,KAAK;EAAK,CAAC,CAAC;EACjH;CACF;CACA,MAAM,MAAqB,CAAC,KAAK,KAAK,GAAG,KAAK,GAAG,CAAC;CAClD,IAAI,MAAM,GAAG,IAAI,QAAQ;EAAE,MAAM;EAAQ,MAAM,mBAAmB,CAAC,OAAO,GAAG,CAAC;EAAG;CAAO,CAAC;CACzF,OAAO;AACT;;;;AAKA,IAAa,kBAAkC;CAC7C,IAAI;CACJ,UAAU,SAAS,WAAW,KAAK,IAAI,KAAK,cAAc,IAAI,MAAM;CACpE,QAAQ,MAAM,OAAO,QAAQ;EAC3B,MAAM,OAAO,cAAc,IAAI;EAC/B,IAAI,CAAC,MAAM,OAAO,CAAC;EACnB,MAAM,MAAM,WAAW,oBAAoB;EAC3C,OAAO,gBAAgB,KAAK,OAAO,KAAK,KAAK,OAAO,KAAK,cAAc,IAAI;CAC7E;AACF"}
@@ -25,7 +25,7 @@ function strokedBounds(path, stroke, scale, screen) {
25
25
  if (align !== "inner") {
26
26
  const k = align === "outer" ? 2 : 1;
27
27
  const insets = strokeInsets(sized, width);
28
- const v = tessellateStroke(path, {
28
+ const mesh = tessellateStroke(path, {
29
29
  ...sized,
30
30
  width: width * k,
31
31
  align: "center",
@@ -34,8 +34,10 @@ function strokedBounds(path, stroke, scale, screen) {
34
34
  startInset: insetIn(insets.start, spaces.start, spaces.ribbon),
35
35
  endInset: insetIn(insets.end, spaces.end, spaces.ribbon),
36
36
  ...spaces.ribbon ? { metric: spaces.ribbon.metric } : {}
37
- }).vertices;
38
- for (let i = 0; i + 1 < v.length; i += 2) box.add(v[i], v[i + 1], 0);
37
+ });
38
+ const v = mesh.vertices;
39
+ const ox = mesh.origin?.x ?? 0, oy = mesh.origin?.y ?? 0;
40
+ for (let i = 0; i + 1 < v.length; i += 2) box.add(v[i] + ox, v[i + 1] + oy, 0);
39
41
  }
40
42
  for (const { entry, path: head, size } of markerHeads(path, sized, width, void 0, spaces)) {
41
43
  const b = boundsOfPath(head);
@@ -1 +1 @@
1
- {"version":3,"file":"strokedBounds.js","names":[],"sources":["../../../src/canvas/nodeShapes/strokedBounds.ts"],"sourcesContent":["import { boundsOfPath, type Path } from '@weasel-js/geom';\nimport { scaleVertexWidth, type Stroke } from '@weasel-js/paint';\nimport {\n markerHeads, resolveStrokeWidth, strokeInsets, tessellateStroke,\n type InsetLength, type ScreenSpace, type StrokeSpaces,\n} from '@weasel-js/stroke';\nimport { resolveStrokeLengths, type GlMat3, type StrokedPathCommand } from '@weasel-js/renderer';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport { nodeMemo } from 'core/scene/nodeMemo';\nimport type { NodeInkCtx } from 'canvas/NodeShape';\nimport { strokeOutsetPerAxis } from './nodePaint';\n\n/**\n * The box `stroke` covers along `path`, from the ribbon the renderer draws:\n * joins, caps, dashes, and per-vertex widths as tessellated, and marker heads.\n * An inner stroke stays inside the path, and an outer one reaches as far out as\n * a centered stroke twice as wide. `null` when the stroke paints nothing.\n *\n * With `screen` (see `NodeInkCtx.screen`), each screen-sized part is built the\n * way the renderer builds it under that map, so a `{ px }` stroke is exact in\n * every direction; without, it is resolved at `scale`.\n */\nexport function strokedBounds(path: Path, stroke: Stroke, scale: number, screen?: ScreenMap): Bounds | null {\n const { sized, spaces } = screen ? onScreen(path, stroke, screen) : atScale(stroke, scale);\n const width = resolveStrokeWidth(sized.width ?? 1, 1);\n if (!(width > 0)) return null;\n const box = new Box();\n const align = stroke.align ?? 'center';\n if (align !== 'inner') {\n const k = align === 'outer' ? 2 : 1;\n const insets = strokeInsets(sized, width);\n const mesh = tessellateStroke(\n path,\n { ...sized, width: width * k, align: 'center', vertexWidths: stroke.vertexWidths?.map((w) => scaleVertexWidth(w, k)) },\n {\n startInset: insetIn(insets.start, spaces.start, spaces.ribbon),\n endInset: insetIn(insets.end, spaces.end, spaces.ribbon),\n ...(spaces.ribbon ? { metric: spaces.ribbon.metric } : {}),\n },\n );\n const v = mesh.vertices;\n for (let i = 0; i + 1 < v.length; i += 2) box.add(v[i]!, v[i + 1]!, 0);\n }\n for (const { entry, path: head, size } of markerHeads(path, sized, width, undefined, spaces)) {\n const b = boundsOfPath(head);\n const pad = entry.outline ? (entry.outline.width * size) / 2 : 0;\n box.add(b.x, b.y, pad);\n box.add(b.x + b.width, b.y + b.height, pad);\n }\n return box.bounds();\n}\n\n/** `[a, b, c, d]`: a frame's linear map to screen pixels. */\nexport type ScreenMap = readonly [number, number, number, number];\n\nfunction atScale(stroke: Stroke, scale: number): { sized: Stroke; spaces: StrokeSpaces } {\n return { sized: { ...stroke, width: resolveStrokeWidth(stroke.width ?? 1, scale) }, spaces: {} };\n}\n\nfunction onScreen(path: Path, stroke: Stroke, [a, b, c, d]: ScreenMap): { sized: Stroke; spaces: StrokeSpaces } {\n const transform = new Float32Array([a, b, 0, c, d, 0, 0, 0, 1]) as GlMat3;\n const { cmd, spaces } = resolveStrokeLengths(transform, { kind: 'path', path, stroke } as StrokedPathCommand);\n return { sized: cmd.stroke as Stroke, spaces };\n}\n\n/** `length`, measured in `head`'s space, as the tessellator reads it. */\nfunction insetIn(length: number, head: ScreenSpace | undefined, ribbon: ScreenSpace | undefined): InsetLength {\n return head?.metric === ribbon?.metric ? length : { length, metric: head?.metric ?? null };\n}\n\nclass Box {\n private x0 = Infinity;\n private y0 = Infinity;\n private x1 = -Infinity;\n private y1 = -Infinity;\n add(x: number, y: number, pad: number): void {\n this.x0 = Math.min(this.x0, x - pad);\n this.y0 = Math.min(this.y0, y - pad);\n this.x1 = Math.max(this.x1, x + pad);\n this.y1 = Math.max(this.y1, y + pad);\n }\n bounds(): Bounds | null {\n return this.x0 === Infinity ? null : { x: this.x0, y: this.y0, width: this.x1 - this.x0, height: this.y1 - this.y0 };\n }\n}\n\nconst DRAWN_SLOT = 'shape:drawn';\n\ninterface DrawnRecord {\n width: number;\n height: number;\n scale: number;\n screen: ScreenMap | undefined;\n /** Relative to the pose's top-left, so a move reuses it. */\n box: Bounds | null;\n}\n\n/** The pose box grown by a uniform stroke outset: exact for a rect or an\n * ellipse, whose stroke reaches the same distance past the box on every side. */\nexport function outsetPoseBox(pose: Bounds, stroke: Stroke | null, ctx: NodeInkCtx | undefined): Bounds {\n const box = normalized(pose);\n if (!stroke) return box;\n const r = strokeOutsetPerAxis(stroke, ctx);\n return { x: box.x - r.x, y: box.y - r.y, width: box.width + 2 * r.x, height: box.height + 2 * r.y };\n}\n\n/**\n * The pose box together with what `stroke` covers along `pathAt(pose)`,\n * memoized per node on the pose's size and the view scale: a drag that only\n * moves the node does not stroke it again.\n */\nexport function strokedPoseBox(\n node: { data?: unknown },\n pose: Bounds,\n stroke: Stroke | null,\n ctx: NodeInkCtx | undefined,\n pathAt: (pose: Bounds) => Path,\n): Bounds {\n const box = normalized(pose);\n if (stroke === null) return box;\n const scale = ctx?.scale ?? 1;\n const screen = ctx?.screen;\n const held = nodeMemo(node, DRAWN_SLOT, undefined, (): DrawnRecord => ({ width: NaN, height: NaN, scale: NaN, screen: undefined, box: null }));\n if (held.width !== pose.width || held.height !== pose.height || held.scale !== scale || !sameMap(held.screen, screen)) {\n const at = { x: 0, y: 0, width: pose.width, height: pose.height };\n held.width = pose.width;\n held.height = pose.height;\n held.scale = scale;\n held.screen = screen;\n held.box = strokedBounds(pathAt(at), stroke, scale, screen);\n }\n if (held.box === null) return box;\n const x0 = Math.min(box.x, pose.x + held.box.x);\n const y0 = Math.min(box.y, pose.y + held.box.y);\n const x1 = Math.max(box.x + box.width, pose.x + held.box.x + held.box.width);\n const y1 = Math.max(box.y + box.height, pose.y + held.box.y + held.box.height);\n return { x: x0, y: y0, width: x1 - x0, height: y1 - y0 };\n}\n\nfunction sameMap(m: ScreenMap | undefined, n: ScreenMap | undefined): boolean {\n if (m === n) return true;\n return !!m && !!n && m[0] === n[0] && m[1] === n[1] && m[2] === n[2] && m[3] === n[3];\n}\n\nfunction normalized(pose: Bounds): Bounds {\n return {\n x: Math.min(pose.x, pose.x + pose.width),\n y: Math.min(pose.y, pose.y + pose.height),\n width: Math.abs(pose.width),\n height: Math.abs(pose.height),\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"strokedBounds.js","names":[],"sources":["../../../src/canvas/nodeShapes/strokedBounds.ts"],"sourcesContent":["import { boundsOfPath, type Path } from '@weasel-js/geom';\nimport { scaleVertexWidth, type Stroke } from '@weasel-js/paint';\nimport {\n markerHeads, resolveStrokeWidth, strokeInsets, tessellateStroke,\n type InsetLength, type ScreenSpace, type StrokeSpaces,\n} from '@weasel-js/stroke';\nimport { resolveStrokeLengths, type GlMat3, type StrokedPathCommand } from '@weasel-js/renderer';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport { nodeMemo } from 'core/scene/nodeMemo';\nimport type { NodeInkCtx } from 'canvas/NodeShape';\nimport { strokeOutsetPerAxis } from './nodePaint';\n\n/**\n * The box `stroke` covers along `path`, from the ribbon the renderer draws:\n * joins, caps, dashes, and per-vertex widths as tessellated, and marker heads.\n * An inner stroke stays inside the path, and an outer one reaches as far out as\n * a centered stroke twice as wide. `null` when the stroke paints nothing.\n *\n * With `screen` (see `NodeInkCtx.screen`), each screen-sized part is built the\n * way the renderer builds it under that map, so a `{ px }` stroke is exact in\n * every direction; without, it is resolved at `scale`.\n */\nexport function strokedBounds(path: Path, stroke: Stroke, scale: number, screen?: ScreenMap): Bounds | null {\n const { sized, spaces } = screen ? onScreen(path, stroke, screen) : atScale(stroke, scale);\n const width = resolveStrokeWidth(sized.width ?? 1, 1);\n if (!(width > 0)) return null;\n const box = new Box();\n const align = stroke.align ?? 'center';\n if (align !== 'inner') {\n const k = align === 'outer' ? 2 : 1;\n const insets = strokeInsets(sized, width);\n const mesh = tessellateStroke(\n path,\n { ...sized, width: width * k, align: 'center', vertexWidths: stroke.vertexWidths?.map((w) => scaleVertexWidth(w, k)) },\n {\n startInset: insetIn(insets.start, spaces.start, spaces.ribbon),\n endInset: insetIn(insets.end, spaces.end, spaces.ribbon),\n ...(spaces.ribbon ? { metric: spaces.ribbon.metric } : {}),\n },\n );\n const v = mesh.vertices;\n const ox = mesh.origin?.x ?? 0, oy = mesh.origin?.y ?? 0;\n for (let i = 0; i + 1 < v.length; i += 2) box.add(v[i]! + ox, v[i + 1]! + oy, 0);\n }\n for (const { entry, path: head, size } of markerHeads(path, sized, width, undefined, spaces)) {\n const b = boundsOfPath(head);\n const pad = entry.outline ? (entry.outline.width * size) / 2 : 0;\n box.add(b.x, b.y, pad);\n box.add(b.x + b.width, b.y + b.height, pad);\n }\n return box.bounds();\n}\n\n/** `[a, b, c, d]`: a frame's linear map to screen pixels. */\nexport type ScreenMap = readonly [number, number, number, number];\n\nfunction atScale(stroke: Stroke, scale: number): { sized: Stroke; spaces: StrokeSpaces } {\n return { sized: { ...stroke, width: resolveStrokeWidth(stroke.width ?? 1, scale) }, spaces: {} };\n}\n\nfunction onScreen(path: Path, stroke: Stroke, [a, b, c, d]: ScreenMap): { sized: Stroke; spaces: StrokeSpaces } {\n const transform = new Float32Array([a, b, 0, c, d, 0, 0, 0, 1]) as GlMat3;\n const { cmd, spaces } = resolveStrokeLengths(transform, { kind: 'path', path, stroke } as StrokedPathCommand);\n return { sized: cmd.stroke as Stroke, spaces };\n}\n\n/** `length`, measured in `head`'s space, as the tessellator reads it. */\nfunction insetIn(length: number, head: ScreenSpace | undefined, ribbon: ScreenSpace | undefined): InsetLength {\n return head?.metric === ribbon?.metric ? length : { length, metric: head?.metric ?? null };\n}\n\nclass Box {\n private x0 = Infinity;\n private y0 = Infinity;\n private x1 = -Infinity;\n private y1 = -Infinity;\n add(x: number, y: number, pad: number): void {\n this.x0 = Math.min(this.x0, x - pad);\n this.y0 = Math.min(this.y0, y - pad);\n this.x1 = Math.max(this.x1, x + pad);\n this.y1 = Math.max(this.y1, y + pad);\n }\n bounds(): Bounds | null {\n return this.x0 === Infinity ? null : { x: this.x0, y: this.y0, width: this.x1 - this.x0, height: this.y1 - this.y0 };\n }\n}\n\nconst DRAWN_SLOT = 'shape:drawn';\n\ninterface DrawnRecord {\n width: number;\n height: number;\n scale: number;\n screen: ScreenMap | undefined;\n /** Relative to the pose's top-left, so a move reuses it. */\n box: Bounds | null;\n}\n\n/** The pose box grown by a uniform stroke outset: exact for a rect or an\n * ellipse, whose stroke reaches the same distance past the box on every side. */\nexport function outsetPoseBox(pose: Bounds, stroke: Stroke | null, ctx: NodeInkCtx | undefined): Bounds {\n const box = normalized(pose);\n if (!stroke) return box;\n const r = strokeOutsetPerAxis(stroke, ctx);\n return { x: box.x - r.x, y: box.y - r.y, width: box.width + 2 * r.x, height: box.height + 2 * r.y };\n}\n\n/**\n * The pose box together with what `stroke` covers along `pathAt(pose)`,\n * memoized per node on the pose's size and the view scale: a drag that only\n * moves the node does not stroke it again.\n */\nexport function strokedPoseBox(\n node: { data?: unknown },\n pose: Bounds,\n stroke: Stroke | null,\n ctx: NodeInkCtx | undefined,\n pathAt: (pose: Bounds) => Path,\n): Bounds {\n const box = normalized(pose);\n if (stroke === null) return box;\n const scale = ctx?.scale ?? 1;\n const screen = ctx?.screen;\n const held = nodeMemo(node, DRAWN_SLOT, undefined, (): DrawnRecord => ({ width: NaN, height: NaN, scale: NaN, screen: undefined, box: null }));\n if (held.width !== pose.width || held.height !== pose.height || held.scale !== scale || !sameMap(held.screen, screen)) {\n const at = { x: 0, y: 0, width: pose.width, height: pose.height };\n held.width = pose.width;\n held.height = pose.height;\n held.scale = scale;\n held.screen = screen;\n held.box = strokedBounds(pathAt(at), stroke, scale, screen);\n }\n if (held.box === null) return box;\n const x0 = Math.min(box.x, pose.x + held.box.x);\n const y0 = Math.min(box.y, pose.y + held.box.y);\n const x1 = Math.max(box.x + box.width, pose.x + held.box.x + held.box.width);\n const y1 = Math.max(box.y + box.height, pose.y + held.box.y + held.box.height);\n return { x: x0, y: y0, width: x1 - x0, height: y1 - y0 };\n}\n\nfunction sameMap(m: ScreenMap | undefined, n: ScreenMap | undefined): boolean {\n if (m === n) return true;\n return !!m && !!n && m[0] === n[0] && m[1] === n[1] && m[2] === n[2] && m[3] === n[3];\n}\n\nfunction normalized(pose: Bounds): Bounds {\n return {\n x: Math.min(pose.x, pose.x + pose.width),\n y: Math.min(pose.y, pose.y + pose.height),\n width: Math.abs(pose.width),\n height: Math.abs(pose.height),\n };\n}\n"],"mappings":"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"}
@@ -27,6 +27,12 @@ var OVERLAY_ROLE_STROKES = {
27
27
  paint: { color: "#d9480f" },
28
28
  width: 1.5
29
29
  },
30
+ /** What a dragged item would be dropped on. */
31
+ target: {
32
+ paint: { color: "#1f8fff" },
33
+ width: 1.5,
34
+ dash: [5, 3]
35
+ },
30
36
  /** Where a dragged item would land. */
31
37
  insert: {
32
38
  paint: { color: "#1f8fff" },
@@ -1 +1 @@
1
- {"version":3,"file":"overlayRoleStrokes.js","names":[],"sources":["../../src/canvas/overlayRoleStrokes.ts"],"sourcesContent":["import type { CommandPaint, CommandStroke } from '@weasel-js/paint';\n\n/** How each `polyline` overlay role reads. An action publishes the run and\n * the word; this table is the whole of the paint, for the dispatcher overlay\n * and for any tool previewing the same kind of run between gestures. */\nexport const OVERLAY_ROLE_STROKES: Readonly<Record<string, CommandStroke & { paint: CommandPaint }>> = {\n cut: { paint: { color: '#e23b3b' }, width: 1, dash: [6, 4] },\n connector: { paint: { color: '#7ba7c7' }, width: 2, dash: [4, 4] },\n falloff: { paint: { color: 'rgba(52, 120, 246, 0.28)' }, width: 10, cap: 'round', join: 'round' },\n measure: { paint: { color: '#1f8fff' }, width: 1 },\n lock: { paint: { color: '#d9480f' }, width: 1.5 },\n /** Where a dragged item would land. */\n insert: { paint: { color: '#1f8fff' }, width: 3, cap: 'round' },\n /** Where a dragged item is over but cannot land. */\n refused: { paint: { color: '#ff5d5d' }, width: 3, cap: 'round' },\n};\n"],"mappings":";;;;AAKA,IAAa,uBAA0F;CACrG,KAAK;EAAE,OAAO,EAAE,OAAO,UAAU;EAAG,OAAO;EAAG,MAAM,CAAC,GAAG,CAAC;CAAE;CAC3D,WAAW;EAAE,OAAO,EAAE,OAAO,UAAU;EAAG,OAAO;EAAG,MAAM,CAAC,GAAG,CAAC;CAAE;CACjE,SAAS;EAAE,OAAO,EAAE,OAAO,2BAA2B;EAAG,OAAO;EAAI,KAAK;EAAS,MAAM;CAAQ;CAChG,SAAS;EAAE,OAAO,EAAE,OAAO,UAAU;EAAG,OAAO;CAAE;CACjD,MAAM;EAAE,OAAO,EAAE,OAAO,UAAU;EAAG,OAAO;CAAI;;CAEhD,QAAQ;EAAE,OAAO,EAAE,OAAO,UAAU;EAAG,OAAO;EAAG,KAAK;CAAQ;;CAE9D,SAAS;EAAE,OAAO,EAAE,OAAO,UAAU;EAAG,OAAO;EAAG,KAAK;CAAQ;AACjE"}
1
+ {"version":3,"file":"overlayRoleStrokes.js","names":[],"sources":["../../src/canvas/overlayRoleStrokes.ts"],"sourcesContent":["import type { CommandPaint, CommandStroke } from '@weasel-js/paint';\n\n/** How each `polyline` overlay role reads. An action publishes the run and\n * the word; this table is the whole of the paint, for the dispatcher overlay\n * and for any tool previewing the same kind of run between gestures. */\nexport const OVERLAY_ROLE_STROKES: Readonly<Record<string, CommandStroke & { paint: CommandPaint }>> = {\n cut: { paint: { color: '#e23b3b' }, width: 1, dash: [6, 4] },\n connector: { paint: { color: '#7ba7c7' }, width: 2, dash: [4, 4] },\n falloff: { paint: { color: 'rgba(52, 120, 246, 0.28)' }, width: 10, cap: 'round', join: 'round' },\n measure: { paint: { color: '#1f8fff' }, width: 1 },\n lock: { paint: { color: '#d9480f' }, width: 1.5 },\n /** What a dragged item would be dropped on. */\n target: { paint: { color: '#1f8fff' }, width: 1.5, dash: [5, 3] },\n /** Where a dragged item would land. */\n insert: { paint: { color: '#1f8fff' }, width: 3, cap: 'round' },\n /** Where a dragged item is over but cannot land. */\n refused: { paint: { color: '#ff5d5d' }, width: 3, cap: 'round' },\n};\n"],"mappings":";;;;AAKA,IAAa,uBAA0F;CACrG,KAAK;EAAE,OAAO,EAAE,OAAO,UAAU;EAAG,OAAO;EAAG,MAAM,CAAC,GAAG,CAAC;CAAE;CAC3D,WAAW;EAAE,OAAO,EAAE,OAAO,UAAU;EAAG,OAAO;EAAG,MAAM,CAAC,GAAG,CAAC;CAAE;CACjE,SAAS;EAAE,OAAO,EAAE,OAAO,2BAA2B;EAAG,OAAO;EAAI,KAAK;EAAS,MAAM;CAAQ;CAChG,SAAS;EAAE,OAAO,EAAE,OAAO,UAAU;EAAG,OAAO;CAAE;CACjD,MAAM;EAAE,OAAO,EAAE,OAAO,UAAU;EAAG,OAAO;CAAI;;CAEhD,QAAQ;EAAE,OAAO,EAAE,OAAO,UAAU;EAAG,OAAO;EAAK,MAAM,CAAC,GAAG,CAAC;CAAE;;CAEhE,QAAQ;EAAE,OAAO,EAAE,OAAO,UAAU;EAAG,OAAO;EAAG,KAAK;CAAQ;;CAE9D,SAAS;EAAE,OAAO,EAAE,OAAO,UAAU;EAAG,OAAO;EAAG,KAAK;CAAQ;AACjE"}
@@ -6,7 +6,7 @@ import { scenePaintedPoseOf } from "../derivedPath.js";
6
6
  import { sceneParentsOf, sceneShownPathOf } from "../scenePickReads.js";
7
7
  import { nodeBroadBox } from "./nodeBroadBox.js";
8
8
  import { markerGeneration } from "@weasel-js/stroke";
9
- import { createBvh } from "@weasel-js/geom/nd";
9
+ import { createBvh, createSettledCache } from "@weasel-js/geom/nd";
10
10
 
11
11
  //#region src/canvas/pickIndex/scenePickIndex.ts
12
12
  /**
@@ -169,25 +169,23 @@ function current(scene, descriptor) {
169
169
  getNodeShapes();
170
170
  const version = scene.getVersion();
171
171
  const overrides = scene.overrides.getGeneration();
172
+ const scrolled = scene.scroll?.getGeneration() ?? 0;
172
173
  const memo = nodeMemoGeneration();
173
174
  const markers = markerGeneration();
174
175
  let perScene = CACHE.get(scene);
175
176
  if (perScene === void 0) CACHE.set(scene, perScene = /* @__PURE__ */ new WeakMap());
176
- const hit = perScene.get(descriptor);
177
+ let cache = perScene.get(descriptor);
178
+ if (cache === void 0) perScene.set(descriptor, cache = createSettledCache());
177
179
  const order = nodesAsShown(scene.overrides, scene.renderOrderNodes());
178
- if (hit && hit.version === version && hit.overrides === overrides && hit.memo === memo && hit.markers === markers) {
179
- if (hit.index === null) hit.index = build(scene, descriptor, order);
180
- else hit.index.rankBy(order);
181
- return hit.index;
182
- }
183
- perScene.set(descriptor, {
180
+ const index = cache.read([
184
181
  version,
185
182
  overrides,
183
+ scrolled,
186
184
  memo,
187
- markers,
188
- index: null
189
- });
190
- return null;
185
+ markers
186
+ ], () => build(scene, descriptor, order));
187
+ index?.rankBy(order);
188
+ return index;
191
189
  }
192
190
  /** What `PickSource.candidates` answers for a scene source with an index. */
193
191
  function indexedCandidates(scene, descriptor, broadFor) {
@@ -1 +1 @@
1
- {"version":3,"file":"scenePickIndex.js","names":[],"sources":["../../../src/canvas/pickIndex/scenePickIndex.ts"],"sourcesContent":["/**\n * The per-scene pick index: one BVH per scene layer over every node's broad\n * box (`nodeBroadBox`), keyed by the node's place in the render order it was\n * built from. A pick after anything it read can have changed walks the scene\n * and only records that state; the next pick to find the scene unchanged\n * builds, so a scene edited before every pick never pays for a build. Nodes it\n * cannot box conservatively are on `always` and are tested by every walk.\n */\nimport { createBvh, type Bvh } from '@weasel-js/geom/nd';\nimport { markerGeneration } from '@weasel-js/stroke';\nimport { getNodeShapes } from 'canvas/NodeShape';\nimport { inheritedPaintOf, inheritsPaint } from 'core/scene/inheritedPaint';\nimport { nodesAsShown } from 'core/scene/nodeAsShown';\nimport { nodeMemoGeneration } from 'core/scene/nodeMemo';\nimport type { Scene } from 'core/scene/types';\nimport type { PoseDescriptor } from 'interactions/actions/resize/geometry';\nimport { scenePaintedPoseOf } from '../derivedPath';\nimport type { PickBroad, PickCandidate } from '../pickWalk';\nimport { sceneParentsOf, sceneShownPathOf } from '../scenePickReads';\nimport { nodeBroadBox, type Box2 } from './nodeBroadBox';\n\ntype AnyScene = Scene<unknown, string, unknown>;\ntype Order = readonly PickCandidate<unknown>[];\n\nexport interface ScenePickIndex {\n /** The layers holding indexed nodes; `undefined` for nodes with none. */\n readonly layers: readonly (string | undefined)[];\n readonly maxOutsetPx: number;\n}\n\nlet builds = 0;\n\n/** How many indexes have been built, for tests that pin when one rebuilds. */\nexport function pickIndexBuildCount(): number {\n return builds;\n}\n\ninterface Cached {\n version: number;\n overrides: number;\n memo: number;\n markers: number;\n /** Null until a second pick finds the same state. */\n index: BuiltIndex | null;\n}\n\nconst CACHE = new WeakMap<object, WeakMap<PoseDescriptor<unknown>, Cached>>();\n\n/** Past this share of the scene the index costs more than the walk it narrows. */\nconst MAX_SHARE = 0.5;\n\nclass BuiltIndex implements ScenePickIndex {\n readonly layers: readonly (string | undefined)[];\n /** The render order the candidates are read out of. */\n private order: Order;\n /** Build position to position in `order`, or null while they are the same array. */\n private remap: Int32Array | null = null;\n private readonly marks: Uint8Array;\n private found: Int32Array;\n\n constructor(\n private readonly built: Order,\n private readonly trees: Map<string | undefined, Bvh<number>>,\n private readonly always: Int32Array,\n readonly maxOutsetPx: number,\n ) {\n this.layers = [...trees.keys()];\n this.order = built;\n this.marks = new Uint8Array(built.length);\n this.found = new Int32Array(Math.max(16, always.length));\n }\n\n /** Follow a new render order array the scene hands back for the same state. */\n rankBy(order: Order): void {\n if (order === this.order) return;\n this.order = order;\n if (order === this.built) {\n this.remap = null;\n return;\n }\n const rank = new Map<string, number>();\n for (let i = 0; i < order.length; i++) rank.set(order[i].id, i);\n const remap = new Int32Array(this.built.length);\n for (let i = 0; i < remap.length; i++) remap[i] = rank.get(this.built[i].id) ?? -1;\n this.remap = remap;\n }\n\n /** The nodes the boxes reach, in render order, or null when that is most of the scene. */\n candidates(broads: readonly (PickBroad | null)[]): Order | null {\n const limit = Math.floor(this.order.length * MAX_SHARE);\n let count = 0;\n const add = (built: number): boolean => {\n const at = this.remap === null ? built : this.remap[built];\n if (at < 0) return true;\n if (count === this.found.length) {\n const next = new Int32Array(count * 2);\n next.set(this.found);\n this.found = next;\n }\n this.found[count++] = at;\n return count <= limit;\n };\n for (const i of this.always) if (!add(i)) return null;\n for (let l = 0; l < this.layers.length; l++) {\n const broad = broads[l];\n const tree = this.trees.get(this.layers[l]);\n if (broad === null || tree === undefined) continue;\n const { x, y, width, height } = broad.box;\n const k = broad.pxPerWorld;\n const by = this.maxOutsetPx > 0 && k !== undefined ? (k > 0 ? this.maxOutsetPx / k : Infinity) : 0;\n let over = false;\n tree.queryBox([x - by, y - by, x + width + by, y + height + by], (i) => {\n if (add(i)) return true;\n over = true;\n return false;\n });\n if (over) return null;\n }\n return this.readOut(count);\n }\n\n private readOut(count: number): Order {\n const { order, found } = this;\n const out: PickCandidate<unknown>[] = new Array(count);\n // A handful sorts faster than a pass over every mark.\n if (count * 32 < order.length) {\n const ranks = found.subarray(0, count).sort();\n for (let i = 0; i < count; i++) out[i] = order[ranks[i]];\n return out;\n }\n const marks = this.marks.length >= order.length ? this.marks : new Uint8Array(order.length);\n for (let i = 0; i < count; i++) marks[found[i]] = 1;\n let n = 0;\n for (let i = 0; n < count && i < order.length; i++) {\n if (marks[i] === 0) continue;\n marks[i] = 0;\n out[n++] = order[i];\n }\n return out;\n }\n}\n\nfunction hasCopyingAncestor(parents: Order): boolean {\n return parents.some((p) => typeof p.copiesFromPose === 'function');\n}\n\nfunction build(scene: AnyScene, descriptor: PoseDescriptor<unknown>, order: Order): BuiltIndex {\n builds++;\n const poseOf = scenePaintedPoseOf(scene);\n const pathOf = sceneShownPathOf(scene);\n const parentsOf = sceneParentsOf(scene);\n const entries = new Map<string | undefined, [number, Box2][]>();\n const always: number[] = [];\n let maxOutsetPx = 0;\n for (let i = 0; i < order.length; i++) {\n const node = order[i];\n const parents = parentsOf(node);\n // Also hit wherever an ancestor paints a copy of it, which its own box does not cover.\n if (hasCopyingAncestor(parents)) {\n always.push(i);\n continue;\n }\n const box: Box2 = [0, 0, 0, 0];\n let outsetPx: number;\n try {\n const inherited = inheritsPaint(node) ? inheritedPaintOf(node, parents) : undefined;\n outsetPx = nodeBroadBox(node, poseOf(node as never), pathOf(node), inherited, descriptor, box);\n } catch {\n always.push(i);\n continue;\n }\n if (!Number.isFinite(outsetPx) || !box.every(Number.isFinite)) {\n always.push(i);\n continue;\n }\n if (outsetPx > maxOutsetPx) maxOutsetPx = outsetPx;\n let list = entries.get(node.layer);\n if (list === undefined) entries.set(node.layer, (list = []));\n list.push([i, box]);\n }\n const trees = new Map<string | undefined, Bvh<number>>();\n for (const [layer, list] of entries) {\n const tree = createBvh<number>(2, list);\n // The tree drops an inverted box: test that node the old way.\n if (tree.size !== list.length) {\n for (const [i, box] of list) if (!(box[0] <= box[2] && box[1] <= box[3])) always.push(i);\n }\n trees.set(layer, tree);\n }\n return new BuiltIndex(order, trees, Int32Array.from(always), maxOutsetPx);\n}\n\n/** The index for `scene` read through `descriptor`, or null when this pick walks instead. */\nexport function scenePickIndex(scene: AnyScene, descriptor: PoseDescriptor<unknown>): ScenePickIndex | null {\n return current(scene, descriptor);\n}\n\nfunction current(scene: AnyScene, descriptor: PoseDescriptor<unknown>): BuiltIndex | null {\n // Registers the built-in painters first, which bumps the memo generation.\n getNodeShapes();\n const version = scene.getVersion();\n const overrides = scene.overrides.getGeneration();\n const memo = nodeMemoGeneration();\n const markers = markerGeneration();\n let perScene = CACHE.get(scene);\n if (perScene === undefined) CACHE.set(scene, (perScene = new WeakMap()));\n const hit = perScene.get(descriptor);\n // Boxed as shown, so a data override that grows a node's ink is met where it is painted.\n const order = nodesAsShown(scene.overrides, scene.renderOrderNodes()) as unknown as Order;\n if (hit && hit.version === version && hit.overrides === overrides && hit.memo === memo && hit.markers === markers) {\n if (hit.index === null) hit.index = build(scene, descriptor, order);\n else hit.index.rankBy(order);\n return hit.index;\n }\n perScene.set(descriptor, { version, overrides, memo, markers, index: null });\n return null;\n}\n\n/** What `PickSource.candidates` answers for a scene source with an index. */\nexport function indexedCandidates(\n scene: AnyScene,\n descriptor: PoseDescriptor<unknown>,\n broadFor: (layer: string | undefined) => PickBroad | null | undefined,\n): Order | null {\n const index = current(scene, descriptor);\n if (index === null) return null;\n const broads: (PickBroad | null)[] = [];\n for (const layer of index.layers) {\n const broad = broadFor(layer);\n if (broad === undefined) return null;\n if (broad !== null && Number.isNaN(broad.box.x + broad.box.y + broad.box.width + broad.box.height)) return null;\n broads.push(broad);\n }\n return index.candidates(broads);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"scenePickIndex.js","names":[],"sources":["../../../src/canvas/pickIndex/scenePickIndex.ts"],"sourcesContent":["/**\n * The per-scene pick index: one BVH per scene layer over every node's broad\n * box (`nodeBroadBox`), keyed by the node's place in the render order it was\n * built from. A pick after anything it read can have changed walks the scene\n * and only records that state; the next pick to find the scene unchanged\n * builds, so a scene edited before every pick never pays for a build. Nodes it\n * cannot box conservatively are on `always` and are tested by every walk.\n */\nimport { createBvh, createSettledCache, type Bvh, type SettledCache } from '@weasel-js/geom/nd';\nimport { markerGeneration } from '@weasel-js/stroke';\nimport { getNodeShapes } from 'canvas/NodeShape';\nimport { inheritedPaintOf, inheritsPaint } from 'core/scene/inheritedPaint';\nimport { nodesAsShown } from 'core/scene/nodeAsShown';\nimport { nodeMemoGeneration } from 'core/scene/nodeMemo';\nimport type { Scene } from 'core/scene/types';\nimport type { PoseDescriptor } from 'interactions/actions/resize/geometry';\nimport { scenePaintedPoseOf } from '../derivedPath';\nimport type { PickBroad, PickCandidate } from '../pickWalk';\nimport { sceneParentsOf, sceneShownPathOf } from '../scenePickReads';\nimport { nodeBroadBox, type Box2 } from './nodeBroadBox';\n\ntype AnyScene = Scene<unknown, string, unknown>;\ntype Order = readonly PickCandidate<unknown>[];\n\nexport interface ScenePickIndex {\n /** The layers holding indexed nodes; `undefined` for nodes with none. */\n readonly layers: readonly (string | undefined)[];\n readonly maxOutsetPx: number;\n}\n\nlet builds = 0;\n\n/** How many indexes have been built, for tests that pin when one rebuilds. */\nexport function pickIndexBuildCount(): number {\n return builds;\n}\n\nconst CACHE = new WeakMap<object, WeakMap<PoseDescriptor<unknown>, SettledCache<BuiltIndex>>>();\n\n/** Past this share of the scene the index costs more than the walk it narrows. */\nconst MAX_SHARE = 0.5;\n\nclass BuiltIndex implements ScenePickIndex {\n readonly layers: readonly (string | undefined)[];\n /** The render order the candidates are read out of. */\n private order: Order;\n /** Build position to position in `order`, or null while they are the same array. */\n private remap: Int32Array | null = null;\n private readonly marks: Uint8Array;\n private found: Int32Array;\n\n constructor(\n private readonly built: Order,\n private readonly trees: Map<string | undefined, Bvh<number>>,\n private readonly always: Int32Array,\n readonly maxOutsetPx: number,\n ) {\n this.layers = [...trees.keys()];\n this.order = built;\n this.marks = new Uint8Array(built.length);\n this.found = new Int32Array(Math.max(16, always.length));\n }\n\n /** Follow a new render order array the scene hands back for the same state. */\n rankBy(order: Order): void {\n if (order === this.order) return;\n this.order = order;\n if (order === this.built) {\n this.remap = null;\n return;\n }\n const rank = new Map<string, number>();\n for (let i = 0; i < order.length; i++) rank.set(order[i].id, i);\n const remap = new Int32Array(this.built.length);\n for (let i = 0; i < remap.length; i++) remap[i] = rank.get(this.built[i].id) ?? -1;\n this.remap = remap;\n }\n\n /** The nodes the boxes reach, in render order, or null when that is most of the scene. */\n candidates(broads: readonly (PickBroad | null)[]): Order | null {\n const limit = Math.floor(this.order.length * MAX_SHARE);\n let count = 0;\n const add = (built: number): boolean => {\n const at = this.remap === null ? built : this.remap[built];\n if (at < 0) return true;\n if (count === this.found.length) {\n const next = new Int32Array(count * 2);\n next.set(this.found);\n this.found = next;\n }\n this.found[count++] = at;\n return count <= limit;\n };\n for (const i of this.always) if (!add(i)) return null;\n for (let l = 0; l < this.layers.length; l++) {\n const broad = broads[l];\n const tree = this.trees.get(this.layers[l]);\n if (broad === null || tree === undefined) continue;\n const { x, y, width, height } = broad.box;\n const k = broad.pxPerWorld;\n const by = this.maxOutsetPx > 0 && k !== undefined ? (k > 0 ? this.maxOutsetPx / k : Infinity) : 0;\n let over = false;\n tree.queryBox([x - by, y - by, x + width + by, y + height + by], (i) => {\n if (add(i)) return true;\n over = true;\n return false;\n });\n if (over) return null;\n }\n return this.readOut(count);\n }\n\n private readOut(count: number): Order {\n const { order, found } = this;\n const out: PickCandidate<unknown>[] = new Array(count);\n // A handful sorts faster than a pass over every mark.\n if (count * 32 < order.length) {\n const ranks = found.subarray(0, count).sort();\n for (let i = 0; i < count; i++) out[i] = order[ranks[i]];\n return out;\n }\n const marks = this.marks.length >= order.length ? this.marks : new Uint8Array(order.length);\n for (let i = 0; i < count; i++) marks[found[i]] = 1;\n let n = 0;\n for (let i = 0; n < count && i < order.length; i++) {\n if (marks[i] === 0) continue;\n marks[i] = 0;\n out[n++] = order[i];\n }\n return out;\n }\n}\n\nfunction hasCopyingAncestor(parents: Order): boolean {\n return parents.some((p) => typeof p.copiesFromPose === 'function');\n}\n\nfunction build(scene: AnyScene, descriptor: PoseDescriptor<unknown>, order: Order): BuiltIndex {\n builds++;\n const poseOf = scenePaintedPoseOf(scene);\n const pathOf = sceneShownPathOf(scene);\n const parentsOf = sceneParentsOf(scene);\n const entries = new Map<string | undefined, [number, Box2][]>();\n const always: number[] = [];\n let maxOutsetPx = 0;\n for (let i = 0; i < order.length; i++) {\n const node = order[i];\n const parents = parentsOf(node);\n // Also hit wherever an ancestor paints a copy of it, which its own box does not cover.\n if (hasCopyingAncestor(parents)) {\n always.push(i);\n continue;\n }\n const box: Box2 = [0, 0, 0, 0];\n let outsetPx: number;\n try {\n const inherited = inheritsPaint(node) ? inheritedPaintOf(node, parents) : undefined;\n outsetPx = nodeBroadBox(node, poseOf(node as never), pathOf(node), inherited, descriptor, box);\n } catch {\n always.push(i);\n continue;\n }\n if (!Number.isFinite(outsetPx) || !box.every(Number.isFinite)) {\n always.push(i);\n continue;\n }\n if (outsetPx > maxOutsetPx) maxOutsetPx = outsetPx;\n let list = entries.get(node.layer);\n if (list === undefined) entries.set(node.layer, (list = []));\n list.push([i, box]);\n }\n const trees = new Map<string | undefined, Bvh<number>>();\n for (const [layer, list] of entries) {\n const tree = createBvh<number>(2, list);\n // The tree drops an inverted box: test that node the old way.\n if (tree.size !== list.length) {\n for (const [i, box] of list) if (!(box[0] <= box[2] && box[1] <= box[3])) always.push(i);\n }\n trees.set(layer, tree);\n }\n return new BuiltIndex(order, trees, Int32Array.from(always), maxOutsetPx);\n}\n\n/** The index for `scene` read through `descriptor`, or null when this pick walks instead. */\nexport function scenePickIndex(scene: AnyScene, descriptor: PoseDescriptor<unknown>): ScenePickIndex | null {\n return current(scene, descriptor);\n}\n\nfunction current(scene: AnyScene, descriptor: PoseDescriptor<unknown>): BuiltIndex | null {\n // Registers the built-in painters first, which bumps the memo generation.\n getNodeShapes();\n const version = scene.getVersion();\n const overrides = scene.overrides.getGeneration();\n // Boxed where each node is painted, which a container's scroll moves.\n const scrolled = scene.scroll?.getGeneration() ?? 0;\n const memo = nodeMemoGeneration();\n const markers = markerGeneration();\n let perScene = CACHE.get(scene);\n if (perScene === undefined) CACHE.set(scene, (perScene = new WeakMap()));\n let cache = perScene.get(descriptor);\n if (cache === undefined) perScene.set(descriptor, (cache = createSettledCache()));\n // Boxed as shown, so a data override that grows a node's ink is met where it is painted.\n const order = nodesAsShown(scene.overrides, scene.renderOrderNodes()) as unknown as Order;\n const index = cache.read([version, overrides, scrolled, memo, markers], () => build(scene, descriptor, order));\n index?.rankBy(order);\n return index;\n}\n\n/** What `PickSource.candidates` answers for a scene source with an index. */\nexport function indexedCandidates(\n scene: AnyScene,\n descriptor: PoseDescriptor<unknown>,\n broadFor: (layer: string | undefined) => PickBroad | null | undefined,\n): Order | null {\n const index = current(scene, descriptor);\n if (index === null) return null;\n const broads: (PickBroad | null)[] = [];\n for (const layer of index.layers) {\n const broad = broadFor(layer);\n if (broad === undefined) return null;\n if (broad !== null && Number.isNaN(broad.box.x + broad.box.y + broad.box.width + broad.box.height)) return null;\n broads.push(broad);\n }\n return index.candidates(broads);\n}\n"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
- import { composeWorldPose, composesFrames, frameAtOrAbove } from "../core/scene/composeFrame.js";
1
+ import { composeWorldPose, composesFrames, frameAtOrAbove, memoizeCompose } from "../core/scene/composeFrame.js";
2
2
  import { definesFrame, effectivePose } from "../core/scene/effectivePose.js";
3
3
  import { poseDescriptorForNode, translatePoseViaDescriptor, visualBoundsViaDescriptor } from "../core/geometry/poseDescriptor.js";
4
+ import { scrolledFrames } from "../core/scene/scrollOffsets.js";
4
5
  import { AUTO_POSE_DESCRIPTOR } from "../core/geometry/autoPoseDescriptor.js";
5
6
  import { asNodeId } from "../core/scene/types.js";
6
7
  import { nodeGates } from "../core/scene/nodeGates.js";
@@ -9,19 +10,13 @@ import { applyOpsTo } from "../core/applyOps.js";
9
10
  import { hitTestArea, hitTestLassoPolygon, regionPickOptions } from "./deps/hitTestArea.js";
10
11
  import { insertSite } from "./deps/insertSite.js";
11
12
  import { scenePoseTarget } from "../animation/poseTarget.js";
12
- import { cascadingSetPose, containerCascade } from "./poseCascade.js";
13
13
  import { pasteSceneSnapshot, snapshotSceneSelection } from "./sceneClipboard.js";
14
14
 
15
15
  //#region src/canvas/sceneAdapter.ts
16
16
  /** Derive the adapter for a scene. Reads go straight to the scene; writes are
17
17
  * ops applied through its history, so anything done through the adapter is
18
- * undoable.
19
- *
20
- * Moving a container carries its contents either way the scene composes
21
- * poses. Under a frame (`scene.composition`) the one pose written moves them.
22
- * Under the absolute-pose model `setPose` on a container translates every
23
- * descendant by the same delta, and a batch that poses descendants too undoes
24
- * exactly only when it goes through this adapter's `applyOps`. */
18
+ * undoable. A container's pose is a frame its children are stored in, so the
19
+ * one pose written moves its contents. */
25
20
  function sceneToAdapter(scene, options = {}) {
26
21
  const sel = options.selection;
27
22
  const getSelection = sel?.getSelection ?? sel?.get ?? (() => []);
@@ -31,15 +26,15 @@ function sceneToAdapter(scene, options = {}) {
31
26
  const translate = (p, dx, dy, node) => translatePoseViaDescriptor(p, dx, dy, node === void 0 ? d : poseDescriptorForNode(d, node));
32
27
  const composition = scene.composition;
33
28
  const composes = composesFrames(composition);
29
+ const compose = memoizeCompose(composition.compose);
30
+ const childFrame = composes ? scrolledFrames(scene) : void 0;
34
31
  const pickOpts = {};
35
32
  const pickOptsFor = (view) => view ? {
36
33
  ...pickOpts,
37
34
  ...regionPickOptions(view, void 0)
38
35
  } : pickOpts;
39
- const cascade = composes ? null : { setPose: cascadingSetPose(scene, d) };
40
36
  const clipboardEnv = {
41
37
  scene,
42
- composes,
43
38
  worldPoseOf: (id) => adapter.getWorldPose(id),
44
39
  frameAtOrAbove: (id) => frameAtOrAbove(adapter, id),
45
40
  poseBounds,
@@ -64,15 +59,11 @@ function sceneToAdapter(scene, options = {}) {
64
59
  },
65
60
  getWorldPose(id) {
66
61
  if (!composes) return adapter.getPose(id);
67
- return composeWorldPose(adapter, id, composition.compose);
68
- },
69
- ...composes ? { composePose: composition.compose } : {},
70
- ...cascade ? { setPose: (id, pose) => cascade.setPose(asNodeId(id), pose) } : {},
71
- poseTarget(id, descriptor) {
72
- if (cascade === null || scene.get(asNodeId(id))?.kind !== "group") return scenePoseTarget(scene, id, descriptor ?? d);
73
- const carried = containerCascade(scene, asNodeId(id), d, (pose) => adapter.setPose(id, pose));
74
- return scenePoseTarget(scene, id, descriptor ?? d, carried);
62
+ return composeWorldPose(adapter, id, compose);
75
63
  },
64
+ ...composes ? { composePose: compose } : {},
65
+ ...childFrame ? { childFrame } : {},
66
+ poseTarget: (id, descriptor) => scenePoseTarget(scene, id, descriptor ?? d),
76
67
  getLayers: () => scene.layers.map((l) => ({
77
68
  id: l.id,
78
69
  visible: l.visible,
@@ -1 +1 @@
1
- {"version":3,"file":"sceneAdapter.js","names":[],"sources":["../../src/canvas/sceneAdapter.ts"],"sourcesContent":["/**\n * Synthesize a `<Canvas>`-compatible adapter from a `Scene` primitive.\n * `getPose` / `setPose` return / write **local** coordinates (relative to\n * parent); world composition is the renderer's job via `composeWorldPose`.\n */\nimport type {\n AreaSelectAdapter,\n ClipboardSnapshot,\n InsertAdapter,\n PasteCtx,\n LassoHitMode,\n LassoSelectAdapter,\n RegionPickView,\n LayerEnumerableAdapter,\n MoveAdapter,\n ResizeAdapter,\n RotateAdapter,\n} from 'core/adapters/types';\nimport type { LayoutStrategy } from '../layout/types';\nimport type { Op } from 'core/ops/types';\nimport type { Node, Scene } from 'core/scene/types';\nimport { definesFrame, effectivePose } from 'core/scene/effectivePose';\nimport { sceneMutators } from 'core/scene/sceneMutators';\nimport { asNodeId } from 'core/scene/types';\nimport { nodeGates } from 'core/scene/nodeGates';\nimport { applyOpsTo } from 'core/applyOps';\nimport { hitTestArea, hitTestLassoPolygon, regionPickOptions } from 'canvas/deps/hitTestArea';\nimport type { ScenePickSourceOptions } from 'canvas/pickWalk';\nimport {\n poseDescriptorForNode,\n translatePoseViaDescriptor,\n visualBoundsViaDescriptor,\n type PoseDescriptor,\n} from 'interactions/actions/resize/geometry';\nimport { AUTO_POSE_DESCRIPTOR } from 'core/geometry/autoPoseDescriptor';\nimport { composesFrames, composeWorldPose, frameAtOrAbove } from 'core/scene/composePose';\nimport { insertSite } from './deps/insertSite';\nimport type { Bounds } from '../core/viewport/fitViewToBounds';\nimport { scenePoseTarget } from '../animation/poseTarget';\nimport { cascadingSetPose, containerCascade } from './poseCascade';\nimport { pasteSceneSnapshot, snapshotSceneSelection, type SceneClipboardEnv } from './sceneClipboard';\n\n/** Minimal selection contract `sceneToAdapter` needs to wire `getSelection` /\n * `setSelection`. Matches `useSelection().adapterMethods` plus an imperative\n * read; pass `useSelection()` itself or `selection.adapterMethods`. */\nexport interface SceneAdapterSelection {\n get?(): string[];\n set?(ids: string[]): void;\n getSelection?(): string[];\n setSelection?(ids: string[]): void;\n}\n\n/** Sibling z-order surface used by reorder ops. Inlined here (rather than\n * pulled from `core/ops/reorder` to avoid import cycles); the `null` parent\n * channel addresses the root sibling list. */\n/** @internal */\ninterface ReorderAdapter {\n getChildren(parentId: string | null): string[];\n setChildOrder(parentId: string | null, ids: string[]): void;\n}\n\n/** Everything the interaction layer can ask of a scene, as one type: the\n * move, resize, rotate, selection, layer and reorder contracts combined.\n * `<SceneCanvas>` synthesizes one of these, which is why most consumers never\n * touch an adapter directly. */\nexport type SceneCanvasAdapter<TData, TLayer extends string, TPose> =\n & MoveAdapter<Node<TData, TLayer, TPose>, TPose>\n & ResizeAdapter<Node<TData, TLayer, TPose>, TPose>\n & RotateAdapter<Node<TData, TLayer, TPose>, TPose>\n & AreaSelectAdapter\n & LassoSelectAdapter\n & LayerEnumerableAdapter<TLayer>\n & ReorderAdapter\n & Partial<InsertAdapter<Node<TData, TLayer, TPose>>>\n // Optional on the underlying adapter contracts, but unconditionally\n // provided by a scene-backed adapter.\n & {\n getParent(id: string): string | null;\n getLayers: NonNullable<LayerEnumerableAdapter<TLayer>['getLayers']>;\n /** The node's pose with every ancestor's frame folded in. Equal to\n * `getPose` unless the scene composes poses. */\n getWorldPose(id: string): TPose;\n /** Whether `id`'s pose is a frame its children are expressed in — see\n * `definesFrame`. */\n definesFrame(id: string): boolean;\n /** Present only when a composing strategy is configured; the render\n * walk feature-detects it. */\n composePose?(parent: TPose, child: TPose): TPose;\n getSelection(): string[];\n setSelection(ids: string[]): void;\n insertNode(node: Node<TData, TLayer, TPose>, index?: number): void;\n removeNode(id: string): void;\n /** Replace a node's `data` — what a `setData` op writes through. */\n setData(id: string, data: TData): void;\n /** Swap a container's layout — what a `setLayout` op writes through. */\n setLayout(id: string, layout: LayoutStrategy<TPose> | string | null): void;\n /** Move a node to another layer — what a `setLayer` op writes through. */\n setLayer(id: string, layer: TLayer): void;\n /** Retarget a derived node's sources — what a `setDependsOn` op writes through. */\n setDependsOn(id: string, dependsOn: readonly string[] | 'children' | undefined): void;\n /** Hide or show a node — what a `setHidden` op writes through. */\n setHidden(id: string, hidden: boolean): void;\n /** Lock or unlock a node — what a `setLocked` op writes through. */\n setLocked(id: string, locked: boolean): void;\n getRemovalClosure(ids: readonly string[]): string[];\n applyOps(ops: Op[], label?: string): void;\n snapshotSelection(ids: string[]): ClipboardSnapshot;\n commitPaste(\n clipboard: ClipboardSnapshot,\n offset: { dx: number; dy: number },\n ctx?: PasteCtx,\n ): Node<TData, TLayer, TPose>[];\n getPasteOffset(clipboard: ClipboardSnapshot): { dx: number; dy: number };\n };\n\n/** Optional extras for the synthesized adapter. Pass `commitInsert` to wire\n * the insert gesture into a Scene-owned canvas; the returned object becomes\n * a leaf on the layer named by `layer` (default `'default'`). */\nexport interface SceneToAdapterOptions<TData, TLayer extends string, TPose> {\n /** Factory for new objects. Returning `null` aborts the insert. */\n commitInsert?: (bounds: Bounds) => {\n pose: TPose;\n data: TData;\n id?: string;\n } | null;\n /** Layer to place inserted nodes on. Defaults to the trivial-form layer. */\n insertLayer?: TLayer;\n /** Selection source: the result of `useSelection()` (or its\n * `adapterMethods`). When omitted, `getSelection` returns `[]` and\n * `setSelection` is a noop, so the marquee gesture updates nothing. */\n selection?: SceneAdapterSelection;\n /** How to read and rewrite this scene's poses. Default `AUTO_POSE_DESCRIPTOR`. */\n poseDescriptor?: PoseDescriptor<TPose>;\n /** The container a paste lands in, read at each paste: `<SceneCanvas>` passes the `insertInto` dep's. */\n pasteInto?: () => string | null;\n}\n\n/** Derive the adapter for a scene. Reads go straight to the scene; writes are\n * ops applied through its history, so anything done through the adapter is\n * undoable.\n *\n * Moving a container carries its contents either way the scene composes\n * poses. Under a frame (`scene.composition`) the one pose written moves them.\n * Under the absolute-pose model `setPose` on a container translates every\n * descendant by the same delta, and a batch that poses descendants too undoes\n * exactly only when it goes through this adapter's `applyOps`. */\nexport function sceneToAdapter<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n options: SceneToAdapterOptions<TData, TLayer, TPose> = {},\n): SceneCanvasAdapter<TData, TLayer, TPose> {\n const sel = options.selection;\n const getSelection = sel?.getSelection ?? sel?.get ?? (() => [] as string[]);\n const setSelection = sel?.setSelection ?? sel?.set ?? (() => {});\n const d = (options.poseDescriptor ?? AUTO_POSE_DESCRIPTOR) as PoseDescriptor<TPose>;\n const poseBounds = (p: TPose, node: unknown): Bounds =>\n visualBoundsViaDescriptor(p, poseDescriptorForNode(d, node));\n const translate = (p: TPose, dx: number, dy: number, node?: unknown): TPose =>\n translatePoseViaDescriptor(\n p, dx, dy, node === undefined ? d : poseDescriptorForNode(d, node),\n );\n\n const composition = scene.composition;\n const composes = composesFrames(composition);\n const pickOpts: ScenePickSourceOptions<unknown> = {};\n // The asking view's layer gate and camera, over the adapter's own options.\n const pickOptsFor = (view: RegionPickView | undefined): ScenePickSourceOptions<unknown> =>\n (view ? { ...pickOpts, ...regionPickOptions(view, undefined) } : pickOpts);\n\n const cascade = composes ? null : { setPose: cascadingSetPose(scene, d) };\n\n const clipboardEnv: SceneClipboardEnv<TData, TLayer, TPose> = {\n scene,\n composes,\n worldPoseOf: (id) => adapter.getWorldPose(id),\n frameAtOrAbove: (id) => frameAtOrAbove(adapter, id),\n poseBounds,\n translate, ...(options.pasteInto ? { landing: () => insertSite(scene as never, options.pasteInto!()) } : {}),\n };\n\n const adapter: SceneCanvasAdapter<TData, TLayer, TPose> = {\n ...sceneMutators(scene),\n // What is shown, where the mutators' other users read the document:\n // the nodes not hidden, and the effective pose.\n getNodes() {\n const gates = nodeGates(scene);\n return scene.renderOrderNodes().filter((n) => !gates.hidden(n.id));\n },\n getPose(id) {\n const nid = asNodeId(id);\n const n = scene.get(nid);\n if (!n) throw new Error(`sceneToAdapter: unknown node \"${id}\"`);\n return effectivePose(scene, n);\n },\n definesFrame(id) {\n const n = scene.get(asNodeId(id));\n return n === undefined || definesFrame(n);\n },\n getWorldPose(id) {\n if (!composes) return adapter.getPose(id);\n return composeWorldPose(adapter, id, composition.compose);\n },\n // Only present when it would do something: the render walk skips the fold\n // entirely when this is absent, which is the absolute-pose path.\n ...(composes ? { composePose: composition.compose } : {}),\n ...(cascade ? { setPose: (id: string, pose: TPose) => cascade.setPose(asNodeId(id), pose) } : {}),\n poseTarget(id, descriptor) {\n if (cascade === null || scene.get(asNodeId(id))?.kind !== 'group') {\n return scenePoseTarget(scene, id, descriptor ?? d);\n }\n // Its descendants hold poses of their own: a commit moves them, and they are shown beside it.\n const carried = containerCascade(scene, asNodeId(id), d, (pose) => adapter.setPose(id, pose));\n return scenePoseTarget(scene, id, descriptor ?? d, carried);\n },\n getLayers: () => scene.layers.map((l) => ({ id: l.id, visible: l.visible, parallax: l.parallax })),\n subscribeLayers: (listener) => scene.subscribe(listener),\n getLayout: (id) => scene.layoutOf(asNodeId(id)),\n // With a label the ops are one batch checkpoint; without one they are\n // applied transiently, with no checkpoint (the AreaSelectAdapter contract).\n applyOps(ops: Op[], label?: string) {\n if (label !== undefined) {\n scene.applyBatch(ops, label, this);\n } else {\n applyOpsTo(this, ops);\n }\n },\n getSelection,\n setSelection,\n // The live marquee's and lasso's own hit-tests. Groups come back:\n // this adapter's consumer has nothing to fold children into them.\n hitTestArea(rect: Bounds, view?: RegionPickView) {\n return hitTestArea(\n scene,\n rect,\n pickOptsFor(view),\n d as PoseDescriptor<unknown>,\n { includeGroups: true },\n );\n },\n hitTestLasso(polygon, mode: LassoHitMode, view?: RegionPickView) {\n return hitTestLassoPolygon(\n scene,\n polygon,\n mode,\n pickOptsFor(view),\n d as PoseDescriptor<unknown>,\n { includeGroups: true },\n );\n },\n // A pure factory: it constructs a Node spec but does NOT touch the scene.\n // The `InsertOp` → `adapter.insertNode` → `scene.add` path is the sole\n // insertion path. Calling `scene.add` here too would double-insert, and\n // the second add throws on id collision.\n ...(options.commitInsert\n ? {\n commitInsert: (bounds: { x: number; y: number; width: number; height: number }) => {\n const created = options.commitInsert!(bounds);\n if (!created) return null;\n const layer = (options.insertLayer ?? ('default' as TLayer));\n const id = created.id === undefined ? scene.mintId() : asNodeId(created.id);\n return {\n kind: 'mark',\n id,\n layer,\n pose: created.pose,\n data: created.data,\n parent: null,\n } as Node<TData, TLayer, TPose>;\n },\n }\n : {}),\n // Clipboard seam (`useClipboardOps` / the paste ingestion path).\n snapshotSelection(ids: string[]): ClipboardSnapshot {\n return snapshotSceneSelection(clipboardEnv, ids);\n },\n commitPaste(\n clipboard: ClipboardSnapshot,\n offset: { dx: number; dy: number },\n ctx?: PasteCtx,\n ): Node<TData, TLayer, TPose>[] {\n return pasteSceneSnapshot(clipboardEnv, clipboard, offset, ctx);\n },\n // Constant cascade: the hook re-snapshots the just-created items, so\n // repeated pastes stack +12/+12 each time.\n getPasteOffset(): { dx: number; dy: number } {\n return { dx: 12, dy: 12 };\n },\n };\n\n return adapter;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sceneAdapter.js","names":[],"sources":["../../src/canvas/sceneAdapter.ts"],"sourcesContent":["/**\n * Synthesize a `<Canvas>`-compatible adapter from a `Scene` primitive.\n * `getPose` / `setPose` return / write **local** coordinates (relative to\n * parent); world composition is the renderer's job via `composeWorldPose`.\n */\nimport type {\n AreaSelectAdapter,\n ClipboardSnapshot,\n InsertAdapter,\n PasteCtx,\n LassoHitMode,\n LassoSelectAdapter,\n RegionPickView,\n LayerEnumerableAdapter,\n MoveAdapter,\n ResizeAdapter,\n RotateAdapter,\n} from 'core/adapters/types';\nimport type { LayoutStrategy } from '../layout/types';\nimport type { Op } from 'core/ops/types';\nimport type { Node, Scene } from 'core/scene/types';\nimport { definesFrame, effectivePose } from 'core/scene/effectivePose';\nimport { sceneMutators } from 'core/scene/sceneMutators';\nimport { asNodeId } from 'core/scene/types';\nimport { nodeGates } from 'core/scene/nodeGates';\nimport { applyOpsTo } from 'core/applyOps';\nimport { hitTestArea, hitTestLassoPolygon, regionPickOptions } from 'canvas/deps/hitTestArea';\nimport type { ScenePickSourceOptions } from 'canvas/pickWalk';\nimport {\n poseDescriptorForNode,\n translatePoseViaDescriptor,\n visualBoundsViaDescriptor,\n type PoseDescriptor,\n} from 'interactions/actions/resize/geometry';\nimport { AUTO_POSE_DESCRIPTOR } from 'core/geometry/autoPoseDescriptor';\nimport { composesFrames, composeWorldPose, frameAtOrAbove } from 'core/scene/composePose';\nimport { memoizeCompose } from 'core/scene/composeFrame';\nimport { scrolledFrames } from 'core/scene/scrollOffsets';\nimport { insertSite } from './deps/insertSite';\nimport type { Bounds } from '../core/viewport/fitViewToBounds';\nimport { scenePoseTarget } from '../animation/poseTarget';\nimport { pasteSceneSnapshot, snapshotSceneSelection, type SceneClipboardEnv } from './sceneClipboard';\n\n/** Minimal selection contract `sceneToAdapter` needs to wire `getSelection` /\n * `setSelection`. Matches `useSelection().adapterMethods` plus an imperative\n * read; pass `useSelection()` itself or `selection.adapterMethods`. */\nexport interface SceneAdapterSelection {\n get?(): string[];\n set?(ids: string[]): void;\n getSelection?(): string[];\n setSelection?(ids: string[]): void;\n}\n\n/** Sibling z-order surface used by reorder ops. Inlined here (rather than\n * pulled from `core/ops/reorder` to avoid import cycles); the `null` parent\n * channel addresses the root sibling list. */\n/** @internal */\ninterface ReorderAdapter {\n getChildren(parentId: string | null): string[];\n setChildOrder(parentId: string | null, ids: string[]): void;\n}\n\n/** Everything the interaction layer can ask of a scene, as one type: the\n * move, resize, rotate, selection, layer and reorder contracts combined.\n * `<SceneCanvas>` synthesizes one of these, which is why most consumers never\n * touch an adapter directly. */\nexport type SceneCanvasAdapter<TData, TLayer extends string, TPose> =\n & MoveAdapter<Node<TData, TLayer, TPose>, TPose>\n & ResizeAdapter<Node<TData, TLayer, TPose>, TPose>\n & RotateAdapter<Node<TData, TLayer, TPose>, TPose>\n & AreaSelectAdapter\n & LassoSelectAdapter\n & LayerEnumerableAdapter<TLayer>\n & ReorderAdapter\n & Partial<InsertAdapter<Node<TData, TLayer, TPose>>>\n // Optional on the underlying adapter contracts, but unconditionally\n // provided by a scene-backed adapter.\n & {\n getParent(id: string): string | null;\n getLayers: NonNullable<LayerEnumerableAdapter<TLayer>['getLayers']>;\n /** The node's pose with every ancestor's frame folded in. Equal to\n * `getPose` unless the scene composes poses. */\n getWorldPose(id: string): TPose;\n /** Whether `id`'s pose is a frame its children are expressed in — see\n * `definesFrame`. */\n definesFrame(id: string): boolean;\n /** Present only when a composing strategy is configured; the render\n * walk feature-detects it. */\n composePose?(parent: TPose, child: TPose): TPose;\n /** The frame a container gives its children, scrolled by the scene's\n * `scroll` offset — see `PoseAdapter.childFrame`. Present only when the\n * composition can scroll. */\n childFrame?(id: string, world: TPose): TPose;\n getSelection(): string[];\n setSelection(ids: string[]): void;\n insertNode(node: Node<TData, TLayer, TPose>, index?: number): void;\n removeNode(id: string): void;\n /** Replace a node's `data` — what a `setData` op writes through. */\n setData(id: string, data: TData): void;\n /** Swap a container's layout — what a `setLayout` op writes through. */\n setLayout(id: string, layout: LayoutStrategy<TPose> | string | null): void;\n /** Move a node to another layer — what a `setLayer` op writes through. */\n setLayer(id: string, layer: TLayer): void;\n /** Retarget a derived node's sources — what a `setDependsOn` op writes through. */\n setDependsOn(id: string, dependsOn: readonly string[] | 'children' | undefined): void;\n /** Hide or show a node — what a `setHidden` op writes through. */\n setHidden(id: string, hidden: boolean): void;\n /** Lock or unlock a node — what a `setLocked` op writes through. */\n setLocked(id: string, locked: boolean): void;\n getRemovalClosure(ids: readonly string[]): string[];\n applyOps(ops: Op[], label?: string): void;\n snapshotSelection(ids: string[]): ClipboardSnapshot;\n commitPaste(\n clipboard: ClipboardSnapshot,\n offset: { dx: number; dy: number },\n ctx?: PasteCtx,\n ): Node<TData, TLayer, TPose>[];\n getPasteOffset(clipboard: ClipboardSnapshot): { dx: number; dy: number };\n };\n\n/** Optional extras for the synthesized adapter. Pass `commitInsert` to wire\n * the insert gesture into a Scene-owned canvas; the returned object becomes\n * a leaf on the layer named by `layer` (default `'default'`). */\nexport interface SceneToAdapterOptions<TData, TLayer extends string, TPose> {\n /** Factory for new objects. Returning `null` aborts the insert. */\n commitInsert?: (bounds: Bounds) => {\n pose: TPose;\n data: TData;\n id?: string;\n } | null;\n /** Layer to place inserted nodes on. Defaults to the trivial-form layer. */\n insertLayer?: TLayer;\n /** Selection source: the result of `useSelection()` (or its\n * `adapterMethods`). When omitted, `getSelection` returns `[]` and\n * `setSelection` is a noop, so the marquee gesture updates nothing. */\n selection?: SceneAdapterSelection;\n /** How to read and rewrite this scene's poses. Default `AUTO_POSE_DESCRIPTOR`. */\n poseDescriptor?: PoseDescriptor<TPose>;\n /** The container a paste lands in, read at each paste: `<SceneCanvas>` passes the `insertInto` dep's. */\n pasteInto?: () => string | null;\n}\n\n/** Derive the adapter for a scene. Reads go straight to the scene; writes are\n * ops applied through its history, so anything done through the adapter is\n * undoable. A container's pose is a frame its children are stored in, so the\n * one pose written moves its contents. */\nexport function sceneToAdapter<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n options: SceneToAdapterOptions<TData, TLayer, TPose> = {},\n): SceneCanvasAdapter<TData, TLayer, TPose> {\n const sel = options.selection;\n const getSelection = sel?.getSelection ?? sel?.get ?? (() => [] as string[]);\n const setSelection = sel?.setSelection ?? sel?.set ?? (() => {});\n const d = (options.poseDescriptor ?? AUTO_POSE_DESCRIPTOR) as PoseDescriptor<TPose>;\n const poseBounds = (p: TPose, node: unknown): Bounds =>\n visualBoundsViaDescriptor(p, poseDescriptorForNode(d, node));\n const translate = (p: TPose, dx: number, dy: number, node?: unknown): TPose =>\n translatePoseViaDescriptor(p, dx, dy, node === undefined ? d : poseDescriptorForNode(d, node));\n\n const composition = scene.composition;\n const composes = composesFrames(composition);\n const compose = memoizeCompose(composition.compose);\n const childFrame = composes ? scrolledFrames(scene) : undefined;\n const pickOpts: ScenePickSourceOptions<unknown> = {};\n // The asking view's layer gate and camera, over the adapter's own options.\n const pickOptsFor = (view: RegionPickView | undefined): ScenePickSourceOptions<unknown> =>\n (view ? { ...pickOpts, ...regionPickOptions(view, undefined) } : pickOpts);\n\n const clipboardEnv: SceneClipboardEnv<TData, TLayer, TPose> = {\n scene,\n worldPoseOf: (id) => adapter.getWorldPose(id),\n frameAtOrAbove: (id) => frameAtOrAbove(adapter, id),\n poseBounds,\n translate, ...(options.pasteInto ? { landing: () => insertSite(scene as never, options.pasteInto!()) } : {}),\n };\n\n const adapter: SceneCanvasAdapter<TData, TLayer, TPose> = {\n ...sceneMutators(scene),\n // What is shown, where the mutators' other users read the document:\n // the nodes not hidden, and the effective pose.\n getNodes() {\n const gates = nodeGates(scene);\n return scene.renderOrderNodes().filter((n) => !gates.hidden(n.id));\n },\n getPose(id) {\n const nid = asNodeId(id);\n const n = scene.get(nid);\n if (!n) throw new Error(`sceneToAdapter: unknown node \"${id}\"`);\n return effectivePose(scene, n);\n },\n definesFrame(id) {\n const n = scene.get(asNodeId(id));\n return n === undefined || definesFrame(n);\n },\n getWorldPose(id) {\n if (!composes) return adapter.getPose(id);\n return composeWorldPose(adapter, id, compose);\n },\n // Only present when it would do something: the render walk skips the fold\n // entirely when this is absent, which is the absolute-pose path.\n ...(composes ? { composePose: compose } : {}),\n ...(childFrame ? { childFrame } : {}),\n poseTarget: (id, descriptor) => scenePoseTarget(scene, id, descriptor ?? d),\n getLayers: () => scene.layers.map((l) => ({ id: l.id, visible: l.visible, parallax: l.parallax })),\n subscribeLayers: (listener) => scene.subscribe(listener),\n getLayout: (id) => scene.layoutOf(asNodeId(id)),\n // With a label the ops are one batch checkpoint; without one they are\n // applied transiently, with no checkpoint (the AreaSelectAdapter contract).\n applyOps(ops: Op[], label?: string) {\n if (label !== undefined) {\n scene.applyBatch(ops, label, this);\n } else {\n applyOpsTo(this, ops);\n }\n },\n getSelection,\n setSelection,\n // The live marquee's and lasso's own hit-tests. Groups come back:\n // this adapter's consumer has nothing to fold children into them.\n hitTestArea(rect: Bounds, view?: RegionPickView) {\n return hitTestArea(\n scene,\n rect,\n pickOptsFor(view),\n d as PoseDescriptor<unknown>,\n { includeGroups: true },\n );\n },\n hitTestLasso(polygon, mode: LassoHitMode, view?: RegionPickView) {\n return hitTestLassoPolygon(\n scene,\n polygon,\n mode,\n pickOptsFor(view),\n d as PoseDescriptor<unknown>,\n { includeGroups: true },\n );\n },\n // A pure factory: it constructs a Node spec but does NOT touch the scene.\n // The `InsertOp` → `adapter.insertNode` → `scene.add` path is the sole\n // insertion path. Calling `scene.add` here too would double-insert, and\n // the second add throws on id collision.\n ...(options.commitInsert\n ? {\n commitInsert: (bounds: { x: number; y: number; width: number; height: number }) => {\n const created = options.commitInsert!(bounds);\n if (!created) return null;\n const layer = (options.insertLayer ?? ('default' as TLayer));\n const id = created.id === undefined ? scene.mintId() : asNodeId(created.id);\n return {\n kind: 'mark',\n id,\n layer,\n pose: created.pose,\n data: created.data,\n parent: null,\n } as Node<TData, TLayer, TPose>;\n },\n }\n : {}),\n // Clipboard seam (`useClipboardOps` / the paste ingestion path).\n snapshotSelection(ids: string[]): ClipboardSnapshot {\n return snapshotSceneSelection(clipboardEnv, ids);\n },\n commitPaste(\n clipboard: ClipboardSnapshot,\n offset: { dx: number; dy: number },\n ctx?: PasteCtx,\n ): Node<TData, TLayer, TPose>[] {\n return pasteSceneSnapshot(clipboardEnv, clipboard, offset, ctx);\n },\n // Constant cascade: the hook re-snapshots the just-created items, so\n // repeated pastes stack +12/+12 each time.\n getPasteOffset(): { dx: number; dy: number } {\n return { dx: 12, dy: 12 };\n },\n };\n\n return adapter;\n}\n"],"mappings":"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"}
@@ -18,7 +18,6 @@ function snapshotSceneSelection(env, ids) {
18
18
  const taken = /* @__PURE__ */ new Set();
19
19
  const items = [];
20
20
  const poseFor = (n, isRoot) => {
21
- if (!env.composes) return n.pose;
22
21
  if (isRoot) return env.worldPoseOf(n.id);
23
22
  const frame = env.frameAtOrAbove(n.parent);
24
23
  return frame !== null && taken.has(frame) ? n.pose : env.worldPoseOf(n.id);
@@ -43,8 +42,23 @@ function snapshotSceneSelection(env, ids) {
43
42
  });
44
43
  if (hasChildren(n)) for (const cid of scene.childrenOf(asNodeId(id))) capture(cid);
45
44
  };
45
+ const covered = (id) => idSet.has(id) || scene.ancestorsOf(asNodeId(id)).some((a) => idSet.has(a));
46
+ const carrying = /* @__PURE__ */ new Set();
47
+ const carrySources = (id) => {
48
+ if (carrying.has(id)) return;
49
+ carrying.add(id);
50
+ const n = scene.get(asNodeId(id));
51
+ if (!n) return;
52
+ for (const source of sourcesOf(n)) {
53
+ if (taken.has(source) || covered(source) || scene.get(source)?.hidden !== true) continue;
54
+ carrySources(source);
55
+ capture(source, true);
56
+ }
57
+ if (hasChildren(n)) for (const cid of scene.childrenOf(asNodeId(id))) carrySources(cid);
58
+ };
46
59
  for (const id of ids) {
47
60
  if (scene.ancestorsOf(asNodeId(id)).some((a) => idSet.has(a))) continue;
61
+ carrySources(id);
48
62
  capture(id, true);
49
63
  }
50
64
  return { items };
@@ -118,7 +132,6 @@ function pasteSceneSnapshot(env, clipboard, offset, ctx) {
118
132
  const isRoot = (item) => item.parent === null || !idMap.has(item.parent);
119
133
  const byId = new Map(src.map((item) => [item.id, item]));
120
134
  const ridesPastedFrame = (item) => {
121
- if (!env.composes) return false;
122
135
  let p = item.parent === null ? void 0 : byId.get(item.parent);
123
136
  while (p !== void 0) {
124
137
  if (definesFrame(p)) return true;
@@ -138,8 +151,8 @@ function pasteSceneSnapshot(env, clipboard, offset, ctx) {
138
151
  if (b.y < minY) minY = b.y;
139
152
  }
140
153
  if (Number.isFinite(minX) && Number.isFinite(minY)) {
141
- dx = ctx.dropPoint.worldX - minX;
142
- dy = ctx.dropPoint.worldY - minY;
154
+ dx = ctx.dropPoint.x - minX;
155
+ dy = ctx.dropPoint.y - minY;
143
156
  }
144
157
  }
145
158
  const site = env.landing?.();
@@ -1 +1 @@
1
- {"version":3,"file":"sceneClipboard.js","names":[],"sources":["../../src/canvas/sceneClipboard.ts"],"sourcesContent":["/**\n * The scene adapter's clipboard seam: what a copy captures and what a paste\n * mints from it.\n *\n * `pasteSceneSnapshot` is a pure factory — it mints fresh nodes and never\n * touches the scene. Insertion happens through the caller's InsertOps (one per\n * created node, parents before children).\n *\n * A snapshot item carries its node's function fields twice: live, for a paste\n * in the same session, and as the scene's registry keys, for a snapshot that\n * crossed JSON on the way back.\n */\nimport type { ClipboardSnapshot, PasteCtx, PasteOmission } from 'core/adapters/types';\nimport type { Node, NodeId, Scene } from 'core/scene/types';\nimport { asNodeId } from 'core/scene/types';\nimport { hasChildren } from 'core/scene/treeShape';\nimport { sourcesOf } from 'core/scene/dependents';\nimport { definesFrame } from 'core/scene/effectivePose';\nimport {\n NODE_FN_FIELDS, createNodeFnFields, fnFieldsOfNode, fnKeysOf, type FnBearingNode,\n} from 'core/scene/nodeFnFields';\nimport { statedFields } from 'core/scene/statedFields';\nimport type { Bounds } from '../core/viewport/fitViewToBounds';\nimport type { InsertSite } from './deps/insertSite';\n\n/** What the seam needs from the adapter it serves. */\nexport interface SceneClipboardEnv<TData, TLayer extends string, TPose> {\n scene: Scene<TData, TLayer, TPose>;\n /** Whether a stored pose is relative to its container's frame. */\n composes: boolean;\n worldPoseOf(id: string): TPose;\n /** The nearest frame-defining container at or above `id`. */\n frameAtOrAbove(id: string | null): string | null;\n poseBounds(pose: TPose, node: unknown): Bounds;\n translate(pose: TPose, dx: number, dy: number, node?: unknown): TPose;\n /** Where the pasted cluster's roots land; absent, or naming no parent, they keep their layer at the top of the tree. */\n landing?: () => InsertSite;\n}\n\n// Spread-level copy per the clipboard contract: mutating a snapshot's\n// pose/data/children must not touch the scene.\nfunction copyField<T>(value: T): T {\n if (Array.isArray(value)) return [...value] as T;\n if (value !== null && typeof value === 'object') return { ...(value as object) } as T;\n return value;\n}\n\ntype Item<TData, TLayer extends string, TPose> =\n Node<TData, TLayer, TPose> & Record<string, unknown>;\n\nexport function snapshotSceneSelection<TData, TLayer extends string, TPose>(\n env: SceneClipboardEnv<TData, TLayer, TPose>,\n ids: string[],\n): ClipboardSnapshot {\n const { scene } = env;\n const fnFields = createNodeFnFields<TPose>(scene.registry as never);\n const idSet = new Set(ids);\n const taken = new Set<string>();\n const items: Node<TData, TLayer, TPose>[] = [];\n // A stored pose is relative to its frame, and a node whose frame is not\n // copied with it lands somewhere else entirely on paste. Capture those in\n // world coordinates: every root, and under an envelope root, the nodes\n // sharing the frame the root left behind.\n const poseFor = (n: Node<TData, TLayer, TPose>, isRoot: boolean): TPose => {\n if (!env.composes) return n.pose;\n if (isRoot) return env.worldPoseOf(n.id);\n const frame = env.frameAtOrAbove(n.parent);\n return frame !== null && taken.has(frame) ? n.pose : env.worldPoseOf(n.id);\n };\n\n // DFS from one snapshot root: push the node, then its subtree —\n // parents-before-children, so paste can re-insert in array order.\n const capture = (id: string, isRoot = false): void => {\n if (taken.has(id)) return;\n const n = scene.get(asNodeId(id));\n if (!n) return;\n taken.add(id);\n items.push({\n kind: n.kind,\n id: n.id,\n layer: n.layer,\n parent: n.parent,\n pose: copyField(poseFor(n, isRoot)),\n data: copyField(n.data),\n ...(n.children !== undefined ? { children: [...n.children] } : {}),\n ...statedFields(n),\n ...(n.dependsOn !== undefined ? { dependsOn: copyField(n.dependsOn) } : {}),\n ...fnFieldsOfNode(n),\n ...fnKeysOf(fnFields, n as unknown as FnBearingNode, 'jsonKey'),\n } as Node<TData, TLayer, TPose>);\n if (hasChildren(n)) {\n for (const cid of scene.childrenOf(asNodeId(id))) capture(cid);\n }\n };\n for (const id of ids) {\n // Dedupe: an id whose ancestor is also selected is already covered\n // by that ancestor's subtree walk.\n if (scene.ancestorsOf(asNodeId(id)).some((a) => idSet.has(a))) continue;\n capture(id, true);\n }\n return { items };\n}\n\n// JSON drops a function, and turns a layout strategy into an object with no\n// methods on it: neither is something a pasted node can run.\nconst isLive = (value: unknown): boolean =>\n typeof value === 'function'\n || (typeof value === 'object' && value !== null\n && Object.values(value).some((member) => typeof member === 'function'));\n\n/**\n * The item's function fields: for each, what its registry key names in this\n * scene, else the live one it carries, else nothing.\n */\nfunction restoredFnFields<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n item: Item<TData, TLayer, TPose>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n const registry = scene.registry as Record<string, Record<string, unknown> | undefined>;\n for (const spec of NODE_FN_FIELDS) {\n const key = item[spec.jsonKey];\n const registered = typeof key === 'string' ? registry[spec.field]?.[key] : undefined;\n const fn = registered ?? (isLive(item[spec.field]) ? item[spec.field] : undefined);\n if (fn !== undefined) out[spec.field] = fn;\n }\n return out;\n}\n\n/**\n * Which items a paste leaves out, and why. A node deriving from an id list is\n * pasted only with everything it depends on, since a copy still naming the\n * originals would follow them rather than the copies beside it; and only with\n * a derivation to run, since without one it is a placeholder that paints\n * nothing. Leaving one out can strand another, so this runs to a fixed point.\n */\nfunction omissions<TData, TLayer extends string, TPose>(\n src: readonly Item<TData, TLayer, TPose>[],\n fns: ReadonlyMap<string, Record<string, unknown>>,\n): Map<string, PasteOmission> {\n const out = new Map<string, PasteOmission>();\n const present = new Set(src.map((item) => item.id as string));\n for (let changed = true; changed;) {\n changed = false;\n for (const item of src) {\n if (out.has(item.id)) continue;\n let omission: PasteOmission | undefined;\n const deps = sourcesOf(item);\n const missing = deps.filter((dep) => !present.has(dep));\n const fn = fns.get(item.id)!;\n if (item.parent !== null && out.has(item.parent)) {\n omission = { id: item.id, reason: 'container-not-pasted' };\n } else if (missing.length > 0) {\n omission = { id: item.id, reason: 'dependency-not-copied', missing };\n } else if (deps.length > 0 && fn.derivePath === undefined && fn.derivePose === undefined) {\n omission = { id: item.id, reason: 'derivation-not-registered' };\n }\n if (omission === undefined) continue;\n out.set(item.id, omission);\n present.delete(item.id);\n changed = true;\n }\n }\n return out;\n}\n\nexport function pasteSceneSnapshot<TData, TLayer extends string, TPose>(\n env: SceneClipboardEnv<TData, TLayer, TPose>,\n clipboard: ClipboardSnapshot,\n offset: { dx: number; dy: number },\n ctx?: PasteCtx,\n): Node<TData, TLayer, TPose>[] {\n const all = clipboard.items as Item<TData, TLayer, TPose>[];\n if (all.length === 0) return [];\n const fns = new Map(all.map((item) => [item.id as string, restoredFnFields(env.scene, item)]));\n const omitted = omissions(all, fns);\n if (ctx?.onOmit) for (const item of all) {\n const omission = omitted.get(item.id);\n if (omission) ctx.onOmit(omission);\n }\n const src = omitted.size === 0 ? all : all.filter((item) => !omitted.has(item.id));\n\n const idMap = new Map<string, NodeId>();\n for (const item of src) idMap.set(item.id, env.scene.mintId());\n // Items whose captured parent isn't part of the snapshot become roots\n // of the pasted cluster.\n const isRoot = (item: Node<TData, TLayer, TPose>): boolean =>\n item.parent === null || !idMap.has(item.parent);\n // Under a composing strategy a node inside a pasted frame rides it, and\n // offsetting it as well would move it twice.\n const byId = new Map(src.map((item) => [item.id as string, item]));\n const ridesPastedFrame = (item: Node<TData, TLayer, TPose>): boolean => {\n if (!env.composes) return false;\n let p = item.parent === null ? undefined : byId.get(item.parent);\n while (p !== undefined) {\n if (definesFrame(p)) return true;\n p = p.parent === null ? undefined : byId.get(p.parent);\n }\n return false;\n };\n // Without composition poses are absolute, so the delta applies to EVERY\n // pasted node.\n let dx = offset.dx;\n let dy = offset.dy;\n if (ctx?.dropPoint) {\n // Drop-point policy: land the cluster origin (min corner of the root\n // items' bounds) on the drop point; `offset` is ignored.\n let minX = Infinity;\n let minY = Infinity;\n for (const item of src) {\n if (!isRoot(item)) continue;\n const b = env.poseBounds(item.pose, item);\n if (b.x < minX) minX = b.x;\n if (b.y < minY) minY = b.y;\n }\n if (Number.isFinite(minX) && Number.isFinite(minY)) {\n dx = ctx.dropPoint.worldX - minX;\n dy = ctx.dropPoint.worldY - minY;\n }\n }\n const site = env.landing?.();\n const into = site?.parent != null ? site : null;\n const placed = (item: Item<TData, TLayer, TPose>): TPose => {\n if (ridesPastedFrame(item)) return copyField(item.pose);\n const moved = env.translate(copyField(item.pose), dx, dy, item);\n return into && isRoot(item) ? into.local(moved) as TPose : moved;\n };\n // `src` is parents-before-children and this keeps it, so children re-attach\n // to freshly inserted parents when the caller's InsertOps apply in order.\n return src.map((item) => ({\n id: idMap.get(item.id)!,\n kind: item.kind,\n layer: into && isRoot(item) ? into.layer as TLayer : item.layer,\n parent: isRoot(item) ? into?.parent ?? null : idMap.get(item.parent as string)!,\n pose: placed(item),\n data: copyField(item.data),\n ...statedFields(item),\n ...(item.children !== undefined\n ? { children: item.children.filter((c) => idMap.has(c)).map((c) => idMap.get(c)!) }\n : {}),\n ...(item.dependsOn === 'children'\n ? { dependsOn: 'children' as const }\n : sourcesOf(item).length > 0\n ? { dependsOn: sourcesOf(item).map((dep) => idMap.get(dep)!) }\n : {}),\n ...fns.get(item.id),\n } as Node<TData, TLayer, TPose>));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sceneClipboard.js","names":[],"sources":["../../src/canvas/sceneClipboard.ts"],"sourcesContent":["/**\n * The scene adapter's clipboard seam: what a copy captures and what a paste\n * mints from it.\n *\n * `pasteSceneSnapshot` is a pure factory — it mints fresh nodes and never\n * touches the scene. Insertion happens through the caller's InsertOps (one per\n * created node, parents before children).\n *\n * A snapshot item carries its node's function fields twice: live, for a paste\n * in the same session, and as the scene's registry keys, for a snapshot that\n * crossed JSON on the way back.\n */\nimport type { ClipboardSnapshot, PasteCtx, PasteOmission } from 'core/adapters/types';\nimport type { Node, NodeId, Scene } from 'core/scene/types';\nimport { asNodeId } from 'core/scene/types';\nimport { hasChildren } from 'core/scene/treeShape';\nimport { sourcesOf } from 'core/scene/dependents';\nimport { definesFrame } from 'core/scene/effectivePose';\nimport {\n NODE_FN_FIELDS, createNodeFnFields, fnFieldsOfNode, fnKeysOf, type FnBearingNode,\n} from 'core/scene/nodeFnFields';\nimport { statedFields } from 'core/scene/statedFields';\nimport type { Bounds } from '../core/viewport/fitViewToBounds';\nimport type { InsertSite } from './deps/insertSite';\n\n/** What the seam needs from the adapter it serves. */\nexport interface SceneClipboardEnv<TData, TLayer extends string, TPose> {\n scene: Scene<TData, TLayer, TPose>;\n worldPoseOf(id: string): TPose;\n /** The nearest frame-defining container at or above `id`. */\n frameAtOrAbove(id: string | null): string | null;\n poseBounds(pose: TPose, node: unknown): Bounds;\n translate(pose: TPose, dx: number, dy: number, node?: unknown): TPose;\n /** Where the pasted cluster's roots land; absent, or naming no parent, they keep their layer at the top of the tree. */\n landing?: () => InsertSite;\n}\n\n// Spread-level copy per the clipboard contract: mutating a snapshot's\n// pose/data/children must not touch the scene.\nfunction copyField<T>(value: T): T {\n if (Array.isArray(value)) return [...value] as T;\n if (value !== null && typeof value === 'object') return { ...(value as object) } as T;\n return value;\n}\n\ntype Item<TData, TLayer extends string, TPose> =\n Node<TData, TLayer, TPose> & Record<string, unknown>;\n\nexport function snapshotSceneSelection<TData, TLayer extends string, TPose>(\n env: SceneClipboardEnv<TData, TLayer, TPose>,\n ids: string[],\n): ClipboardSnapshot {\n const { scene } = env;\n const fnFields = createNodeFnFields<TPose>(scene.registry as never);\n const idSet = new Set(ids);\n const taken = new Set<string>();\n const items: Node<TData, TLayer, TPose>[] = [];\n // A stored pose is relative to its frame, and a node whose frame is not\n // copied with it lands somewhere else entirely on paste. Capture those in\n // world coordinates: every root, and under an envelope root, the nodes\n // sharing the frame the root left behind.\n const poseFor = (n: Node<TData, TLayer, TPose>, isRoot: boolean): TPose => {\n if (isRoot) return env.worldPoseOf(n.id);\n const frame = env.frameAtOrAbove(n.parent);\n return frame !== null && taken.has(frame) ? n.pose : env.worldPoseOf(n.id);\n };\n\n // DFS from one snapshot root: push the node, then its subtree —\n // parents-before-children, so paste can re-insert in array order.\n const capture = (id: string, isRoot = false): void => {\n if (taken.has(id)) return;\n const n = scene.get(asNodeId(id));\n if (!n) return;\n taken.add(id);\n items.push({\n kind: n.kind,\n id: n.id,\n layer: n.layer,\n parent: n.parent,\n pose: copyField(poseFor(n, isRoot)),\n data: copyField(n.data),\n ...(n.children !== undefined ? { children: [...n.children] } : {}),\n ...statedFields(n),\n ...(n.dependsOn !== undefined ? { dependsOn: copyField(n.dependsOn) } : {}),\n ...fnFieldsOfNode(n),\n ...fnKeysOf(fnFields, n as unknown as FnBearingNode, 'jsonKey'),\n } as Node<TData, TLayer, TPose>);\n if (hasChildren(n)) {\n for (const cid of scene.childrenOf(asNodeId(id))) capture(cid);\n }\n };\n const covered = (id: string): boolean =>\n idSet.has(id) || scene.ancestorsOf(asNodeId(id)).some((a) => idSet.has(a));\n // A hidden node cannot be selected, so one that a copied node derives from\n // comes with it, ahead of it: without it the paste would leave the copy out.\n const carrying = new Set<string>();\n const carrySources = (id: string): void => {\n if (carrying.has(id)) return;\n carrying.add(id);\n const n = scene.get(asNodeId(id));\n if (!n) return;\n for (const source of sourcesOf(n)) {\n if (taken.has(source) || covered(source) || scene.get(source)?.hidden !== true) continue;\n carrySources(source);\n capture(source, true);\n }\n if (hasChildren(n)) for (const cid of scene.childrenOf(asNodeId(id))) carrySources(cid);\n };\n for (const id of ids) {\n // Dedupe: an id whose ancestor is also selected is already covered\n // by that ancestor's subtree walk.\n if (scene.ancestorsOf(asNodeId(id)).some((a) => idSet.has(a))) continue;\n carrySources(id);\n capture(id, true);\n }\n return { items };\n}\n\n// JSON drops a function, and turns a layout strategy into an object with no\n// methods on it: neither is something a pasted node can run.\nconst isLive = (value: unknown): boolean =>\n typeof value === 'function'\n || (typeof value === 'object' && value !== null\n && Object.values(value).some((member) => typeof member === 'function'));\n\n/**\n * The item's function fields: for each, what its registry key names in this\n * scene, else the live one it carries, else nothing.\n */\nfunction restoredFnFields<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n item: Item<TData, TLayer, TPose>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n const registry = scene.registry as Record<string, Record<string, unknown> | undefined>;\n for (const spec of NODE_FN_FIELDS) {\n const key = item[spec.jsonKey];\n const registered = typeof key === 'string' ? registry[spec.field]?.[key] : undefined;\n const fn = registered ?? (isLive(item[spec.field]) ? item[spec.field] : undefined);\n if (fn !== undefined) out[spec.field] = fn;\n }\n return out;\n}\n\n/**\n * Which items a paste leaves out, and why. A node deriving from an id list is\n * pasted only with everything it depends on, since a copy still naming the\n * originals would follow them rather than the copies beside it; and only with\n * a derivation to run, since without one it is a placeholder that paints\n * nothing. Leaving one out can strand another, so this runs to a fixed point.\n */\nfunction omissions<TData, TLayer extends string, TPose>(\n src: readonly Item<TData, TLayer, TPose>[],\n fns: ReadonlyMap<string, Record<string, unknown>>,\n): Map<string, PasteOmission> {\n const out = new Map<string, PasteOmission>();\n const present = new Set(src.map((item) => item.id as string));\n for (let changed = true; changed;) {\n changed = false;\n for (const item of src) {\n if (out.has(item.id)) continue;\n let omission: PasteOmission | undefined;\n const deps = sourcesOf(item);\n const missing = deps.filter((dep) => !present.has(dep));\n const fn = fns.get(item.id)!;\n if (item.parent !== null && out.has(item.parent)) {\n omission = { id: item.id, reason: 'container-not-pasted' };\n } else if (missing.length > 0) {\n omission = { id: item.id, reason: 'dependency-not-copied', missing };\n } else if (deps.length > 0 && fn.derivePath === undefined && fn.derivePose === undefined) {\n omission = { id: item.id, reason: 'derivation-not-registered' };\n }\n if (omission === undefined) continue;\n out.set(item.id, omission);\n present.delete(item.id);\n changed = true;\n }\n }\n return out;\n}\n\nexport function pasteSceneSnapshot<TData, TLayer extends string, TPose>(\n env: SceneClipboardEnv<TData, TLayer, TPose>,\n clipboard: ClipboardSnapshot,\n offset: { dx: number; dy: number },\n ctx?: PasteCtx,\n): Node<TData, TLayer, TPose>[] {\n const all = clipboard.items as Item<TData, TLayer, TPose>[];\n if (all.length === 0) return [];\n const fns = new Map(all.map((item) => [item.id as string, restoredFnFields(env.scene, item)]));\n const omitted = omissions(all, fns);\n if (ctx?.onOmit) for (const item of all) {\n const omission = omitted.get(item.id);\n if (omission) ctx.onOmit(omission);\n }\n const src = omitted.size === 0 ? all : all.filter((item) => !omitted.has(item.id));\n\n const idMap = new Map<string, NodeId>();\n for (const item of src) idMap.set(item.id, env.scene.mintId());\n // Items whose captured parent isn't part of the snapshot become roots\n // of the pasted cluster.\n const isRoot = (item: Node<TData, TLayer, TPose>): boolean =>\n item.parent === null || !idMap.has(item.parent);\n // A node inside a pasted frame rides it, and offsetting it as well would\n // move it twice.\n const byId = new Map(src.map((item) => [item.id as string, item]));\n const ridesPastedFrame = (item: Node<TData, TLayer, TPose>): boolean => {\n let p = item.parent === null ? undefined : byId.get(item.parent);\n while (p !== undefined) {\n if (definesFrame(p)) return true;\n p = p.parent === null ? undefined : byId.get(p.parent);\n }\n return false;\n };\n let dx = offset.dx;\n let dy = offset.dy;\n if (ctx?.dropPoint) {\n // Drop-point policy: land the cluster origin (min corner of the root\n // items' bounds) on the drop point; `offset` is ignored.\n let minX = Infinity;\n let minY = Infinity;\n for (const item of src) {\n if (!isRoot(item)) continue;\n const b = env.poseBounds(item.pose, item);\n if (b.x < minX) minX = b.x;\n if (b.y < minY) minY = b.y;\n }\n if (Number.isFinite(minX) && Number.isFinite(minY)) {\n dx = ctx.dropPoint.x - minX;\n dy = ctx.dropPoint.y - minY;\n }\n }\n const site = env.landing?.();\n const into = site?.parent != null ? site : null;\n const placed = (item: Item<TData, TLayer, TPose>): TPose => {\n if (ridesPastedFrame(item)) return copyField(item.pose);\n const moved = env.translate(copyField(item.pose), dx, dy, item);\n return into && isRoot(item) ? into.local(moved) as TPose : moved;\n };\n // `src` is parents-before-children and this keeps it, so children re-attach\n // to freshly inserted parents when the caller's InsertOps apply in order.\n return src.map((item) => ({\n id: idMap.get(item.id)!,\n kind: item.kind,\n layer: into && isRoot(item) ? into.layer as TLayer : item.layer,\n parent: isRoot(item) ? into?.parent ?? null : idMap.get(item.parent as string)!,\n pose: placed(item),\n data: copyField(item.data),\n ...statedFields(item),\n ...(item.children !== undefined\n ? { children: item.children.filter((c) => idMap.has(c)).map((c) => idMap.get(c)!) }\n : {}),\n ...(item.dependsOn === 'children'\n ? { dependsOn: 'children' as const }\n : sourcesOf(item).length > 0\n ? { dependsOn: sourcesOf(item).map((dep) => idMap.get(dep)!) }\n : {}),\n ...fns.get(item.id),\n } as Node<TData, TLayer, TPose>));\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { nodeAsShown, shownLookup } from "../core/scene/nodeAsShown.js";
2
2
  import { withInheritedPaintBounds, withInheritedPaints } from "./inheritedPaints.js";
3
3
  import { paintedSceneLayers } from "./sceneLayerPaint.js";
4
- import { paintCanvas } from "./canvasRenderer.js";
4
+ import { paintCanvas, resolveDpr } from "./canvasRenderer.js";
5
5
  import { wrapNodeOutput } from "./wrapNodeOutput.js";
6
6
  import { sceneDerivedPathOf, withDerivedPaths } from "./derivedPath.js";
7
7
  import { buildSceneTree } from "./buildSceneTree.js";
@@ -19,7 +19,7 @@ import { viewToMat3 as viewToMat3$1 } from "@weasel-js/geom";
19
19
  * adapter's own reads, under this view's layer list.
20
20
  */
21
21
  function sceneAsHierarchy(scene, layers = {}) {
22
- const { getNode, getChildren, getPose, composePose } = sceneToAdapter(scene);
22
+ const { getNode, getChildren, getPose, composePose, childFrame } = sceneToAdapter(scene);
23
23
  return {
24
24
  getLayers: () => paintedSceneLayers(scene.layers, layers).map((l) => ({
25
25
  id: l.id,
@@ -29,7 +29,8 @@ function sceneAsHierarchy(scene, layers = {}) {
29
29
  getNode,
30
30
  getChildren,
31
31
  getPose,
32
- ...composePose ? { composePose } : {}
32
+ ...composePose ? { composePose } : {},
33
+ ...childFrame ? { childFrame } : {}
33
34
  };
34
35
  }
35
36
  /**
@@ -64,7 +65,7 @@ function buildSceneViewCommands(scene, view, drawOne, extraCommands, alphaFor, l
64
65
  } : void 0;
65
66
  const culled = cullRect && cull?.paintBounds ? paintMissesView(withShownNode(scene.overrides, withInheritedPaintBounds(shown, cull.paintBounds)), view, cullRect) : void 0;
66
67
  const tree = buildSceneTree(hierarchy, wrappedDrawOne, view, void 0, derivedPathOf, culled);
67
- const children = cullRect ? cullDrawCommands(tree, viewToMat3$1(view), cullRect) : tree;
68
+ const children = cull && cullRect ? cullDrawCommands(tree, viewToMat3$1(view), cullRect, cull.dpr) : tree;
68
69
  if (extraCommands && extraCommands.length > 0) for (const cmd of extraCommands) children.push(cmd);
69
70
  return [{
70
71
  kind: "group",
@@ -78,13 +79,14 @@ function buildSceneViewCommands(scene, view, drawOne, extraCommands, alphaFor, l
78
79
  * renderer lease.
79
80
  */
80
81
  function sceneViewFrame(args) {
81
- const { scene, view, width, height, drawOne, extraCommands, alphaFor, layerVisibility, layerOrder, colorOverrides, cull } = args;
82
+ const { scene, view, width, height, drawOne, extraCommands, alphaFor, layerVisibility, layerOrder, colorOverrides, cull, dpr } = args;
82
83
  return buildSceneViewCommands(scene, view, drawOne, extraCommands, alphaFor, {
83
84
  layerVisibility,
84
85
  layerOrder
85
86
  }, colorOverrides, cull ? {
86
87
  width,
87
88
  height,
89
+ dpr: resolveDpr(dpr),
88
90
  paintBounds: args.paintBounds ?? paintBoundsFor(drawOne)
89
91
  } : void 0);
90
92
  }