@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":"affordanceAt.js","names":[],"sources":["../../src/canvas/affordanceAt.ts"],"sourcesContent":["/**\n * The dispatcher-side affordance classifier: answers \"what piece of selection\n * chrome is under this world point?\" by assembling the kit's affordances and\n * running the one shared walk (`hitAffordanceRegions`).\n */\n\nimport type { BodyClassification } from '@weasel-js/gestures';\nimport type { ChromeState } from 'core/selection/chromeState';\nimport type { EditAnchorsDep } from 'interactions/actions/depSchema';\nimport type { View } from 'core/viewport/view';\nimport type { AffordanceHit } from '@weasel-js/routing';\nimport type { Affordance, CommonAffordanceScratch, LayerHit } from 'affordances/types';\nimport { hitAffordanceRegions, type AffordanceRegionHit } from 'affordances/hitAffordanceRegions';\nimport { createCornerResizeAffordance } from 'affordances/cornerResize';\nimport { createRotationAffordance } from 'affordances/rotationHandle';\nimport { createPathAnchorAffordances, type AnchorState } from 'affordances/pathAnchors';\nimport { targetSizesPx } from 'core/device/targets';\n\nexport type { AnchorState };\n\n/** What decides the kit's chrome affordances — their sizes and which ones\n * exist. Shared by the hit-test and the slops overlay, which draws them. */\nexport interface ChromeAffordanceOptions {\n /** Pointer-size multiplier from the live `DeviceProfile`. Every default\n * below is resolved through {@link targetSizesPx} at this scale, so the\n * grab zone tracks the painted chrome on a coarse pointer. Default 1. */\n targetScale?: number;\n /** Corner-handle hit radius in screen px. Overrides the device-scaled default. */\n handleHitRadius?: number;\n /** Anchor / control hit radius in screen px. Overrides the device-scaled default. */\n anchorHitRadius?: number;\n /** Minimum rotate-band thickness outside the selection AABB, in screen px.\n * Overrides the device-scaled default. */\n rotateBandPx?: number;\n /** The rotate badge the selection overlay paints (`rotationBadgeOf`), so\n * it can be grabbed where it is drawn. Omitted or null, only the rotate\n * ring is hit-tested. */\n rotationBadge?: { distancePx: number; sizePx: number } | null;\n /** Resize handles at the edge midpoints as well as the corners. Default true. */\n edgeHandles?: boolean;\n /** Anchor-editing state. When omitted, anchors aren't hit-tested. */\n getAnchorState?: () => AnchorState | null;\n}\n\nexport interface BuildAffordanceAtOptions extends ChromeAffordanceOptions {\n /** Live ChromeState at call time — current selection plus effective bounds,\n * including in-flight move/resize ghost poses. */\n getChromeState: () => ChromeState;\n /** Live view. Needed because region hit radii and the rotate band are\n * declared in screen pixels and resolved against the current scale. */\n getView: () => View;\n /** The affordances to walk, when the caller built them itself to share\n * them (`chromeAffordances`). Otherwise built from these options. */\n affordances?: readonly Affordance[];\n /** Chrome-caps resolver. Keeps the hit-test and the renderer agreeing on\n * which chrome is live (resize handles gated by\n * `'selection.resize-handles'`, rotation by `'selection.rotation-handle'`,\n * anchors by `'path-edit.anchors'`). Omitting defaults to always-visible. */\n getIsVisible?: () => (id: string) => boolean;\n}\n\n/**\n * The kit's selection chrome as affordances, bottom → top: rotate ring (and\n * badge), resize handles, then anchors and their controls — a corner handle\n * beats the rotate band it sits inside, and a control handle beats everything.\n */\nexport function chromeAffordances(opts: ChromeAffordanceOptions = {}): Affordance[] {\n const sizes = targetSizesPx(opts.targetScale);\n const {\n handleHitRadius = sizes.handle,\n anchorHitRadius = sizes.anchor,\n rotateBandPx = sizes.rotationDistance,\n getAnchorState,\n rotationBadge,\n edgeHandles = true,\n } = opts;\n return [\n createRotationAffordance({\n bandPx: rotateBandPx,\n paint: null,\n ...(rotationBadge\n ? { handle: { distancePx: rotationBadge.distancePx, hitRadiusPx: rotationBadge.sizePx } }\n : {}),\n }),\n createCornerResizeAffordance({ handleHitRadius, edges: edgeHandles }),\n ...(getAnchorState\n ? createPathAnchorAffordances(getAnchorState, { hitRadius: anchorHitRadius })\n : []),\n ];\n}\n\n/**\n * Read anchor-editing state off a dep registry, for\n * {@link ChromeAffordanceOptions.getAnchorState}. Returns `null` while no\n * `editAnchors` dep is registered, so anchors are not hit-tested.\n */\nexport function anchorStateFrom(\n getRegistry: () => { get(name: 'editAnchors'): EditAnchorsDep | undefined } | null,\n /** The editable path in the world the hit-test is asked in. A node on a\n * parallax plane stores its path in the plane's world; the surface carries\n * it into the asking camera's. Default: unchanged. */\n toCamera?: (id: string, path: unknown) => unknown,\n): () => AnchorState | null {\n return () => {\n const dep = getRegistry()?.get('editAnchors');\n if (!dep) return null;\n return {\n editingId: dep.editingId ?? null,\n tool: dep.tool ?? null,\n // The hit-test reads world-coord anchor positions, so route through\n // `getEditablePath`: both pose-as-polygon and `data.path` consumers\n // resolve correctly.\n getPose: (id) => (toCamera ? toCamera(id, dep.getEditablePath(id)) : dep.getEditablePath(id)),\n getNode: (id) => dep.getNodeShape(id),\n ...(dep.extensions ? { extensions: dep.extensions } : {}),\n getOutline: (id) => {\n const path = dep.getOutlinePath?.(id) ?? dep.getEditablePath(id);\n return toCamera ? toCamera(id, path) : path;\n },\n };\n };\n}\n\n/**\n * Build the `affordanceAt` thunk for `useSceneGestureDispatcher`. The point is\n * **world-space**; callers convert from client coords first. Priority follows\n * the order of {@link chromeAffordances}, topmost first.\n */\nexport function buildAffordanceAt(\n opts: BuildAffordanceAtOptions,\n): (worldPoint: { x: number; y: number }) => AffordanceHit | null {\n const { getChromeState, getView, getIsVisible } = opts;\n const affordances = opts.affordances ?? chromeAffordances(opts);\n\n return function affordanceAt({ x: wx, y: wy }) {\n const hit = hitAffordanceRegions(\n affordances,\n wx,\n wy,\n getChromeState(),\n getView(),\n getIsVisible?.(),\n );\n return hit ? toAffordanceHit(hit) : null;\n };\n}\n\n/**\n * Region hit → `AffordanceHit`. The scratch fields named by\n * {@link CommonAffordanceScratch} are lifted onto the hit because the actions\n * that consume them read them there; the whole scratch rides along as `payload`.\n */\nfunction toAffordanceHit(hit: AffordanceRegionHit): AffordanceHit {\n const scratch = (hit.binding.initialScratch ?? {}) as CommonAffordanceScratch;\n return {\n kind: hit.region.hitKind ?? `${hit.affordanceId}:${hit.regionId}`,\n owner: hit.affordanceId,\n strength: 'shared',\n ...(scratch.targetId !== undefined ? { targetIds: [scratch.targetId] } : {}),\n ...(scratch.anchor !== undefined ? { anchor: scratch.anchor } : {}),\n ...(scratch.fixedPoint !== undefined ? { fixedPoint: scratch.fixedPoint } : {}),\n ...(hit.cursor !== undefined ? { cursor: hit.cursor } : {}),\n ...(hit.binding.initialScratch !== undefined ? { payload: hit.binding.initialScratch } : {}),\n } as AffordanceHit;\n}\n\n/**\n * Classify a world-space point against the current selection: `'empty'`,\n * `'selected-body'`, or `'unselected-body'`.\n *\n * @param kindOfNode - Resolver from a hit node id to its semantic kind.\n * Omitting it leaves `kind` undefined, which makes every `kind:` TargetSpec\n * form no-match.\n * @param doubleClickNames - The node a double-click on a hit names\n * (`doubleClickTarget`). Where that is another node, it rides along as\n * `doubleClick`, classified the same way.\n */\nexport function buildClassifyTarget(\n getSelection: () => readonly string[],\n pickBest: (wx: number, wy: number) => string | null,\n kindOfNode?: (id: string) => string | undefined,\n doubleClickNames?: (hitId: string) => string,\n): (worldPoint: { x: number; y: number }) => BodyClassification {\n return function classifyTarget({ x: wx, y: wy }) {\n const hitId = pickBest(wx, wy);\n if (!hitId) return { body: 'empty' };\n const sel = getSelection();\n const classify = (id: string) => ({\n body: sel.includes(id) ? 'selected-body' as const : 'unselected-body' as const,\n kind: kindOfNode?.(id),\n id,\n });\n const named = doubleClickNames?.(hitId);\n return named !== undefined && named !== hitId\n ? { ...classify(hitId), doubleClick: classify(named) }\n : classify(hitId);\n };\n}\n\n/** A registered layer's claim on a point, as the affordance the dispatcher\n * routes: its kind names the layer, and it carries what the layer's hit-test\n * resolved. */\nexport function layerAffordance(layerId: string, claim: LayerHit): AffordanceHit {\n return {\n kind: `layer:${layerId}`,\n owner: layerId,\n strength: claim.strength ?? 'shared',\n ...(claim.claimedKinds !== undefined ? { claimedKinds: claim.claimedKinds } : {}),\n ...(claim.cursor !== undefined ? { cursor: claim.cursor } : {}),\n ...(claim.initialScratch !== undefined ? { payload: claim.initialScratch } : {}),\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"affordanceAt.js","names":[],"sources":["../../src/canvas/affordanceAt.ts"],"sourcesContent":["/**\n * The dispatcher-side affordance classifier: answers \"what piece of selection\n * chrome is under this world point?\" by assembling the kit's affordances and\n * running the one shared walk (`hitAffordanceRegions`).\n */\n\nimport type { BodyClassification } from '@weasel-js/gestures';\nimport type { ChromeState } from 'core/selection/chromeState';\nimport type { EditAnchorsDep } from 'interactions/actions/depSchema';\nimport type { View } from 'core/viewport/view';\nimport type { AffordanceHit, WorldPoint } from '@weasel-js/routing';\nimport type { Affordance, CommonAffordanceScratch, LayerHit } from 'affordances/types';\nimport { hitAffordanceRegions, type AffordanceRegionHit } from 'affordances/hitAffordanceRegions';\nimport { createCornerResizeAffordance } from 'affordances/cornerResize';\nimport { createRotationAffordance } from 'affordances/rotationHandle';\nimport { createPathAnchorAffordances, type AnchorState } from 'affordances/pathAnchors';\nimport { targetSizesPx } from 'core/device/targets';\n\nexport type { AnchorState };\n\n/** What decides the kit's chrome affordances — their sizes and which ones\n * exist. Shared by the hit-test and the slops overlay, which draws them. */\nexport interface ChromeAffordanceOptions {\n /** Pointer-size multiplier from the live `DeviceProfile`. Every default\n * below is resolved through {@link targetSizesPx} at this scale, so the\n * grab zone tracks the painted chrome on a coarse pointer. Default 1. */\n targetScale?: number;\n /** Corner-handle hit radius in screen px. Overrides the device-scaled default. */\n handleHitRadius?: number;\n /** Anchor / control hit radius in screen px. Overrides the device-scaled default. */\n anchorHitRadius?: number;\n /** Minimum rotate-band thickness outside the selection AABB, in screen px.\n * Overrides the device-scaled default. */\n rotateBandPx?: number;\n /** The rotate badge the selection overlay paints (`rotationBadgeOf`), so\n * it can be grabbed where it is drawn. Omitted or null, only the rotate\n * ring is hit-tested. */\n rotationBadge?: { distancePx: number; sizePx: number } | null;\n /** Resize handles at the edge midpoints as well as the corners. Default true. */\n edgeHandles?: boolean;\n /** Anchor-editing state. When omitted, anchors aren't hit-tested. */\n getAnchorState?: () => AnchorState | null;\n}\n\nexport interface BuildAffordanceAtOptions extends ChromeAffordanceOptions {\n /** Live ChromeState at call time — current selection plus effective bounds,\n * including in-flight move/resize ghost poses. */\n getChromeState: () => ChromeState;\n /** Live view. Needed because region hit radii and the rotate band are\n * declared in screen pixels and resolved against the current scale. */\n getView: () => View;\n /** The affordances to walk, when the caller built them itself to share\n * them (`chromeAffordances`). Otherwise built from these options. */\n affordances?: readonly Affordance[];\n /** Chrome-caps resolver. Keeps the hit-test and the renderer agreeing on\n * which chrome is live (resize handles gated by\n * `'selection.resize-handles'`, rotation by `'selection.rotation-handle'`,\n * anchors by `'path-edit.anchors'`). Omitting defaults to always-visible. */\n getIsVisible?: () => (id: string) => boolean;\n}\n\n/**\n * The kit's selection chrome as affordances, bottom → top: rotate ring (and\n * badge), resize handles, then anchors and their controls — a corner handle\n * beats the rotate band it sits inside, and a control handle beats everything.\n */\nexport function chromeAffordances(opts: ChromeAffordanceOptions = {}): Affordance[] {\n const sizes = targetSizesPx(opts.targetScale);\n const {\n handleHitRadius = sizes.handle,\n anchorHitRadius = sizes.anchor,\n rotateBandPx = sizes.rotationDistance,\n getAnchorState,\n rotationBadge,\n edgeHandles = true,\n } = opts;\n return [\n createRotationAffordance({\n bandPx: rotateBandPx,\n paint: null,\n ...(rotationBadge\n ? { handle: { distancePx: rotationBadge.distancePx, hitRadiusPx: rotationBadge.sizePx } }\n : {}),\n }),\n createCornerResizeAffordance({ handleHitRadius, edges: edgeHandles }),\n ...(getAnchorState\n ? createPathAnchorAffordances(getAnchorState, { hitRadius: anchorHitRadius })\n : []),\n ];\n}\n\n/**\n * Read anchor-editing state off a dep registry, for\n * {@link ChromeAffordanceOptions.getAnchorState}. Returns `null` while no\n * `editAnchors` dep is registered, so anchors are not hit-tested.\n */\nexport function anchorStateFrom(\n getRegistry: () => { get(name: 'editAnchors'): EditAnchorsDep | undefined } | null,\n /** The editable path in the world the hit-test is asked in. A node on a\n * parallax plane stores its path in the plane's world; the surface carries\n * it into the asking camera's. Default: unchanged. */\n toCamera?: (id: string, path: unknown) => unknown,\n): () => AnchorState | null {\n return () => {\n const dep = getRegistry()?.get('editAnchors');\n if (!dep) return null;\n return {\n editingId: dep.editingId ?? null,\n tool: dep.tool ?? null,\n // The hit-test reads world-coord anchor positions, so route through\n // `getEditablePath`: both pose-as-polygon and `data.path` consumers\n // resolve correctly.\n getPose: (id) => (toCamera ? toCamera(id, dep.getEditablePath(id)) : dep.getEditablePath(id)),\n getNode: (id) => dep.getNodeShape(id),\n ...(dep.extensions ? { extensions: dep.extensions } : {}),\n getOutline: (id) => {\n const path = dep.getOutlinePath?.(id) ?? dep.getEditablePath(id);\n return toCamera ? toCamera(id, path) : path;\n },\n };\n };\n}\n\n/**\n * Build the `affordanceAt` thunk for `useSceneGestureDispatcher`. The point is\n * **world-space**; callers convert from client coords first. Priority follows\n * the order of {@link chromeAffordances}, topmost first.\n */\nexport function buildAffordanceAt(\n opts: BuildAffordanceAtOptions,\n): (worldPoint: WorldPoint) => AffordanceHit | null {\n const { getChromeState, getView, getIsVisible } = opts;\n const affordances = opts.affordances ?? chromeAffordances(opts);\n\n return function affordanceAt({ x: wx, y: wy }) {\n const hit = hitAffordanceRegions(\n affordances,\n wx,\n wy,\n getChromeState(),\n getView(),\n getIsVisible?.(),\n );\n return hit ? toAffordanceHit(hit) : null;\n };\n}\n\n/**\n * Region hit → `AffordanceHit`. The scratch fields named by\n * {@link CommonAffordanceScratch} are lifted onto the hit because the actions\n * that consume them read them there; the whole scratch rides along as `payload`.\n */\nfunction toAffordanceHit(hit: AffordanceRegionHit): AffordanceHit {\n const scratch = (hit.binding.initialScratch ?? {}) as CommonAffordanceScratch;\n return {\n kind: hit.region.hitKind ?? `${hit.affordanceId}:${hit.regionId}`,\n owner: hit.affordanceId,\n strength: 'shared',\n ...(scratch.targetId !== undefined ? { targetIds: [scratch.targetId] } : {}),\n ...(scratch.anchor !== undefined ? { anchor: scratch.anchor } : {}),\n ...(scratch.fixedPoint !== undefined ? { fixedPoint: scratch.fixedPoint } : {}),\n ...(hit.cursor !== undefined ? { cursor: hit.cursor } : {}),\n ...(hit.binding.initialScratch !== undefined ? { payload: hit.binding.initialScratch } : {}),\n } as AffordanceHit;\n}\n\n/**\n * Classify a world-space point against the current selection: `'empty'`,\n * `'selected-body'`, or `'unselected-body'`.\n *\n * @param kindOfNode - Resolver from a hit node id to its semantic kind.\n * Omitting it leaves `kind` undefined, which makes every `kind:` TargetSpec\n * form no-match.\n * @param doubleClickNames - The node a double-click on a hit names\n * (`doubleClickTarget`). Where that is another node, it rides along as\n * `doubleClick`, classified the same way.\n */\nexport function buildClassifyTarget(\n getSelection: () => readonly string[],\n pickBest: (world: WorldPoint) => string | null,\n kindOfNode?: (id: string) => string | undefined,\n doubleClickNames?: (hitId: string) => string,\n): (worldPoint: WorldPoint) => BodyClassification {\n return function classifyTarget(world) {\n const hitId = pickBest(world);\n if (!hitId) return { body: 'empty' };\n const sel = getSelection();\n const classify = (id: string) => ({\n body: sel.includes(id) ? 'selected-body' as const : 'unselected-body' as const,\n kind: kindOfNode?.(id),\n id,\n });\n const named = doubleClickNames?.(hitId);\n return named !== undefined && named !== hitId\n ? { ...classify(hitId), doubleClick: classify(named) }\n : classify(hitId);\n };\n}\n\n/** A registered layer's claim on a point, as the affordance the dispatcher\n * routes: its kind names the layer, and it carries what the layer's hit-test\n * resolved. */\nexport function layerAffordance(layerId: string, claim: LayerHit): AffordanceHit {\n return {\n kind: `layer:${layerId}`,\n owner: layerId,\n strength: claim.strength ?? 'shared',\n ...(claim.claimedKinds !== undefined ? { claimedKinds: claim.claimedKinds } : {}),\n ...(claim.cursor !== undefined ? { cursor: claim.cursor } : {}),\n ...(claim.initialScratch !== undefined ? { payload: claim.initialScratch } : {}),\n };\n}\n"],"mappings":"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"}
@@ -44,7 +44,7 @@ function buildSceneLayer(cfg, adapter, boundsOfFn, hideIds, slot) {
44
44
  y: 0,
45
45
  width: dims.width,
46
46
  height: dims.height
47
- }) : (cmds) => cmds;
47
+ }, dims.dpr) : (cmds) => cmds;
48
48
  const paintBounds = cfg.cull ? cfg.paintBounds : void 0;
49
49
  const culledIn = (view, dims) => paintBounds ? paintMissesView(paintBounds, view, {
50
50
  x: 0,
@@ -1 +1 @@
1
- {"version":3,"file":"buildSceneLayer.js","names":[],"sources":["../../src/canvas/buildSceneLayer.ts"],"sourcesContent":["/**\n * The scene slot: an adapter's tree as render layers, one per scene layer.\n */\nimport type { Path } from '@weasel-js/geom';\nimport type { Dims, RenderLayer } from 'core/layers/render';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport type { View } from 'core/viewport/view';\nimport { cullDrawCommands, viewToMat3, type DrawCommand } from '@weasel-js/renderer';\nimport { adapterDerivedPathOf } from './adapterDerivedPath';\nimport { buildSceneTree, type HierarchicalAdapter } from './buildSceneTree';\nimport { debugSinkFrom } from './drawEnvelope';\nimport type { NodePaintCtx } from './NodeShape';\nimport { paintMissesView, type PaintBoundsFn } from './paintCull';\nimport { sceneLayerKey } from './sceneLayerPaint';\nimport { wrapNodeOutput } from './wrapNodeOutput';\n\n/** Scene slot config — describes how to draw one object with its effective pose. */\nexport interface SceneSlotConfig<TNode extends { id: string }, TPose> {\n /** Paint these, in this order, in place of the adapter's tree: one flat run with no\n * scene layers, no container clips, and no pose composition. */\n objects?: TNode[];\n /** The pose an object is painted at. Defaults to `adapter.getPose(obj.id)`. `<SceneCanvas>`\n * hands it each node as the scene shows it, its `pose` an override's or a derived one,\n * and carries an override's `path` into the box it answers; return a pose placed from\n * `obj.pose`, or a drag in flight and a rig's motion do not show in this view. */\n toPose?: (obj: TNode) => TPose;\n /** Draw a single object as a `DrawCommand` tree. `ctx` carries a derived\n * node's path: `<SceneCanvas>` resolves it from the scene, and\n * `buildSceneLayer` called without one from its adapter. */\n drawOne: (obj: TNode, pose: TPose, view: View, ctx?: NodePaintCtx) => DrawCommand[];\n /** The path a container derives from its dependencies' poses, for the clip\n * it imposes on its subtree — `drawOne`'s `ctx` carries the derived path a\n * node *paints*, this one the clip it *imposes*. A scene-aware caller\n * supplies it; left out, both are resolved off the adapter. */\n derivedPathOf?: (obj: TNode, pose: TPose) => Path | null;\n /** Default ghost alpha for the move-overlay slot. Default 0.85. */\n ghostAlpha?: number;\n /** Per-id alpha multiplier. A node's emitted `DrawCommand[]` is wrapped in a\n * `{ kind: 'group', alpha }` when the returned value is not 1. */\n alphaFor?: (id: string) => number;\n /**\n * Post-processor for the scene slot's emitted commands: called with the final\n * world-space `DrawCommand[]` each time a scene canvas layer draws (after per-node\n * rotation wrapping and `alphaFor`), once per layer when the slot is split into\n * `scene:<layerId>` layers. Its return value replaces the array handed to the renderer.\n *\n * Commands are world-space — `drawLayers` applies the view transform afterward. Clip\n * nesting is capped at 7 levels; a wrapping group with `clip` consumes one. The\n * move-overlay ghost (drag preview) is not post-processed.\n */\n postProcess?: (cmds: DrawCommand[], view: View, dims: Dims) => DrawCommand[];\n /**\n * Skip what cannot reach the view. A node `paintBounds` places outside the\n * `dims` rectangle under `view` is not painted at all; then, after\n * `postProcess`, `cullDrawCommands` drops any remaining command that cannot\n * reach it — see there for what it can and cannot bound. So `postProcess`\n * sees an empty group where a culled node would have painted.\n *\n * Off by default because it makes the layer's world-space output depend on\n * the view: anything that draws this layer once and shows the result under\n * another view — a cache keyed without `view`, a consumer re-wrapping the\n * commands — would show the first view's cull.\n */\n cull?: boolean;\n /**\n * Where `drawOne`'s output for a node can reach, in the frame it paints in\n * — before the pose rotation the slot wraps it in. `null` for a node it\n * cannot bound cheaply. Read only under `cull`, which skips `drawOne` for a\n * node whose box misses the view, so it must enclose everything `drawOne`\n * paints: a box too small makes a node vanish before it leaves the screen.\n *\n * `<SceneCanvas>` supplies `defaultPaintBounds` while `drawOne` is\n * `defaultDrawOne`. Omit it with any other `drawOne` and culling only\n * trims commands after painting.\n */\n paintBounds?: PaintBoundsFn<TNode, TPose>;\n}\n\n/**\n * `cfg`'s `drawOne` and clip lookup, resolving derived paths off the adapter\n * when nothing scene-aware filled `derivedPathOf` in.\n */\nfunction withAdapterDerivedPaths<TNode extends { id: string }, TPose>(\n cfg: SceneSlotConfig<TNode, TPose>,\n adapter: HierarchicalAdapter<TNode, TPose> | undefined,\n toPose: (obj: TNode) => TPose,\n): Pick<SceneSlotConfig<TNode, TPose>, 'drawOne' | 'derivedPathOf'> {\n const drawOne = cfg.drawOne;\n if (cfg.derivedPathOf !== undefined || !drawOne || !adapter) return cfg;\n const pathOf = adapterDerivedPathOf<TPose>(cfg.toPose === undefined ? adapter : {\n getNode: (id) => adapter.getNode(id),\n getPose: (id) => {\n const obj = adapter.getNode(id);\n return obj ? toPose(obj) : adapter.getPose(id);\n },\n getChildren: (id) => adapter.getChildren(id),\n });\n return {\n derivedPathOf: (obj) => pathOf(obj.id),\n drawOne: (obj, pose, view, ctx) => ((obj as { dependsOn?: unknown }).dependsOn === undefined\n ? drawOne(obj, pose, view, ctx)\n : drawOne(obj, pose, view, { ...ctx, derivedPath: pathOf(obj.id) })),\n };\n}\n\n/**\n * Build the scene layer, which draws committed poses. `hideIds()` skips the committed paint\n * of ids the active tool is currently ghosting so the source doesn't show through. With a\n * debug sink on its draw envelope, it records each node's bounds and origin there.\n */\nexport function buildSceneLayer<TNode extends { id: string }, TPose>(\n cfg: SceneSlotConfig<TNode, TPose>,\n adapter: HierarchicalAdapter<TNode, TPose> | undefined,\n boundsOfFn: ((id: string) => Bounds | null) | undefined,\n hideIds: () => Set<string> | null,\n /** When set, the returned layer carries this `id` and paints ONLY the named\n * scene layer (the rest are walked for ancestor clips). Omit for the single\n * bundled `scene` layer. */\n slot?: { id: string; forLayer: string },\n): RenderLayer<unknown> {\n const toPose =\n cfg.toPose ??\n ((obj: TNode) => (adapter ? adapter.getPose(obj.id) : (obj as unknown as TPose)));\n const { drawOne, derivedPathOf } = withAdapterDerivedPaths(cfg, adapter, toPose);\n const postProcess = cfg.postProcess;\n const finish = cfg.cull\n ? (cmds: DrawCommand[], view: View, dims: Dims): DrawCommand[] =>\n cullDrawCommands(cmds, viewToMat3(view), { x: 0, y: 0, width: dims.width, height: dims.height })\n : (cmds: DrawCommand[]): DrawCommand[] => cmds;\n const paintBounds = cfg.cull ? cfg.paintBounds : undefined;\n const culledIn = (view: View, dims: Dims) => paintBounds\n ? paintMissesView(paintBounds, view, { x: 0, y: 0, width: dims.width, height: dims.height })\n : undefined;\n return {\n id: slot?.id ?? 'scene',\n label: slot ? `Scene: ${slot.forLayer}` : 'Scene',\n draw: (data, view, dims) => {\n const debugSink = debugSinkFrom(data);\n const hidden = hideIds();\n const a = adapter;\n if (a && cfg.objects === undefined && drawOne) {\n const filteredDrawOne = (obj: TNode, pose: TPose, v: View): DrawCommand[] => {\n if (hidden && hidden.has(obj.id)) return [];\n const cmds = drawOne(obj, pose, v);\n if (debugSink) {\n const b = boundsOfFn ? boundsOfFn(obj.id) : null;\n if (b) debugSink.recordBounds(obj.id, b);\n const ox = (pose as { x?: number }).x ?? (b ? b.x : 0);\n const oy = (pose as { y?: number }).y ?? (b ? b.y : 0);\n debugSink.recordOrigin(obj.id, { x: ox, y: oy });\n }\n return wrapNodeOutput(cmds, pose, cfg.alphaFor ? cfg.alphaFor(obj.id) : 1);\n };\n // Honor cfg.toPose on the hierarchical path by shimming getPose on the\n // adapter so buildSceneTree routes through it instead of the raw adapter.\n const hierarchicalAdapter = cfg.toPose\n ? {\n ...a,\n getPose: (id: string) => {\n const obj = a.getNode(id);\n return obj ? toPose(obj) : a.getPose(id);\n },\n }\n : a;\n // World-space commands; drawLayers wraps in viewToMat3 automatically.\n const tree = buildSceneTree(\n hierarchicalAdapter as unknown as Parameters<typeof buildSceneTree>[0],\n filteredDrawOne as unknown as Parameters<typeof buildSceneTree>[1],\n view,\n slot?.forLayer,\n derivedPathOf as Parameters<typeof buildSceneTree>[4],\n culledIn(view, dims) as Parameters<typeof buildSceneTree>[5],\n );\n return finish(postProcess ? postProcess(tree, view, dims) : tree, view, dims);\n }\n // `cfg.objects`: the caller's own list, which has no scene layers to split by.\n const objects = cfg.objects ?? [];\n const children: DrawCommand[] = [];\n const culled = culledIn(view, dims);\n for (const obj of objects) {\n if (hidden && hidden.has(obj.id)) continue;\n const pose: TPose = toPose(obj);\n if (drawOne && !culled?.(obj, pose)) {\n const cmds = drawOne(obj, pose, view);\n const wrapped = wrapNodeOutput(cmds, pose, cfg.alphaFor ? cfg.alphaFor(obj.id) : 1);\n for (const cmd of wrapped) children.push(cmd);\n }\n if (debugSink) {\n const b = boundsOfFn ? boundsOfFn(obj.id) : null;\n if (b) debugSink.recordBounds(obj.id, { x: b.x, y: b.y, width: b.width, height: b.height });\n const ox = (pose as { x?: number }).x ?? (b ? b.x : 0);\n const oy = (pose as { y?: number }).y ?? (b ? b.y : 0);\n debugSink.recordOrigin(obj.id, { x: ox, y: oy });\n }\n }\n // World-space commands; drawLayers wraps in viewToMat3 automatically.\n return finish(postProcess ? postProcess(children, view, dims) : children, view, dims);\n },\n };\n}\n\n/**\n * The scene slot as one canvas layer **per scene layer** — keyed `scene:<layerId>` — so\n * consumers can interleave custom layers *between* scene layers via the standard slot\n * anchoring (`before: 'scene:plantings'`). A single `scene` layer when `cfg.objects` replaces\n * the tree, or the adapter declares no layers.\n */\nexport function buildSceneLayers<TNode extends { id: string }, TPose>(\n cfg: SceneSlotConfig<TNode, TPose>,\n adapter: HierarchicalAdapter<TNode, TPose> | undefined,\n boundsOfFn: ((id: string) => Bounds | null) | undefined,\n hideIds: () => Set<string> | null,\n): Array<{ key: string; layer: RenderLayer<unknown> }> {\n const sceneLayerIds =\n cfg.objects === undefined && adapter ? adapter.getLayers().map((l) => l.id) : null;\n if (!sceneLayerIds || sceneLayerIds.length === 0) {\n return [{ key: 'scene', layer: buildSceneLayer(cfg, adapter, boundsOfFn, hideIds) }];\n }\n return sceneLayerIds.map((id) => ({\n key: sceneLayerKey(id),\n layer: buildSceneLayer(cfg, adapter, boundsOfFn, hideIds, {\n id: sceneLayerKey(id),\n forLayer: id,\n }),\n }));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"buildSceneLayer.js","names":[],"sources":["../../src/canvas/buildSceneLayer.ts"],"sourcesContent":["/**\n * The scene slot: an adapter's tree as render layers, one per scene layer.\n */\nimport type { Path } from '@weasel-js/geom';\nimport type { Dims, LayerDims, RenderLayer } from 'core/layers/render';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport type { View } from 'core/viewport/view';\nimport { cullDrawCommands, viewToMat3, type DrawCommand } from '@weasel-js/renderer';\nimport { adapterDerivedPathOf } from './adapterDerivedPath';\nimport { buildSceneTree, type HierarchicalAdapter } from './buildSceneTree';\nimport { debugSinkFrom } from './drawEnvelope';\nimport type { NodePaintCtx } from './NodeShape';\nimport { paintMissesView, type PaintBoundsFn } from './paintCull';\nimport { sceneLayerKey } from './sceneLayerPaint';\nimport { wrapNodeOutput } from './wrapNodeOutput';\n\n/** Scene slot config — describes how to draw one object with its effective pose. */\nexport interface SceneSlotConfig<TNode extends { id: string }, TPose> {\n /** Paint these, in this order, in place of the adapter's tree: one flat run with no\n * scene layers, no container clips, and no pose composition. */\n objects?: TNode[];\n /** The pose an object is painted at. Defaults to `adapter.getPose(obj.id)`. `<SceneCanvas>`\n * hands it each node as the scene shows it, its `pose` an override's or a derived one,\n * and carries an override's `path` into the box it answers; return a pose placed from\n * `obj.pose`, or a drag in flight and a rig's motion do not show in this view. */\n toPose?: (obj: TNode) => TPose;\n /** Draw a single object as a `DrawCommand` tree. `ctx` carries a derived\n * node's path: `<SceneCanvas>` resolves it from the scene, and\n * `buildSceneLayer` called without one from its adapter. */\n drawOne: (obj: TNode, pose: TPose, view: View, ctx?: NodePaintCtx) => DrawCommand[];\n /** The path a container derives from its dependencies' poses, for the clip\n * it imposes on its subtree — `drawOne`'s `ctx` carries the derived path a\n * node *paints*, this one the clip it *imposes*. A scene-aware caller\n * supplies it; left out, both are resolved off the adapter. */\n derivedPathOf?: (obj: TNode, pose: TPose) => Path | null;\n /** Default ghost alpha for the move-overlay slot. Default 0.85. */\n ghostAlpha?: number;\n /** Per-id alpha multiplier. A node's emitted `DrawCommand[]` is wrapped in a\n * `{ kind: 'group', alpha }` when the returned value is not 1. */\n alphaFor?: (id: string) => number;\n /**\n * Post-processor for the scene slot's emitted commands: called with the final\n * world-space `DrawCommand[]` each time a scene canvas layer draws (after per-node\n * rotation wrapping and `alphaFor`), once per layer when the slot is split into\n * `scene:<layerId>` layers. Its return value replaces the array handed to the renderer.\n *\n * Commands are world-space — `drawLayers` applies the view transform afterward. Clip\n * nesting is capped at 7 levels; a wrapping group with `clip` consumes one. The\n * move-overlay ghost (drag preview) is not post-processed.\n */\n postProcess?: (cmds: DrawCommand[], view: View, dims: Dims) => DrawCommand[];\n /**\n * Skip what cannot reach the view. A node `paintBounds` places outside the\n * `dims` rectangle under `view` is not painted at all; then, after\n * `postProcess`, `cullDrawCommands` drops any remaining command that cannot\n * reach it — see there for what it can and cannot bound. So `postProcess`\n * sees an empty group where a culled node would have painted.\n *\n * Off by default because it makes the layer's world-space output depend on\n * the view: anything that draws this layer once and shows the result under\n * another view — a cache keyed without `view`, a consumer re-wrapping the\n * commands — would show the first view's cull.\n */\n cull?: boolean;\n /**\n * Where `drawOne`'s output for a node can reach, in the frame it paints in\n * — before the pose rotation the slot wraps it in. `null` for a node it\n * cannot bound cheaply. Read only under `cull`, which skips `drawOne` for a\n * node whose box misses the view, so it must enclose everything `drawOne`\n * paints: a box too small makes a node vanish before it leaves the screen.\n *\n * `<SceneCanvas>` supplies `defaultPaintBounds` while `drawOne` is\n * `defaultDrawOne`. Omit it with any other `drawOne` and culling only\n * trims commands after painting.\n */\n paintBounds?: PaintBoundsFn<TNode, TPose>;\n}\n\n/**\n * `cfg`'s `drawOne` and clip lookup, resolving derived paths off the adapter\n * when nothing scene-aware filled `derivedPathOf` in.\n */\nfunction withAdapterDerivedPaths<TNode extends { id: string }, TPose>(\n cfg: SceneSlotConfig<TNode, TPose>,\n adapter: HierarchicalAdapter<TNode, TPose> | undefined,\n toPose: (obj: TNode) => TPose,\n): Pick<SceneSlotConfig<TNode, TPose>, 'drawOne' | 'derivedPathOf'> {\n const drawOne = cfg.drawOne;\n if (cfg.derivedPathOf !== undefined || !drawOne || !adapter) return cfg;\n const pathOf = adapterDerivedPathOf<TPose>(cfg.toPose === undefined ? adapter : {\n getNode: (id) => adapter.getNode(id),\n getPose: (id) => {\n const obj = adapter.getNode(id);\n return obj ? toPose(obj) : adapter.getPose(id);\n },\n getChildren: (id) => adapter.getChildren(id),\n });\n return {\n derivedPathOf: (obj) => pathOf(obj.id),\n drawOne: (obj, pose, view, ctx) => ((obj as { dependsOn?: unknown }).dependsOn === undefined\n ? drawOne(obj, pose, view, ctx)\n : drawOne(obj, pose, view, { ...ctx, derivedPath: pathOf(obj.id) })),\n };\n}\n\n/**\n * Build the scene layer, which draws committed poses. `hideIds()` skips the committed paint\n * of ids the active tool is currently ghosting so the source doesn't show through. With a\n * debug sink on its draw envelope, it records each node's bounds and origin there.\n */\nexport function buildSceneLayer<TNode extends { id: string }, TPose>(\n cfg: SceneSlotConfig<TNode, TPose>,\n adapter: HierarchicalAdapter<TNode, TPose> | undefined,\n boundsOfFn: ((id: string) => Bounds | null) | undefined,\n hideIds: () => Set<string> | null,\n /** When set, the returned layer carries this `id` and paints ONLY the named\n * scene layer (the rest are walked for ancestor clips). Omit for the single\n * bundled `scene` layer. */\n slot?: { id: string; forLayer: string },\n): RenderLayer<unknown> {\n const toPose =\n cfg.toPose ??\n ((obj: TNode) => (adapter ? adapter.getPose(obj.id) : (obj as unknown as TPose)));\n const { drawOne, derivedPathOf } = withAdapterDerivedPaths(cfg, adapter, toPose);\n const postProcess = cfg.postProcess;\n const finish = cfg.cull\n ? (cmds: DrawCommand[], view: View, dims: LayerDims): DrawCommand[] =>\n cullDrawCommands(cmds, viewToMat3(view), { x: 0, y: 0, width: dims.width, height: dims.height }, dims.dpr)\n : (cmds: DrawCommand[]): DrawCommand[] => cmds;\n const paintBounds = cfg.cull ? cfg.paintBounds : undefined;\n const culledIn = (view: View, dims: Dims) => paintBounds\n ? paintMissesView(paintBounds, view, { x: 0, y: 0, width: dims.width, height: dims.height })\n : undefined;\n return {\n id: slot?.id ?? 'scene',\n label: slot ? `Scene: ${slot.forLayer}` : 'Scene',\n draw: (data, view, dims) => {\n const debugSink = debugSinkFrom(data);\n const hidden = hideIds();\n const a = adapter;\n if (a && cfg.objects === undefined && drawOne) {\n const filteredDrawOne = (obj: TNode, pose: TPose, v: View): DrawCommand[] => {\n if (hidden && hidden.has(obj.id)) return [];\n const cmds = drawOne(obj, pose, v);\n if (debugSink) {\n const b = boundsOfFn ? boundsOfFn(obj.id) : null;\n if (b) debugSink.recordBounds(obj.id, b);\n const ox = (pose as { x?: number }).x ?? (b ? b.x : 0);\n const oy = (pose as { y?: number }).y ?? (b ? b.y : 0);\n debugSink.recordOrigin(obj.id, { x: ox, y: oy });\n }\n return wrapNodeOutput(cmds, pose, cfg.alphaFor ? cfg.alphaFor(obj.id) : 1);\n };\n // Honor cfg.toPose on the hierarchical path by shimming getPose on the\n // adapter so buildSceneTree routes through it instead of the raw adapter.\n const hierarchicalAdapter = cfg.toPose\n ? {\n ...a,\n getPose: (id: string) => {\n const obj = a.getNode(id);\n return obj ? toPose(obj) : a.getPose(id);\n },\n }\n : a;\n // World-space commands; drawLayers wraps in viewToMat3 automatically.\n const tree = buildSceneTree(\n hierarchicalAdapter as unknown as Parameters<typeof buildSceneTree>[0],\n filteredDrawOne as unknown as Parameters<typeof buildSceneTree>[1],\n view,\n slot?.forLayer,\n derivedPathOf as Parameters<typeof buildSceneTree>[4],\n culledIn(view, dims) as Parameters<typeof buildSceneTree>[5],\n );\n return finish(postProcess ? postProcess(tree, view, dims) : tree, view, dims);\n }\n // `cfg.objects`: the caller's own list, which has no scene layers to split by.\n const objects = cfg.objects ?? [];\n const children: DrawCommand[] = [];\n const culled = culledIn(view, dims);\n for (const obj of objects) {\n if (hidden && hidden.has(obj.id)) continue;\n const pose: TPose = toPose(obj);\n if (drawOne && !culled?.(obj, pose)) {\n const cmds = drawOne(obj, pose, view);\n const wrapped = wrapNodeOutput(cmds, pose, cfg.alphaFor ? cfg.alphaFor(obj.id) : 1);\n for (const cmd of wrapped) children.push(cmd);\n }\n if (debugSink) {\n const b = boundsOfFn ? boundsOfFn(obj.id) : null;\n if (b) debugSink.recordBounds(obj.id, { x: b.x, y: b.y, width: b.width, height: b.height });\n const ox = (pose as { x?: number }).x ?? (b ? b.x : 0);\n const oy = (pose as { y?: number }).y ?? (b ? b.y : 0);\n debugSink.recordOrigin(obj.id, { x: ox, y: oy });\n }\n }\n // World-space commands; drawLayers wraps in viewToMat3 automatically.\n return finish(postProcess ? postProcess(children, view, dims) : children, view, dims);\n },\n };\n}\n\n/**\n * The scene slot as one canvas layer **per scene layer** — keyed `scene:<layerId>` — so\n * consumers can interleave custom layers *between* scene layers via the standard slot\n * anchoring (`before: 'scene:plantings'`). A single `scene` layer when `cfg.objects` replaces\n * the tree, or the adapter declares no layers.\n */\nexport function buildSceneLayers<TNode extends { id: string }, TPose>(\n cfg: SceneSlotConfig<TNode, TPose>,\n adapter: HierarchicalAdapter<TNode, TPose> | undefined,\n boundsOfFn: ((id: string) => Bounds | null) | undefined,\n hideIds: () => Set<string> | null,\n): Array<{ key: string; layer: RenderLayer<unknown> }> {\n const sceneLayerIds =\n cfg.objects === undefined && adapter ? adapter.getLayers().map((l) => l.id) : null;\n if (!sceneLayerIds || sceneLayerIds.length === 0) {\n return [{ key: 'scene', layer: buildSceneLayer(cfg, adapter, boundsOfFn, hideIds) }];\n }\n return sceneLayerIds.map((id) => ({\n key: sceneLayerKey(id),\n layer: buildSceneLayer(cfg, adapter, boundsOfFn, hideIds, {\n id: sceneLayerKey(id),\n forLayer: id,\n }),\n }));\n}\n"],"mappings":"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"}
@@ -35,7 +35,8 @@ function wrapInChain(cmds, chain, toLayer, carry) {
35
35
  * **that node's own `layer`** — not its parent's. A node carrying `hidden` is skipped with
36
36
  * its whole subtree; see `nodeGates`. So a child whose `layer` differs from its
37
37
  * parent draws in its own layer's pass while staying a tree child for hit-testing / move /
38
- * reparent. Output is one group per visible layer, in adapter (`getLayers`) order.
38
+ * reparent. Output is one group per visible layer, in adapter (`getLayers`) order. A node that
39
+ * consumes its children paints none of them.
39
40
  *
40
41
  * A node paints before its children. What its `drawOne` wraps in `overChildren` paints after
41
42
  * them instead, in the node's own layer.
@@ -57,6 +58,7 @@ function buildSceneTree(adapter, drawOne, view, forLayer, derivedPathOf, culled)
57
58
  let planeViews = null;
58
59
  for (const l of layers) if (l.parallax) (planeViews ??= /* @__PURE__ */ new Map()).set(l.id, deriveParallaxView(view, l.parallax));
59
60
  const compose = adapter.composePose?.bind(adapter);
61
+ const scrolled = compose ? adapter.childFrame?.bind(adapter) : void 0;
60
62
  const carry = clipCarrier(planeViews ? scenePlaneOf(layers, view) : null);
61
63
  function visit(id, ancestorClips, parentFrame, copied) {
62
64
  const node = adapter.getNode(id);
@@ -82,8 +84,8 @@ function buildSceneTree(adapter, drawOne, view, forLayer, derivedPathOf, culled)
82
84
  children: cmds
83
85
  };
84
86
  if (bucket) bucket.push(wrap(self));
85
- const childFrame = definesFrame(node) ? pose : parentFrame;
86
- const children = adapter.getChildren(id);
87
+ const childFrame = definesFrame(node) ? scrolled ? scrolled(id, pose) : pose : parentFrame;
88
+ const children = node.consumesChildren === true ? [] : adapter.getChildren(id);
87
89
  for (const cid of children) visit(cid, ownClips, childFrame, copied);
88
90
  const copies = typeof maybeContainer.copiesFromPose === "function" ? maybeContainer.copiesFromPose(pose, node) : null;
89
91
  for (const copy of copies ?? []) {
@@ -1 +1 @@
1
- {"version":3,"file":"buildSceneTree.js","names":[],"sources":["../../src/canvas/buildSceneTree.ts"],"sourcesContent":["import type { DrawCommand, GroupDrawCommand } from '@weasel-js/renderer';\nimport type { View } from 'core/viewport/view';\nimport { ownClipOf, type ClipBearing } from './ownClip';\nimport { mat3, type Affine, type Path } from '@weasel-js/geom';\nimport { definesFrame } from 'core/scene/effectivePose';\nimport { deriveParallaxView, planeMap, type ParallaxOpts } from 'core/viewport/parallax';\nimport { scenePlaneOf } from './pickWalk';\nimport { clipCarrier, planeTransform } from './planeClips';\nimport { splitOverChildren } from './overChildren';\nimport { hidesSubtree } from 'core/scene/nodeGates';\n\n/** The scene-tree reading surface `buildSceneTree` walks. A `SceneCanvasAdapter` is one. */\nexport interface HierarchicalAdapter<TNode, TPose> {\n getLayers(): readonly { id: string; visible: boolean; parallax?: ParallaxOpts }[];\n getNode(id: string): TNode | undefined;\n getChildren(parentId: string | null): readonly string[];\n getPose(id: string): TPose;\n /** Folds a child's local pose into its parent's frame. Absent means the\n * scene stores absolute poses and a parent contributes no transform —\n * see `PoseComposition`. */\n composePose?(parent: TPose, child: TPose): TPose;\n}\n\n/** A clip on the chain, with the layer whose world it is drawn in, or the\n * transform of a copy a container draws — see `GroupNode.copiesFromPose`. */\ntype ChainEntry = { path: Path; layer: string } | { copy: Affine };\n\n/** Wrap `cmds` in one nested group per chain entry, so the renderer intersects\n * the clips and applies each copy's transform to everything inside it.\n * Outermost group carries the ancestor-most entry; innermost holds `cmds`. */\nfunction wrapInChain(\n cmds: DrawCommand[],\n chain: readonly ChainEntry[],\n toLayer: string,\n carry: ReturnType<typeof clipCarrier>,\n): DrawCommand {\n let node: GroupDrawCommand = { kind: 'group', children: cmds };\n for (let i = chain.length - 1; i >= 0; i--) {\n const e = chain[i];\n node = 'copy' in e\n ? { kind: 'group', children: [node], transform: mat3.fromAffine(e.copy) }\n : { kind: 'group', children: [node], clip: carry(e.path, e.layer, toLayer) };\n }\n return node;\n}\n\n/**\n * Walk the adapter's scene tree once and emit each node's own paint into the bucket for\n * **that node's own `layer`** — not its parent's. A node carrying `hidden` is skipped with\n * its whole subtree; see `nodeGates`. So a child whose `layer` differs from its\n * parent draws in its own layer's pass while staying a tree child for hit-testing / move /\n * reparent. Output is one group per visible layer, in adapter (`getLayers`) order.\n *\n * A node paints before its children. What its `drawOne` wraps in `overChildren` paints after\n * them instead, in the node's own layer.\n *\n * `drawOne` returns world-coord commands. Because a node may be drawn outside its parent's\n * group, the tree nesting cannot carry geometry as renderer state; the walk folds each node's\n * pose into its parent's frame on the way down and hands the painter a **world** pose. Clips\n * are accumulated the same way — each in its container's plane, carried into the plane of the\n * node it clips. With no `composePose` on the adapter the fold is the identity.\n *\n * A layer carrying `parallax` is a plane: its painters and its cull see the view derived for\n * it, and its group carries the transform from its world to the camera's, so the caller's\n * single camera wrap still lands it right.\n */\nexport function buildSceneTree<\n TNode extends { id: string; layer: string; hidden?: boolean },\n TPose,\n>(\n adapter: HierarchicalAdapter<TNode, TPose>,\n drawOne: (obj: TNode, pose: TPose, view: View) => DrawCommand[],\n view: View,\n /** When set, only nodes on this layer paint (others are still walked for\n * ancestor clips) and only this layer's group is returned. */\n forLayer?: string,\n /** The path a container computes from its dependencies' poses: the scene's\n * lookup, or `adapterDerivedPathOf` for an adapter with no scene. Left out,\n * a derived container contributes no clip. */\n derivedPathOf?: (node: TNode, pose: TPose) => Path | null,\n /** True for a node whose paint cannot be seen — see `paintMissesView`. Its\n * painter is not called; its clip and its children are walked as usual. */\n culled?: (node: TNode, pose: TPose, view: View) => boolean,\n): DrawCommand[] {\n const layers = adapter.getLayers();\n // A hidden layer gets no bucket, and a node with no bucket is not painted.\n const buckets = new Map<string, DrawCommand[]>();\n for (const l of layers) if (l.visible) buckets.set(l.id, []);\n let planeViews: Map<string, View> | null = null;\n for (const l of layers) {\n if (l.parallax) (planeViews ??= new Map()).set(l.id, deriveParallaxView(view, l.parallax));\n }\n\n const compose = adapter.composePose?.bind(adapter);\n const carry = clipCarrier(planeViews ? scenePlaneOf(layers, view) : null);\n\n function visit(\n id: string,\n ancestorClips: readonly ChainEntry[],\n parentFrame: TPose | null,\n /** Under a copy: the pose is not where it paints, so the cull cannot judge it. */\n copied: boolean,\n ): void {\n const node = adapter.getNode(id);\n if (!node || hidesSubtree(node)) return;\n const local = adapter.getPose(id);\n // Fold once, on the way down: O(1) per node where walking the parent chain\n // per node would be O(depth).\n const pose = compose && parentFrame !== null ? compose(parentFrame, local) : local;\n // Skip the (potentially expensive) painter for nodes we won't emit; their\n // clip still extends the chain for descendants below.\n const bucket = forLayer === undefined || node.layer === forLayer\n ? buckets.get(node.layer) : undefined;\n const v = planeViews?.get(node.layer) ?? view;\n const { under: self, over } = splitOverChildren(\n bucket && (copied || !culled?.(node, pose, v)) ? drawOne(node, pose, v) : [],\n );\n\n let ownClips = ancestorClips;\n let ownClip: ChainEntry | null = null;\n const maybeContainer = node as ClipBearing<TPose> & {\n copiesFromPose?: (pose: TPose, node: unknown) => readonly Affine[] | null;\n };\n const clip = ownClipOf(maybeContainer, pose, () => derivedPathOf?.(node, pose));\n if (clip) {\n ownClip = { path: clip, layer: node.layer };\n ownClips = [...ancestorClips, ownClip];\n }\n\n // Every node emits a wrapper group even with empty paint, keeping the per-node tree\n // shape stable. A node's own clip is its children's, so its own paint, over\n // its children or under them, takes only the clips above it.\n const wrap = (cmds: DrawCommand[]): DrawCommand => (ancestorClips.length > 0\n ? wrapInChain(cmds, ancestorClips, node.layer, carry) : { kind: 'group', children: cmds });\n if (bucket) bucket.push(wrap(self));\n\n // An envelope's children share its own parent's frame.\n const childFrame = definesFrame(node as never) ? pose : parentFrame;\n const children = adapter.getChildren(id);\n for (const cid of children) visit(cid, ownClips, childFrame, copied);\n\n // A copy is a copy of the clipped content, so its transform encloses the\n // container's own clip and sits inside every clip above it.\n const copies = typeof maybeContainer.copiesFromPose === 'function'\n ? maybeContainer.copiesFromPose(pose, node) : null;\n for (const copy of copies ?? []) {\n const chain: ChainEntry[] = [...ancestorClips, { copy }, ...(ownClip ? [ownClip] : [])];\n for (const cid of children) visit(cid, chain, childFrame, true);\n }\n\n if (bucket && over.length > 0) bucket.push(wrap(over));\n }\n\n for (const rootId of adapter.getChildren(null)) visit(rootId, [], null, false);\n\n const out: DrawCommand[] = [];\n for (const layer of layers) {\n if (!layer.visible) continue;\n if (forLayer !== undefined && layer.id !== forLayer) continue;\n const children = buckets.get(layer.id) ?? [];\n const transform = layer.parallax ? planeTransform(planeMap(view, layer.parallax), null) : undefined;\n out.push(transform ? { kind: 'group', transform, children } : { kind: 'group', children });\n }\n return out;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"buildSceneTree.js","names":[],"sources":["../../src/canvas/buildSceneTree.ts"],"sourcesContent":["import type { DrawCommand, GroupDrawCommand } from '@weasel-js/renderer';\nimport type { View } from 'core/viewport/view';\nimport { ownClipOf, type ClipBearing } from './ownClip';\nimport { mat3, type Affine, type Path } from '@weasel-js/geom';\nimport { definesFrame } from 'core/scene/effectivePose';\nimport { deriveParallaxView, planeMap, type ParallaxOpts } from 'core/viewport/parallax';\nimport { scenePlaneOf } from './pickWalk';\nimport { clipCarrier, planeTransform } from './planeClips';\nimport { splitOverChildren } from './overChildren';\nimport { hidesSubtree } from 'core/scene/nodeGates';\n\n/** The scene-tree reading surface `buildSceneTree` walks. A `SceneCanvasAdapter` is one. */\nexport interface HierarchicalAdapter<TNode, TPose> {\n getLayers(): readonly { id: string; visible: boolean; parallax?: ParallaxOpts }[];\n getNode(id: string): TNode | undefined;\n getChildren(parentId: string | null): readonly string[];\n getPose(id: string): TPose;\n /** Folds a child's local pose into its parent's frame. Absent means the\n * scene stores absolute poses and a parent contributes no transform —\n * see `PoseComposition`. */\n composePose?(parent: TPose, child: TPose): TPose;\n /** The frame a container gives its children from its world pose: moved by\n * its scroll offset where the view scrolls it. Absent means the pose itself. */\n childFrame?(id: string, world: TPose): TPose;\n}\n\n/** A clip on the chain, with the layer whose world it is drawn in, or the\n * transform of a copy a container draws — see `GroupNode.copiesFromPose`. */\ntype ChainEntry = { path: Path; layer: string } | { copy: Affine };\n\n/** Wrap `cmds` in one nested group per chain entry, so the renderer intersects\n * the clips and applies each copy's transform to everything inside it.\n * Outermost group carries the ancestor-most entry; innermost holds `cmds`. */\nfunction wrapInChain(\n cmds: DrawCommand[],\n chain: readonly ChainEntry[],\n toLayer: string,\n carry: ReturnType<typeof clipCarrier>,\n): DrawCommand {\n let node: GroupDrawCommand = { kind: 'group', children: cmds };\n for (let i = chain.length - 1; i >= 0; i--) {\n const e = chain[i];\n node = 'copy' in e\n ? { kind: 'group', children: [node], transform: mat3.fromAffine(e.copy) }\n : { kind: 'group', children: [node], clip: carry(e.path, e.layer, toLayer) };\n }\n return node;\n}\n\n/**\n * Walk the adapter's scene tree once and emit each node's own paint into the bucket for\n * **that node's own `layer`** — not its parent's. A node carrying `hidden` is skipped with\n * its whole subtree; see `nodeGates`. So a child whose `layer` differs from its\n * parent draws in its own layer's pass while staying a tree child for hit-testing / move /\n * reparent. Output is one group per visible layer, in adapter (`getLayers`) order. A node that\n * consumes its children paints none of them.\n *\n * A node paints before its children. What its `drawOne` wraps in `overChildren` paints after\n * them instead, in the node's own layer.\n *\n * `drawOne` returns world-coord commands. Because a node may be drawn outside its parent's\n * group, the tree nesting cannot carry geometry as renderer state; the walk folds each node's\n * pose into its parent's frame on the way down and hands the painter a **world** pose. Clips\n * are accumulated the same way — each in its container's plane, carried into the plane of the\n * node it clips. With no `composePose` on the adapter the fold is the identity.\n *\n * A layer carrying `parallax` is a plane: its painters and its cull see the view derived for\n * it, and its group carries the transform from its world to the camera's, so the caller's\n * single camera wrap still lands it right.\n */\nexport function buildSceneTree<\n TNode extends { id: string; layer: string; hidden?: boolean; consumesChildren?: boolean },\n TPose,\n>(\n adapter: HierarchicalAdapter<TNode, TPose>,\n drawOne: (obj: TNode, pose: TPose, view: View) => DrawCommand[],\n view: View,\n /** When set, only nodes on this layer paint (others are still walked for\n * ancestor clips) and only this layer's group is returned. */\n forLayer?: string,\n /** The path a container computes from its dependencies' poses: the scene's\n * lookup, or `adapterDerivedPathOf` for an adapter with no scene. Left out,\n * a derived container contributes no clip. */\n derivedPathOf?: (node: TNode, pose: TPose) => Path | null,\n /** True for a node whose paint cannot be seen — see `paintMissesView`. Its\n * painter is not called; its clip and its children are walked as usual. */\n culled?: (node: TNode, pose: TPose, view: View) => boolean,\n): DrawCommand[] {\n const layers = adapter.getLayers();\n // A hidden layer gets no bucket, and a node with no bucket is not painted.\n const buckets = new Map<string, DrawCommand[]>();\n for (const l of layers) if (l.visible) buckets.set(l.id, []);\n let planeViews: Map<string, View> | null = null;\n for (const l of layers) {\n if (l.parallax) (planeViews ??= new Map()).set(l.id, deriveParallaxView(view, l.parallax));\n }\n\n const compose = adapter.composePose?.bind(adapter);\n const scrolled = compose ? adapter.childFrame?.bind(adapter) : undefined;\n const carry = clipCarrier(planeViews ? scenePlaneOf(layers, view) : null);\n\n function visit(\n id: string,\n ancestorClips: readonly ChainEntry[],\n parentFrame: TPose | null,\n /** Under a copy: the pose is not where it paints, so the cull cannot judge it. */\n copied: boolean,\n ): void {\n const node = adapter.getNode(id);\n if (!node || hidesSubtree(node)) return;\n const local = adapter.getPose(id);\n // Fold once, on the way down: O(1) per node where walking the parent chain\n // per node would be O(depth).\n const pose = compose && parentFrame !== null ? compose(parentFrame, local) : local;\n // Skip the (potentially expensive) painter for nodes we won't emit; their\n // clip still extends the chain for descendants below.\n const bucket = forLayer === undefined || node.layer === forLayer\n ? buckets.get(node.layer) : undefined;\n const v = planeViews?.get(node.layer) ?? view;\n const { under: self, over } = splitOverChildren(\n bucket && (copied || !culled?.(node, pose, v)) ? drawOne(node, pose, v) : [],\n );\n\n let ownClips = ancestorClips;\n let ownClip: ChainEntry | null = null;\n const maybeContainer = node as ClipBearing<TPose> & {\n copiesFromPose?: (pose: TPose, node: unknown) => readonly Affine[] | null;\n };\n const clip = ownClipOf(maybeContainer, pose, () => derivedPathOf?.(node, pose));\n if (clip) {\n ownClip = { path: clip, layer: node.layer };\n ownClips = [...ancestorClips, ownClip];\n }\n\n // Every node emits a wrapper group even with empty paint, keeping the per-node tree\n // shape stable. A node's own clip is its children's, so its own paint, over\n // its children or under them, takes only the clips above it.\n const wrap = (cmds: DrawCommand[]): DrawCommand => (ancestorClips.length > 0\n ? wrapInChain(cmds, ancestorClips, node.layer, carry) : { kind: 'group', children: cmds });\n if (bucket) bucket.push(wrap(self));\n\n // An envelope's children share its own parent's frame.\n const childFrame = definesFrame(node as never) ? (scrolled ? scrolled(id, pose) : pose) : parentFrame;\n const children = node.consumesChildren === true ? [] : adapter.getChildren(id);\n for (const cid of children) visit(cid, ownClips, childFrame, copied);\n\n // A copy is a copy of the clipped content, so its transform encloses the\n // container's own clip and sits inside every clip above it.\n const copies = typeof maybeContainer.copiesFromPose === 'function'\n ? maybeContainer.copiesFromPose(pose, node) : null;\n for (const copy of copies ?? []) {\n const chain: ChainEntry[] = [...ancestorClips, { copy }, ...(ownClip ? [ownClip] : [])];\n for (const cid of children) visit(cid, chain, childFrame, true);\n }\n\n if (bucket && over.length > 0) bucket.push(wrap(over));\n }\n\n for (const rootId of adapter.getChildren(null)) visit(rootId, [], null, false);\n\n const out: DrawCommand[] = [];\n for (const layer of layers) {\n if (!layer.visible) continue;\n if (forLayer !== undefined && layer.id !== forLayer) continue;\n const children = buckets.get(layer.id) ?? [];\n const transform = layer.parallax ? planeTransform(planeMap(view, layer.parallax), null) : undefined;\n out.push(transform ? { kind: 'group', transform, children } : { kind: 'group', children });\n }\n return out;\n}\n"],"mappings":"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"}
@@ -18,6 +18,10 @@ var TENANTS = /* @__PURE__ */ new WeakMap();
18
18
  /** Canvases whose context the renderer refused, so a redraw does not rebuild
19
19
  * and fail again, and the reason is reported once. */
20
20
  var REFUSED = /* @__PURE__ */ new WeakSet();
21
+ /** The density a canvas given `dpr` paints at. */
22
+ function resolveDpr(dpr) {
23
+ return dpr ?? (typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1);
24
+ }
21
25
  /** Identifies a set of custom programs by their ids, in order. */
22
26
  var shaderKeyOf = (shaders) => shaders?.map((h) => h.id).join("|") ?? "";
23
27
  function registerShaders(renderer, shaders) {
@@ -42,7 +46,7 @@ function leaseCanvasRenderer() {
42
46
  const restored = createEmitter();
43
47
  const bind = (canvas, size, options = {}) => {
44
48
  const { width, height } = size;
45
- const dpr = size.dpr ?? (typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1);
49
+ const dpr = resolveDpr(size.dpr);
46
50
  const tile = options.tile ?? null;
47
51
  const shared = tile !== null;
48
52
  if (held && (held.canvas !== canvas || held.shared !== shared)) {
@@ -186,5 +190,5 @@ function releaseCanvasRenderer(canvas) {
186
190
  }
187
191
 
188
192
  //#endregion
189
- export { leaseCanvasRenderer, paintCanvas, releaseCanvasRenderer, shaderKeyOf };
193
+ export { leaseCanvasRenderer, paintCanvas, releaseCanvasRenderer, resolveDpr, shaderKeyOf };
190
194
  //# sourceMappingURL=canvasRenderer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"canvasRenderer.js","names":["viewToMat3"],"sources":["../../src/canvas/canvasRenderer.ts"],"sourcesContent":["/**\n * The one renderer lifetime in core. Every on-screen surface holds its\n * `WeaselRenderer` through a `leaseCanvasRenderer` lease.\n *\n * A lease holds its own renderer, so several can share one element: each\n * tenant painting a `tile` of a shared buffer gets a renderer of its own on\n * the element's one context. Release frees only what that renderer created;\n * the context itself is never lost.\n */\nimport { WeaselRenderer, type FlattenTolerance, type ImageMinification, type RenderOptions } from '@weasel-js/renderer';\nimport { viewToMat3 } from '@weasel-js/geom';\nimport type { ShaderProgramHandle } from '@weasel-js/renderer';\nimport type { DrawCommand } from '@weasel-js/renderer';\nimport type { View } from '../core/viewport/view';\nimport { createEmitter } from '@weasel-js/registry';\nimport { CANVAS_CONTEXT_ATTRIBUTES } from './contextAttributes';\n\n/** The live renderers painting into each element, across every lease. */\nconst TENANTS: WeakMap<HTMLCanvasElement, Set<WeaselRenderer>> = new WeakMap();\n\n/** Canvases whose context the renderer refused, so a redraw does not rebuild\n * and fail again, and the reason is reported once. */\nconst REFUSED: WeakSet<HTMLCanvasElement> = new WeakSet();\n\n/** A renderer painting into the canvas, if any lease holds one. Test seam. */\nexport function cachedCanvasRenderer(canvas: HTMLCanvasElement): WeaselRenderer | undefined {\n const set = TENANTS.get(canvas);\n return set ? set.values().next().value : undefined;\n}\n\n/** The size and density to paint a canvas at. `dpr` defaults to\n * `window.devicePixelRatio`, read per paint. */\nexport interface CanvasPaintSize {\n width: number;\n height: number;\n dpr?: number;\n}\n\n/** How one paint reaches the canvas, beyond its size. */\nexport interface CanvasPaintOptions {\n /** Paint a `width × height` tile of a buffer other surfaces share, at this\n * origin in CSS pixels. The buffer is then the caller's to size: the\n * renderer never resizes the element, which would have every co-tenant\n * resize it to its own pane. Omit to own the whole element. */\n tile?: { x: number; y: number } | null;\n /** `WeaselRendererOptions.flattenTolerance`, applied from this paint on. */\n flattenTolerance?: FlattenTolerance;\n /** `WeaselRendererOptions.retainBudget`, applied from this paint on. */\n retainBudget?: number;\n /** `WeaselRendererOptions.imageMinification`, applied from this paint on. */\n imageMinification?: ImageMinification;\n /** Custom programs to compile on the renderer. Compiled when the renderer\n * is created, and again when the set of ids changes. */\n shaders?: readonly ShaderProgramHandle[];\n}\n\n/** A paint's options, plus what `paint` hands `WeaselRenderer.render`. */\nexport interface CanvasRenderOptions extends CanvasPaintOptions {\n render?: RenderOptions;\n}\n\n/** A canvas renderer held for a surface's lifetime. */\nexport interface CanvasRendererLease {\n /** Draw `commands` into `canvas` — screen-space, one unit per CSS pixel,\n * with `view` passed on as the frame's world→screen transform. `bind`,\n * then render. Returns `false` without painting where `bind` finds no\n * renderer. */\n paint(\n canvas: HTMLCanvasElement,\n commands: DrawCommand[],\n view: View,\n size: CanvasPaintSize,\n options?: CanvasRenderOptions,\n ): boolean;\n /** The renderer that paints `canvas` at `size`, ready to render: created on\n * first use, resized, and given this paint's tile, tolerance, retain\n * budget, and shaders.\n * Frees the previous renderer first if the surface moved to another\n * element, or between owning it whole and painting a tile of it. `null`\n * where no WebGL2 context is available (jsdom, a failed context creation,\n * or a context that is lost for now). */\n bind(canvas: HTMLCanvasElement, size: CanvasPaintSize, options?: CanvasPaintOptions): WeaselRenderer | null;\n /** Run `fn` when the canvas gets a lost context back, after the renderer\n * has rebuilt itself on it. Nothing repaints a restored buffer on its own.\n * Returns an unsubscribe. */\n subscribeRestored(fn: () => void): () => void;\n /** Free the held renderer and every GL object it owns. A later `paint`\n * creates a fresh one. */\n release(): void;\n}\n\ninterface Held {\n canvas: HTMLCanvasElement;\n renderer: WeaselRenderer;\n shared: boolean;\n lost: boolean;\n onLost: () => void;\n onRestored: () => void;\n width: number;\n height: number;\n dpr: number;\n tileX: number | null;\n tileY: number | null;\n flattenTolerance: FlattenTolerance | undefined;\n retainBudget: number | undefined;\n imageMinification: ImageMinification | undefined;\n shaderKey: string;\n}\n\n/** Identifies a set of custom programs by their ids, in order. */\nexport const shaderKeyOf = (shaders: readonly ShaderProgramHandle[] | undefined): string =>\n shaders?.map((h) => h.id).join('|') ?? '';\n\nfunction registerShaders(renderer: WeaselRenderer, shaders: readonly ShaderProgramHandle[] | undefined): void {\n if (!shaders) return;\n for (const handle of shaders) {\n try {\n renderer.registerProgram(handle);\n } catch (e) {\n console.warn(`weasel: failed to register shader \"${handle.id}\":`, e);\n }\n }\n}\n\nfunction free(held: Held): void {\n const set = TENANTS.get(held.canvas);\n set?.delete(held.renderer);\n held.canvas.removeEventListener('webglcontextlost', held.onLost);\n held.canvas.removeEventListener('webglcontextrestored', held.onRestored);\n if (set && set.size === 0) TENANTS.delete(held.canvas);\n held.renderer.dispose();\n}\n\n/** Hold the renderer of the canvas a surface paints into, until released. */\nexport function leaseCanvasRenderer(): CanvasRendererLease {\n let held: Held | null = null;\n const restored = createEmitter();\n const bind = (canvas: HTMLCanvasElement, size: CanvasPaintSize, options: CanvasPaintOptions = {}): WeaselRenderer | null => {\n const { width, height } = size;\n const dpr = size.dpr ?? (typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1);\n const tile = options.tile ?? null;\n const shared = tile !== null;\n\n if (held && (held.canvas !== canvas || held.shared !== shared)) {\n free(held);\n held = null;\n }\n if (!held) {\n if (REFUSED.has(canvas)) return null;\n const gl = canvas.getContext('webgl2', CANVAS_CONTEXT_ATTRIBUTES);\n if (!gl || typeof (gl as Partial<WebGL2RenderingContext>).enable !== 'function') return null;\n let renderer: WeaselRenderer;\n try {\n renderer = new WeaselRenderer({\n gl: gl as WebGL2RenderingContext,\n ...(shared ? {} : { canvas }),\n width, height, dpr,\n flattenTolerance: options.flattenTolerance,\n retainBudget: options.retainBudget,\n imageMinification: options.imageMinification,\n });\n } catch (e) {\n // A lost context fails to compile and may yet come back; anything\n // else fails the same way next time.\n if (!(gl as Partial<WebGL2RenderingContext>).isContextLost?.()) {\n REFUSED.add(canvas);\n console.warn('weasel: this canvas cannot be painted:', e);\n }\n return null;\n }\n // Added after the renderer's own listeners, so a subscriber runs against\n // a renderer that has already recompiled.\n const mine: Held = held = {\n canvas, renderer, shared, width, height, dpr, tileX: null, tileY: null,\n flattenTolerance: options.flattenTolerance, retainBudget: options.retainBudget,\n imageMinification: options.imageMinification, shaderKey: '',\n lost: false,\n onLost: () => { mine.lost = true; },\n onRestored: () => {\n mine.lost = false;\n restored.emit();\n },\n };\n canvas.addEventListener('webglcontextlost', held.onLost);\n canvas.addEventListener('webglcontextrestored', held.onRestored);\n let set = TENANTS.get(canvas);\n if (!set) { set = new Set(); TENANTS.set(canvas, set); }\n set.add(renderer);\n } else {\n // React writes the element's `width` and `height` attributes in CSS\n // pixels on every size change, so a size that went away and came back\n // matches the record while the buffer does not.\n const stale = !shared\n && (canvas.width !== Math.round(width * dpr) || canvas.height !== Math.round(height * dpr));\n if (stale || held.width !== width || held.height !== height || held.dpr !== dpr) {\n held.renderer.resize({ width, height, dpr });\n held.width = width;\n held.height = height;\n held.dpr = dpr;\n }\n if (held.flattenTolerance !== options.flattenTolerance) {\n held.renderer.setFlattenTolerance(options.flattenTolerance);\n held.flattenTolerance = options.flattenTolerance;\n }\n if (held.retainBudget !== options.retainBudget) {\n held.renderer.setRetainBudget(options.retainBudget);\n held.retainBudget = options.retainBudget;\n }\n if (held.imageMinification !== options.imageMinification) {\n held.renderer.setImageMinification(options.imageMinification);\n held.imageMinification = options.imageMinification;\n }\n }\n\n if (held.lost) return null;\n const { renderer } = held;\n const shaderKey = shaderKeyOf(options.shaders);\n if (shaderKey !== held.shaderKey) {\n registerShaders(renderer, options.shaders);\n held.shaderKey = shaderKey;\n }\n const tileX = tile?.x ?? null;\n const tileY = tile?.y ?? null;\n if (tileX !== held.tileX || tileY !== held.tileY) {\n renderer.setTarget(tile ? { origin: { x: tile.x, y: tile.y } } : null);\n held.tileX = tileX;\n held.tileY = tileY;\n }\n return renderer;\n };\n return {\n bind,\n paint(canvas, commands, view, size, options) {\n const renderer = bind(canvas, size, options);\n if (!renderer) return false;\n renderer.render(commands, viewToMat3(view), options?.render);\n return true;\n },\n subscribeRestored: restored.subscribe,\n release() {\n if (held) free(held);\n held = null;\n },\n };\n}\n\n/** Leases held on a canvas's behalf by `paintCanvas`, one per element. */\nconst CANVAS_LEASES: WeakMap<HTMLCanvasElement, CanvasRendererLease> = new WeakMap();\n\n/**\n * Paint `canvas` through a lease held for the element itself, for callers\n * with no component lifetime of their own. The renderer lives until\n * `releaseCanvasRenderer(canvas)`.\n */\nexport function paintCanvas(\n canvas: HTMLCanvasElement,\n commands: DrawCommand[],\n view: View,\n size: CanvasPaintSize,\n): boolean {\n let lease = CANVAS_LEASES.get(canvas);\n if (!lease) {\n lease = leaseCanvasRenderer();\n CANVAS_LEASES.set(canvas, lease);\n }\n return lease.paint(canvas, commands, view, size);\n}\n\n/** Free the renderer `paintCanvas` holds for the canvas, and every GL object\n * it owns. The next paint creates a fresh one. Renderers a surface component\n * holds are that component's to free, on unmount. */\nexport function releaseCanvasRenderer(canvas: HTMLCanvasElement): void {\n const lease = CANVAS_LEASES.get(canvas);\n if (!lease) return;\n CANVAS_LEASES.delete(canvas);\n lease.release();\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"canvasRenderer.js","names":["viewToMat3"],"sources":["../../src/canvas/canvasRenderer.ts"],"sourcesContent":["/**\n * The one renderer lifetime in core. Every on-screen surface holds its\n * `WeaselRenderer` through a `leaseCanvasRenderer` lease.\n *\n * A lease holds its own renderer, so several can share one element: each\n * tenant painting a `tile` of a shared buffer gets a renderer of its own on\n * the element's one context. Release frees only what that renderer created;\n * the context itself is never lost.\n */\nimport { WeaselRenderer, type FlattenTolerance, type ImageMinification, type RenderOptions } from '@weasel-js/renderer';\nimport { viewToMat3 } from '@weasel-js/geom';\nimport type { ShaderProgramHandle } from '@weasel-js/renderer';\nimport type { DrawCommand } from '@weasel-js/renderer';\nimport type { View } from '../core/viewport/view';\nimport { createEmitter } from '@weasel-js/registry';\nimport { CANVAS_CONTEXT_ATTRIBUTES } from './contextAttributes';\n\n/** The live renderers painting into each element, across every lease. */\nconst TENANTS: WeakMap<HTMLCanvasElement, Set<WeaselRenderer>> = new WeakMap();\n\n/** Canvases whose context the renderer refused, so a redraw does not rebuild\n * and fail again, and the reason is reported once. */\nconst REFUSED: WeakSet<HTMLCanvasElement> = new WeakSet();\n\n/** A renderer painting into the canvas, if any lease holds one. Test seam. */\nexport function cachedCanvasRenderer(canvas: HTMLCanvasElement): WeaselRenderer | undefined {\n const set = TENANTS.get(canvas);\n return set ? set.values().next().value : undefined;\n}\n\n/** The size and density to paint a canvas at. `dpr` defaults to\n * `window.devicePixelRatio`, read per paint. */\nexport interface CanvasPaintSize {\n width: number;\n height: number;\n dpr?: number;\n}\n\n/** The density a canvas given `dpr` paints at. */\nexport function resolveDpr(dpr: number | undefined): number {\n return dpr ?? (typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1);\n}\n\n/** How one paint reaches the canvas, beyond its size. */\nexport interface CanvasPaintOptions {\n /** Paint a `width × height` tile of a buffer other surfaces share, at this\n * origin in CSS pixels. The buffer is then the caller's to size: the\n * renderer never resizes the element, which would have every co-tenant\n * resize it to its own pane. Omit to own the whole element. */\n tile?: { x: number; y: number } | null;\n /** `WeaselRendererOptions.flattenTolerance`, applied from this paint on. */\n flattenTolerance?: FlattenTolerance;\n /** `WeaselRendererOptions.retainBudget`, applied from this paint on. */\n retainBudget?: number;\n /** `WeaselRendererOptions.imageMinification`, applied from this paint on. */\n imageMinification?: ImageMinification;\n /** Custom programs to compile on the renderer. Compiled when the renderer\n * is created, and again when the set of ids changes. */\n shaders?: readonly ShaderProgramHandle[];\n}\n\n/** A paint's options, plus what `paint` hands `WeaselRenderer.render`. */\nexport interface CanvasRenderOptions extends CanvasPaintOptions {\n render?: RenderOptions;\n}\n\n/** A canvas renderer held for a surface's lifetime. */\nexport interface CanvasRendererLease {\n /** Draw `commands` into `canvas` — screen-space, one unit per CSS pixel,\n * with `view` passed on as the frame's world→screen transform. `bind`,\n * then render. Returns `false` without painting where `bind` finds no\n * renderer. */\n paint(\n canvas: HTMLCanvasElement,\n commands: DrawCommand[],\n view: View,\n size: CanvasPaintSize,\n options?: CanvasRenderOptions,\n ): boolean;\n /** The renderer that paints `canvas` at `size`, ready to render: created on\n * first use, resized, and given this paint's tile, tolerance, retain\n * budget, and shaders.\n * Frees the previous renderer first if the surface moved to another\n * element, or between owning it whole and painting a tile of it. `null`\n * where no WebGL2 context is available (jsdom, a failed context creation,\n * or a context that is lost for now). */\n bind(canvas: HTMLCanvasElement, size: CanvasPaintSize, options?: CanvasPaintOptions): WeaselRenderer | null;\n /** Run `fn` when the canvas gets a lost context back, after the renderer\n * has rebuilt itself on it. Nothing repaints a restored buffer on its own.\n * Returns an unsubscribe. */\n subscribeRestored(fn: () => void): () => void;\n /** Free the held renderer and every GL object it owns. A later `paint`\n * creates a fresh one. */\n release(): void;\n}\n\ninterface Held {\n canvas: HTMLCanvasElement;\n renderer: WeaselRenderer;\n shared: boolean;\n lost: boolean;\n onLost: () => void;\n onRestored: () => void;\n width: number;\n height: number;\n dpr: number;\n tileX: number | null;\n tileY: number | null;\n flattenTolerance: FlattenTolerance | undefined;\n retainBudget: number | undefined;\n imageMinification: ImageMinification | undefined;\n shaderKey: string;\n}\n\n/** Identifies a set of custom programs by their ids, in order. */\nexport const shaderKeyOf = (shaders: readonly ShaderProgramHandle[] | undefined): string =>\n shaders?.map((h) => h.id).join('|') ?? '';\n\nfunction registerShaders(renderer: WeaselRenderer, shaders: readonly ShaderProgramHandle[] | undefined): void {\n if (!shaders) return;\n for (const handle of shaders) {\n try {\n renderer.registerProgram(handle);\n } catch (e) {\n console.warn(`weasel: failed to register shader \"${handle.id}\":`, e);\n }\n }\n}\n\nfunction free(held: Held): void {\n const set = TENANTS.get(held.canvas);\n set?.delete(held.renderer);\n held.canvas.removeEventListener('webglcontextlost', held.onLost);\n held.canvas.removeEventListener('webglcontextrestored', held.onRestored);\n if (set && set.size === 0) TENANTS.delete(held.canvas);\n held.renderer.dispose();\n}\n\n/** Hold the renderer of the canvas a surface paints into, until released. */\nexport function leaseCanvasRenderer(): CanvasRendererLease {\n let held: Held | null = null;\n const restored = createEmitter();\n const bind = (canvas: HTMLCanvasElement, size: CanvasPaintSize, options: CanvasPaintOptions = {}): WeaselRenderer | null => {\n const { width, height } = size;\n const dpr = resolveDpr(size.dpr);\n const tile = options.tile ?? null;\n const shared = tile !== null;\n\n if (held && (held.canvas !== canvas || held.shared !== shared)) {\n free(held);\n held = null;\n }\n if (!held) {\n if (REFUSED.has(canvas)) return null;\n const gl = canvas.getContext('webgl2', CANVAS_CONTEXT_ATTRIBUTES);\n if (!gl || typeof (gl as Partial<WebGL2RenderingContext>).enable !== 'function') return null;\n let renderer: WeaselRenderer;\n try {\n renderer = new WeaselRenderer({\n gl: gl as WebGL2RenderingContext,\n ...(shared ? {} : { canvas }),\n width, height, dpr,\n flattenTolerance: options.flattenTolerance,\n retainBudget: options.retainBudget,\n imageMinification: options.imageMinification,\n });\n } catch (e) {\n // A lost context fails to compile and may yet come back; anything\n // else fails the same way next time.\n if (!(gl as Partial<WebGL2RenderingContext>).isContextLost?.()) {\n REFUSED.add(canvas);\n console.warn('weasel: this canvas cannot be painted:', e);\n }\n return null;\n }\n // Added after the renderer's own listeners, so a subscriber runs against\n // a renderer that has already recompiled.\n const mine: Held = held = {\n canvas, renderer, shared, width, height, dpr, tileX: null, tileY: null,\n flattenTolerance: options.flattenTolerance, retainBudget: options.retainBudget,\n imageMinification: options.imageMinification, shaderKey: '',\n lost: false,\n onLost: () => { mine.lost = true; },\n onRestored: () => {\n mine.lost = false;\n restored.emit();\n },\n };\n canvas.addEventListener('webglcontextlost', held.onLost);\n canvas.addEventListener('webglcontextrestored', held.onRestored);\n let set = TENANTS.get(canvas);\n if (!set) { set = new Set(); TENANTS.set(canvas, set); }\n set.add(renderer);\n } else {\n // React writes the element's `width` and `height` attributes in CSS\n // pixels on every size change, so a size that went away and came back\n // matches the record while the buffer does not.\n const stale = !shared\n && (canvas.width !== Math.round(width * dpr) || canvas.height !== Math.round(height * dpr));\n if (stale || held.width !== width || held.height !== height || held.dpr !== dpr) {\n held.renderer.resize({ width, height, dpr });\n held.width = width;\n held.height = height;\n held.dpr = dpr;\n }\n if (held.flattenTolerance !== options.flattenTolerance) {\n held.renderer.setFlattenTolerance(options.flattenTolerance);\n held.flattenTolerance = options.flattenTolerance;\n }\n if (held.retainBudget !== options.retainBudget) {\n held.renderer.setRetainBudget(options.retainBudget);\n held.retainBudget = options.retainBudget;\n }\n if (held.imageMinification !== options.imageMinification) {\n held.renderer.setImageMinification(options.imageMinification);\n held.imageMinification = options.imageMinification;\n }\n }\n\n if (held.lost) return null;\n const { renderer } = held;\n const shaderKey = shaderKeyOf(options.shaders);\n if (shaderKey !== held.shaderKey) {\n registerShaders(renderer, options.shaders);\n held.shaderKey = shaderKey;\n }\n const tileX = tile?.x ?? null;\n const tileY = tile?.y ?? null;\n if (tileX !== held.tileX || tileY !== held.tileY) {\n renderer.setTarget(tile ? { origin: { x: tile.x, y: tile.y } } : null);\n held.tileX = tileX;\n held.tileY = tileY;\n }\n return renderer;\n };\n return {\n bind,\n paint(canvas, commands, view, size, options) {\n const renderer = bind(canvas, size, options);\n if (!renderer) return false;\n renderer.render(commands, viewToMat3(view), options?.render);\n return true;\n },\n subscribeRestored: restored.subscribe,\n release() {\n if (held) free(held);\n held = null;\n },\n };\n}\n\n/** Leases held on a canvas's behalf by `paintCanvas`, one per element. */\nconst CANVAS_LEASES: WeakMap<HTMLCanvasElement, CanvasRendererLease> = new WeakMap();\n\n/**\n * Paint `canvas` through a lease held for the element itself, for callers\n * with no component lifetime of their own. The renderer lives until\n * `releaseCanvasRenderer(canvas)`.\n */\nexport function paintCanvas(\n canvas: HTMLCanvasElement,\n commands: DrawCommand[],\n view: View,\n size: CanvasPaintSize,\n): boolean {\n let lease = CANVAS_LEASES.get(canvas);\n if (!lease) {\n lease = leaseCanvasRenderer();\n CANVAS_LEASES.set(canvas, lease);\n }\n return lease.paint(canvas, commands, view, size);\n}\n\n/** Free the renderer `paintCanvas` holds for the canvas, and every GL object\n * it owns. The next paint creates a fresh one. Renderers a surface component\n * holds are that component's to free, on unmount. */\nexport function releaseCanvasRenderer(canvas: HTMLCanvasElement): void {\n const lease = CANVAS_LEASES.get(canvas);\n if (!lease) return;\n CANVAS_LEASES.delete(canvas);\n lease.release();\n}\n"],"mappings":"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"}
@@ -1,10 +1,10 @@
1
1
  import { locksOf } from "../../interactions/actions/edit-anchors/params.js";
2
- import { representationOf } from "../../features/paths/pathRepresentation.js";
3
- import { classifyPathStorage, pathEditPathOf, resolveEditablePathOf } from "../../features/paths/editablePath.js";
4
- import { anchorDataPatch } from "../../features/paths/anchorData.js";
2
+ import { representationOf } from "../../domains/paths/pathRepresentation.js";
3
+ import { classifyPathStorage, pathEditPathOf, resolveEditablePathOf } from "../../domains/paths/editablePath.js";
4
+ import { anchorDataPatch } from "../../domains/paths/anchorData.js";
5
5
  import { finishCtx, finishEdit, otherOps, pathEditFollower, reshapeOps } from "./pathEditCommits.js";
6
6
  import { useTransformFollower } from "./transformFollowers.js";
7
- import { createPathEditState, isSelectedNode } from "../../features/paths/pathEditState.js";
7
+ import { createPathEditState, isSelectedNode } from "../../domains/paths/pathEditState.js";
8
8
  import { useState } from "react";
9
9
  import { useLatest } from "@weasel-js/react";
10
10
  import { resolveParams } from "@weasel-js/routing";
@@ -1 +1 @@
1
- {"version":3,"file":"editAnchors.js","names":[],"sources":["../../../src/canvas/deps/editAnchors.ts"],"sourcesContent":["/**\n * `useEditAnchorsDepSource` — wires the `editAnchors` dep.\n *\n * A node's editable polygon lives in one of two places: the pose itself\n * (`pose.kind === 'polygon'`), or `data.path` with a rect pose, stored pose-local.\n * `getEditablePath` returns it in **world coords** either way, and `applyEdit` commits in the\n * correct storage shape as one undoable batch, preserving the render invariant\n * `pathInWorld(stored, pose) === world`. What the node keeps per anchor — a `data.skin`, vertex\n * colors — is renumbered in that batch when the caller says where the points came from, and each path-edit extension finishes\n * the edit first (`PathEditExtension.finishEdit`). Live previews ride the standard\n * `OngoingHandle.previewIds/Pose/Data` triple — this dep doesn't own preview state.\n */\nimport { useState } from 'react';\nimport { useDepRegistry, useDepSource } from '@weasel-js/routing/react';\nimport { useLatest } from '@weasel-js/react';\nimport type { EditAnchorsDep } from 'interactions/actions/depSchema';\nimport { locksOf, type EditAnchorsParams } from 'interactions/actions/edit-anchors/params';\nimport type { Scene, NodeId } from 'core/scene/types';\nimport type { SelectionApi } from 'core/selection/useSelection';\nimport type { PointOrigin, PolygonPath } from '@weasel-js/geom';\nimport { anchorDataPatch } from 'features/paths/anchorData';\nimport { classifyPathStorage, pathEditPathOf, resolveEditablePathOf } from 'features/paths/editablePath';\nimport { representationOf } from 'features/paths/pathRepresentation';\nimport type { PathEditExtension } from 'features/paths/pathEditExtension';\nimport {\n finishCtx, finishEdit, otherOps, pathEditFollower, reshapeOps, type EditableNode,\n} from './pathEditCommits';\nimport { useTransformFollower } from './transformFollowers';\nimport { createPathEditState, isSelectedNode, type PathEditState } from 'features/paths/pathEditState';\nimport { resolveParams, type ActionTunings } from '@weasel-js/routing';\nimport type { Op } from 'core/ops/types';\n\ninterface OpsApplier {\n applyOps(ops: Op[], label?: string): void;\n}\n\n/** The part of a `PathEditState` the dep reads and writes. */\nexport type EditAnchorsStateRef = Omit<PathEditState, 'subscribe' | 'getEditingIds' | 'setExtensions'>;\n/** The same, with the change notice chrome reading the dep waits on. */\nexport type EditAnchorsStateSource = EditAnchorsStateRef & Partial<Pick<PathEditState, 'subscribe'>>;\n\nexport { resolveEditablePathOf };\n\nfunction tunedHandleLock(tune: ActionTunings | undefined): EditAnchorsParams['handleLock'] {\n const params = resolveParams(tune?.editAnchors?.params as Parameters<typeof resolveParams>[0]);\n return (params as EditAnchorsParams | undefined)?.handleLock;\n}\n\nconst EMPTY_ANCHOR_SELECTION: ReadonlySet<number> = new Set<number>();\n\n/**\n * Publish the `editAnchors` dep for `scene`: its path access and commits\n * through `adapter`, and the edit state of `externalState` (or one of its\n * own), with the extensions that state holds finishing every edit.\n */\nexport function useEditAnchorsDepSource(\n scene: Scene<unknown, string, unknown>,\n selection: SelectionApi,\n adapter: OpsApplier,\n externalState?: EditAnchorsStateRef,\n /** `<SceneCanvas tune>`: the handle locks start from its `editAnchors`\n * `handleLock` param until a toggle sets them. */\n tune?: ActionTunings,\n): void {\n const registry = useDepRegistry();\n const tuneRef = useLatest(tune);\n const sceneRef = useLatest(scene);\n const selectionRef = useLatest(selection);\n const adapterRef = useLatest(adapter);\n\n // A caller that shares the state with nothing else gets one of its own.\n const [ownState] = useState(() => createPathEditState({\n isSelectedNode: (id) => isSelectedNode(sceneRef.current, selectionRef.current, id),\n }));\n const stateRef = useLatest(externalState ?? ownState);\n\n useDepSource('editAnchors', (): EditAnchorsDep => {\n const sc = sceneRef.current;\n const ad = adapterRef.current;\n const state = stateRef.current;\n const effectiveId = state.getEditingId();\n // Anchor selection and the marquee belong to one specific edited node, so never\n // hand them out alongside an empty id.\n const selectedAnchors = effectiveId ? state.getSelectedAnchors() : EMPTY_ANCHOR_SELECTION;\n const marquee = effectiveId ? state.getMarquee() : null;\n const tool = effectiveId ? state.getTool() : null;\n return {\n editingId: effectiveId,\n setEditingId(id: string | null) {\n const next = id ?? '';\n if (effectiveId !== next) {\n registry.get('dispatchTrace')?.recordModeSwitch(\n 'editAnchors.editingId',\n effectiveId || null,\n next || null,\n next ? 'enter' : 'exit',\n );\n // Anchor indices only mean something relative to one path.\n state.setSelectedAnchors(EMPTY_ANCHOR_SELECTION);\n state.setSelectedMark(null);\n state.setMarquee(null);\n }\n stateRef.current.setEditingId(id);\n },\n selectedAnchors,\n setSelectedAnchors(next: Iterable<number>) {\n if (!effectiveId) return;\n const set = new Set(next);\n if (set.size > 0) state.setSelectedMark(null);\n state.setSelectedAnchors(set);\n },\n selectedMark: effectiveId ? state.getSelectedMark() : null,\n setSelectedMark(mark) {\n if (!effectiveId) return;\n if (mark !== null) state.setSelectedAnchors(EMPTY_ANCHOR_SELECTION);\n state.setSelectedMark(mark);\n },\n extensions: state.getExtensions(),\n marquee,\n setMarquee(rect) {\n if (!effectiveId) return;\n state.setMarquee(rect);\n },\n handleLocks: state.getHandleLocks() ?? locksOf(tunedHandleLock(tuneRef.current)),\n setHandleLocks(next) {\n state.setHandleLocks(next);\n registry.changed?.();\n },\n tool,\n setTool(id) {\n if (!effectiveId) return;\n state.setTool(id);\n },\n ...pathAccess(sc, ad, state.getExtensions()),\n };\n });\n useTransformFollower(() => pathEditFollower(sceneRef.current, stateRef.current.getExtensions()));\n}\n\n/** The members of the dep that read the scene and commit to it. */\nfunction pathAccess(\n sc: Scene<unknown, string, unknown>,\n ad: OpsApplier,\n extensions: readonly PathEditExtension[],\n): Pick<\n EditAnchorsDep,\n | 'getEditablePath' | 'getOutlinePath' | 'getStorageKind' | 'getNodeShape' | 'applyEdit' | 'editOps' | 'finishEdit'\n | 'store'\n> {\n const editOps = (id: string, worldPath: unknown, label: string, origins?: readonly (PointOrigin | null)[]): Op[] => {\n const node = sc.get(id as NodeId) as EditableNode | undefined;\n if (!node) return [];\n const done = finishEdit(extensions, node, worldPath as PolygonPath, origins, finishCtx(sc, id, extensions));\n if (!done) return [];\n // A form's origins number the anchors of its edit path, not data.path's.\n const before = origins && !representationOf(extensions, node.data) ? resolveEditablePathOf(node) : null;\n const patch = { ...(before && origins ? anchorDataPatch(node.data, before, done.path, origins) : {}), ...done.data };\n return [\n ...reshapeOps(id, node, done.path, label, Object.keys(patch).length > 0 ? patch : null, extensions),\n ...otherOps(sc, done.others, label, extensions),\n ];\n };\n return {\n getEditablePath(id: string): PolygonPath | null {\n const node = sc.get(id as NodeId);\n return pathEditPathOf(node as { pose: unknown; data: unknown }, extensions);\n },\n getOutlinePath(id: string): PolygonPath | null {\n return resolveEditablePathOf(sc.get(id as NodeId) as { pose: unknown; data: unknown });\n },\n getStorageKind(id: string): 'pose' | 'data' | null {\n const node = sc.get(id as NodeId);\n const storage = classifyPathStorage(node as { pose: unknown; data: unknown });\n return storage?.kind ?? null;\n },\n getNodeShape(id: string): { pose: unknown; data: unknown } | null {\n const node = sc.get(id as NodeId);\n if (!node) return null;\n return { pose: node.pose, data: node.data };\n },\n applyEdit(id: string, worldPath: unknown, label: string, origins?: readonly (PointOrigin | null)[]) {\n const ops = editOps(id, worldPath, label, origins);\n if (ops.length > 0) ad.applyOps(ops, label);\n },\n editOps,\n finishEdit(id: string, worldPath: unknown, origins?: readonly (PointOrigin | null)[]) {\n const node = sc.get(id as NodeId) as EditableNode | undefined;\n if (!node) return { path: worldPath, others: [] };\n const done = finishEdit(extensions, node, worldPath as PolygonPath, origins, finishCtx(sc, id, extensions));\n if (!done) return null;\n const others = [...done.others.values()].flatMap((o) => (o.path && !o.remove ? [{ id: o.id, path: o.path }] : []));\n return { path: done.path, others };\n },\n store(id, worldPath, label, data) {\n const node = sc.get(id as NodeId) as EditableNode | undefined;\n const ops = node ? reshapeOps(id, node, worldPath, label, data ?? null, extensions) : [];\n if (ops.length > 0) ad.applyOps(ops, label);\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"editAnchors.js","names":[],"sources":["../../../src/canvas/deps/editAnchors.ts"],"sourcesContent":["/**\n * `useEditAnchorsDepSource` — wires the `editAnchors` dep.\n *\n * A node's editable polygon lives in one of two places: the pose itself\n * (`pose.kind === 'polygon'`), or `data.path` with a rect pose, stored pose-local.\n * `getEditablePath` returns it in **world coords** either way, and `applyEdit` commits in the\n * correct storage shape as one undoable batch, preserving the render invariant\n * `pathInWorld(stored, pose) === world`. What the node keeps per anchor — a `data.skin`, vertex\n * colors — is renumbered in that batch when the caller says where the points came from, and each path-edit extension finishes\n * the edit first (`PathEditExtension.finishEdit`). Live previews ride the standard\n * `OngoingHandle.previewIds/Pose/Data` triple — this dep doesn't own preview state.\n */\nimport { useState } from 'react';\nimport { useDepRegistry, useDepSource } from '@weasel-js/routing/react';\nimport { useLatest } from '@weasel-js/react';\nimport type { EditAnchorsDep } from 'interactions/actions/depSchema';\nimport { locksOf, type EditAnchorsParams } from 'interactions/actions/edit-anchors/params';\nimport type { Scene, NodeId } from 'core/scene/types';\nimport type { SelectionApi } from 'core/selection/useSelection';\nimport type { PointOrigin, PolygonPath } from '@weasel-js/geom';\nimport { anchorDataPatch } from 'domains/paths/anchorData';\nimport { classifyPathStorage, pathEditPathOf, resolveEditablePathOf } from 'domains/paths/editablePath';\nimport { representationOf } from 'domains/paths/pathRepresentation';\nimport type { PathEditExtension } from 'domains/paths/pathEditExtension';\nimport {\n finishCtx, finishEdit, otherOps, pathEditFollower, reshapeOps, type EditableNode,\n} from './pathEditCommits';\nimport { useTransformFollower } from './transformFollowers';\nimport { createPathEditState, isSelectedNode, type PathEditState } from 'domains/paths/pathEditState';\nimport { resolveParams, type ActionTunings } from '@weasel-js/routing';\nimport type { Op } from 'core/ops/types';\n\ninterface OpsApplier {\n applyOps(ops: Op[], label?: string): void;\n}\n\n/** The part of a `PathEditState` the dep reads and writes. */\nexport type EditAnchorsStateRef = Omit<PathEditState, 'subscribe' | 'getEditingIds' | 'setExtensions'>;\n/** The same, with the change notice chrome reading the dep waits on. */\nexport type EditAnchorsStateSource = EditAnchorsStateRef & Partial<Pick<PathEditState, 'subscribe'>>;\n\nexport { resolveEditablePathOf };\n\nfunction tunedHandleLock(tune: ActionTunings | undefined): EditAnchorsParams['handleLock'] {\n const params = resolveParams(tune?.editAnchors?.params as Parameters<typeof resolveParams>[0]);\n return (params as EditAnchorsParams | undefined)?.handleLock;\n}\n\nconst EMPTY_ANCHOR_SELECTION: ReadonlySet<number> = new Set<number>();\n\n/**\n * Publish the `editAnchors` dep for `scene`: its path access and commits\n * through `adapter`, and the edit state of `externalState` (or one of its\n * own), with the extensions that state holds finishing every edit.\n */\nexport function useEditAnchorsDepSource(\n scene: Scene<unknown, string, unknown>,\n selection: SelectionApi,\n adapter: OpsApplier,\n externalState?: EditAnchorsStateRef,\n /** `<SceneCanvas tune>`: the handle locks start from its `editAnchors`\n * `handleLock` param until a toggle sets them. */\n tune?: ActionTunings,\n): void {\n const registry = useDepRegistry();\n const tuneRef = useLatest(tune);\n const sceneRef = useLatest(scene);\n const selectionRef = useLatest(selection);\n const adapterRef = useLatest(adapter);\n\n // A caller that shares the state with nothing else gets one of its own.\n const [ownState] = useState(() => createPathEditState({\n isSelectedNode: (id) => isSelectedNode(sceneRef.current, selectionRef.current, id),\n }));\n const stateRef = useLatest(externalState ?? ownState);\n\n useDepSource('editAnchors', (): EditAnchorsDep => {\n const sc = sceneRef.current;\n const ad = adapterRef.current;\n const state = stateRef.current;\n const effectiveId = state.getEditingId();\n // Anchor selection and the marquee belong to one specific edited node, so never\n // hand them out alongside an empty id.\n const selectedAnchors = effectiveId ? state.getSelectedAnchors() : EMPTY_ANCHOR_SELECTION;\n const marquee = effectiveId ? state.getMarquee() : null;\n const tool = effectiveId ? state.getTool() : null;\n return {\n editingId: effectiveId,\n setEditingId(id: string | null) {\n const next = id ?? '';\n if (effectiveId !== next) {\n registry.get('dispatchTrace')?.recordModeSwitch(\n 'editAnchors.editingId',\n effectiveId || null,\n next || null,\n next ? 'enter' : 'exit',\n );\n // Anchor indices only mean something relative to one path.\n state.setSelectedAnchors(EMPTY_ANCHOR_SELECTION);\n state.setSelectedMark(null);\n state.setMarquee(null);\n }\n stateRef.current.setEditingId(id);\n },\n selectedAnchors,\n setSelectedAnchors(next: Iterable<number>) {\n if (!effectiveId) return;\n const set = new Set(next);\n if (set.size > 0) state.setSelectedMark(null);\n state.setSelectedAnchors(set);\n },\n selectedMark: effectiveId ? state.getSelectedMark() : null,\n setSelectedMark(mark) {\n if (!effectiveId) return;\n if (mark !== null) state.setSelectedAnchors(EMPTY_ANCHOR_SELECTION);\n state.setSelectedMark(mark);\n },\n extensions: state.getExtensions(),\n marquee,\n setMarquee(rect) {\n if (!effectiveId) return;\n state.setMarquee(rect);\n },\n handleLocks: state.getHandleLocks() ?? locksOf(tunedHandleLock(tuneRef.current)),\n setHandleLocks(next) {\n state.setHandleLocks(next);\n registry.changed?.();\n },\n tool,\n setTool(id) {\n if (!effectiveId) return;\n state.setTool(id);\n },\n ...pathAccess(sc, ad, state.getExtensions()),\n };\n });\n useTransformFollower(() => pathEditFollower(sceneRef.current, stateRef.current.getExtensions()));\n}\n\n/** The members of the dep that read the scene and commit to it. */\nfunction pathAccess(\n sc: Scene<unknown, string, unknown>,\n ad: OpsApplier,\n extensions: readonly PathEditExtension[],\n): Pick<\n EditAnchorsDep,\n | 'getEditablePath' | 'getOutlinePath' | 'getStorageKind' | 'getNodeShape' | 'applyEdit' | 'editOps' | 'finishEdit'\n | 'store'\n> {\n const editOps = (id: string, worldPath: unknown, label: string, origins?: readonly (PointOrigin | null)[]): Op[] => {\n const node = sc.get(id as NodeId) as EditableNode | undefined;\n if (!node) return [];\n const done = finishEdit(extensions, node, worldPath as PolygonPath, origins, finishCtx(sc, id, extensions));\n if (!done) return [];\n // A form's origins number the anchors of its edit path, not data.path's.\n const before = origins && !representationOf(extensions, node.data) ? resolveEditablePathOf(node) : null;\n const patch = { ...(before && origins ? anchorDataPatch(node.data, before, done.path, origins) : {}), ...done.data };\n return [\n ...reshapeOps(id, node, done.path, label, Object.keys(patch).length > 0 ? patch : null, extensions),\n ...otherOps(sc, done.others, label, extensions),\n ];\n };\n return {\n getEditablePath(id: string): PolygonPath | null {\n const node = sc.get(id as NodeId);\n return pathEditPathOf(node as { pose: unknown; data: unknown }, extensions);\n },\n getOutlinePath(id: string): PolygonPath | null {\n return resolveEditablePathOf(sc.get(id as NodeId) as { pose: unknown; data: unknown });\n },\n getStorageKind(id: string): 'pose' | 'data' | null {\n const node = sc.get(id as NodeId);\n const storage = classifyPathStorage(node as { pose: unknown; data: unknown });\n return storage?.kind ?? null;\n },\n getNodeShape(id: string): { pose: unknown; data: unknown } | null {\n const node = sc.get(id as NodeId);\n if (!node) return null;\n return { pose: node.pose, data: node.data };\n },\n applyEdit(id: string, worldPath: unknown, label: string, origins?: readonly (PointOrigin | null)[]) {\n const ops = editOps(id, worldPath, label, origins);\n if (ops.length > 0) ad.applyOps(ops, label);\n },\n editOps,\n finishEdit(id: string, worldPath: unknown, origins?: readonly (PointOrigin | null)[]) {\n const node = sc.get(id as NodeId) as EditableNode | undefined;\n if (!node) return { path: worldPath, others: [] };\n const done = finishEdit(extensions, node, worldPath as PolygonPath, origins, finishCtx(sc, id, extensions));\n if (!done) return null;\n const others = [...done.others.values()].flatMap((o) => (o.path && !o.remove ? [{ id: o.id, path: o.path }] : []));\n return { path: done.path, others };\n },\n store(id, worldPath, label, data) {\n const node = sc.get(id as NodeId) as EditableNode | undefined;\n const ops = node ? reshapeOps(id, node, worldPath, label, data ?? null, extensions) : [];\n if (ops.length > 0) ad.applyOps(ops, label);\n },\n };\n}\n"],"mappings":"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"}
@@ -10,17 +10,26 @@ import { useDepRegistry, useDepSource } from "@weasel-js/routing/react";
10
10
  * innermost of those still in the scene, so deleting the group lands on its
11
11
  * parent, and an undo that brings it back lands on it again.
12
12
  */
13
- /** Whether `id` is reachable while `group` is entered: inside it, and not the group itself. With
14
- * nothing entered every node is. Picking, region selects and select-all all ask this. */
13
+ /** Whether a node is entered as a group is: a group, or a node that consumes its children. */
14
+ function isEnterable(node) {
15
+ return node?.kind === "group" || node?.consumesChildren === true;
16
+ }
17
+ /** Whether `id` is reachable while `group` is entered: inside it, and not the group itself, and
18
+ * not consumed by a node inside it (`SceneNode.consumesChildren`). With nothing entered every node
19
+ * no node consumes is. Picking, region selects and select-all all ask this. */
15
20
  function reachableInGroup(scene, group, id) {
16
- return group === null || scene.ancestorsOf(id).includes(group);
21
+ for (const above of scene.ancestorsOf(id)) {
22
+ if (above === group) return true;
23
+ if (scene.get(above)?.consumesChildren === true) return false;
24
+ }
25
+ return group === null;
17
26
  }
18
27
  /** An entered-group store over `scene`, with nothing entered. */
19
28
  function createEnteredGroup(scene) {
20
29
  let chain = [];
21
30
  let announced = null;
22
31
  const listeners = /* @__PURE__ */ new Set();
23
- const isGroup = (id) => scene.get(id)?.kind === "group";
32
+ const isGroup = (id) => isEnterable(scene.get(id));
24
33
  const get = () => chain.find(isGroup) ?? null;
25
34
  const groupsFrom = (id) => [id, ...scene.ancestorsOf(id)].filter(isGroup);
26
35
  const announce = () => {
@@ -77,5 +86,5 @@ function useEnteredGroupApiSource(dep) {
77
86
  }
78
87
 
79
88
  //#endregion
80
- export { createEnteredGroup, reachableInGroup, useEnteredGroup, useEnteredGroupApiSource };
89
+ export { createEnteredGroup, isEnterable, reachableInGroup, useEnteredGroup, useEnteredGroupApiSource };
81
90
  //# sourceMappingURL=enteredGroup.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"enteredGroup.js","names":[],"sources":["../../../src/canvas/deps/enteredGroup.ts"],"sourcesContent":["/**\n * The entered group: the group a press resolves inside of. Session state beside\n * the selection, not scene data, so it is never saved and never undone.\n *\n * It remembers the entered group and the groups above it. `get()` answers the\n * innermost of those still in the scene, so deleting the group lands on its\n * parent, and an undo that brings it back lands on it again.\n */\nimport { useEffect, useMemo } from 'react';\nimport { useDepRegistry, useDepSource } from '@weasel-js/routing/react';\nimport type { NodeId, Scene } from 'core/scene/types';\n\n/** The group a press resolves inside of, held beside the selection; `<SceneCanvas enteredGroup>` takes one. */\nexport interface EnteredGroupApi {\n get(): NodeId | null;\n enter(id: NodeId): void;\n /** Moves to the nearest group above the current one, or to none. */\n leave(): void;\n /** Leaves every entered group at once. */\n leaveAll(): void;\n subscribe(fn: () => void): () => void;\n}\n\n/** Whether `id` is reachable while `group` is entered: inside it, and not the group itself. With\n * nothing entered every node is. Picking, region selects and select-all all ask this. */\nexport function reachableInGroup(\n scene: Pick<Scene<unknown, string, unknown>, 'ancestorsOf'>,\n group: NodeId | null,\n id: string,\n): boolean {\n return group === null || scene.ancestorsOf(id as NodeId).includes(group);\n}\n\ntype SceneReads = Pick<Scene<unknown, string, unknown>, 'get' | 'ancestorsOf' | 'subscribe'>;\n\n/** An entered-group store over `scene`, with nothing entered. */\nexport function createEnteredGroup(scene: SceneReads): EnteredGroupApi {\n let chain: NodeId[] = [];\n let announced: NodeId | null = null;\n const listeners = new Set<() => void>();\n\n const isGroup = (id: NodeId) => scene.get(id)?.kind === 'group';\n const get = (): NodeId | null => chain.find(isGroup) ?? null;\n const groupsFrom = (id: NodeId): NodeId[] => [id, ...scene.ancestorsOf(id)].filter(isGroup);\n\n const announce = () => {\n announced = get();\n for (const fn of [...listeners]) fn();\n };\n\n let stopScene: (() => void) | null = null;\n const watchScene = () => scene.subscribe(() => {\n if (get() !== announced) announce();\n });\n\n return {\n get,\n enter(id) {\n if (!isGroup(id)) return;\n chain = groupsFrom(id);\n announce();\n },\n leave() {\n const here = get();\n if (here === null) return;\n chain = groupsFrom(here).slice(1);\n announce();\n },\n leaveAll() {\n if (get() === null) return;\n chain = [];\n announce();\n },\n subscribe(fn) {\n if (listeners.size === 0) {\n announced = get();\n stopScene = watchScene();\n }\n listeners.add(fn);\n return () => {\n listeners.delete(fn);\n if (listeners.size === 0) { stopScene?.(); stopScene = null; }\n };\n },\n };\n}\n\n/** One entered-group store for the life of the scene. */\nexport function useEnteredGroup(scene: SceneReads): EnteredGroupApi {\n return useMemo(() => createEnteredGroup(scene), [scene]);\n}\n\nexport function useEnteredGroupApiSource(dep: EnteredGroupApi): void {\n useDepSource('enteredGroup', () => dep);\n // What is drawn or pasted while a group is entered lands in it, unless a source\n // registered under this one, such as a symmetry group, names a container.\n useDepSource('insertInto', (below) => ({ get: () => below()?.get() ?? dep.get() }));\n const registry = useDepRegistry();\n useEffect(() => dep.subscribe(() => registry.changed?.()), [dep, registry]);\n}\n"],"mappings":";;;;;;;;;;;;;;AAyBA,SAAgB,iBACd,OACA,OACA,IACS;CACT,OAAO,UAAU,QAAQ,MAAM,YAAY,EAAY,CAAC,CAAC,SAAS,KAAK;AACzE;;AAKA,SAAgB,mBAAmB,OAAoC;CACrE,IAAI,QAAkB,CAAC;CACvB,IAAI,YAA2B;CAC/B,MAAM,4BAAY,IAAI,IAAgB;CAEtC,MAAM,WAAW,OAAe,MAAM,IAAI,EAAE,CAAC,EAAE,SAAS;CACxD,MAAM,YAA2B,MAAM,KAAK,OAAO,KAAK;CACxD,MAAM,cAAc,OAAyB,CAAC,IAAI,GAAG,MAAM,YAAY,EAAE,CAAC,CAAC,CAAC,OAAO,OAAO;CAE1F,MAAM,iBAAiB;EACrB,YAAY,IAAI;EAChB,KAAK,MAAM,MAAM,CAAC,GAAG,SAAS,GAAG,GAAG;CACtC;CAEA,IAAI,YAAiC;CACrC,MAAM,mBAAmB,MAAM,gBAAgB;EAC7C,IAAI,IAAI,MAAM,WAAW,SAAS;CACpC,CAAC;CAED,OAAO;EACL;EACA,MAAM,IAAI;GACR,IAAI,CAAC,QAAQ,EAAE,GAAG;GAClB,QAAQ,WAAW,EAAE;GACrB,SAAS;EACX;EACA,QAAQ;GACN,MAAM,OAAO,IAAI;GACjB,IAAI,SAAS,MAAM;GACnB,QAAQ,WAAW,IAAI,CAAC,CAAC,MAAM,CAAC;GAChC,SAAS;EACX;EACA,WAAW;GACT,IAAI,IAAI,MAAM,MAAM;GACpB,QAAQ,CAAC;GACT,SAAS;EACX;EACA,UAAU,IAAI;GACZ,IAAI,UAAU,SAAS,GAAG;IACxB,YAAY,IAAI;IAChB,YAAY,WAAW;GACzB;GACA,UAAU,IAAI,EAAE;GAChB,aAAa;IACX,UAAU,OAAO,EAAE;IACnB,IAAI,UAAU,SAAS,GAAG;KAAE,YAAY;KAAG,YAAY;IAAM;GAC/D;EACF;CACF;AACF;;AAGA,SAAgB,gBAAgB,OAAoC;CAClE,OAAO,cAAc,mBAAmB,KAAK,GAAG,CAAC,KAAK,CAAC;AACzD;AAEA,SAAgB,yBAAyB,KAA4B;CACnE,aAAa,sBAAsB,GAAG;CAGtC,aAAa,eAAe,WAAW,EAAE,WAAW,MAAM,CAAC,EAAE,IAAI,KAAK,IAAI,IAAI,EAAE,EAAE;CAClF,MAAM,WAAW,eAAe;CAChC,gBAAgB,IAAI,gBAAgB,SAAS,UAAU,CAAC,GAAG,CAAC,KAAK,QAAQ,CAAC;AAC5E"}
1
+ {"version":3,"file":"enteredGroup.js","names":[],"sources":["../../../src/canvas/deps/enteredGroup.ts"],"sourcesContent":["/**\n * The entered group: the group a press resolves inside of. Session state beside\n * the selection, not scene data, so it is never saved and never undone.\n *\n * It remembers the entered group and the groups above it. `get()` answers the\n * innermost of those still in the scene, so deleting the group lands on its\n * parent, and an undo that brings it back lands on it again.\n */\nimport { useEffect, useMemo } from 'react';\nimport { useDepRegistry, useDepSource } from '@weasel-js/routing/react';\nimport type { NodeId, Scene } from 'core/scene/types';\n\n/** The group a press resolves inside of, held beside the selection; `<SceneCanvas enteredGroup>` takes one. */\nexport interface EnteredGroupApi {\n get(): NodeId | null;\n enter(id: NodeId): void;\n /** Moves to the nearest group above the current one, or to none. */\n leave(): void;\n /** Leaves every entered group at once. */\n leaveAll(): void;\n subscribe(fn: () => void): () => void;\n}\n\n/** Whether a node is entered as a group is: a group, or a node that consumes its children. */\nexport function isEnterable(node: { kind?: string; consumesChildren?: boolean } | undefined): boolean {\n return node?.kind === 'group' || node?.consumesChildren === true;\n}\n\n/** Whether `id` is reachable while `group` is entered: inside it, and not the group itself, and\n * not consumed by a node inside it (`SceneNode.consumesChildren`). With nothing entered every node\n * no node consumes is. Picking, region selects and select-all all ask this. */\nexport function reachableInGroup(\n scene: Pick<Scene<unknown, string, unknown>, 'ancestorsOf' | 'get'>,\n group: NodeId | null,\n id: string,\n): boolean {\n for (const above of scene.ancestorsOf(id as NodeId)) {\n if (above === group) return true;\n if (scene.get(above)?.consumesChildren === true) return false;\n }\n return group === null;\n}\n\ntype SceneReads = Pick<Scene<unknown, string, unknown>, 'get' | 'ancestorsOf' | 'subscribe'>;\n\n/** An entered-group store over `scene`, with nothing entered. */\nexport function createEnteredGroup(scene: SceneReads): EnteredGroupApi {\n let chain: NodeId[] = [];\n let announced: NodeId | null = null;\n const listeners = new Set<() => void>();\n\n const isGroup = (id: NodeId) => isEnterable(scene.get(id));\n const get = (): NodeId | null => chain.find(isGroup) ?? null;\n const groupsFrom = (id: NodeId): NodeId[] => [id, ...scene.ancestorsOf(id)].filter(isGroup);\n\n const announce = () => {\n announced = get();\n for (const fn of [...listeners]) fn();\n };\n\n let stopScene: (() => void) | null = null;\n const watchScene = () => scene.subscribe(() => {\n if (get() !== announced) announce();\n });\n\n return {\n get,\n enter(id) {\n if (!isGroup(id)) return;\n chain = groupsFrom(id);\n announce();\n },\n leave() {\n const here = get();\n if (here === null) return;\n chain = groupsFrom(here).slice(1);\n announce();\n },\n leaveAll() {\n if (get() === null) return;\n chain = [];\n announce();\n },\n subscribe(fn) {\n if (listeners.size === 0) {\n announced = get();\n stopScene = watchScene();\n }\n listeners.add(fn);\n return () => {\n listeners.delete(fn);\n if (listeners.size === 0) { stopScene?.(); stopScene = null; }\n };\n },\n };\n}\n\n/** One entered-group store for the life of the scene. */\nexport function useEnteredGroup(scene: SceneReads): EnteredGroupApi {\n return useMemo(() => createEnteredGroup(scene), [scene]);\n}\n\nexport function useEnteredGroupApiSource(dep: EnteredGroupApi): void {\n useDepSource('enteredGroup', () => dep);\n // What is drawn or pasted while a group is entered lands in it, unless a source\n // registered under this one, such as a symmetry group, names a container.\n useDepSource('insertInto', (below) => ({ get: () => below()?.get() ?? dep.get() }));\n const registry = useDepRegistry();\n useEffect(() => dep.subscribe(() => registry.changed?.()), [dep, registry]);\n}\n"],"mappings":";;;;;;;;;;;;;AAwBA,SAAgB,YAAY,MAA0E;CACpG,OAAO,MAAM,SAAS,WAAW,MAAM,qBAAqB;AAC9D;;;;AAKA,SAAgB,iBACd,OACA,OACA,IACS;CACT,KAAK,MAAM,SAAS,MAAM,YAAY,EAAY,GAAG;EACnD,IAAI,UAAU,OAAO,OAAO;EAC5B,IAAI,MAAM,IAAI,KAAK,CAAC,EAAE,qBAAqB,MAAM,OAAO;CAC1D;CACA,OAAO,UAAU;AACnB;;AAKA,SAAgB,mBAAmB,OAAoC;CACrE,IAAI,QAAkB,CAAC;CACvB,IAAI,YAA2B;CAC/B,MAAM,4BAAY,IAAI,IAAgB;CAEtC,MAAM,WAAW,OAAe,YAAY,MAAM,IAAI,EAAE,CAAC;CACzD,MAAM,YAA2B,MAAM,KAAK,OAAO,KAAK;CACxD,MAAM,cAAc,OAAyB,CAAC,IAAI,GAAG,MAAM,YAAY,EAAE,CAAC,CAAC,CAAC,OAAO,OAAO;CAE1F,MAAM,iBAAiB;EACrB,YAAY,IAAI;EAChB,KAAK,MAAM,MAAM,CAAC,GAAG,SAAS,GAAG,GAAG;CACtC;CAEA,IAAI,YAAiC;CACrC,MAAM,mBAAmB,MAAM,gBAAgB;EAC7C,IAAI,IAAI,MAAM,WAAW,SAAS;CACpC,CAAC;CAED,OAAO;EACL;EACA,MAAM,IAAI;GACR,IAAI,CAAC,QAAQ,EAAE,GAAG;GAClB,QAAQ,WAAW,EAAE;GACrB,SAAS;EACX;EACA,QAAQ;GACN,MAAM,OAAO,IAAI;GACjB,IAAI,SAAS,MAAM;GACnB,QAAQ,WAAW,IAAI,CAAC,CAAC,MAAM,CAAC;GAChC,SAAS;EACX;EACA,WAAW;GACT,IAAI,IAAI,MAAM,MAAM;GACpB,QAAQ,CAAC;GACT,SAAS;EACX;EACA,UAAU,IAAI;GACZ,IAAI,UAAU,SAAS,GAAG;IACxB,YAAY,IAAI;IAChB,YAAY,WAAW;GACzB;GACA,UAAU,IAAI,EAAE;GAChB,aAAa;IACX,UAAU,OAAO,EAAE;IACnB,IAAI,UAAU,SAAS,GAAG;KAAE,YAAY;KAAG,YAAY;IAAM;GAC/D;EACF;CACF;AACF;;AAGA,SAAgB,gBAAgB,OAAoC;CAClE,OAAO,cAAc,mBAAmB,KAAK,GAAG,CAAC,KAAK,CAAC;AACzD;AAEA,SAAgB,yBAAyB,KAA4B;CACnE,aAAa,sBAAsB,GAAG;CAGtC,aAAa,eAAe,WAAW,EAAE,WAAW,MAAM,CAAC,EAAE,IAAI,KAAK,IAAI,IAAI,EAAE,EAAE;CAClF,MAAM,WAAW,eAAe;CAChC,gBAAgB,IAAI,gBAAgB,SAAS,UAAU,CAAC,GAAG,CAAC,KAAK,QAAQ,CAAC;AAC5E"}
@@ -3,11 +3,12 @@ import { useLatest } from "@weasel-js/react";
3
3
  import { useDepSource } from "@weasel-js/routing/react";
4
4
 
5
5
  //#region src/canvas/deps/ingestion.ts
6
- function useIngestionDepSource(canvasRef, getView, resolveSrc, svg, clipboard) {
6
+ function useIngestionDepSource(canvasRef, getView, resolveSrc, svg, clipboard, handlers) {
7
7
  const getViewRef = useLatest(getView);
8
8
  const resolveSrcRef = useLatest(resolveSrc);
9
9
  const svgRef = useLatest(svg);
10
10
  const clipboardRef = useLatest(clipboard);
11
+ const handlersRef = useLatest(handlers);
11
12
  useDepSource("ingestion", () => ({
12
13
  viewportWorldRect() {
13
14
  const canvas = canvasRef.current;
@@ -27,6 +28,9 @@ function useIngestionDepSource(canvasRef, getView, resolveSrc, svg, clipboard) {
27
28
  },
28
29
  get clipboard() {
29
30
  return clipboardRef.current;
31
+ },
32
+ get handlers() {
33
+ return handlersRef.current;
30
34
  }
31
35
  }));
32
36
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ingestion.js","names":[],"sources":["../../../src/canvas/deps/ingestion.ts"],"sourcesContent":["/**\n * `useIngestionDepSource` — wires the `ingestion` dep consumed by\n * `ingestAction`, for the canvas's own view. Computes the visible world rect\n * from the canvas's client rect + current view, and forwards the consumer's\n * optional `resolveSrc`. A `<CanvasView>` overlays this dep with its own rect.\n */\nimport type { RefObject } from 'react';\nimport { useDepSource } from '@weasel-js/routing/react';\nimport { useLatest } from '@weasel-js/react';\nimport type { ClipboardIngestCtx, IngestionDep, SvgIngestOptions } from 'interactions/actions/depSchema';\nimport { viewportWorldRect } from 'core/viewport/viewportWorldRect';\nimport type { View } from 'core/viewport/view';\n\nexport function useIngestionDepSource(\n canvasRef: RefObject<HTMLElement | null>,\n getView: () => View,\n resolveSrc?: (file: File) => Promise<string>,\n svg?: SvgIngestOptions,\n clipboard?: ClipboardIngestCtx,\n): void {\n const getViewRef = useLatest(getView);\n const resolveSrcRef = useLatest(resolveSrc);\n const svgRef = useLatest(svg);\n const clipboardRef = useLatest(clipboard);\n\n useDepSource('ingestion', (): IngestionDep => ({\n viewportWorldRect() {\n const canvas = canvasRef.current;\n if (!canvas) return { x: 0, y: 0, width: 0, height: 0 };\n return viewportWorldRect(getViewRef.current(), canvas.getBoundingClientRect());\n },\n get resolveSrc() {\n return resolveSrcRef.current;\n },\n get svg() {\n return svgRef.current;\n },\n get clipboard() {\n return clipboardRef.current;\n },\n }));\n}\n"],"mappings":";;;;;AAaA,SAAgB,sBACd,WACA,SACA,YACA,KACA,WACM;CACN,MAAM,aAAa,UAAU,OAAO;CACpC,MAAM,gBAAgB,UAAU,UAAU;CAC1C,MAAM,SAAS,UAAU,GAAG;CAC5B,MAAM,eAAe,UAAU,SAAS;CAExC,aAAa,oBAAkC;EAC7C,oBAAoB;GAClB,MAAM,SAAS,UAAU;GACzB,IAAI,CAAC,QAAQ,OAAO;IAAE,GAAG;IAAG,GAAG;IAAG,OAAO;IAAG,QAAQ;GAAE;GACtD,OAAO,kBAAkB,WAAW,QAAQ,GAAG,OAAO,sBAAsB,CAAC;EAC/E;EACA,IAAI,aAAa;GACf,OAAO,cAAc;EACvB;EACA,IAAI,MAAM;GACR,OAAO,OAAO;EAChB;EACA,IAAI,YAAY;GACd,OAAO,aAAa;EACtB;CACF,EAAE;AACJ"}
1
+ {"version":3,"file":"ingestion.js","names":[],"sources":["../../../src/canvas/deps/ingestion.ts"],"sourcesContent":["/**\n * `useIngestionDepSource` — wires the `ingestion` dep consumed by\n * `ingestAction`, for the canvas's own view. Computes the visible world rect\n * from the canvas's client rect + current view, and forwards the consumer's\n * optional `resolveSrc`, `svg`, the clipboard seam and the canvas's content handlers. A `<CanvasView>` overlays this dep with its own rect.\n */\nimport type { RefObject } from 'react';\nimport { useDepSource } from '@weasel-js/routing/react';\nimport { useLatest } from '@weasel-js/react';\nimport type { ClipboardIngestCtx, IngestionDep, SvgIngestOptions } from 'interactions/actions/depSchema';\nimport type { ContentHandlerEntry } from 'domains/ingestion/contentHandlers';\nimport { viewportWorldRect } from 'core/viewport/viewportWorldRect';\nimport type { View } from 'core/viewport/view';\n\nexport function useIngestionDepSource(\n canvasRef: RefObject<HTMLElement | null>,\n getView: () => View,\n resolveSrc?: (file: File) => Promise<string>,\n svg?: SvgIngestOptions,\n clipboard?: ClipboardIngestCtx,\n handlers?: readonly ContentHandlerEntry[],\n): void {\n const getViewRef = useLatest(getView);\n const resolveSrcRef = useLatest(resolveSrc);\n const svgRef = useLatest(svg);\n const clipboardRef = useLatest(clipboard);\n const handlersRef = useLatest(handlers);\n\n useDepSource('ingestion', (): IngestionDep => ({\n viewportWorldRect() {\n const canvas = canvasRef.current;\n if (!canvas) return { x: 0, y: 0, width: 0, height: 0 };\n return viewportWorldRect(getViewRef.current(), canvas.getBoundingClientRect());\n },\n get resolveSrc() {\n return resolveSrcRef.current;\n },\n get svg() {\n return svgRef.current;\n },\n get clipboard() {\n return clipboardRef.current;\n },\n get handlers() {\n return handlersRef.current;\n },\n }));\n}\n"],"mappings":";;;;;AAcA,SAAgB,sBACd,WACA,SACA,YACA,KACA,WACA,UACM;CACN,MAAM,aAAa,UAAU,OAAO;CACpC,MAAM,gBAAgB,UAAU,UAAU;CAC1C,MAAM,SAAS,UAAU,GAAG;CAC5B,MAAM,eAAe,UAAU,SAAS;CACxC,MAAM,cAAc,UAAU,QAAQ;CAEtC,aAAa,oBAAkC;EAC7C,oBAAoB;GAClB,MAAM,SAAS,UAAU;GACzB,IAAI,CAAC,QAAQ,OAAO;IAAE,GAAG;IAAG,GAAG;IAAG,OAAO;IAAG,QAAQ;GAAE;GACtD,OAAO,kBAAkB,WAAW,QAAQ,GAAG,OAAO,sBAAsB,CAAC;EAC/E;EACA,IAAI,aAAa;GACf,OAAO,cAAc;EACvB;EACA,IAAI,MAAM;GACR,OAAO,OAAO;EAChB;EACA,IAAI,YAAY;GACd,OAAO,aAAa;EACtB;EACA,IAAI,WAAW;GACb,OAAO,YAAY;EACrB;CACF,EAAE;AACJ"}