@weasel-js/core 1.13.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 (741) hide show
  1. package/CHANGELOG.md +300 -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 +15 -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/buildSceneTree.js +5 -3
  133. package/dist/canvas/buildSceneTree.js.map +1 -1
  134. package/dist/canvas/deps/editAnchors.js +4 -4
  135. package/dist/canvas/deps/editAnchors.js.map +1 -1
  136. package/dist/canvas/deps/enteredGroup.js +14 -5
  137. package/dist/canvas/deps/enteredGroup.js.map +1 -1
  138. package/dist/canvas/deps/ingestion.js +5 -1
  139. package/dist/canvas/deps/ingestion.js.map +1 -1
  140. package/dist/canvas/deps/insert.js.map +1 -1
  141. package/dist/canvas/deps/insertSite.js +5 -3
  142. package/dist/canvas/deps/insertSite.js.map +1 -1
  143. package/dist/canvas/deps/nodeAtPoint.js +1 -12
  144. package/dist/canvas/deps/nodeAtPoint.js.map +1 -1
  145. package/dist/canvas/deps/pathEditCommits.js +3 -3
  146. package/dist/canvas/deps/pathEditCommits.js.map +1 -1
  147. package/dist/canvas/deps/slice.js +16 -2
  148. package/dist/canvas/deps/slice.js.map +1 -1
  149. package/dist/canvas/deps/snap.js +5 -5
  150. package/dist/canvas/deps/snap.js.map +1 -1
  151. package/dist/canvas/deps/textEdit.js +5 -6
  152. package/dist/canvas/deps/textEdit.js.map +1 -1
  153. package/dist/canvas/derivedPath.js +8 -3
  154. package/dist/canvas/derivedPath.js.map +1 -1
  155. package/dist/canvas/getNodeAtPoint.js +2 -2
  156. package/dist/canvas/getNodeAtPoint.js.map +1 -1
  157. package/dist/canvas/insertPreviewExtent.js.map +1 -1
  158. package/dist/canvas/layerSlots.js.map +1 -1
  159. package/dist/canvas/nodeShapes/measurePainter.js +1 -1
  160. package/dist/canvas/nodeShapes/measurePainter.js.map +1 -1
  161. package/dist/canvas/nodeShapes/strokedBounds.js +5 -3
  162. package/dist/canvas/nodeShapes/strokedBounds.js.map +1 -1
  163. package/dist/canvas/overlayRoleStrokes.js +6 -0
  164. package/dist/canvas/overlayRoleStrokes.js.map +1 -1
  165. package/dist/canvas/pickIndex/scenePickIndex.js +10 -12
  166. package/dist/canvas/pickIndex/scenePickIndex.js.map +1 -1
  167. package/dist/canvas/sceneAdapter.js +10 -19
  168. package/dist/canvas/sceneAdapter.js.map +1 -1
  169. package/dist/canvas/sceneClipboard.js +17 -4
  170. package/dist/canvas/sceneClipboard.js.map +1 -1
  171. package/dist/canvas/sceneViewRender.js +3 -2
  172. package/dist/canvas/sceneViewRender.js.map +1 -1
  173. package/dist/canvas/surfaceContribution.js.map +1 -1
  174. package/dist/canvas/useCanvasPaint.js +10 -4
  175. package/dist/canvas/useCanvasPaint.js.map +1 -1
  176. package/dist/canvas/useHitObserver.js +1 -1
  177. package/dist/canvas/useHitObserver.js.map +1 -1
  178. package/dist/canvas/useViewAim.js +1 -1
  179. package/dist/canvas/useViewAim.js.map +1 -1
  180. package/dist/canvas/useViewHelpers.js +8 -5
  181. package/dist/canvas/useViewHelpers.js.map +1 -1
  182. package/dist/canvas/useViewNesting.js +39 -12
  183. package/dist/canvas/useViewNesting.js.map +1 -1
  184. package/dist/canvas/viewInputs.js.map +1 -1
  185. package/dist/canvas/viewMarkers.js +2 -2
  186. package/dist/canvas/viewMarkers.js.map +1 -1
  187. package/dist/canvas/viewRegistry.js +2 -2
  188. package/dist/canvas/viewRegistry.js.map +1 -1
  189. package/dist/canvas/viewSnap.js +67 -0
  190. package/dist/canvas/viewSnap.js.map +1 -0
  191. package/dist/clipboard.d.ts +6 -12
  192. package/dist/{clone-qb2e1F5p.d.ts → clone-Bz4iqGfE.d.ts} +10 -5
  193. package/dist/clone.d.ts +5 -5
  194. package/dist/core/geometry/booleanOfPaths.js +23 -0
  195. package/dist/core/geometry/booleanOfPaths.js.map +1 -0
  196. package/dist/core/geometry/pathPoseDescriptor.js +12 -9
  197. package/dist/core/geometry/pathPoseDescriptor.js.map +1 -1
  198. package/dist/core/geometry/poseDescriptor.js +12 -3
  199. package/dist/core/geometry/poseDescriptor.js.map +1 -1
  200. package/dist/core/geometry/targetFrame.js +2 -13
  201. package/dist/core/geometry/targetFrame.js.map +1 -1
  202. package/dist/core/layers/render.js +2 -1
  203. package/dist/core/layers/render.js.map +1 -1
  204. package/dist/core/layers/retain.js +5 -3
  205. package/dist/core/layers/retain.js.map +1 -1
  206. package/dist/core/ops/setPath.js +3 -0
  207. package/dist/core/ops/setPath.js.map +1 -1
  208. package/dist/core/paint/meshKindLazy.js.map +1 -1
  209. package/dist/core/paint/paintKindEntry.js.map +1 -1
  210. package/dist/core/paint/paintKinds.js +8 -1
  211. package/dist/core/paint/paintKinds.js.map +1 -1
  212. package/dist/core/scene/NodeProperties.js.map +1 -1
  213. package/dist/core/scene/NodeRouting.js.map +1 -1
  214. package/dist/core/scene/booleanGroup.js +33 -0
  215. package/dist/core/scene/booleanGroup.js.map +1 -0
  216. package/dist/core/scene/booleanOfChildren.js +53 -0
  217. package/dist/core/scene/booleanOfChildren.js.map +1 -0
  218. package/dist/core/scene/carryDescendants.js +18 -6
  219. package/dist/core/scene/carryDescendants.js.map +1 -1
  220. package/dist/core/scene/composeFrame.js +42 -5
  221. package/dist/core/scene/composeFrame.js.map +1 -1
  222. package/dist/core/scene/composePose.js +35 -16
  223. package/dist/core/scene/composePose.js.map +1 -1
  224. package/dist/core/scene/depMemo.js +36 -18
  225. package/dist/core/scene/depMemo.js.map +1 -1
  226. package/dist/core/scene/derivedParts.js +2 -23
  227. package/dist/core/scene/derivedParts.js.map +1 -1
  228. package/dist/core/scene/effectivePose.js +10 -0
  229. package/dist/core/scene/effectivePose.js.map +1 -1
  230. package/dist/core/scene/historyForm.js +5 -4
  231. package/dist/core/scene/historyForm.js.map +1 -1
  232. package/dist/core/scene/kitOps.js +5 -71
  233. package/dist/core/scene/kitOps.js.map +1 -1
  234. package/dist/core/scene/kitRegistry.js +5 -0
  235. package/dist/core/scene/kitRegistry.js.map +1 -1
  236. package/dist/core/scene/layerOps.js +82 -0
  237. package/dist/core/scene/layerOps.js.map +1 -0
  238. package/dist/core/scene/layoutPass.js +14 -12
  239. package/dist/core/scene/layoutPass.js.map +1 -1
  240. package/dist/core/scene/lockGuard.js +9 -3
  241. package/dist/core/scene/lockGuard.js.map +1 -1
  242. package/dist/core/scene/nestedHit.js +5 -5
  243. package/dist/core/scene/nestedHit.js.map +1 -1
  244. package/dist/core/scene/nodeFlags.js +8 -2
  245. package/dist/core/scene/nodeFlags.js.map +1 -1
  246. package/dist/core/scene/nodeGates.js +31 -2
  247. package/dist/core/scene/nodeGates.js.map +1 -1
  248. package/dist/core/scene/pathEffects/effects.js +22 -3
  249. package/dist/core/scene/pathEffects/effects.js.map +1 -1
  250. package/dist/core/scene/pathEffects/textAlong.js +16 -3
  251. package/dist/core/scene/pathEffects/textAlong.js.map +1 -1
  252. package/dist/core/scene/pathEffects/textAlongBands.js +65 -0
  253. package/dist/core/scene/pathEffects/textAlongBands.js.map +1 -0
  254. package/dist/core/scene/reconcileSpecs.js +1 -1
  255. package/dist/core/scene/reconcileSpecs.js.map +1 -1
  256. package/dist/core/scene/scene.js +11 -5
  257. package/dist/core/scene/scene.js.map +1 -1
  258. package/dist/core/scene/sceneEdits.js +5 -2
  259. package/dist/core/scene/sceneEdits.js.map +1 -1
  260. package/dist/core/scene/scenePoseAdapter.js +10 -1
  261. package/dist/core/scene/scenePoseAdapter.js.map +1 -1
  262. package/dist/core/scene/scrollOffsets.js +93 -0
  263. package/dist/core/scene/scrollOffsets.js.map +1 -0
  264. package/dist/core/scene/serialization.js +15 -5
  265. package/dist/core/scene/serialization.js.map +1 -1
  266. package/dist/core/scene/statedFields.js +2 -1
  267. package/dist/core/scene/statedFields.js.map +1 -1
  268. package/dist/core/scene/strategyView.js +35 -0
  269. package/dist/core/scene/strategyView.js.map +1 -0
  270. package/dist/core/scene/types.js.map +1 -1
  271. package/dist/core/scene/useScene.js +2 -0
  272. package/dist/core/scene/useScene.js.map +1 -1
  273. package/dist/core/scene/worldSave.js +87 -0
  274. package/dist/core/scene/worldSave.js.map +1 -0
  275. package/dist/core/selection/selectionTarget.js +1 -1
  276. package/dist/core/selection/selectionTarget.js.map +1 -1
  277. package/dist/{cornerHandles-CNCb7aTY.d.ts → cornerHandles-DpmQM_z5.d.ts} +1 -1
  278. package/dist/{depSchema-CpIQzKWQ.d.ts → depSchema-C3BwxitB.d.ts} +119 -99
  279. package/dist/devtools-protocol.d.ts +2 -2
  280. package/dist/devtools.d.ts +2 -2
  281. package/dist/{features → domains}/chrome-caps/buildChromeCtx.js +1 -1
  282. package/dist/domains/chrome-caps/buildChromeCtx.js.map +1 -0
  283. package/dist/{features → domains}/chrome-caps/conditions.js +1 -1
  284. package/dist/domains/chrome-caps/conditions.js.map +1 -0
  285. package/dist/{features → domains}/chrome-caps/defaults.js +1 -1
  286. package/dist/domains/chrome-caps/defaults.js.map +1 -0
  287. package/dist/{features → domains}/chrome-caps/resolve.js +1 -1
  288. package/dist/domains/chrome-caps/resolve.js.map +1 -0
  289. package/dist/{features → domains}/chrome-caps/useChromeCaps.js +1 -1
  290. package/dist/domains/chrome-caps/useChromeCaps.js.map +1 -0
  291. package/dist/{features → domains}/chrome-caps/useHoverTracking.js +1 -1
  292. package/dist/domains/chrome-caps/useHoverTracking.js.map +1 -0
  293. package/dist/{features → domains}/ingestion/contentHandlers.js +20 -3
  294. package/dist/domains/ingestion/contentHandlers.js.map +1 -0
  295. package/dist/domains/ingestion/ghost.js +56 -0
  296. package/dist/domains/ingestion/ghost.js.map +1 -0
  297. package/dist/{features → domains}/ingestion/imageHandler.js +1 -1
  298. package/dist/domains/ingestion/imageHandler.js.map +1 -0
  299. package/dist/domains/ingestion/kitHandlers.js +22 -0
  300. package/dist/domains/ingestion/kitHandlers.js.map +1 -0
  301. package/dist/{features → domains}/ingestion/openFilePicker.js +1 -1
  302. package/dist/domains/ingestion/openFilePicker.js.map +1 -0
  303. package/dist/{features → domains}/ingestion/placement.js +1 -1
  304. package/dist/domains/ingestion/placement.js.map +1 -0
  305. package/dist/{features → domains}/ingestion/svgHandler.js +1 -1
  306. package/dist/domains/ingestion/svgHandler.js.map +1 -0
  307. package/dist/{features → domains}/ingestion/textHandler.js +9 -7
  308. package/dist/domains/ingestion/textHandler.js.map +1 -0
  309. package/dist/{features → domains}/ingestion/useCanvasIngestion.js +13 -15
  310. package/dist/domains/ingestion/useCanvasIngestion.js.map +1 -0
  311. package/dist/{features → domains}/ingestion/weaselJsonHandler.js +1 -1
  312. package/dist/domains/ingestion/weaselJsonHandler.js.map +1 -0
  313. package/dist/{features → domains}/longPress/longPressFeedback.js +1 -1
  314. package/dist/domains/longPress/longPressFeedback.js.map +1 -0
  315. package/dist/{features → domains}/measure/actionIds.js +1 -1
  316. package/dist/domains/measure/actionIds.js.map +1 -0
  317. package/dist/{features → domains}/measure/measureCommands.js +1 -1
  318. package/dist/domains/measure/measureCommands.js.map +1 -0
  319. package/dist/{features → domains}/measure/measureContribution.js +1 -1
  320. package/dist/domains/measure/measureContribution.js.map +1 -0
  321. package/dist/{features → domains}/measure/measureEdits.js +1 -1
  322. package/dist/domains/measure/measureEdits.js.map +1 -0
  323. package/dist/{features → domains}/measure/measureGestures.js +4 -7
  324. package/dist/domains/measure/measureGestures.js.map +1 -0
  325. package/dist/{features → domains}/measure/measureHit.js +1 -1
  326. package/dist/domains/measure/measureHit.js.map +1 -0
  327. package/dist/{features → domains}/measure/measureLayer.js +1 -1
  328. package/dist/domains/measure/measureLayer.js.map +1 -0
  329. package/dist/{features → domains}/measure/measurement.js +1 -1
  330. package/dist/domains/measure/measurement.js.map +1 -0
  331. package/dist/{features → domains}/measure/measurements.js +2 -2
  332. package/dist/domains/measure/measurements.js.map +1 -0
  333. package/dist/{features → domains}/modes/modeLayers.js +1 -1
  334. package/dist/domains/modes/modeLayers.js.map +1 -0
  335. package/dist/{features → domains}/modes/modeTriggers.js +3 -6
  336. package/dist/domains/modes/modeTriggers.js.map +1 -0
  337. package/dist/{features → domains}/pathEffects/effectSources.js +1 -1
  338. package/dist/domains/pathEffects/effectSources.js.map +1 -0
  339. package/dist/{features → domains}/pathEffects/index.js +1 -1
  340. package/dist/domains/pathEffects/index.js.map +1 -0
  341. package/dist/{features → domains}/pathEffects/readSource.js +1 -1
  342. package/dist/domains/pathEffects/readSource.js.map +1 -0
  343. package/dist/{features → domains}/pathEffects/textItems.js +11 -2
  344. package/dist/domains/pathEffects/textItems.js.map +1 -0
  345. package/dist/{features → domains}/pathEffects/useEffectSourcesLayer.js +1 -1
  346. package/dist/domains/pathEffects/useEffectSourcesLayer.js.map +1 -0
  347. package/dist/{features → domains}/paths/anchorData.js +1 -1
  348. package/dist/domains/paths/anchorData.js.map +1 -0
  349. package/dist/{features → domains}/paths/anchorMarkers.js +9 -7
  350. package/dist/domains/paths/anchorMarkers.js.map +1 -0
  351. package/dist/{features → domains}/paths/editablePath.js +1 -1
  352. package/dist/domains/paths/editablePath.js.map +1 -0
  353. package/dist/{features → domains}/paths/index.js +3 -3
  354. package/dist/{features → domains}/paths/originProjection.js +1 -1
  355. package/dist/domains/paths/originProjection.js.map +1 -0
  356. package/dist/{features → domains}/paths/pathEditExtension.js +1 -1
  357. package/dist/domains/paths/pathEditExtension.js.map +1 -0
  358. package/dist/{features → domains}/paths/pathEditState.js +1 -1
  359. package/dist/domains/paths/pathEditState.js.map +1 -0
  360. package/dist/{features → domains}/paths/pathEditingOverlayLayer.js +1 -1
  361. package/dist/domains/paths/pathEditingOverlayLayer.js.map +1 -0
  362. package/dist/{features → domains}/paths/pathLayer.js +1 -1
  363. package/dist/domains/paths/pathLayer.js.map +1 -0
  364. package/dist/{features → domains}/paths/pathRepresentation.js +1 -1
  365. package/dist/domains/paths/pathRepresentation.js.map +1 -0
  366. package/dist/{features → domains}/paths/pathSnap/glyphs.js +1 -1
  367. package/dist/domains/paths/pathSnap/glyphs.js.map +1 -0
  368. package/dist/{features → domains}/paths/pathSnap/pathSnapSource.js +1 -1
  369. package/dist/domains/paths/pathSnap/pathSnapSource.js.map +1 -0
  370. package/dist/{features → domains}/paths/pathSnap/sceneIndex.js +1 -1
  371. package/dist/domains/paths/pathSnap/sceneIndex.js.map +1 -0
  372. package/dist/{features → domains}/paths/pathSnap/snapGeometry.js +3 -2
  373. package/dist/domains/paths/pathSnap/snapGeometry.js.map +1 -0
  374. package/dist/{features → domains}/paths/penPreviewLayer.js +1 -1
  375. package/dist/domains/paths/penPreviewLayer.js.map +1 -0
  376. package/dist/{features → domains}/paths/shapeGenerator.js +6 -4
  377. package/dist/domains/paths/shapeGenerator.js.map +1 -0
  378. package/dist/{features → domains}/paths/strokeOutline.js +1 -1
  379. package/dist/domains/paths/strokeOutline.js.map +1 -0
  380. package/dist/{features → domains}/paths/useAnchorFields.js +1 -1
  381. package/dist/domains/paths/useAnchorFields.js.map +1 -0
  382. package/dist/{features → domains}/paths/usePathEdit.js +1 -1
  383. package/dist/domains/paths/usePathEdit.js.map +1 -0
  384. package/dist/{features → domains}/poseRun/usePoseRun.js +1 -1
  385. package/dist/domains/poseRun/usePoseRun.js.map +1 -0
  386. package/dist/{features → domains}/selection/enteredGroupFrame.js +49 -10
  387. package/dist/domains/selection/enteredGroupFrame.js.map +1 -0
  388. package/dist/{features → domains}/selection/enteredGroupScope.js +1 -1
  389. package/dist/domains/selection/enteredGroupScope.js.map +1 -0
  390. package/dist/domains/selection/enteredOperands.js +29 -0
  391. package/dist/domains/selection/enteredOperands.js.map +1 -0
  392. package/dist/{features → domains}/selection/handleCommands.js +1 -1
  393. package/dist/domains/selection/handleCommands.js.map +1 -0
  394. package/dist/{features → domains}/selection/overlay.js +1 -1
  395. package/dist/domains/selection/overlay.js.map +1 -0
  396. package/dist/{features → domains}/selection/selectionPose.js +1 -1
  397. package/dist/domains/selection/selectionPose.js.map +1 -0
  398. package/dist/{features → domains}/snap/angle2d.js +1 -1
  399. package/dist/domains/snap/angle2d.js.map +1 -0
  400. package/dist/{features → domains}/snap/debugCandidates.js +1 -1
  401. package/dist/domains/snap/debugCandidates.js.map +1 -0
  402. package/dist/{features → domains}/snap/gather.js +1 -1
  403. package/dist/domains/snap/gather.js.map +1 -0
  404. package/dist/{features → domains}/snap/inputSubspace.js +1 -1
  405. package/dist/domains/snap/inputSubspace.js.map +1 -0
  406. package/dist/{features → domains}/snap/lazySession.js +1 -1
  407. package/dist/domains/snap/lazySession.js.map +1 -0
  408. package/dist/{features → domains}/snap/marks.js +1 -1
  409. package/dist/domains/snap/marks.js.map +1 -0
  410. package/dist/{features → domains}/snap/outcomeStore.js +1 -1
  411. package/dist/domains/snap/outcomeStore.js.map +1 -0
  412. package/dist/{features → domains}/snap/pointSnap.js +1 -1
  413. package/dist/domains/snap/pointSnap.js.map +1 -0
  414. package/dist/{features → domains}/snap/probes2d.js +1 -1
  415. package/dist/domains/snap/probes2d.js.map +1 -0
  416. package/dist/{features → domains}/snap/readoutDraw.js +1 -1
  417. package/dist/domains/snap/readoutDraw.js.map +1 -0
  418. package/dist/{features → domains}/snap/readoutLayer.js +1 -1
  419. package/dist/domains/snap/readoutLayer.js.map +1 -0
  420. package/dist/{features → domains}/snap/registry.js +1 -1
  421. package/dist/domains/snap/registry.js.map +1 -0
  422. package/dist/{features → domains}/snap/resizeBox.js +1 -1
  423. package/dist/domains/snap/resizeBox.js.map +1 -0
  424. package/dist/{features → domains}/snap/roles.js +1 -1
  425. package/dist/domains/snap/roles.js.map +1 -0
  426. package/dist/{features → domains}/snap/rotationSource.js +1 -1
  427. package/dist/domains/snap/rotationSource.js.map +1 -0
  428. package/dist/{features → domains}/snap/sceneReads.js +24 -3
  429. package/dist/domains/snap/sceneReads.js.map +1 -0
  430. package/dist/{features → domains}/snap/sceneSession2d.js +3 -2
  431. package/dist/domains/snap/sceneSession2d.js.map +1 -0
  432. package/dist/{features → domains}/snap/screen.js +1 -1
  433. package/dist/domains/snap/screen.js.map +1 -0
  434. package/dist/{features → domains}/snap/session2d.js +3 -2
  435. package/dist/domains/snap/session2d.js.map +1 -0
  436. package/dist/{features → domains}/snap/spanSnap.js +1 -1
  437. package/dist/domains/snap/spanSnap.js.map +1 -0
  438. package/dist/{features → domains}/snap/stepAngles.js +1 -1
  439. package/dist/domains/snap/stepAngles.js.map +1 -0
  440. package/dist/{features → domains}/text/editingDrawOne.js +4 -4
  441. package/dist/domains/text/editingDrawOne.js.map +1 -0
  442. package/dist/domains/text/hostTextEditors.js +24 -0
  443. package/dist/domains/text/hostTextEditors.js.map +1 -0
  444. package/dist/{features → domains}/text/index.js +1 -1
  445. package/dist/{features → domains}/text/sceneTextDrafts.js +1 -1
  446. package/dist/domains/text/sceneTextDrafts.js.map +1 -0
  447. package/dist/{features → domains}/text/textHitAt.js +18 -2
  448. package/dist/domains/text/textHitAt.js.map +1 -0
  449. package/dist/{features → domains}/text/textLayer.js +1 -1
  450. package/dist/domains/text/textLayer.js.map +1 -0
  451. package/dist/{features → domains}/text/useSceneTextEdit.js +11 -3
  452. package/dist/domains/text/useSceneTextEdit.js.map +1 -0
  453. package/dist/{features → domains}/viewports/aim.js +1 -1
  454. package/dist/domains/viewports/aim.js.map +1 -0
  455. package/dist/{features → domains}/viewports/viewMarker.js +23 -5
  456. package/dist/domains/viewports/viewMarker.js.map +1 -0
  457. package/dist/{features → domains}/viewports/viewMarkerDrag.js +3 -3
  458. package/dist/domains/viewports/viewMarkerDrag.js.map +1 -0
  459. package/dist/{features → domains}/viewports/viewResolver.js +1 -1
  460. package/dist/domains/viewports/viewResolver.js.map +1 -0
  461. package/dist/{features → domains}/viewports/viewportLayer.js +1 -1
  462. package/dist/domains/viewports/viewportLayer.js.map +1 -0
  463. package/dist/icons.table.js +1 -1
  464. package/dist/icons.table.js.map +1 -1
  465. package/dist/icons.table.json +106 -6
  466. package/dist/index.d.ts +1441 -1050
  467. package/dist/index.js +90 -77
  468. package/dist/interactions/actions/admitsChild.js +6 -2
  469. package/dist/interactions/actions/admitsChild.js.map +1 -1
  470. package/dist/interactions/actions/align/align.js +1 -4
  471. package/dist/interactions/actions/align/align.js.map +1 -1
  472. package/dist/interactions/actions/booleans/booleanGroupAction.js +66 -0
  473. package/dist/interactions/actions/booleans/booleanGroupAction.js.map +1 -0
  474. package/dist/interactions/actions/booleans/keymap.js +52 -0
  475. package/dist/interactions/actions/booleans/keymap.js.map +1 -0
  476. package/dist/interactions/actions/booleans/operands.js +1 -1
  477. package/dist/interactions/actions/booleans/pieces.js +4 -11
  478. package/dist/interactions/actions/booleans/pieces.js.map +1 -1
  479. package/dist/interactions/actions/clipboard/action.js +3 -7
  480. package/dist/interactions/actions/clipboard/action.js.map +1 -1
  481. package/dist/interactions/actions/clipboard/clipboardOps.js +1 -1
  482. package/dist/interactions/actions/clipboard/clipboardOps.js.map +1 -1
  483. package/dist/interactions/actions/clipboard/materializePaste.js.map +1 -1
  484. package/dist/interactions/actions/defaults/anchorEditing.js +6 -6
  485. package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
  486. package/dist/interactions/actions/defaults/bendSegment.js +1 -1
  487. package/dist/interactions/actions/defaults/bendSegment.js.map +1 -1
  488. package/dist/interactions/actions/defaults/containerScroll.js +170 -0
  489. package/dist/interactions/actions/defaults/containerScroll.js.map +1 -0
  490. package/dist/interactions/actions/defaults/contextMenu.js +2 -5
  491. package/dist/interactions/actions/defaults/contextMenu.js.map +1 -1
  492. package/dist/interactions/actions/defaults/delete.js +1 -1
  493. package/dist/interactions/actions/defaults/delete.js.map +1 -1
  494. package/dist/interactions/actions/defaults/duplicate.js +1 -3
  495. package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
  496. package/dist/interactions/actions/defaults/enterGroup.js +7 -12
  497. package/dist/interactions/actions/defaults/enterGroup.js.map +1 -1
  498. package/dist/interactions/actions/defaults/enterPathEdit.js +4 -8
  499. package/dist/interactions/actions/defaults/enterPathEdit.js.map +1 -1
  500. package/dist/interactions/actions/defaults/group.js +12 -24
  501. package/dist/interactions/actions/defaults/group.js.map +1 -1
  502. package/dist/interactions/actions/defaults/ingest.js +18 -10
  503. package/dist/interactions/actions/defaults/ingest.js.map +1 -1
  504. package/dist/interactions/actions/defaults/insertPathAnchor.js +3 -3
  505. package/dist/interactions/actions/defaults/insertPathAnchor.js.map +1 -1
  506. package/dist/interactions/actions/defaults/nudgeSteps.js +3 -21
  507. package/dist/interactions/actions/defaults/nudgeSteps.js.map +1 -1
  508. package/dist/interactions/actions/defaults/pathEditMarks.js.map +1 -1
  509. package/dist/interactions/actions/defaults/selectDeep.js +2 -2
  510. package/dist/interactions/actions/defaults/selectDeep.js.map +1 -1
  511. package/dist/interactions/actions/distribute/distribute.js.map +1 -1
  512. package/dist/interactions/actions/dropVerdict.js +9 -0
  513. package/dist/interactions/actions/dropVerdict.js.map +1 -0
  514. package/dist/interactions/actions/edit-anchors/action.js +2 -2
  515. package/dist/interactions/actions/edit-anchors/action.js.map +1 -1
  516. package/dist/interactions/actions/edit-anchors/extensionDrags.js.map +1 -1
  517. package/dist/interactions/actions/edit-anchors/geometry.js +8 -6
  518. package/dist/interactions/actions/edit-anchors/geometry.js.map +1 -1
  519. package/dist/interactions/actions/edit-anchors/handleDrag.js.map +1 -1
  520. package/dist/interactions/actions/edit-anchors/preview.js +1 -1
  521. package/dist/interactions/actions/edit-anchors/preview.js.map +1 -1
  522. package/dist/interactions/actions/flip/action.js +3 -2
  523. package/dist/interactions/actions/flip/action.js.map +1 -1
  524. package/dist/interactions/actions/geometryProjection.js +8 -3
  525. package/dist/interactions/actions/geometryProjection.js.map +1 -1
  526. package/dist/interactions/actions/gestureFrames.js +1 -0
  527. package/dist/interactions/actions/gestureFrames.js.map +1 -1
  528. package/dist/interactions/actions/insert/action.js +6 -2
  529. package/dist/interactions/actions/insert/action.js.map +1 -1
  530. package/dist/interactions/actions/insert/frame.js +1 -1
  531. package/dist/interactions/actions/insert/frame.js.map +1 -1
  532. package/dist/interactions/actions/insert/placeAction.js +6 -8
  533. package/dist/interactions/actions/insert/placeAction.js.map +1 -1
  534. package/dist/interactions/actions/insert/session.js +2 -2
  535. package/dist/interactions/actions/insert/session.js.map +1 -1
  536. package/dist/interactions/actions/move/action.js +6 -15
  537. package/dist/interactions/actions/move/action.js.map +1 -1
  538. package/dist/interactions/actions/move/behaviors/layoutDrop.js +12 -2
  539. package/dist/interactions/actions/move/behaviors/layoutDrop.js.map +1 -1
  540. package/dist/interactions/actions/move/behaviors/reparentOnDrop.js +29 -10
  541. package/dist/interactions/actions/move/behaviors/reparentOnDrop.js.map +1 -1
  542. package/dist/interactions/actions/move/behaviors/snapToContainer.js +42 -6
  543. package/dist/interactions/actions/move/behaviors/snapToContainer.js.map +1 -1
  544. package/dist/interactions/actions/move/capture.js +4 -8
  545. package/dist/interactions/actions/move/capture.js.map +1 -1
  546. package/dist/interactions/actions/move/commit/index.js.map +1 -1
  547. package/dist/interactions/actions/move/commit/layoutDrop.js +4 -3
  548. package/dist/interactions/actions/move/commit/layoutDrop.js.map +1 -1
  549. package/dist/interactions/actions/move/commit/layoutRelease.js +15 -9
  550. package/dist/interactions/actions/move/commit/layoutRelease.js.map +1 -1
  551. package/dist/interactions/actions/move/commit/reparent.js +19 -13
  552. package/dist/interactions/actions/move/commit/reparent.js.map +1 -1
  553. package/dist/interactions/actions/move/delta.js +51 -5
  554. package/dist/interactions/actions/move/delta.js.map +1 -1
  555. package/dist/interactions/actions/move/frame.js +8 -4
  556. package/dist/interactions/actions/move/frame.js.map +1 -1
  557. package/dist/interactions/actions/move/layout/container.js +19 -9
  558. package/dist/interactions/actions/move/layout/container.js.map +1 -1
  559. package/dist/interactions/actions/move/layout/dragged.js +2 -2
  560. package/dist/interactions/actions/move/layout/dragged.js.map +1 -1
  561. package/dist/interactions/actions/move/layout/dropPass.js +36 -18
  562. package/dist/interactions/actions/move/layout/dropPass.js.map +1 -1
  563. package/dist/interactions/actions/move/layout/exits.js +2 -2
  564. package/dist/interactions/actions/move/layout/exits.js.map +1 -1
  565. package/dist/interactions/actions/move/layout/glide.js +5 -5
  566. package/dist/interactions/actions/move/layout/glide.js.map +1 -1
  567. package/dist/interactions/actions/move/layout/view.js +55 -0
  568. package/dist/interactions/actions/move/layout/view.js.map +1 -0
  569. package/dist/interactions/actions/move/refusal.js +52 -0
  570. package/dist/interactions/actions/move/refusal.js.map +1 -0
  571. package/dist/interactions/actions/move/session.js +9 -8
  572. package/dist/interactions/actions/move/session.js.map +1 -1
  573. package/dist/interactions/actions/move/translate.js +10 -4
  574. package/dist/interactions/actions/move/translate.js.map +1 -1
  575. package/dist/interactions/actions/moveNodes.js +21 -5
  576. package/dist/interactions/actions/moveNodes.js.map +1 -1
  577. package/dist/interactions/actions/pathCommands/pathNodes.js +3 -3
  578. package/dist/interactions/actions/pathCommands/pathNodes.js.map +1 -1
  579. package/dist/interactions/actions/planeInput.js +14 -13
  580. package/dist/interactions/actions/planeInput.js.map +1 -1
  581. package/dist/interactions/actions/poseFrame.js +21 -8
  582. package/dist/interactions/actions/poseFrame.js.map +1 -1
  583. package/dist/interactions/actions/resize/action.js +1 -1
  584. package/dist/interactions/actions/resize/action.js.map +1 -1
  585. package/dist/interactions/actions/resize/aspect.js +1 -1
  586. package/dist/interactions/actions/resize/aspect.js.map +1 -1
  587. package/dist/interactions/actions/resize/commit.js +10 -1
  588. package/dist/interactions/actions/resize/commit.js.map +1 -1
  589. package/dist/interactions/actions/resize/frame.js +3 -2
  590. package/dist/interactions/actions/resize/frame.js.map +1 -1
  591. package/dist/interactions/actions/resize/held.js +44 -0
  592. package/dist/interactions/actions/resize/held.js.map +1 -0
  593. package/dist/interactions/actions/resize/session.js +3 -1
  594. package/dist/interactions/actions/resize/session.js.map +1 -1
  595. package/dist/interactions/actions/resolveOverlays.js +9 -0
  596. package/dist/interactions/actions/resolveOverlays.js.map +1 -1
  597. package/dist/interactions/actions/resolvePreviews.js +23 -7
  598. package/dist/interactions/actions/resolvePreviews.js.map +1 -1
  599. package/dist/interactions/actions/rotate/action.js +1 -1
  600. package/dist/interactions/actions/rotate/action.js.map +1 -1
  601. package/dist/interactions/actions/rotate/frame.js +1 -1
  602. package/dist/interactions/actions/rotate/frame.js.map +1 -1
  603. package/dist/interactions/actions/rotate/session.js +2 -2
  604. package/dist/interactions/actions/rotate/session.js.map +1 -1
  605. package/dist/interactions/actions/selectionSubtree.js +2 -4
  606. package/dist/interactions/actions/selectionSubtree.js.map +1 -1
  607. package/dist/interactions/actions/spatialReference.js +4 -9
  608. package/dist/interactions/actions/spatialReference.js.map +1 -1
  609. package/dist/interactions/gestures/dragRadial/dragRadial.js.map +1 -1
  610. package/dist/interactions/gestures/dragRect/dragRect.js.map +1 -1
  611. package/dist/kitSettingsSchema.js +2 -2
  612. package/dist/kitSettingsSchema.js.map +1 -1
  613. package/dist/{math-C_elShP_.d.ts → math-B_unOnF4.d.ts} +2 -2
  614. package/dist/math.d.ts +3 -3
  615. package/dist/move.d.ts +6 -6
  616. package/dist/{options-ClEm3SEl.d.ts → options-DAjkhbcs.d.ts} +5 -5
  617. package/dist/{poseDescriptor-DN2yfnq_.d.ts → poseDescriptor-CvSxVhCd.d.ts} +17 -4
  618. package/dist/resize.d.ts +6 -6
  619. package/dist/style.css +14 -0
  620. package/dist/test-seams.d.ts +11 -15
  621. package/dist/test-seams.js +1 -1
  622. package/dist/tools/builtin/eyedropper/useEyedropperTool.js +3 -6
  623. package/dist/tools/builtin/eyedropper/useEyedropperTool.js.map +1 -1
  624. package/dist/tools/builtin/line/useLineTool.js.map +1 -1
  625. package/dist/tools/builtin/measure/useMeasureTool.js +2 -2
  626. package/dist/tools/builtin/measure/useMeasureTool.js.map +1 -1
  627. package/dist/tools/builtin/pen/penHandle.js +2 -2
  628. package/dist/tools/builtin/pen/penHandle.js.map +1 -1
  629. package/dist/tools/builtin/pen/usePenTool.js +13 -19
  630. package/dist/tools/builtin/pen/usePenTool.js.map +1 -1
  631. package/dist/tools/builtin/polygon/usePolygonTool.js +1 -1
  632. package/dist/tools/builtin/polygon/usePolygonTool.js.map +1 -1
  633. package/dist/tools/builtin/select/groupEntry.js +1 -1
  634. package/dist/tools/builtin/select/groupEntry.js.map +1 -1
  635. package/dist/tools/builtin/select/selectionContributions.js +1 -1
  636. package/dist/tools/builtin/select/selectionContributions.js.map +1 -1
  637. package/dist/tools/builtin/select/useSelectTool.js +7 -7
  638. package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
  639. package/dist/tools/builtin/slice/useSliceTool.js +5 -8
  640. package/dist/tools/builtin/slice/useSliceTool.js.map +1 -1
  641. package/dist/tools/builtin/star/useStarTool.js +1 -1
  642. package/dist/tools/builtin/star/useStarTool.js.map +1 -1
  643. package/dist/tools/index.js +2 -1
  644. package/dist/{types-49uYJN3j.d.ts → types-A_sNJYRd.d.ts} +8 -0
  645. package/dist/{types-BEdRIhNc.d.ts → types-DW14eu9B.d.ts} +131 -36
  646. package/dist/{types-p2alu-R1.d.ts → types-VCtfchp-.d.ts} +4 -6
  647. package/dist/util/color.js +12 -14
  648. package/dist/util/color.js.map +1 -1
  649. package/dist/version.js +1 -1
  650. package/package.json +25 -24
  651. package/dist/canvas/poseCascade.js +0 -65
  652. package/dist/canvas/poseCascade.js.map +0 -1
  653. package/dist/features/chrome-caps/buildChromeCtx.js.map +0 -1
  654. package/dist/features/chrome-caps/conditions.js.map +0 -1
  655. package/dist/features/chrome-caps/defaults.js.map +0 -1
  656. package/dist/features/chrome-caps/resolve.js.map +0 -1
  657. package/dist/features/chrome-caps/useChromeCaps.js.map +0 -1
  658. package/dist/features/chrome-caps/useHoverTracking.js.map +0 -1
  659. package/dist/features/ingestion/contentHandlers.js.map +0 -1
  660. package/dist/features/ingestion/imageHandler.js.map +0 -1
  661. package/dist/features/ingestion/openFilePicker.js.map +0 -1
  662. package/dist/features/ingestion/placement.js.map +0 -1
  663. package/dist/features/ingestion/registerKitHandlers.js +0 -43
  664. package/dist/features/ingestion/registerKitHandlers.js.map +0 -1
  665. package/dist/features/ingestion/svgHandler.js.map +0 -1
  666. package/dist/features/ingestion/textHandler.js.map +0 -1
  667. package/dist/features/ingestion/useCanvasIngestion.js.map +0 -1
  668. package/dist/features/ingestion/weaselJsonHandler.js.map +0 -1
  669. package/dist/features/longPress/longPressFeedback.js.map +0 -1
  670. package/dist/features/measure/actionIds.js.map +0 -1
  671. package/dist/features/measure/measureCommands.js.map +0 -1
  672. package/dist/features/measure/measureContribution.js.map +0 -1
  673. package/dist/features/measure/measureEdits.js.map +0 -1
  674. package/dist/features/measure/measureGestures.js.map +0 -1
  675. package/dist/features/measure/measureHit.js.map +0 -1
  676. package/dist/features/measure/measureLayer.js.map +0 -1
  677. package/dist/features/measure/measurement.js.map +0 -1
  678. package/dist/features/measure/measurements.js.map +0 -1
  679. package/dist/features/modes/modeLayers.js.map +0 -1
  680. package/dist/features/modes/modeTriggers.js.map +0 -1
  681. package/dist/features/pathEffects/effectSources.js.map +0 -1
  682. package/dist/features/pathEffects/index.js.map +0 -1
  683. package/dist/features/pathEffects/readSource.js.map +0 -1
  684. package/dist/features/pathEffects/textItems.js.map +0 -1
  685. package/dist/features/pathEffects/useEffectSourcesLayer.js.map +0 -1
  686. package/dist/features/paths/anchorData.js.map +0 -1
  687. package/dist/features/paths/anchorMarkers.js.map +0 -1
  688. package/dist/features/paths/editablePath.js.map +0 -1
  689. package/dist/features/paths/originProjection.js.map +0 -1
  690. package/dist/features/paths/pathEditExtension.js.map +0 -1
  691. package/dist/features/paths/pathEditState.js.map +0 -1
  692. package/dist/features/paths/pathEditingOverlayLayer.js.map +0 -1
  693. package/dist/features/paths/pathLayer.js.map +0 -1
  694. package/dist/features/paths/pathRepresentation.js.map +0 -1
  695. package/dist/features/paths/pathSnap/glyphs.js.map +0 -1
  696. package/dist/features/paths/pathSnap/pathSnapSource.js.map +0 -1
  697. package/dist/features/paths/pathSnap/sceneIndex.js.map +0 -1
  698. package/dist/features/paths/pathSnap/snapGeometry.js.map +0 -1
  699. package/dist/features/paths/penPreviewLayer.js.map +0 -1
  700. package/dist/features/paths/shapeGenerator.js.map +0 -1
  701. package/dist/features/paths/strokeOutline.js.map +0 -1
  702. package/dist/features/paths/useAnchorFields.js.map +0 -1
  703. package/dist/features/paths/usePathEdit.js.map +0 -1
  704. package/dist/features/poseRun/usePoseRun.js.map +0 -1
  705. package/dist/features/selection/enteredGroupFrame.js.map +0 -1
  706. package/dist/features/selection/enteredGroupScope.js.map +0 -1
  707. package/dist/features/selection/handleCommands.js.map +0 -1
  708. package/dist/features/selection/overlay.js.map +0 -1
  709. package/dist/features/selection/selectionPose.js.map +0 -1
  710. package/dist/features/snap/angle2d.js.map +0 -1
  711. package/dist/features/snap/debugCandidates.js.map +0 -1
  712. package/dist/features/snap/gather.js.map +0 -1
  713. package/dist/features/snap/inputSubspace.js.map +0 -1
  714. package/dist/features/snap/lazySession.js.map +0 -1
  715. package/dist/features/snap/marks.js.map +0 -1
  716. package/dist/features/snap/outcomeStore.js.map +0 -1
  717. package/dist/features/snap/pointSnap.js.map +0 -1
  718. package/dist/features/snap/probes2d.js.map +0 -1
  719. package/dist/features/snap/readoutDraw.js.map +0 -1
  720. package/dist/features/snap/readoutLayer.js.map +0 -1
  721. package/dist/features/snap/registry.js.map +0 -1
  722. package/dist/features/snap/resizeBox.js.map +0 -1
  723. package/dist/features/snap/roles.js.map +0 -1
  724. package/dist/features/snap/rotationSource.js.map +0 -1
  725. package/dist/features/snap/sceneReads.js.map +0 -1
  726. package/dist/features/snap/sceneSession2d.js.map +0 -1
  727. package/dist/features/snap/screen.js.map +0 -1
  728. package/dist/features/snap/session2d.js.map +0 -1
  729. package/dist/features/snap/spanSnap.js.map +0 -1
  730. package/dist/features/snap/stepAngles.js.map +0 -1
  731. package/dist/features/text/editingDrawOne.js.map +0 -1
  732. package/dist/features/text/sceneTextDrafts.js.map +0 -1
  733. package/dist/features/text/textHitAt.js.map +0 -1
  734. package/dist/features/text/textLayer.js.map +0 -1
  735. package/dist/features/text/useSceneTextEdit.js.map +0 -1
  736. package/dist/features/viewports/aim.js.map +0 -1
  737. package/dist/features/viewports/viewMarker.js.map +0 -1
  738. package/dist/features/viewports/viewMarkerDrag.js.map +0 -1
  739. package/dist/features/viewports/viewResolver.js.map +0 -1
  740. package/dist/features/viewports/viewportLayer.js.map +0 -1
  741. /package/dist/{features → domains}/chrome-caps/index.js +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"ingest.js","names":[],"sources":["../../../../src/interactions/actions/defaults/ingest.ts"],"sourcesContent":["/**\n * `ingest` — routes externally-arrived content (OS drop, clipboard paste,\n * file picker / `CanvasExtensionApi.ingest`) through the content-handler\n * registry (`features/ingestion/contentHandlers`).\n *\n * Registered ambient with both `drop` and `paste` default bindings, so\n * external content works regardless of the active tool. A tool can still\n * bind `{ kind: 'drop', types: [...] }` itself to intercept (active scope\n * beats ambient).\n *\n * This is the `ingest` preset's action. A canvas with `edit` and without\n * `ingest` answers a paste with `clipboard.pasteEvent` instead, which runs\n * the same `ingestFromEvent` limited to the kit's copied-node payload;\n * `useStandardActions` never registers the two together.\n *\n * Every binding declares every modifier `'optional'`: strict matching would\n * otherwise reject Cmd+V paste and macOS Option-drag drops outright.\n *\n * The dispatcher merges event data into `params`: `items` (IngestItem[]),\n * `via` ('drop' | 'paste'), plus `worldX`/`worldY` for drops. Absent\n * coords → `point: null`; handlers pick their own placement policy.\n */\nimport type { Action } from '@weasel-js/routing';\nimport { ActionDisabledReason, type ActionDeps } from '@weasel-js/routing';\nimport {\n couldClaim, runIngest, type ContentHandlerEntry, type IngestCtx, type RunIngestOptions,\n} from 'features/ingestion/contentHandlers';\nimport type { IngestItem } from '@weasel-js/routing';\nimport { defaultCommitAdapter } from '../defaultCommitAdapter';\nimport { MOVE_NODES_REQUIRES } from '../moveNodes';\n\nexport const ANY_INGEST_MODS = {\n alt: 'optional', ctrl: 'optional', meta: 'optional', shift: 'optional',\n} as const;\n\n/** The deps {@link ingestFromEvent} reads, and the ones `IngestCtx.deps` carries for a handler\n * that moves what it inserts with `moveNodes`; an action calling it declares these. */\nexport const INGEST_REQUIRES = ['insert', 'ingestion', 'snap', ...MOVE_NODES_REQUIRES] as const;\n\n/**\n * Route one drop or paste event's dispatcher params through the content\n * handlers: builds the `IngestCtx` from the action's deps and calls\n * `runIngest`. `opts` is `runIngest`'s, so an action accepting only some\n * content passes `handlers`.\n */\nexport function ingestFromEvent(\n deps: ActionDeps,\n params: Record<string, unknown> | undefined,\n opts?: RunIngestOptions,\n): void {\n const items = params?.items as IngestItem[] | undefined;\n if (!items || items.length === 0) return;\n const ingestion = deps.ingestion;\n const insert = deps.insert;\n const scene = deps.scene;\n const selection = deps.selection;\n if (!ingestion || !insert || !scene || !selection) return;\n const applyOps = deps.applyOps;\n\n const point =\n typeof params?.worldX === 'number' && typeof params?.worldY === 'number'\n ? { x: params.worldX as number, y: params.worldY as number }\n : null;\n\n const ctx: IngestCtx = {\n point,\n viewportWorldRect: () => ingestion.viewportWorldRect(),\n insert,\n applyOps: (ops, label) => {\n if (applyOps) applyOps(ops, label ?? 'Ingest');\n else scene.applyBatch(ops, label ?? 'Ingest', defaultCommitAdapter(scene, selection.adapterMethods));\n },\n scene,\n selection,\n // Snapshot per ingest event — intentional. The dep's live getters are\n // fresh at dispatch time, and one consistent resolver per drop/paste\n // beats half a multi-file drop resolving through a swapped-in one.\n resolveSrc: ingestion.resolveSrc,\n svg: ingestion.svg,\n clipboard: ingestion.clipboard,\n ...(deps.snap ? { snap: deps.snap } : {}),\n deps,\n };\n // Fire-and-forget: handler errors are caught inside runIngest, but a\n // malformed item (non-string mime via untyped JS) can still throw in\n // the matching path — keep the rejection out of the event loop.\n runIngest(items, ctx, opts).catch((err) => console.warn('weasel ingest:', err));\n}\n\n/** `Action.enabled` for an action that routes what an event carries through\n * `handlers` (the registry's when omitted): refused when none could claim any\n * of it. An invocation that carries nothing, a trigger, is taken. */\nexport function claimable(\n carried: { items: readonly IngestItem[] } | undefined,\n handlers?: readonly ContentHandlerEntry[],\n): true | ActionDisabledReason {\n return !carried || couldClaim(carried.items, handlers) ? true : ActionDisabledReason.NotApplicable;\n}\n\nexport const ingestAction: Action & { requires: string[] } = {\n id: 'ingest',\n label: 'Insert external content',\n defaultBinding: [\n { spec: { kind: 'drop', mods: ANY_INGEST_MODS }, opts: { id: 'ingest.drop' } },\n { spec: { kind: 'paste', mods: ANY_INGEST_MODS }, opts: { id: 'ingest.paste' } },\n ],\n requires: [...INGEST_REQUIRES],\n invoker: {\n timing: 'immediate',\n run: (deps, params) => ingestFromEvent(deps, params),\n },\n // No `eligible` rule: external-content arrival is mode-agnostic. The\n // listener-level editable-target guard already keeps text-editing pastes\n // away from the scene.\n enabled: (_deps, _at, carried) => claimable(carried),\n};\n"],"mappings":";;;;;;AA+BA,IAAa,kBAAkB;CAC7B,KAAK;CAAY,MAAM;CAAY,MAAM;CAAY,OAAO;AAC9D;;;AAIA,IAAa,kBAAkB;CAAC;CAAU;CAAa;CAAQ,GAAG;AAAmB;;;;;;;AAQrF,SAAgB,gBACd,MACA,QACA,MACM;CACN,MAAM,QAAQ,QAAQ;CACtB,IAAI,CAAC,SAAS,MAAM,WAAW,GAAG;CAClC,MAAM,YAAY,KAAK;CACvB,MAAM,SAAS,KAAK;CACpB,MAAM,QAAQ,KAAK;CACnB,MAAM,YAAY,KAAK;CACvB,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,SAAS,CAAC,WAAW;CACnD,MAAM,WAAW,KAAK;CAOtB,MAAM,MAAiB;EACrB,OALA,OAAO,QAAQ,WAAW,YAAY,OAAO,QAAQ,WAAW,WAC5D;GAAE,GAAG,OAAO;GAAkB,GAAG,OAAO;EAAiB,IACzD;EAIJ,yBAAyB,UAAU,kBAAkB;EACrD;EACA,WAAW,KAAK,UAAU;GACxB,IAAI,UAAU,SAAS,KAAK,SAAS,QAAQ;QACxC,MAAM,WAAW,KAAK,SAAS,UAAU,qBAAqB,OAAO,UAAU,cAAc,CAAC;EACrG;EACA;EACA;EAIA,YAAY,UAAU;EACtB,KAAK,UAAU;EACf,WAAW,UAAU;EACrB,GAAI,KAAK,OAAO,EAAE,MAAM,KAAK,KAAK,IAAI,CAAC;EACvC;CACF;CAIA,UAAU,OAAO,KAAK,IAAI,CAAC,CAAC,OAAO,QAAQ,QAAQ,KAAK,kBAAkB,GAAG,CAAC;AAChF;;;;AAKA,SAAgB,UACd,SACA,UAC6B;CAC7B,OAAO,CAAC,WAAW,WAAW,QAAQ,OAAO,QAAQ,IAAI,OAAO,qBAAqB;AACvF;AAEA,IAAa,eAAgD;CAC3D,IAAI;CACJ,OAAO;CACP,gBAAgB,CACd;EAAE,MAAM;GAAE,MAAM;GAAQ,MAAM;EAAgB;EAAG,MAAM,EAAE,IAAI,cAAc;CAAE,GAC7E;EAAE,MAAM;GAAE,MAAM;GAAS,MAAM;EAAgB;EAAG,MAAM,EAAE,IAAI,eAAe;CAAE,CACjF;CACA,UAAU,CAAC,GAAG,eAAe;CAC7B,SAAS;EACP,QAAQ;EACR,MAAM,MAAM,WAAW,gBAAgB,MAAM,MAAM;CACrD;CAIA,UAAU,OAAO,KAAK,YAAY,UAAU,OAAO;AACrD"}
1
+ {"version":3,"file":"ingest.js","names":[],"sources":["../../../../src/interactions/actions/defaults/ingest.ts"],"sourcesContent":["/**\n * `ingest` — routes externally-arrived content (OS drop, clipboard paste,\n * file picker / `CanvasExtensionApi.ingest`) through the content-handler\n * registry (`domains/ingestion/contentHandlers`).\n *\n * Registered ambient with both `drop` and `paste` default bindings, so\n * external content works regardless of the active tool. A tool can still\n * bind `{ kind: 'drop', types: [...] }` itself to intercept (active scope\n * beats ambient).\n *\n * This is the `ingest` preset's action. A canvas with `edit` and without\n * `ingest` answers a paste with `clipboard.pasteEvent` instead, which runs\n * the same `ingestFromEvent` limited to the kit's copied-node payload;\n * `useStandardActions` never registers the two together.\n *\n * Every binding declares every modifier `'optional'`: strict matching would\n * otherwise reject Cmd+V paste and macOS Option-drag drops outright.\n *\n * The dispatcher merges event data into `params`: `items` (IngestItem[]),\n * `via` ('drop' | 'paste'), plus `world` for drops. Absent\n * coords → `point: null`; handlers pick their own placement policy.\n */\nimport type { Action } from '@weasel-js/routing';\nimport { ActionDisabledReason, type ActionDeps } from '@weasel-js/routing';\nimport {\n couldClaim, handlersFor, runIngest, type ContentHandlerEntry, type IngestCtx, type RunIngestOptions,\n} from 'domains/ingestion/contentHandlers';\nimport type { IngestItem } from '@weasel-js/routing';\nimport { previewIngest } from 'domains/ingestion/ghost';\nimport { defaultCommitAdapter } from '../defaultCommitAdapter';\nimport { MOVE_NODES_REQUIRES } from '../moveNodes';\nimport type { ImmediateParams } from '@weasel-js/routing';\n\nexport const ANY_INGEST_MODS = {\n alt: 'optional', ctrl: 'optional', meta: 'optional', shift: 'optional',\n} as const;\n\n/** The deps {@link ingestFromEvent} reads, and the ones `IngestCtx.deps` carries for a handler\n * that moves what it inserts with `moveNodes`; an action calling it declares these. */\nexport const INGEST_REQUIRES = ['insert', 'ingestion', 'snap', ...MOVE_NODES_REQUIRES] as const;\n\n/** The handlers an arrival on the canvas whose deps these are routes through: the\n * canvas's own and kit ones (the `ingestion` dep's `handlers`), and the registry's. */\nexport function canvasHandlers(deps: ActionDeps | undefined): ContentHandlerEntry[] {\n return handlersFor(deps?.ingestion?.handlers);\n}\n\n/**\n * Route one drop or paste event's dispatcher params through the content\n * handlers: builds the `IngestCtx` from the action's deps and calls\n * `runIngest`. `opts` is `runIngest`'s, so an action accepting only some\n * content passes `handlers`; without them, {@link canvasHandlers}.\n */\nexport function ingestFromEvent(\n deps: ActionDeps,\n params: Record<string, unknown> | undefined,\n opts?: RunIngestOptions,\n): void {\n const items = params?.items as IngestItem[] | undefined;\n if (!items || items.length === 0) return;\n const ingestion = deps.ingestion;\n const insert = deps.insert;\n const scene = deps.scene;\n const selection = deps.selection;\n if (!ingestion || !insert || !scene || !selection) return;\n const applyOps = deps.applyOps;\n\n const point = (params as ImmediateParams | undefined)?.world ?? null;\n\n const ctx: IngestCtx = {\n point,\n items,\n viewportWorldRect: () => ingestion.viewportWorldRect(),\n insert,\n applyOps: (ops, label) => {\n if (applyOps) applyOps(ops, label ?? 'Ingest');\n else scene.applyBatch(ops, label ?? 'Ingest', defaultCommitAdapter(scene, selection.adapterMethods));\n },\n scene,\n selection,\n // Snapshot per ingest event — intentional. The dep's live getters are\n // fresh at dispatch time, and one consistent resolver per drop/paste\n // beats half a multi-file drop resolving through a swapped-in one.\n resolveSrc: ingestion.resolveSrc,\n svg: ingestion.svg,\n clipboard: ingestion.clipboard,\n ...(deps.snap ? { snap: deps.snap } : {}),\n deps,\n };\n // Fire-and-forget: handler errors are caught inside runIngest, but a\n // malformed item (non-string mime via untyped JS) can still throw in\n // the matching path — keep the rejection out of the event loop.\n runIngest(items, ctx, opts?.handlers ? opts : { ...opts, handlers: canvasHandlers(deps) }).catch((err) => console.warn('weasel ingest:', err));\n}\n\n/** `Action.enabled` for an action that routes what an event carries through\n * `handlers` (the registry's when omitted): refused when none could claim any\n * of it. An invocation that carries nothing, a trigger, is taken. */\nexport function claimable(\n carried: { items: readonly IngestItem[] } | undefined,\n handlers?: readonly ContentHandlerEntry[],\n): true | ActionDisabledReason {\n return !carried || couldClaim(carried.items, handlers) ? true : ActionDisabledReason.NotApplicable;\n}\n\nexport const ingestAction: Action & { requires: string[] } = {\n id: 'ingest',\n label: 'Insert external content',\n defaultBinding: [\n { spec: { kind: 'drop', mods: ANY_INGEST_MODS }, opts: { id: 'ingest.drop' } },\n { spec: { kind: 'paste', mods: ANY_INGEST_MODS }, opts: { id: 'ingest.paste' } },\n ],\n requires: [...INGEST_REQUIRES, 'previewPaths'],\n invoker: {\n timing: 'immediate',\n run: (deps, params) => ingestFromEvent(deps, params),\n },\n // Asked by the dispatcher while an OS drag hovers: a ghost box per thing the drop would insert.\n preview: (deps, params) => previewIngest(deps, params, canvasHandlers(deps)),\n // No `eligible` rule: external-content arrival is mode-agnostic. The\n // listener-level editable-target guard already keeps text-editing pastes\n // away from the scene.\n enabled: (deps, _at, carried) => claimable(carried, canvasHandlers(deps)),\n};\n"],"mappings":";;;;;;;AAiCA,IAAa,kBAAkB;CAC7B,KAAK;CAAY,MAAM;CAAY,MAAM;CAAY,OAAO;AAC9D;;;AAIA,IAAa,kBAAkB;CAAC;CAAU;CAAa;CAAQ,GAAG;AAAmB;;;AAIrF,SAAgB,eAAe,MAAqD;CAClF,OAAO,YAAY,MAAM,WAAW,QAAQ;AAC9C;;;;;;;AAQA,SAAgB,gBACd,MACA,QACA,MACM;CACN,MAAM,QAAQ,QAAQ;CACtB,IAAI,CAAC,SAAS,MAAM,WAAW,GAAG;CAClC,MAAM,YAAY,KAAK;CACvB,MAAM,SAAS,KAAK;CACpB,MAAM,QAAQ,KAAK;CACnB,MAAM,YAAY,KAAK;CACvB,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,SAAS,CAAC,WAAW;CACnD,MAAM,WAAW,KAAK;CAItB,MAAM,MAAiB;EACrB,OAHa,QAAwC,SAAS;EAI9D;EACA,yBAAyB,UAAU,kBAAkB;EACrD;EACA,WAAW,KAAK,UAAU;GACxB,IAAI,UAAU,SAAS,KAAK,SAAS,QAAQ;QACxC,MAAM,WAAW,KAAK,SAAS,UAAU,qBAAqB,OAAO,UAAU,cAAc,CAAC;EACrG;EACA;EACA;EAIA,YAAY,UAAU;EACtB,KAAK,UAAU;EACf,WAAW,UAAU;EACrB,GAAI,KAAK,OAAO,EAAE,MAAM,KAAK,KAAK,IAAI,CAAC;EACvC;CACF;CAIA,UAAU,OAAO,KAAK,MAAM,WAAW,OAAO;EAAE,GAAG;EAAM,UAAU,eAAe,IAAI;CAAE,CAAC,CAAC,CAAC,OAAO,QAAQ,QAAQ,KAAK,kBAAkB,GAAG,CAAC;AAC/I;;;;AAKA,SAAgB,UACd,SACA,UAC6B;CAC7B,OAAO,CAAC,WAAW,WAAW,QAAQ,OAAO,QAAQ,IAAI,OAAO,qBAAqB;AACvF;AAEA,IAAa,eAAgD;CAC3D,IAAI;CACJ,OAAO;CACP,gBAAgB,CACd;EAAE,MAAM;GAAE,MAAM;GAAQ,MAAM;EAAgB;EAAG,MAAM,EAAE,IAAI,cAAc;CAAE,GAC7E;EAAE,MAAM;GAAE,MAAM;GAAS,MAAM;EAAgB;EAAG,MAAM,EAAE,IAAI,eAAe;CAAE,CACjF;CACA,UAAU,CAAC,GAAG,iBAAiB,cAAc;CAC7C,SAAS;EACP,QAAQ;EACR,MAAM,MAAM,WAAW,gBAAgB,MAAM,MAAM;CACrD;CAEA,UAAU,MAAM,WAAW,cAAc,MAAM,QAAQ,eAAe,IAAI,CAAC;CAI3E,UAAU,MAAM,KAAK,YAAY,UAAU,SAAS,eAAe,IAAI,CAAC;AAC1E"}
@@ -30,9 +30,9 @@ var insertPathAnchorAction = inPlane({
30
30
  invoker: {
31
31
  timing: "immediate",
32
32
  run(deps, params) {
33
- const wx = params?.worldX;
34
- const wy = params?.worldY;
35
- if (typeof wx !== "number" || typeof wy !== "number") return;
33
+ const world = params?.world;
34
+ if (!world) return;
35
+ const { x: wx, y: wy } = world;
36
36
  const under = segmentUnder(deps, wx, wy);
37
37
  if (!under) return;
38
38
  const { dep, path, hit } = under;
@@ -1 +1 @@
1
- {"version":3,"file":"insertPathAnchor.js","names":[],"sources":["../../../../src/interactions/actions/defaults/insertPathAnchor.ts"],"sourcesContent":["/**\n * `insertPathAnchorAction` — Alt+click on a segment of the path being\n * edited inserts a new anchor there, without changing the path's shape.\n *\n * Alt+click because plain click already belongs to `selectAnchorAction`\n * (select an anchor, or clear the anchor selection on a miss), and\n * Alt+Shift+click to `cutPathAtAnchorAction`.\n *\n * The segment test lives in `enabled`, which the dispatcher hands the\n * event's world point: a click off every segment (or on an anchor) reports\n * not-applicable, so the dispatcher falls through to whatever else the\n * click could mean, and the hover pump shows `cursor` only where a click\n * would insert.\n */\n\nimport type { Action, ImmediateInvoker } from '@weasel-js/routing';\nimport { ActionDisabledReason } from '@weasel-js/routing';\nimport type { CursorSpec } from '@weasel-js/cursor';\nimport { editAnchorSetTracked, insertAnchorOnSegment } from '@weasel-js/geom';\nimport { inPlane, editingLayer } from '../planeInput';\nimport { segmentUnder } from './segmentUnder';\nimport { pathOperationTags } from '../pathGestureTags';\n\nconst INSERT_CURSOR: CursorSpec = { glyph: 'penPlus', fallback: 'crosshair' };\n\nexport const insertPathAnchorAction: Action & { requires: string[] } = inPlane({\n id: 'insertPathAnchor',\n label: 'Insert anchor',\n tags: pathOperationTags('click'),\n defaultBinding: [{ spec: { kind: 'click', mods: { alt: true } }, opts: { id: 'insertPathAnchor.click' } }],\n cursor: INSERT_CURSOR,\n eligible: { capability: 'edits-anchors' },\n requires: ['editAnchors', 'view', 'kitSettings'],\n invoker: {\n timing: 'immediate',\n run(deps, params) {\n const wx = params?.worldX as number | undefined;\n const wy = params?.worldY as number | undefined;\n if (typeof wx !== 'number' || typeof wy !== 'number') return;\n const under = segmentUnder(deps, wx, wy);\n if (!under) return;\n const { dep, path, hit } = under;\n let inserted = -1;\n const next = editAnchorSetTracked(path, (set) => {\n inserted = insertAnchorOnSegment(set, hit);\n return inserted !== -1;\n });\n if (!next) return;\n dep.applyEdit(dep.editingId, next.path, 'Insert anchor', next.origins);\n dep.setSelectedAnchors([inserted]);\n },\n } as ImmediateInvoker,\n enabled: (deps, at) => {\n const dep = deps?.editAnchors;\n if (!dep?.editingId) return ActionDisabledReason.NotApplicable;\n if (at && !segmentUnder(deps, at.x, at.y)) return ActionDisabledReason.NotApplicable;\n return true;\n },\n}, editingLayer);\n"],"mappings":";;;;;;;AAuBA,IAAM,gBAA4B;CAAE,OAAO;CAAW,UAAU;AAAY;AAE5E,IAAa,yBAA0D,QAAQ;CAC7E,IAAI;CACJ,OAAO;CACP,MAAM,kBAAkB,OAAO;CAC/B,gBAAgB,CAAC;EAAE,MAAM;GAAE,MAAM;GAAS,MAAM,EAAE,KAAK,KAAK;EAAE;EAAG,MAAM,EAAE,IAAI,yBAAyB;CAAE,CAAC;CACzG,QAAQ;CACR,UAAU,EAAE,YAAY,gBAAgB;CACxC,UAAU;EAAC;EAAe;EAAQ;CAAa;CAC/C,SAAS;EACP,QAAQ;EACR,IAAI,MAAM,QAAQ;GAChB,MAAM,KAAK,QAAQ;GACnB,MAAM,KAAK,QAAQ;GACnB,IAAI,OAAO,OAAO,YAAY,OAAO,OAAO,UAAU;GACtD,MAAM,QAAQ,aAAa,MAAM,IAAI,EAAE;GACvC,IAAI,CAAC,OAAO;GACZ,MAAM,EAAE,KAAK,MAAM,QAAQ;GAC3B,IAAI,WAAW;GACf,MAAM,OAAO,qBAAqB,OAAO,QAAQ;IAC/C,WAAW,sBAAsB,KAAK,GAAG;IACzC,OAAO,aAAa;GACtB,CAAC;GACD,IAAI,CAAC,MAAM;GACX,IAAI,UAAU,IAAI,WAAW,KAAK,MAAM,iBAAiB,KAAK,OAAO;GACrE,IAAI,mBAAmB,CAAC,QAAQ,CAAC;EACnC;CACF;CACA,UAAU,MAAM,OAAO;EAErB,IAAI,EADQ,MAAM,YACb,EAAK,WAAW,OAAO,qBAAqB;EACjD,IAAI,MAAM,CAAC,aAAa,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,OAAO,qBAAqB;EACvE,OAAO;CACT;AACF,GAAG,YAAY"}
1
+ {"version":3,"file":"insertPathAnchor.js","names":[],"sources":["../../../../src/interactions/actions/defaults/insertPathAnchor.ts"],"sourcesContent":["/**\n * `insertPathAnchorAction` — Alt+click on a segment of the path being\n * edited inserts a new anchor there, without changing the path's shape.\n *\n * Alt+click because plain click already belongs to `selectAnchorAction`\n * (select an anchor, or clear the anchor selection on a miss), and\n * Alt+Shift+click to `cutPathAtAnchorAction`.\n *\n * The segment test lives in `enabled`, which the dispatcher hands the\n * event's world point: a click off every segment (or on an anchor) reports\n * not-applicable, so the dispatcher falls through to whatever else the\n * click could mean, and the hover pump shows `cursor` only where a click\n * would insert.\n */\n\nimport type { Action, ImmediateInvoker } from '@weasel-js/routing';\nimport { ActionDisabledReason } from '@weasel-js/routing';\nimport type { CursorSpec } from '@weasel-js/cursor';\nimport { editAnchorSetTracked, insertAnchorOnSegment } from '@weasel-js/geom';\nimport { inPlane, editingLayer } from '../planeInput';\nimport { segmentUnder } from './segmentUnder';\nimport { pathOperationTags } from '../pathGestureTags';\nimport type { ImmediateParams } from '@weasel-js/routing';\n\nconst INSERT_CURSOR: CursorSpec = { glyph: 'penPlus', fallback: 'crosshair' };\n\nexport const insertPathAnchorAction: Action & { requires: string[] } = inPlane({\n id: 'insertPathAnchor',\n label: 'Insert anchor',\n tags: pathOperationTags('click'),\n defaultBinding: [{ spec: { kind: 'click', mods: { alt: true } }, opts: { id: 'insertPathAnchor.click' } }],\n cursor: INSERT_CURSOR,\n eligible: { capability: 'edits-anchors' },\n requires: ['editAnchors', 'view', 'kitSettings'],\n invoker: {\n timing: 'immediate',\n run(deps, params) {\n const world = (params as ImmediateParams | undefined)?.world;\n if (!world) return;\n const { x: wx, y: wy } = world;\n const under = segmentUnder(deps, wx, wy);\n if (!under) return;\n const { dep, path, hit } = under;\n let inserted = -1;\n const next = editAnchorSetTracked(path, (set) => {\n inserted = insertAnchorOnSegment(set, hit);\n return inserted !== -1;\n });\n if (!next) return;\n dep.applyEdit(dep.editingId, next.path, 'Insert anchor', next.origins);\n dep.setSelectedAnchors([inserted]);\n },\n } as ImmediateInvoker,\n enabled: (deps, at) => {\n const dep = deps?.editAnchors;\n if (!dep?.editingId) return ActionDisabledReason.NotApplicable;\n if (at && !segmentUnder(deps, at.x, at.y)) return ActionDisabledReason.NotApplicable;\n return true;\n },\n}, editingLayer);\n"],"mappings":";;;;;;;AAwBA,IAAM,gBAA4B;CAAE,OAAO;CAAW,UAAU;AAAY;AAE5E,IAAa,yBAA0D,QAAQ;CAC7E,IAAI;CACJ,OAAO;CACP,MAAM,kBAAkB,OAAO;CAC/B,gBAAgB,CAAC;EAAE,MAAM;GAAE,MAAM;GAAS,MAAM,EAAE,KAAK,KAAK;EAAE;EAAG,MAAM,EAAE,IAAI,yBAAyB;CAAE,CAAC;CACzG,QAAQ;CACR,UAAU,EAAE,YAAY,gBAAgB;CACxC,UAAU;EAAC;EAAe;EAAQ;CAAa;CAC/C,SAAS;EACP,QAAQ;EACR,IAAI,MAAM,QAAQ;GAChB,MAAM,QAAS,QAAwC;GACvD,IAAI,CAAC,OAAO;GACZ,MAAM,EAAE,GAAG,IAAI,GAAG,OAAO;GACzB,MAAM,QAAQ,aAAa,MAAM,IAAI,EAAE;GACvC,IAAI,CAAC,OAAO;GACZ,MAAM,EAAE,KAAK,MAAM,QAAQ;GAC3B,IAAI,WAAW;GACf,MAAM,OAAO,qBAAqB,OAAO,QAAQ;IAC/C,WAAW,sBAAsB,KAAK,GAAG;IACzC,OAAO,aAAa;GACtB,CAAC;GACD,IAAI,CAAC,MAAM;GACX,IAAI,UAAU,IAAI,WAAW,KAAK,MAAM,iBAAiB,KAAK,OAAO;GACrE,IAAI,mBAAmB,CAAC,QAAQ,CAAC;EACnC;CACF;CACA,UAAU,MAAM,OAAO;EAErB,IAAI,EADQ,MAAM,YACb,EAAK,WAAW,OAAO,qBAAqB;EACjD,IAAI,MAAM,CAAC,aAAa,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,OAAO,qBAAqB;EACvE,OAAO;CACT;AACF,GAAG,YAAY"}
@@ -1,3 +1,5 @@
1
+ import { arrowMove } from "@weasel-js/headless";
2
+
1
3
  //#region src/interactions/actions/defaults/nudgeSteps.ts
2
4
  var NUDGE_KEY = {
3
5
  up: "ArrowUp",
@@ -11,30 +13,10 @@ var NUDGE_LABEL = {
11
13
  left: "Left",
12
14
  right: "Right"
13
15
  };
14
- var SMALL_STEP = 1;
15
- var BIG_STEP = 10;
16
16
  /** The world delta of one nudge. `magnitude` is the binding param: `'big'`
17
17
  * for the Shift step, anything else for the small one. */
18
18
  function nudgeDelta(dir, magnitude) {
19
- const step = magnitude === "big" ? BIG_STEP : SMALL_STEP;
20
- switch (dir) {
21
- case "up": return {
22
- dx: 0,
23
- dy: -step
24
- };
25
- case "down": return {
26
- dx: 0,
27
- dy: step
28
- };
29
- case "left": return {
30
- dx: -step,
31
- dy: 0
32
- };
33
- case "right": return {
34
- dx: step,
35
- dy: 0
36
- };
37
- }
19
+ return arrowMove(NUDGE_KEY[dir], magnitude === "big");
38
20
  }
39
21
 
40
22
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"nudgeSteps.js","names":[],"sources":["../../../../src/interactions/actions/defaults/nudgeSteps.ts"],"sourcesContent":["/** The arrow-key nudge shared by nodes and path anchors: one direction per\n * key, a small step bare and a big one with Shift. */\nexport type NudgeDirection = 'up' | 'down' | 'left' | 'right';\n\nexport const NUDGE_KEY: Record<NudgeDirection, string> = {\n up: 'ArrowUp', down: 'ArrowDown', left: 'ArrowLeft', right: 'ArrowRight',\n};\n\nexport const NUDGE_LABEL: Record<NudgeDirection, string> = {\n up: 'Up', down: 'Down', left: 'Left', right: 'Right',\n};\n\nconst SMALL_STEP = 1;\nconst BIG_STEP = 10;\n\n/** The world delta of one nudge. `magnitude` is the binding param: `'big'`\n * for the Shift step, anything else for the small one. */\nexport function nudgeDelta(dir: NudgeDirection, magnitude: unknown): { dx: number; dy: number } {\n const step = magnitude === 'big' ? BIG_STEP : SMALL_STEP;\n switch (dir) {\n case 'up': return { dx: 0, dy: -step };\n case 'down': return { dx: 0, dy: step };\n case 'left': return { dx: -step, dy: 0 };\n case 'right': return { dx: step, dy: 0 };\n }\n}\n"],"mappings":";AAIA,IAAa,YAA4C;CACvD,IAAI;CAAW,MAAM;CAAa,MAAM;CAAa,OAAO;AAC9D;AAEA,IAAa,cAA8C;CACzD,IAAI;CAAM,MAAM;CAAQ,MAAM;CAAQ,OAAO;AAC/C;AAEA,IAAM,aAAa;AACnB,IAAM,WAAW;;;AAIjB,SAAgB,WAAW,KAAqB,WAAgD;CAC9F,MAAM,OAAO,cAAc,QAAQ,WAAW;CAC9C,QAAQ,KAAR;EACE,KAAK,MAAS,OAAO;GAAE,IAAI;GAAO,IAAI,CAAC;EAAK;EAC5C,KAAK,QAAS,OAAO;GAAE,IAAI;GAAO,IAAK;EAAK;EAC5C,KAAK,QAAS,OAAO;GAAE,IAAI,CAAC;GAAM,IAAI;EAAM;EAC5C,KAAK,SAAS,OAAO;GAAE,IAAK;GAAM,IAAI;EAAM;CAC9C;AACF"}
1
+ {"version":3,"file":"nudgeSteps.js","names":[],"sources":["../../../../src/interactions/actions/defaults/nudgeSteps.ts"],"sourcesContent":["import { arrowMove } from '@weasel-js/headless';\n\n/** The arrow-key nudge shared by nodes and path anchors: one direction per\n * key, a small step bare and a big one with Shift. */\nexport type NudgeDirection = 'up' | 'down' | 'left' | 'right';\n\nexport const NUDGE_KEY: Record<NudgeDirection, string> = {\n up: 'ArrowUp', down: 'ArrowDown', left: 'ArrowLeft', right: 'ArrowRight',\n};\n\nexport const NUDGE_LABEL: Record<NudgeDirection, string> = {\n up: 'Up', down: 'Down', left: 'Left', right: 'Right',\n};\n\n/** The world delta of one nudge. `magnitude` is the binding param: `'big'`\n * for the Shift step, anything else for the small one. */\nexport function nudgeDelta(dir: NudgeDirection, magnitude: unknown): { dx: number; dy: number } {\n return arrowMove(NUDGE_KEY[dir], magnitude === 'big')!;\n}\n"],"mappings":";;;AAMA,IAAa,YAA4C;CACvD,IAAI;CAAW,MAAM;CAAa,MAAM;CAAa,OAAO;AAC9D;AAEA,IAAa,cAA8C;CACzD,IAAI;CAAM,MAAM;CAAQ,MAAM;CAAQ,OAAO;AAC/C;;;AAIA,SAAgB,WAAW,KAAqB,WAAgD;CAC9F,OAAO,UAAU,UAAU,MAAM,cAAc,KAAK;AACtD"}
@@ -1 +1 @@
1
- {"version":3,"file":"pathEditMarks.js","names":[],"sources":["../../../../src/interactions/actions/defaults/pathEditMarks.ts"],"sourcesContent":["/** Path edit's side of the marks its extensions draw: finding the one a click\n * lands on, and deleting the selected one. */\n\nimport type { ActionDeps } from '@weasel-js/routing';\nimport { pathToAnchors, type AnchorSet, type PolygonPath } from '@weasel-js/geom';\nimport type { PathEditMark, PathEditTarget } from 'features/paths/pathEditExtension';\nimport type { EditAnchorsDep } from '../depSchema';\n\n/** The open path as extensions read it, or null with none open. */\nexport function editTargetOf(dep: EditAnchorsDep): PathEditTarget | null {\n const node = dep.editingId ? dep.getNodeShape(dep.editingId) : null;\n const path = node ? (dep.getEditablePath(dep.editingId) as PolygonPath | null | undefined) : null;\n if (!node || path?.kind !== 'polygon') return null;\n return { id: dep.editingId, node, path, anchors: pathToAnchors(path) as AnchorSet };\n}\n\n/** The extension mark under the world point `(wx, wy)`, or null. */\nexport function markUnder(dep: EditAnchorsDep, deps: ActionDeps | undefined, wx: number, wy: number): PathEditMark | null {\n const extensions = dep.extensions?.filter((e) => e.markAt) ?? [];\n const target = extensions.length > 0 ? editTargetOf(dep) : null;\n if (!target) return null;\n const view = deps?.view?.get() ?? { x: 0, y: 0, scale: { x: 1, y: 1 } };\n const toScreen = (p: { x: number; y: number }) => ({ x: (p.x - view.x) * view.scale.x, y: (p.y - view.y) * view.scale.y });\n const screen = toScreen({ x: wx, y: wy });\n for (const ext of [...extensions].reverse()) {\n const index = ext.markAt!(target, toScreen, screen);\n if (index !== -1) return { owner: ext.id, index };\n }\n return null;\n}\n\n/** Delete the selected extension mark. False when none is selected, or its\n * owner cannot delete it. */\nexport function deleteSelectedMark(dep: EditAnchorsDep): boolean {\n const mark = dep.selectedMark;\n const owner = mark ? dep.extensions?.find((e) => e.id === mark.owner) : undefined;\n const target = owner?.deleteMark && dep.store ? editTargetOf(dep) : null;\n if (!mark || !target) return false;\n owner!.deleteMark!(target, mark.index, dep.store!);\n dep.setSelectedMark?.(null);\n return true;\n}\n"],"mappings":";;;;AASA,SAAgB,aAAa,KAA4C;CACvE,MAAM,OAAO,IAAI,YAAY,IAAI,aAAa,IAAI,SAAS,IAAI;CAC/D,MAAM,OAAO,OAAQ,IAAI,gBAAgB,IAAI,SAAS,IAAuC;CAC7F,IAAI,CAAC,QAAQ,MAAM,SAAS,WAAW,OAAO;CAC9C,OAAO;EAAE,IAAI,IAAI;EAAW;EAAM;EAAM,SAAS,cAAc,IAAI;CAAe;AACpF;;AAGA,SAAgB,UAAU,KAAqB,MAA8B,IAAY,IAAiC;CACxH,MAAM,aAAa,IAAI,YAAY,QAAQ,MAAM,EAAE,MAAM,KAAK,CAAC;CAC/D,MAAM,SAAS,WAAW,SAAS,IAAI,aAAa,GAAG,IAAI;CAC3D,IAAI,CAAC,QAAQ,OAAO;CACpB,MAAM,OAAO,MAAM,MAAM,IAAI,KAAK;EAAE,GAAG;EAAG,GAAG;EAAG,OAAO;GAAE,GAAG;GAAG,GAAG;EAAE;CAAE;CACtE,MAAM,YAAY,OAAiC;EAAE,IAAI,EAAE,IAAI,KAAK,KAAK,KAAK,MAAM;EAAG,IAAI,EAAE,IAAI,KAAK,KAAK,KAAK,MAAM;CAAE;CACxH,MAAM,SAAS,SAAS;EAAE,GAAG;EAAI,GAAG;CAAG,CAAC;CACxC,KAAK,MAAM,OAAO,CAAC,GAAG,UAAU,CAAC,CAAC,QAAQ,GAAG;EAC3C,MAAM,QAAQ,IAAI,OAAQ,QAAQ,UAAU,MAAM;EAClD,IAAI,UAAU,IAAI,OAAO;GAAE,OAAO,IAAI;GAAI;EAAM;CAClD;CACA,OAAO;AACT;;;AAIA,SAAgB,mBAAmB,KAA8B;CAC/D,MAAM,OAAO,IAAI;CACjB,MAAM,QAAQ,OAAO,IAAI,YAAY,MAAM,MAAM,EAAE,OAAO,KAAK,KAAK,IAAI;CACxE,MAAM,SAAS,OAAO,cAAc,IAAI,QAAQ,aAAa,GAAG,IAAI;CACpE,IAAI,CAAC,QAAQ,CAAC,QAAQ,OAAO;CAC7B,MAAO,WAAY,QAAQ,KAAK,OAAO,IAAI,KAAM;CACjD,IAAI,kBAAkB,IAAI;CAC1B,OAAO;AACT"}
1
+ {"version":3,"file":"pathEditMarks.js","names":[],"sources":["../../../../src/interactions/actions/defaults/pathEditMarks.ts"],"sourcesContent":["/** Path edit's side of the marks its extensions draw: finding the one a click\n * lands on, and deleting the selected one. */\n\nimport type { ActionDeps } from '@weasel-js/routing';\nimport { pathToAnchors, type AnchorSet, type PolygonPath } from '@weasel-js/geom';\nimport type { PathEditMark, PathEditTarget } from 'domains/paths/pathEditExtension';\nimport type { EditAnchorsDep } from '../depSchema';\n\n/** The open path as extensions read it, or null with none open. */\nexport function editTargetOf(dep: EditAnchorsDep): PathEditTarget | null {\n const node = dep.editingId ? dep.getNodeShape(dep.editingId) : null;\n const path = node ? (dep.getEditablePath(dep.editingId) as PolygonPath | null | undefined) : null;\n if (!node || path?.kind !== 'polygon') return null;\n return { id: dep.editingId, node, path, anchors: pathToAnchors(path) as AnchorSet };\n}\n\n/** The extension mark under the world point `(wx, wy)`, or null. */\nexport function markUnder(dep: EditAnchorsDep, deps: ActionDeps | undefined, wx: number, wy: number): PathEditMark | null {\n const extensions = dep.extensions?.filter((e) => e.markAt) ?? [];\n const target = extensions.length > 0 ? editTargetOf(dep) : null;\n if (!target) return null;\n const view = deps?.view?.get() ?? { x: 0, y: 0, scale: { x: 1, y: 1 } };\n const toScreen = (p: { x: number; y: number }) => ({ x: (p.x - view.x) * view.scale.x, y: (p.y - view.y) * view.scale.y });\n const screen = toScreen({ x: wx, y: wy });\n for (const ext of [...extensions].reverse()) {\n const index = ext.markAt!(target, toScreen, screen);\n if (index !== -1) return { owner: ext.id, index };\n }\n return null;\n}\n\n/** Delete the selected extension mark. False when none is selected, or its\n * owner cannot delete it. */\nexport function deleteSelectedMark(dep: EditAnchorsDep): boolean {\n const mark = dep.selectedMark;\n const owner = mark ? dep.extensions?.find((e) => e.id === mark.owner) : undefined;\n const target = owner?.deleteMark && dep.store ? editTargetOf(dep) : null;\n if (!mark || !target) return false;\n owner!.deleteMark!(target, mark.index, dep.store!);\n dep.setSelectedMark?.(null);\n return true;\n}\n"],"mappings":";;;;AASA,SAAgB,aAAa,KAA4C;CACvE,MAAM,OAAO,IAAI,YAAY,IAAI,aAAa,IAAI,SAAS,IAAI;CAC/D,MAAM,OAAO,OAAQ,IAAI,gBAAgB,IAAI,SAAS,IAAuC;CAC7F,IAAI,CAAC,QAAQ,MAAM,SAAS,WAAW,OAAO;CAC9C,OAAO;EAAE,IAAI,IAAI;EAAW;EAAM;EAAM,SAAS,cAAc,IAAI;CAAe;AACpF;;AAGA,SAAgB,UAAU,KAAqB,MAA8B,IAAY,IAAiC;CACxH,MAAM,aAAa,IAAI,YAAY,QAAQ,MAAM,EAAE,MAAM,KAAK,CAAC;CAC/D,MAAM,SAAS,WAAW,SAAS,IAAI,aAAa,GAAG,IAAI;CAC3D,IAAI,CAAC,QAAQ,OAAO;CACpB,MAAM,OAAO,MAAM,MAAM,IAAI,KAAK;EAAE,GAAG;EAAG,GAAG;EAAG,OAAO;GAAE,GAAG;GAAG,GAAG;EAAE;CAAE;CACtE,MAAM,YAAY,OAAiC;EAAE,IAAI,EAAE,IAAI,KAAK,KAAK,KAAK,MAAM;EAAG,IAAI,EAAE,IAAI,KAAK,KAAK,KAAK,MAAM;CAAE;CACxH,MAAM,SAAS,SAAS;EAAE,GAAG;EAAI,GAAG;CAAG,CAAC;CACxC,KAAK,MAAM,OAAO,CAAC,GAAG,UAAU,CAAC,CAAC,QAAQ,GAAG;EAC3C,MAAM,QAAQ,IAAI,OAAQ,QAAQ,UAAU,MAAM;EAClD,IAAI,UAAU,IAAI,OAAO;GAAE,OAAO,IAAI;GAAI;EAAM;CAClD;CACA,OAAO;AACT;;;AAIA,SAAgB,mBAAmB,KAA8B;CAC/D,MAAM,OAAO,IAAI;CACjB,MAAM,QAAQ,OAAO,IAAI,YAAY,MAAM,MAAM,EAAE,OAAO,KAAK,KAAK,IAAI;CACxE,MAAM,SAAS,OAAO,cAAc,IAAI,QAAQ,aAAa,GAAG,IAAI;CACpE,IAAI,CAAC,QAAQ,CAAC,QAAQ,OAAO;CAC7B,MAAO,WAAY,QAAQ,KAAK,OAAO,IAAI,KAAM;CACjD,IAAI,kBAAkB,IAAI;CAC1B,OAAO;AACT"}
@@ -15,9 +15,9 @@ function createSelectDeepAction(opts) {
15
15
  run: (deps, params) => {
16
16
  const p = params;
17
17
  const selection = deps.selection;
18
- if (!selection || p?.worldX === void 0 || p.worldY === void 0) return;
18
+ if (!selection || !p?.world) return;
19
19
  const shift = p.mods?.shift === true;
20
- const hit = opts.pickHit(p.worldX, p.worldY, p.mods?.alt === true, selection.get());
20
+ const hit = opts.pickHit(p.world, p.mods?.alt === true, selection.get());
21
21
  opts.settled();
22
22
  if (hit === null || shift && opts.extendLocked()) return;
23
23
  selection.applyClick(hit, {
@@ -1 +1 @@
1
- {"version":3,"file":"selectDeep.js","names":[],"sources":["../../../../src/interactions/actions/defaults/selectDeep.ts"],"sourcesContent":["import type { Action } from '@weasel-js/routing';\nimport type { NodeId } from 'core/scene/types';\n\ntype Mods = { alt: boolean; ctrl: boolean; meta: boolean; shift: boolean };\n\nexport interface SelectDeepOptions {\n /** The node under the point, past every group. Locked nodes are the\n * picker's to skip, as they are for `select.pick`. */\n pickHit: (worldX: number, worldY: number, alt: boolean, selection: readonly string[]) => string | null;\n /** True when an extend press must leave the selection alone. */\n extendLocked: () => boolean;\n /** Called once the press is handled, so a deferral left by an earlier press cannot fire on release. */\n settled: () => void;\n}\n\n/**\n * `select.deep` — selects the node under the point itself, not the outermost\n * group above it; with Shift it adds that node. Bound to Cmd-press by\n * `useSelectTool`, ahead of `select.pick`.\n */\nexport function createSelectDeepAction(opts: SelectDeepOptions): Action {\n return {\n id: 'select.deep',\n label: 'Select — pick past groups',\n eligible: { capability: 'creates-selection' },\n requires: ['selection'],\n invoker: {\n timing: 'immediate' as const,\n run: (deps, params) => {\n const p = params as { worldX?: number; worldY?: number; mods?: Mods } | undefined;\n const selection = deps.selection;\n if (!selection || p?.worldX === undefined || p.worldY === undefined) return;\n const shift = p.mods?.shift === true;\n const hit = opts.pickHit(p.worldX, p.worldY, p.mods?.alt === true, selection.get());\n opts.settled();\n if (hit === null || (shift && opts.extendLocked())) return;\n selection.applyClick(hit as NodeId, { ctrl: false, meta: false, shift });\n },\n },\n };\n}\n"],"mappings":";;;;;;AAoBA,SAAgB,uBAAuB,MAAiC;CACtE,OAAO;EACL,IAAI;EACJ,OAAO;EACP,UAAU,EAAE,YAAY,oBAAoB;EAC5C,UAAU,CAAC,WAAW;EACtB,SAAS;GACP,QAAQ;GACR,MAAM,MAAM,WAAW;IACrB,MAAM,IAAI;IACV,MAAM,YAAY,KAAK;IACvB,IAAI,CAAC,aAAa,GAAG,WAAW,UAAa,EAAE,WAAW,QAAW;IACrE,MAAM,QAAQ,EAAE,MAAM,UAAU;IAChC,MAAM,MAAM,KAAK,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,QAAQ,MAAM,UAAU,IAAI,CAAC;IAClF,KAAK,QAAQ;IACb,IAAI,QAAQ,QAAS,SAAS,KAAK,aAAa,GAAI;IACpD,UAAU,WAAW,KAAe;KAAE,MAAM;KAAO,MAAM;KAAO;IAAM,CAAC;GACzE;EACF;CACF;AACF"}
1
+ {"version":3,"file":"selectDeep.js","names":[],"sources":["../../../../src/interactions/actions/defaults/selectDeep.ts"],"sourcesContent":["import type { Action, WorldPoint } from '@weasel-js/routing';\nimport type { NodeId } from 'core/scene/types';\nimport type { ImmediateParams } from '@weasel-js/routing';\n\nexport interface SelectDeepOptions {\n /** The node under the point, past every group. Locked nodes are the\n * picker's to skip, as they are for `select.pick`. */\n pickHit: (world: WorldPoint, alt: boolean, selection: readonly string[]) => string | null;\n /** True when an extend press must leave the selection alone. */\n extendLocked: () => boolean;\n /** Called once the press is handled, so a deferral left by an earlier press cannot fire on release. */\n settled: () => void;\n}\n\n/**\n * `select.deep` — selects the node under the point itself, not the outermost\n * group above it; with Shift it adds that node. Bound to Cmd-press by\n * `useSelectTool`, ahead of `select.pick`.\n */\nexport function createSelectDeepAction(opts: SelectDeepOptions): Action {\n return {\n id: 'select.deep',\n label: 'Select — pick past groups',\n eligible: { capability: 'creates-selection' },\n requires: ['selection'],\n invoker: {\n timing: 'immediate' as const,\n run: (deps, params) => {\n const p = params as ImmediateParams | undefined;\n const selection = deps.selection;\n if (!selection || !p?.world) return;\n const shift = p.mods?.shift === true;\n const hit = opts.pickHit(p.world, p.mods?.alt === true, selection.get());\n opts.settled();\n if (hit === null || (shift && opts.extendLocked())) return;\n selection.applyClick(hit as NodeId, { ctrl: false, meta: false, shift });\n },\n },\n };\n}\n"],"mappings":";;;;;;AAmBA,SAAgB,uBAAuB,MAAiC;CACtE,OAAO;EACL,IAAI;EACJ,OAAO;EACP,UAAU,EAAE,YAAY,oBAAoB;EAC5C,UAAU,CAAC,WAAW;EACtB,SAAS;GACP,QAAQ;GACR,MAAM,MAAM,WAAW;IACrB,MAAM,IAAI;IACV,MAAM,YAAY,KAAK;IACvB,IAAI,CAAC,aAAa,CAAC,GAAG,OAAO;IAC7B,MAAM,QAAQ,EAAE,MAAM,UAAU;IAChC,MAAM,MAAM,KAAK,QAAQ,EAAE,OAAO,EAAE,MAAM,QAAQ,MAAM,UAAU,IAAI,CAAC;IACvE,KAAK,QAAQ;IACb,IAAI,QAAQ,QAAS,SAAS,KAAK,aAAa,GAAI;IACpD,UAAU,WAAW,KAAe;KAAE,MAAM;KAAO,MAAM;KAAO;IAAM,CAAC;GACzE;EACF;CACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"distribute.js","names":[],"sources":["../../../../src/interactions/actions/distribute/distribute.ts"],"sourcesContent":["import { useCallback } from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport { createTransformOp } from 'core/ops/transform';\nimport type { Op } from 'core/ops/types';\nimport { dispatchApplyBatch } from 'core/applyOps';\nimport type { NodeId } from 'core/scene/types';\nimport { RECT_POSE_DESCRIPTOR, type PoseDescriptor } from '../resize/geometry';\nimport { translatePoseViaDescriptor, visualBoundsViaDescriptor } from '../align/align';\nimport { planDistribute } from './plan';\nimport { poseFrame } from '../poseFrame';\nimport { IDENTITY_POSE_COMPOSITION, type PoseComposition } from 'core/scene/composePose';\n\n/** Axis along which selection is distributed. `'x'` spreads horizontally. */\nexport type DistributeAxis = 'x' | 'y';\n\n/** `'centers'` spaces centers equally; `'gaps'` makes the gap between\n * consecutive items equal. Both measure a rotated item by its ink extent.\n * Endpoints stay put in both modes. */\nexport type DistributeMode = 'centers' | 'gaps';\n\n/** Adapter for `useDistribute`. */\nexport interface DistributeAdapter<TPose> {\n getSelection(): NodeId[];\n /** The pose as stored — local to the node's parent. */\n getPose(id: NodeId): TPose;\n /** The node's container, `null` for a root: the chain a pose is composed\n * through when container poses define a frame. */\n getParent(id: NodeId): NodeId | null;\n applyOps?(ops: Op[], label?: string): void;\n}\n\n/** Options for `useDistribute`. */\nexport interface UseDistributeOptions<TPose> {\n /** Projection between `TPose` and bounds. Defaults to `RECT_POSE_DESCRIPTOR`\n * for `{x,y,width,height}` poses. */\n geometry?: PoseDescriptor<TPose>;\n /** Default mode when `distribute(axis)` is called without one. Default 'centers'. */\n defaultMode?: DistributeMode;\n /** Label passed to applyOps. Default 'Distribute'. */\n label?: string;\n /** How local poses fold up to world. Default IDENTITY, where the two are\n * the same value and the spread runs entirely in stored coordinates. */\n composition?: PoseComposition<TPose>;\n}\n\n/** Return shape of `useDistribute`. */\nexport interface UseDistributeReturn {\n /** Imperative trigger. No-op when fewer than 3 items selected. */\n distribute(axis: DistributeAxis, mode?: DistributeMode): void;\n}\n\n/** Distribute the current multi-selection along `axis`. Requires ≥3 items;\n * no-op otherwise. Endpoints (min and max along the axis) stay put; the\n * remaining items are repositioned. Single batch — one undo step.\n *\n * The span being divided is a world span: bounds are measured and translated\n * in world, and each result is stored back in its own parent's frame. */\nexport function useDistribute<TPose>(\n adapter: DistributeAdapter<TPose>,\n options: UseDistributeOptions<TPose> = {},\n): UseDistributeReturn {\n const adapterRef = useLatest(adapter);\n const optsRef = useLatest(options);\n\n const distribute = useCallback((axis: DistributeAxis, mode?: DistributeMode): void => {\n const a = adapterRef.current;\n const o = optsRef.current;\n const sel = a.getSelection();\n if (sel.length < 3) return;\n const geom =\n o.geometry ??\n (RECT_POSE_DESCRIPTOR as unknown as PoseDescriptor<TPose>);\n const m = mode ?? o.defaultMode ?? 'centers';\n\n const frame = poseFrame<TPose>(\n {\n getPose: (id) => a.getPose(id as NodeId),\n getParent: (id) => a.getParent(id as NodeId) as string | null,\n },\n o.composition ?? (IDENTITY_POSE_COMPOSITION as PoseComposition<TPose>),\n );\n const items = sel.map((id) => {\n const pose = frame.world(id);\n return { id, pose, b: visualBoundsViaDescriptor(pose, geom) };\n });\n const targets = planDistribute(items.map((it) => it.b), axis, m);\n\n const ops: Op[] = [];\n for (let i = 0; i < items.length; i++) {\n const it = items[i];\n const delta = targets[i] - (axis === 'x' ? it.b.x : it.b.y);\n if (delta === 0) continue;\n const dx = axis === 'x' ? delta : 0;\n const dy = axis === 'y' ? delta : 0;\n const to = translatePoseViaDescriptor(it.pose, dx, dy, geom);\n ops.push(createTransformOp<TPose>({\n id: it.id,\n from: a.getPose(it.id),\n to: frame.local(it.id, to),\n }));\n }\n if (ops.length === 0) return;\n dispatchApplyBatch(a, ops, o.label ?? 'Distribute');\n }, [adapterRef, optsRef]);\n\n return { distribute };\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAyDA,SAAgB,cACd,SACA,UAAuC,CAAC,GACnB;CACrB,MAAM,aAAa,UAAU,OAAO;CACpC,MAAM,UAAU,UAAU,OAAO;CA2CjC,OAAO,EAAE,YAzCU,aAAa,MAAsB,SAAgC;EACpF,MAAM,IAAI,WAAW;EACrB,MAAM,IAAI,QAAQ;EAClB,MAAM,MAAM,EAAE,aAAa;EAC3B,IAAI,IAAI,SAAS,GAAG;EACpB,MAAM,OACJ,EAAE,YACD;EACH,MAAM,IAAI,QAAQ,EAAE,eAAe;EAEnC,MAAM,QAAQ,UACZ;GACE,UAAU,OAAO,EAAE,QAAQ,EAAY;GACvC,YAAY,OAAO,EAAE,UAAU,EAAY;EAC7C,GACA,EAAE,eAAgB,yBACpB;EACA,MAAM,QAAQ,IAAI,KAAK,OAAO;GAC5B,MAAM,OAAO,MAAM,MAAM,EAAE;GAC3B,OAAO;IAAE;IAAI;IAAM,GAAG,0BAA0B,MAAM,IAAI;GAAE;EAC9D,CAAC;EACD,MAAM,UAAU,eAAe,MAAM,KAAK,OAAO,GAAG,CAAC,GAAG,MAAM,CAAC;EAE/D,MAAM,MAAY,CAAC;EACnB,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;GACrC,MAAM,KAAK,MAAM;GACjB,MAAM,QAAQ,QAAQ,MAAM,SAAS,MAAM,GAAG,EAAE,IAAI,GAAG,EAAE;GACzD,IAAI,UAAU,GAAG;GACjB,MAAM,KAAK,SAAS,MAAM,QAAQ;GAClC,MAAM,KAAK,SAAS,MAAM,QAAQ;GAClC,MAAM,KAAK,2BAA2B,GAAG,MAAM,IAAI,IAAI,IAAI;GAC3D,IAAI,KAAK,kBAAyB;IAChC,IAAI,GAAG;IACP,MAAM,EAAE,QAAQ,GAAG,EAAE;IACrB,IAAI,MAAM,MAAM,GAAG,IAAI,EAAE;GAC3B,CAAC,CAAC;EACJ;EACA,IAAI,IAAI,WAAW,GAAG;EACtB,mBAAmB,GAAG,KAAK,EAAE,SAAS,YAAY;CACpD,GAAG,CAAC,YAAY,OAAO,CAEd,EAAW;AACtB"}
1
+ {"version":3,"file":"distribute.js","names":[],"sources":["../../../../src/interactions/actions/distribute/distribute.ts"],"sourcesContent":["import { useCallback } from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport { createTransformOp } from 'core/ops/transform';\nimport type { Op } from 'core/ops/types';\nimport { dispatchApplyBatch } from 'core/applyOps';\nimport type { NodeId } from 'core/scene/types';\nimport { RECT_POSE_DESCRIPTOR, type PoseDescriptor } from '../resize/geometry';\nimport { translatePoseViaDescriptor, visualBoundsViaDescriptor } from '../align/align';\nimport { planDistribute } from './plan';\nimport { poseFrame } from '../poseFrame';\nimport { IDENTITY_POSE_COMPOSITION, type PoseComposition } from 'core/scene/composePose';\n\n/** Axis along which selection is distributed. `'x'` spreads horizontally. */\nexport type DistributeAxis = 'x' | 'y';\n\n/** `'centers'` spaces centers equally; `'gaps'` makes the gap between\n * consecutive items equal. Both measure a rotated item by its ink extent.\n * Endpoints stay put in both modes. */\nexport type DistributeMode = 'centers' | 'gaps';\n\n/** Adapter for `useDistribute`. */\nexport interface DistributeAdapter<TPose> {\n getSelection(): NodeId[];\n /** The pose as stored — local to the node's parent. */\n getPose(id: NodeId): TPose;\n /** The node's container, `null` for a root: the chain a pose is composed\n * through when container poses define a frame. */\n getParent(id: NodeId): NodeId | null;\n applyOps?(ops: Op[], label?: string): void;\n}\n\n/** Options for `useDistribute`. */\nexport interface UseDistributeOptions<TPose> {\n /** Projection between `TPose` and bounds. Defaults to `RECT_POSE_DESCRIPTOR`\n * for `{x,y,width,height}` poses. */\n geometry?: PoseDescriptor<TPose>;\n /** Default mode when `distribute(axis)` is called without one. Default 'centers'. */\n defaultMode?: DistributeMode;\n /** Label passed to applyOps. Default 'Distribute'. */\n label?: string;\n /** How local poses fold up to world. Default IDENTITY, which takes every\n * pose the adapter answers as world; over a scene's adapter, pass\n * `scene.composition`. */\n composition?: PoseComposition<TPose>;\n}\n\n/** Return shape of `useDistribute`. */\nexport interface UseDistributeReturn {\n /** Imperative trigger. No-op when fewer than 3 items selected. */\n distribute(axis: DistributeAxis, mode?: DistributeMode): void;\n}\n\n/** Distribute the current multi-selection along `axis`. Requires ≥3 items;\n * no-op otherwise. Endpoints (min and max along the axis) stay put; the\n * remaining items are repositioned. Single batch — one undo step.\n *\n * The span being divided is a world span: bounds are measured and translated\n * in world, and each result is stored back in its own parent's frame. */\nexport function useDistribute<TPose>(\n adapter: DistributeAdapter<TPose>,\n options: UseDistributeOptions<TPose> = {},\n): UseDistributeReturn {\n const adapterRef = useLatest(adapter);\n const optsRef = useLatest(options);\n\n const distribute = useCallback((axis: DistributeAxis, mode?: DistributeMode): void => {\n const a = adapterRef.current;\n const o = optsRef.current;\n const sel = a.getSelection();\n if (sel.length < 3) return;\n const geom =\n o.geometry ??\n (RECT_POSE_DESCRIPTOR as unknown as PoseDescriptor<TPose>);\n const m = mode ?? o.defaultMode ?? 'centers';\n\n const frame = poseFrame<TPose>(\n {\n getPose: (id) => a.getPose(id as NodeId),\n getParent: (id) => a.getParent(id as NodeId) as string | null,\n },\n o.composition ?? (IDENTITY_POSE_COMPOSITION as PoseComposition<TPose>),\n );\n const items = sel.map((id) => {\n const pose = frame.world(id);\n return { id, pose, b: visualBoundsViaDescriptor(pose, geom) };\n });\n const targets = planDistribute(items.map((it) => it.b), axis, m);\n\n const ops: Op[] = [];\n for (let i = 0; i < items.length; i++) {\n const it = items[i];\n const delta = targets[i] - (axis === 'x' ? it.b.x : it.b.y);\n if (delta === 0) continue;\n const dx = axis === 'x' ? delta : 0;\n const dy = axis === 'y' ? delta : 0;\n const to = translatePoseViaDescriptor(it.pose, dx, dy, geom);\n ops.push(createTransformOp<TPose>({\n id: it.id,\n from: a.getPose(it.id),\n to: frame.local(it.id, to),\n }));\n }\n if (ops.length === 0) return;\n dispatchApplyBatch(a, ops, o.label ?? 'Distribute');\n }, [adapterRef, optsRef]);\n\n return { distribute };\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA0DA,SAAgB,cACd,SACA,UAAuC,CAAC,GACnB;CACrB,MAAM,aAAa,UAAU,OAAO;CACpC,MAAM,UAAU,UAAU,OAAO;CA2CjC,OAAO,EAAE,YAzCU,aAAa,MAAsB,SAAgC;EACpF,MAAM,IAAI,WAAW;EACrB,MAAM,IAAI,QAAQ;EAClB,MAAM,MAAM,EAAE,aAAa;EAC3B,IAAI,IAAI,SAAS,GAAG;EACpB,MAAM,OACJ,EAAE,YACD;EACH,MAAM,IAAI,QAAQ,EAAE,eAAe;EAEnC,MAAM,QAAQ,UACZ;GACE,UAAU,OAAO,EAAE,QAAQ,EAAY;GACvC,YAAY,OAAO,EAAE,UAAU,EAAY;EAC7C,GACA,EAAE,eAAgB,yBACpB;EACA,MAAM,QAAQ,IAAI,KAAK,OAAO;GAC5B,MAAM,OAAO,MAAM,MAAM,EAAE;GAC3B,OAAO;IAAE;IAAI;IAAM,GAAG,0BAA0B,MAAM,IAAI;GAAE;EAC9D,CAAC;EACD,MAAM,UAAU,eAAe,MAAM,KAAK,OAAO,GAAG,CAAC,GAAG,MAAM,CAAC;EAE/D,MAAM,MAAY,CAAC;EACnB,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;GACrC,MAAM,KAAK,MAAM;GACjB,MAAM,QAAQ,QAAQ,MAAM,SAAS,MAAM,GAAG,EAAE,IAAI,GAAG,EAAE;GACzD,IAAI,UAAU,GAAG;GACjB,MAAM,KAAK,SAAS,MAAM,QAAQ;GAClC,MAAM,KAAK,SAAS,MAAM,QAAQ;GAClC,MAAM,KAAK,2BAA2B,GAAG,MAAM,IAAI,IAAI,IAAI;GAC3D,IAAI,KAAK,kBAAyB;IAChC,IAAI,GAAG;IACP,MAAM,EAAE,QAAQ,GAAG,EAAE;IACrB,IAAI,MAAM,MAAM,GAAG,IAAI,EAAE;GAC3B,CAAC,CAAC;EACJ;EACA,IAAI,IAAI,WAAW,GAAG;EACtB,mBAAmB,GAAG,KAAK,EAAE,SAAS,YAAY;CACpD,GAAG,CAAC,YAAY,OAAO,CAEd,EAAW;AACtB"}
@@ -0,0 +1,9 @@
1
+ //#region src/interactions/actions/dropVerdict.ts
2
+ /** The reason `verdict` gives, or `null` when it lands or says no silently. */
3
+ function refusalOf(verdict) {
4
+ return typeof verdict === "object" && verdict !== null ? verdict.refuse : null;
5
+ }
6
+
7
+ //#endregion
8
+ export { refusalOf };
9
+ //# sourceMappingURL=dropVerdict.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dropVerdict.js","names":[],"sources":["../../../src/interactions/actions/dropVerdict.ts"],"sourcesContent":["/** A no with a reason: words for the user, shown beside the pointer while a drag is over what refuses it. */\nexport interface DropRefusal {\n refuse: string;\n}\n\n/**\n * The answer to \"may this land here?\": `true` lands, `false` is a silent no,\n * and a {@link DropRefusal} is a no the user is told about. A drag over a\n * refusal draws its target refused and the reason beside the pointer, and a\n * release there lands nothing.\n */\nexport type DropVerdict = boolean | DropRefusal;\n\n/** The reason `verdict` gives, or `null` when it lands or says no silently. */\nexport function refusalOf(verdict: DropVerdict | undefined): string | null {\n return typeof verdict === 'object' && verdict !== null ? verdict.refuse : null;\n}\n"],"mappings":";;AAcA,SAAgB,UAAU,SAAiD;CACzE,OAAO,OAAO,YAAY,YAAY,YAAY,OAAO,QAAQ,SAAS;AAC5E"}
@@ -1,7 +1,7 @@
1
1
  import { pathOperationTags } from "../pathGestureTags.js";
2
2
  import { editingLayer, inPlane } from "../planeInput.js";
3
- import { SNAP_ROLES, snapMods } from "../../../features/snap/roles.js";
4
- import { inputSession } from "../../../features/snap/session2d.js";
3
+ import { SNAP_ROLES, snapMods } from "../../../domains/snap/roles.js";
4
+ import { inputSession } from "../../../domains/snap/session2d.js";
5
5
  import { pathEditToolActive } from "../defaults/pathEditTools.js";
6
6
  import { HANDLE_LOCK_ROLES, lockHandle, locksInForce } from "./handleDrag.js";
7
7
  import { locksOf } from "./params.js";
@@ -1 +1 @@
1
- {"version":3,"file":"action.js","names":[],"sources":["../../../../src/interactions/actions/edit-anchors/action.ts"],"sourcesContent":["/**\n * `editAnchorsAction` — ongoing Action descriptor for editing polygon anchors.\n *\n * - **Press** selects. Bare press replaces the anchor selection; Shift\n * toggles the pressed anchor in or out of it. Pressing an anchor that\n * is already part of a multi-anchor selection leaves the selection\n * alone, so you can grab a set and move it.\n * - **Drag** then moves whatever the press left selected — one anchor,\n * or every selected anchor together. The pressed anchor snaps through\n * the `snap` dep's `point` session, to what the drag leaves still of the\n * path and to the canvas's targets; Shift holds it to the axes and\n * diagonals through its press, and Ctrl suspends the snap.\n * - **Dragging a control handle** moves just that handle, mirroring the\n * opposite one while the anchor is smooth. Alt breaks the mirror; Shift\n * holds the handle to the angle it had at the press, changing only its\n * length; Mod (Cmd or Ctrl) holds its length, changing only its angle;\n * both freeze it. The `editAnchors` dep's `handleLocks`, or else the\n * `handleLock` param ({@link EditAnchorsParams}), holds a lock with no\n * modifier. The lock in force draws its guide as a `polyline` overlay\n * of role `lock`: the line, or the circle round the anchor.\n * - **Path-edit extensions shape the drag** through their `drag`: anchors\n * that follow an anchor drag by a share of it, where a dragged handle\n * goes after the locks, the wheel mid-drag, and overlays of their own.\n * - **Every frame is finished** by the dep's `finishEdit`, so what a\n * path-edit extension keeps true holds while the drag runs.\n * - **Under a `pathSymmetry` dep**, on a path that is its own image under\n * some of its transforms, every image of what is dragged follows it.\n *\n * Selection lives on the `editAnchors` dep (flat anchor indices) rather\n * than in this handle's scratch, because the keyboard actions\n * (`nudgeAnchors`, `deleteAnchors`) and the overlay all need to read it\n * between gestures.\n *\n * Operational when the consumer wires the `editAnchors` dep, and\n * `buildAffordanceAt` with a `getAnchorState` thunk so the dispatcher\n * classifies anchor/control-handle hits as `anchor:N`, `controlIn:N`, or\n * `controlOut:N` affordances on pointerdown. On any other affordance `start`\n * returns `{}` so other bindings can handle the drag.\n */\n\nimport type { Action } from '@weasel-js/routing';\nimport type { BindingOpts, InvocationCtx, OngoingHandle, OngoingOverlay } from '@weasel-js/routing';\nimport type { EditAnchorsDep, SnapDep } from '../depSchema';\nimport type { SnapBegin, SnapMods, SnapSession } from '../snapDep';\nimport { inputSession } from 'features/snap/session2d';\nimport { SNAP_ROLES, snapMods } from 'features/snap/roles';\nimport { ActionDisabledReason, isAnchorOrControl, resolveParams, roleReader, type ModifierRoles } from '@weasel-js/routing';\nimport {\n anchorAt,\n editAnchorSetTracked,\n moveHandleSymmetric,\n moveHandleTo,\n pathAwayFromAnchors,\n translateAnchorsSymmetric,\n type AnchorSet,\n type AnchorSymmetry,\n type PenAnchor,\n type PointOrigin,\n} from '@weasel-js/geom';\nimport { pathToAnchors, type PolygonPath } from '@weasel-js/geom';\nimport { inPlane, editingLayer } from '../planeInput';\nimport { HANDLE_LOCK_ROLES, lockHandle, locksInForce, type HandlePress } from './handleDrag';\nimport { locksOf, type EditAnchorsParams } from './params';\nimport { finishPathEditFrame, pathEditPreview } from './preview';\nimport {\n claimWheel, dragWeights, endExtensionDrags, extensionOverlays, placeHandle, startExtensionDrags, translateWeighted,\n} from './extensionDrags';\nimport type { PathEditDrag } from 'features/paths/pathEditExtension';\nimport { pathEditToolActive } from '../defaults/pathEditTools';\nimport { editedPathSymmetries } from './symmetry';\nimport { pathOperationTags } from '../pathGestureTags';\n\n/**\n * Parse an anchor affordance kind — `'anchor:N'` | `'controlIn:N'` |\n * `'controlOut:N'`, where N is the flat anchor index — or `null` when the\n * affordance is not an anchor kind.\n */\nfunction parseAnchorAffordance(\n kind: string,\n): { anchorIndex: number; part: 'anchor' | 'controlIn' | 'controlOut' } | null {\n const m = kind.match(/^(anchor|controlIn|controlOut):(\\d+)$/);\n if (!m) return null;\n return {\n anchorIndex: parseInt(m[2], 10),\n part: m[1] as 'anchor' | 'controlIn' | 'controlOut',\n };\n}\n\n/**\n * Resolve what a press on `anchorIndex` should leave selected.\n *\n * Shift toggles. A bare press on an anchor that's already part of the\n * selection keeps the selection intact (so the drag moves the whole set);\n * a bare press anywhere else collapses to just that anchor.\n */\nexport function selectionAfterAnchorPress(\n current: ReadonlySet<number>,\n anchorIndex: number,\n additive: boolean,\n): Set<number> {\n if (additive) {\n const next = new Set(current);\n if (next.has(anchorIndex)) next.delete(anchorIndex);\n else next.add(anchorIndex);\n return next;\n }\n if (current.has(anchorIndex) && current.size > 1) return new Set(current);\n return new Set([anchorIndex]);\n}\n\ninterface EditAnchorsScratch {\n dep: EditAnchorsDep;\n id: string;\n anchorIndex: number;\n /** `'anchor'` drags on-curve points (and their handles) by a delta.\n * `'controlIn'` / `'controlOut'` drag one control point to an absolute\n * world coord. */\n part: 'anchor' | 'controlIn' | 'controlOut';\n /** Anchors this drag moves — the pressed one, or the whole anchor\n * selection when the press landed inside it. Flat indices. */\n dragging: readonly number[];\n /** World position of the pressed anchor at drag start; the drag delta is\n * measured from here rather than accumulated per move, so the geometry\n * is always derived from `originPose` in one step. */\n anchorOrigin: { x: number; y: number };\n /** The anchor and the dragged control handle at the press, for a handle drag. */\n handlePress: HandlePress | null;\n /** The guide the lock in force drew at the last move. */\n guide: OngoingOverlay | null;\n /** The path-edit extensions' shares of this drag. */\n drags: readonly PathEditDrag[];\n /** The last move, to redo when an extension claims the wheel. */\n lastMove: InvocationCtx | null;\n originPose: PolygonPath;\n currentPose: PolygonPath;\n /** The transforms `originPose` is its own image under, read at the press. */\n symmetries: readonly AnchorSymmetry[];\n /** Where each point of `currentPose` came from in `originPose`. */\n origins?: readonly (PointOrigin | null)[];\n /** Other nodes' world paths the finished edit moves with it. */\n others: readonly { id: string; path: PolygonPath }[];\n}\n\ntype Point = { x: number; y: number };\n\n/**\n * An anchor drag's snap: a `point` session, begun on the first move, for\n * where the pressed anchor goes. The edited path offers no targets but what\n * the drag leaves still of it, and Shift holds the anchor to the axes and\n * diagonals through its press.\n */\nfunction anchorDragSnap(\n snap: SnapDep | undefined,\n press: { id: string; origin: Point; originPose: PolygonPath; dragging: readonly number[] },\n): { at(world: Point, mods: SnapMods): Point; end(): void } {\n let session: SnapSession | null = null;\n return {\n at(world, mods) {\n if (!session) {\n const { path, anchors } = pathAwayFromAnchors(press.originPose, press.dragging);\n const req: SnapBegin = {\n gesture: 'point', press: press.origin, ids: [press.id],\n also: { paths: [path], anchors }, constrain: 'axis',\n };\n session = snap?.begin(req) ?? inputSession(req);\n }\n return session.at(world, mods).point;\n },\n end() {\n session?.end();\n session = null;\n },\n };\n}\n\nconst paramsOf = (opts: BindingOpts | undefined): EditAnchorsParams =>\n (resolveParams(opts?.params) ?? {}) as EditAnchorsParams;\n\n/** Take `next` as the frame to show, finished by the dep. A frame it cannot\n * finish is dropped, so the path stays at the last one it could. */\nfunction track(scratch: EditAnchorsScratch, next: ReturnType<typeof editAnchorSetTracked>): void {\n if (!next) return;\n const { dep, id } = scratch;\n const done = finishPathEditFrame(dep, id, next.path, next.origins);\n if (!done) return;\n scratch.currentPose = done.path as PolygonPath;\n scratch.others = done.others as EditAnchorsScratch['others'];\n scratch.origins = next.origins;\n}\n\n/** The anchor and the handle a press on `part` grabbed; null for an anchor press. */\nfunction handlePressOf(\n origin: PenAnchor,\n part: EditAnchorsScratch['part'],\n): HandlePress | null {\n const handle = part === 'controlIn' ? origin.inHandle : part === 'controlOut' ? origin.outHandle : undefined;\n return handle ? { anchor: { x: origin.x, y: origin.y }, handle: { ...handle } } : null;\n}\n\n/** Move the dragged anchors to follow `to`, and the anchors the extensions\n * weight by their shares. A weighted drag moves no symmetric images. */\nfunction moveAnchors(scratch: EditAnchorsScratch, to: Point): void {\n if (scratch.dragging.length === 0) return;\n const dx = to.x - scratch.anchorOrigin.x;\n const dy = to.y - scratch.anchorOrigin.y;\n const weights = dragWeights(scratch.drags, scratch.dragging);\n track(scratch, editAnchorSetTracked(scratch.originPose, (set) => {\n if (weights) translateWeighted(set, weights, dx, dy);\n else translateAnchorsSymmetric(set, scratch.dragging, dx, dy, scratch.symmetries);\n }));\n}\n\n/** The modifiers `editAnchors` reads while it runs: Shift on the press adds\n * to the anchor selection, Alt breaks a smooth anchor's mirror, the handle\n * locks, and Shift holds an anchor drag to the axes and diagonals. An anchor\n * drag also reads `snap.suspend` at its default key, Ctrl, which is not\n * declared here because on a handle Ctrl holds the length. */\nexport const EDIT_ANCHORS_ROLES = {\n 'selection.add': { label: 'Add to the anchor selection', keys: ['shift'] },\n 'mirror.break': { label: \"Break the handles' mirror\", keys: ['alt'] },\n ...HANDLE_LOCK_ROLES,\n 'snap.constrain': SNAP_ROLES['snap.constrain'],\n} as const satisfies ModifierRoles;\n\nconst roleHeld = roleReader(EDIT_ANCHORS_ROLES);\n\n/** Move the dragged handle toward the pointer, under the locks in force, then\n * wherever the extensions place it. */\nfunction moveHandle(scratch: EditAnchorsScratch, ctx: InvocationCtx, params: EditAnchorsParams): void {\n const side = scratch.part === 'controlIn' ? 'in' : 'out';\n // Modifiers and held locks are read live, so either can change mid-drag.\n const held = (ctx.deps.editAnchors ?? scratch.dep).handleLocks ?? locksOf(params.handleLock);\n const locks = locksInForce(held, ctx);\n const locked = scratch.handlePress ? lockHandle(scratch.handlePress, ctx.world, locks) : { to: ctx.world, guide: null };\n scratch.guide = locked.guide;\n const to = placeHandle(scratch.drags, locked.to, { locks });\n const alt = roleHeld(ctx, 'mirror.break');\n const move = scratch.symmetries.length > 0\n ? (set: AnchorSet) => moveHandleSymmetric(set, scratch.anchorIndex, side, to.x, to.y, alt, scratch.symmetries)\n : (set: AnchorSet) => moveHandleTo(set, scratch.anchorIndex, side, to.x, to.y, alt);\n track(scratch, editAnchorSetTracked(scratch.originPose, move));\n}\n\n/**\n * What a press drags, the selection updated first, before any move: a handle\n * press drags its anchor's handle; an anchor press selects as\n * {@link selectionAfterAnchorPress} says and drags what it left selected —\n * nothing when Shift took the pressed anchor out of it.\n */\nfunction draggedByPress(\n dep: EditAnchorsDep,\n press: { anchorIndex: number; part: EditAnchorsScratch['part'] },\n shift: boolean,\n): readonly number[] {\n if (press.part !== 'anchor') return [press.anchorIndex];\n const next = selectionAfterAnchorPress(dep.selectedAnchors, press.anchorIndex, shift);\n dep.setSelectedAnchors(next);\n return next.has(press.anchorIndex) ? [...next] : [];\n}\n\n/** Static descriptor for the `editAnchors` Action. */\nexport const editAnchorsAction: Action & { requires: string[] } = inPlane({\n id: 'editAnchors',\n label: 'Edit Anchors',\n tags: pathOperationTags('drag'),\n defaultBinding: [{\n spec: {\n kind: 'drag',\n // Only matches drags whose pointerdown hit an anchor or control handle.\n // Ties moveAction's `selected-body` on specificity and registers after\n // it, so move declines these presses in `start`.\n target: { kindOf: isAnchorOrControl },\n },\n opts: { id: 'editAnchors.drag' },\n }],\n eligible: { capability: 'edits-anchors' },\n modifierRoles: EDIT_ANCHORS_ROLES,\n requires: ['selection', 'editAnchors', 'pathSymmetry', 'snap'],\n invoker: {\n timing: 'ongoing',\n start(ctx: InvocationCtx, opts): OngoingHandle {\n const dep = ctx.deps.editAnchors;\n if (!dep) return {};\n\n const affordance = ctx.drag?.affordance;\n if (!affordance) {\n // No affordance hit → this drag doesn't target an anchor handle.\n // Let other bindings handle it (the handle returns {}, dispatcher moves on).\n return {};\n }\n\n const anchorInfo = parseAnchorAffordance(affordance.kind);\n if (!anchorInfo) {\n // Affordance is a non-anchor kind (e.g. resize handle, rotate handle).\n return {};\n }\n\n const { editingId } = dep;\n // World-coord polygon. For pose-as-polygon nodes this equals\n // node.pose; for data.path nodes it's pathInWorld-projected (translate +\n // rotation) so the anchor world coords match what the user clicked.\n const worldPath = dep.getEditablePath(editingId) as PolygonPath | undefined;\n if (!worldPath || worldPath.kind !== 'polygon') return {};\n\n // Decode once to validate the affordance index against the actual\n // geometry and to read the press origin. A stale affordance (the\n // path changed between pointerdown and now) fails here rather than\n // silently editing whichever anchor now holds that index.\n const decoded = pathToAnchors(worldPath) as AnchorSet;\n const origin = anchorAt(decoded, anchorInfo.anchorIndex);\n if (!origin) return {};\n if (anchorInfo.part === 'controlIn' && !origin.inHandle) return {};\n if (anchorInfo.part === 'controlOut' && !origin.outHandle) return {};\n\n let active = false;\n const preview = pathEditPreview(\n dep, editingId, () => (active ? { path: scratch.currentPose } : null), () => (active ? scratch.others : []),\n );\n if (!preview) return {};\n\n const dragging = draggedByPress(dep, anchorInfo, roleHeld(ctx, 'selection.add'));\n\n const scratch: EditAnchorsScratch = {\n dep,\n id: editingId,\n anchorIndex: anchorInfo.anchorIndex,\n part: anchorInfo.part,\n dragging,\n anchorOrigin: { x: origin.x, y: origin.y },\n handlePress: handlePressOf(origin, anchorInfo.part),\n guide: null,\n drags: startExtensionDrags(dep.extensions, {\n id: editingId,\n node: dep.getNodeShape(editingId),\n path: worldPath,\n anchors: decoded,\n part: anchorInfo.part,\n anchorIndex: anchorInfo.anchorIndex,\n dragging: anchorInfo.part === 'anchor' ? dragging : [],\n scale: ctx.deps.view?.get().scale,\n ...(ctx.deps.snap ? { snap: ctx.deps.snap } : {}),\n }),\n lastMove: null,\n originPose: worldPath,\n currentPose: worldPath,\n symmetries: editedPathSymmetries(ctx.deps.pathSymmetry, editingId, decoded),\n others: [],\n };\n const snapAnchor = anchorDragSnap(ctx.deps.snap, {\n id: editingId, origin: scratch.anchorOrigin, originPose: worldPath, dragging,\n });\n\n const move = (moveCtx: InvocationCtx): void => {\n scratch.lastMove = moveCtx;\n if (scratch.part !== 'anchor') return moveHandle(scratch, moveCtx, paramsOf(opts));\n moveAnchors(scratch, snapAnchor.at(moveCtx.world, snapMods(moveCtx)));\n };\n\n return {\n kind: 'edit-anchors',\n onMove(moveCtx: InvocationCtx): void {\n move(moveCtx);\n active = true;\n },\n onWheel(wheelCtx: InvocationCtx): boolean {\n if (!wheelCtx.wheel || !claimWheel(scratch.drags, wheelCtx.wheel)) return false;\n if (scratch.lastMove) move(scratch.lastMove);\n return true;\n },\n overlay(): readonly OngoingOverlay[] | null {\n if (!active) return null;\n const extra = scratch.drags.length > 0\n ? extensionOverlays(scratch.drags, pathToAnchors(scratch.currentPose) as AnchorSet)\n : [];\n const all = scratch.guide ? [scratch.guide, ...extra] : extra;\n return all.length > 0 ? all : null;\n },\n onEnd(_endCtx: InvocationCtx, reason: 'commit' | 'cancel'): void {\n active = false;\n try {\n if (reason === 'cancel' || scratch.originPose === scratch.currentPose) return;\n scratch.dep.applyEdit(scratch.id, scratch.currentPose, 'Edit anchors', scratch.origins);\n } finally {\n snapAnchor.end();\n endExtensionDrags(scratch.drags);\n }\n },\n previewIds: preview.previewIds,\n previewPose: preview.previewPose,\n previewData: preview.previewData,\n };\n },\n },\n // A path-edit tool has the drag.\n enabled: (deps) => (pathEditToolActive(deps) ? ActionDisabledReason.NotApplicable : true),\n}, editingLayer);\n"],"mappings":"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"}
1
+ {"version":3,"file":"action.js","names":[],"sources":["../../../../src/interactions/actions/edit-anchors/action.ts"],"sourcesContent":["/**\n * `editAnchorsAction` — ongoing Action descriptor for editing polygon anchors.\n *\n * - **Press** selects. Bare press replaces the anchor selection; Shift\n * toggles the pressed anchor in or out of it. Pressing an anchor that\n * is already part of a multi-anchor selection leaves the selection\n * alone, so you can grab a set and move it.\n * - **Drag** then moves whatever the press left selected — one anchor,\n * or every selected anchor together. The pressed anchor snaps through\n * the `snap` dep's `point` session, to what the drag leaves still of the\n * path and to the canvas's targets; Shift holds it to the axes and\n * diagonals through its press, and Ctrl suspends the snap.\n * - **Dragging a control handle** moves just that handle, mirroring the\n * opposite one while the anchor is smooth. Alt breaks the mirror; Shift\n * holds the handle to the angle it had at the press, changing only its\n * length; Mod (Cmd or Ctrl) holds its length, changing only its angle;\n * both freeze it. The `editAnchors` dep's `handleLocks`, or else the\n * `handleLock` param ({@link EditAnchorsParams}), holds a lock with no\n * modifier. The lock in force draws its guide as a `polyline` overlay\n * of role `lock`: the line, or the circle round the anchor.\n * - **Path-edit extensions shape the drag** through their `drag`: anchors\n * that follow an anchor drag by a share of it, where a dragged handle\n * goes after the locks, the wheel mid-drag, and overlays of their own.\n * - **Every frame is finished** by the dep's `finishEdit`, so what a\n * path-edit extension keeps true holds while the drag runs.\n * - **Under a `pathSymmetry` dep**, on a path that is its own image under\n * some of its transforms, every image of what is dragged follows it.\n *\n * Selection lives on the `editAnchors` dep (flat anchor indices) rather\n * than in this handle's scratch, because the keyboard actions\n * (`nudgeAnchors`, `deleteAnchors`) and the overlay all need to read it\n * between gestures.\n *\n * Operational when the consumer wires the `editAnchors` dep, and\n * `buildAffordanceAt` with a `getAnchorState` thunk so the dispatcher\n * classifies anchor/control-handle hits as `anchor:N`, `controlIn:N`, or\n * `controlOut:N` affordances on pointerdown. On any other affordance `start`\n * returns `{}` so other bindings can handle the drag.\n */\n\nimport type { Action } from '@weasel-js/routing';\nimport type { BindingOpts, InvocationCtx, OngoingHandle, OngoingOverlay, WorldPoint } from '@weasel-js/routing';\nimport type { EditAnchorsDep, SnapDep } from '../depSchema';\nimport type { SnapBegin, SnapMods, SnapSession } from '../snapDep';\nimport { inputSession } from 'domains/snap/session2d';\nimport { SNAP_ROLES, snapMods } from 'domains/snap/roles';\nimport { ActionDisabledReason, isAnchorOrControl, resolveParams, roleReader, type ModifierRoles } from '@weasel-js/routing';\nimport {\n anchorAt,\n editAnchorSetTracked,\n moveHandleSymmetric,\n moveHandleTo,\n pathAwayFromAnchors,\n translateAnchorsSymmetric,\n type AnchorSet,\n type AnchorSymmetry,\n type PenAnchor,\n type PointOrigin,\n} from '@weasel-js/geom';\nimport { pathToAnchors, type PolygonPath } from '@weasel-js/geom';\nimport { inPlane, editingLayer } from '../planeInput';\nimport { HANDLE_LOCK_ROLES, lockHandle, locksInForce, type HandlePress } from './handleDrag';\nimport { locksOf, type EditAnchorsParams } from './params';\nimport { finishPathEditFrame, pathEditPreview } from './preview';\nimport {\n claimWheel, dragWeights, endExtensionDrags, extensionOverlays, placeHandle, startExtensionDrags, translateWeighted,\n} from './extensionDrags';\nimport type { PathEditDrag } from 'domains/paths/pathEditExtension';\nimport { pathEditToolActive } from '../defaults/pathEditTools';\nimport { editedPathSymmetries } from './symmetry';\nimport { pathOperationTags } from '../pathGestureTags';\n\n/**\n * Parse an anchor affordance kind — `'anchor:N'` | `'controlIn:N'` |\n * `'controlOut:N'`, where N is the flat anchor index — or `null` when the\n * affordance is not an anchor kind.\n */\nfunction parseAnchorAffordance(\n kind: string,\n): { anchorIndex: number; part: 'anchor' | 'controlIn' | 'controlOut' } | null {\n const m = kind.match(/^(anchor|controlIn|controlOut):(\\d+)$/);\n if (!m) return null;\n return {\n anchorIndex: parseInt(m[2], 10),\n part: m[1] as 'anchor' | 'controlIn' | 'controlOut',\n };\n}\n\n/**\n * Resolve what a press on `anchorIndex` should leave selected.\n *\n * Shift toggles. A bare press on an anchor that's already part of the\n * selection keeps the selection intact (so the drag moves the whole set);\n * a bare press anywhere else collapses to just that anchor.\n */\nexport function selectionAfterAnchorPress(\n current: ReadonlySet<number>,\n anchorIndex: number,\n additive: boolean,\n): Set<number> {\n if (additive) {\n const next = new Set(current);\n if (next.has(anchorIndex)) next.delete(anchorIndex);\n else next.add(anchorIndex);\n return next;\n }\n if (current.has(anchorIndex) && current.size > 1) return new Set(current);\n return new Set([anchorIndex]);\n}\n\ninterface EditAnchorsScratch {\n dep: EditAnchorsDep;\n id: string;\n anchorIndex: number;\n /** `'anchor'` drags on-curve points (and their handles) by a delta.\n * `'controlIn'` / `'controlOut'` drag one control point to an absolute\n * world coord. */\n part: 'anchor' | 'controlIn' | 'controlOut';\n /** Anchors this drag moves — the pressed one, or the whole anchor\n * selection when the press landed inside it. Flat indices. */\n dragging: readonly number[];\n /** World position of the pressed anchor at drag start; the drag delta is\n * measured from here rather than accumulated per move, so the geometry\n * is always derived from `originPose` in one step. */\n anchorOrigin: { x: number; y: number };\n /** The anchor and the dragged control handle at the press, for a handle drag. */\n handlePress: HandlePress | null;\n /** The guide the lock in force drew at the last move. */\n guide: OngoingOverlay | null;\n /** The path-edit extensions' shares of this drag. */\n drags: readonly PathEditDrag[];\n /** The last move, to redo when an extension claims the wheel. */\n lastMove: InvocationCtx | null;\n originPose: PolygonPath;\n currentPose: PolygonPath;\n /** The transforms `originPose` is its own image under, read at the press. */\n symmetries: readonly AnchorSymmetry[];\n /** Where each point of `currentPose` came from in `originPose`. */\n origins?: readonly (PointOrigin | null)[];\n /** Other nodes' world paths the finished edit moves with it. */\n others: readonly { id: string; path: PolygonPath }[];\n}\n\n/**\n * An anchor drag's snap: a `point` session, begun on the first move, for\n * where the pressed anchor goes. The edited path offers no targets but what\n * the drag leaves still of it, and Shift holds the anchor to the axes and\n * diagonals through its press.\n */\nfunction anchorDragSnap(\n snap: SnapDep | undefined,\n press: { id: string; origin: WorldPoint; originPose: PolygonPath; dragging: readonly number[] },\n): { at(world: WorldPoint, mods: SnapMods): WorldPoint; end(): void } {\n let session: SnapSession | null = null;\n return {\n at(world, mods) {\n if (!session) {\n const { path, anchors } = pathAwayFromAnchors(press.originPose, press.dragging);\n const req: SnapBegin = {\n gesture: 'point', press: press.origin, ids: [press.id],\n also: { paths: [path], anchors }, constrain: 'axis',\n };\n session = snap?.begin(req) ?? inputSession(req);\n }\n return session.at(world, mods).point;\n },\n end() {\n session?.end();\n session = null;\n },\n };\n}\n\nconst paramsOf = (opts: BindingOpts | undefined): EditAnchorsParams =>\n (resolveParams(opts?.params) ?? {}) as EditAnchorsParams;\n\n/** Take `next` as the frame to show, finished by the dep. A frame it cannot\n * finish is dropped, so the path stays at the last one it could. */\nfunction track(scratch: EditAnchorsScratch, next: ReturnType<typeof editAnchorSetTracked>): void {\n if (!next) return;\n const { dep, id } = scratch;\n const done = finishPathEditFrame(dep, id, next.path, next.origins);\n if (!done) return;\n scratch.currentPose = done.path as PolygonPath;\n scratch.others = done.others as EditAnchorsScratch['others'];\n scratch.origins = next.origins;\n}\n\n/** The anchor and the handle a press on `part` grabbed; null for an anchor press. */\nfunction handlePressOf(\n origin: PenAnchor,\n part: EditAnchorsScratch['part'],\n): HandlePress | null {\n const handle = part === 'controlIn' ? origin.inHandle : part === 'controlOut' ? origin.outHandle : undefined;\n return handle ? { anchor: { x: origin.x, y: origin.y }, handle: { ...handle } } : null;\n}\n\n/** Move the dragged anchors to follow `to`, and the anchors the extensions\n * weight by their shares. A weighted drag moves no symmetric images. */\nfunction moveAnchors(scratch: EditAnchorsScratch, to: WorldPoint): void {\n if (scratch.dragging.length === 0) return;\n const dx = to.x - scratch.anchorOrigin.x;\n const dy = to.y - scratch.anchorOrigin.y;\n const weights = dragWeights(scratch.drags, scratch.dragging);\n track(scratch, editAnchorSetTracked(scratch.originPose, (set) => {\n if (weights) translateWeighted(set, weights, dx, dy);\n else translateAnchorsSymmetric(set, scratch.dragging, dx, dy, scratch.symmetries);\n }));\n}\n\n/** The modifiers `editAnchors` reads while it runs: Shift on the press adds\n * to the anchor selection, Alt breaks a smooth anchor's mirror, the handle\n * locks, and Shift holds an anchor drag to the axes and diagonals. An anchor\n * drag also reads `snap.suspend` at its default key, Ctrl, which is not\n * declared here because on a handle Ctrl holds the length. */\nexport const EDIT_ANCHORS_ROLES = {\n 'selection.add': { label: 'Add to the anchor selection', keys: ['shift'] },\n 'mirror.break': { label: \"Break the handles' mirror\", keys: ['alt'] },\n ...HANDLE_LOCK_ROLES,\n 'snap.constrain': SNAP_ROLES['snap.constrain'],\n} as const satisfies ModifierRoles;\n\nconst roleHeld = roleReader(EDIT_ANCHORS_ROLES);\n\n/** Move the dragged handle toward the pointer, under the locks in force, then\n * wherever the extensions place it. */\nfunction moveHandle(scratch: EditAnchorsScratch, ctx: InvocationCtx, params: EditAnchorsParams): void {\n const side = scratch.part === 'controlIn' ? 'in' : 'out';\n // Modifiers and held locks are read live, so either can change mid-drag.\n const held = (ctx.deps.editAnchors ?? scratch.dep).handleLocks ?? locksOf(params.handleLock);\n const locks = locksInForce(held, ctx);\n const locked = scratch.handlePress ? lockHandle(scratch.handlePress, ctx.world, locks) : { to: ctx.world, guide: null };\n scratch.guide = locked.guide;\n const to = placeHandle(scratch.drags, locked.to, { locks });\n const alt = roleHeld(ctx, 'mirror.break');\n const move = scratch.symmetries.length > 0\n ? (set: AnchorSet) => moveHandleSymmetric(set, scratch.anchorIndex, side, to.x, to.y, alt, scratch.symmetries)\n : (set: AnchorSet) => moveHandleTo(set, scratch.anchorIndex, side, to.x, to.y, alt);\n track(scratch, editAnchorSetTracked(scratch.originPose, move));\n}\n\n/**\n * What a press drags, the selection updated first, before any move: a handle\n * press drags its anchor's handle; an anchor press selects as\n * {@link selectionAfterAnchorPress} says and drags what it left selected —\n * nothing when Shift took the pressed anchor out of it.\n */\nfunction draggedByPress(\n dep: EditAnchorsDep,\n press: { anchorIndex: number; part: EditAnchorsScratch['part'] },\n shift: boolean,\n): readonly number[] {\n if (press.part !== 'anchor') return [press.anchorIndex];\n const next = selectionAfterAnchorPress(dep.selectedAnchors, press.anchorIndex, shift);\n dep.setSelectedAnchors(next);\n return next.has(press.anchorIndex) ? [...next] : [];\n}\n\n/** Static descriptor for the `editAnchors` Action. */\nexport const editAnchorsAction: Action & { requires: string[] } = inPlane({\n id: 'editAnchors',\n label: 'Edit Anchors',\n tags: pathOperationTags('drag'),\n defaultBinding: [{\n spec: {\n kind: 'drag',\n // Only matches drags whose pointerdown hit an anchor or control handle.\n // Ties moveAction's `selected-body` on specificity and registers after\n // it, so move declines these presses in `start`.\n target: { kindOf: isAnchorOrControl },\n },\n opts: { id: 'editAnchors.drag' },\n }],\n eligible: { capability: 'edits-anchors' },\n modifierRoles: EDIT_ANCHORS_ROLES,\n requires: ['selection', 'editAnchors', 'pathSymmetry', 'snap'],\n invoker: {\n timing: 'ongoing',\n start(ctx: InvocationCtx, opts): OngoingHandle {\n const dep = ctx.deps.editAnchors;\n if (!dep) return {};\n\n const affordance = ctx.drag?.affordance;\n if (!affordance) {\n // No affordance hit → this drag doesn't target an anchor handle.\n // Let other bindings handle it (the handle returns {}, dispatcher moves on).\n return {};\n }\n\n const anchorInfo = parseAnchorAffordance(affordance.kind);\n if (!anchorInfo) {\n // Affordance is a non-anchor kind (e.g. resize handle, rotate handle).\n return {};\n }\n\n const { editingId } = dep;\n // World-coord polygon. For pose-as-polygon nodes this equals\n // node.pose; for data.path nodes it's pathInWorld-projected (translate +\n // rotation) so the anchor world coords match what the user clicked.\n const worldPath = dep.getEditablePath(editingId) as PolygonPath | undefined;\n if (!worldPath || worldPath.kind !== 'polygon') return {};\n\n // Decode once to validate the affordance index against the actual\n // geometry and to read the press origin. A stale affordance (the\n // path changed between pointerdown and now) fails here rather than\n // silently editing whichever anchor now holds that index.\n const decoded = pathToAnchors(worldPath) as AnchorSet;\n const origin = anchorAt(decoded, anchorInfo.anchorIndex);\n if (!origin) return {};\n if (anchorInfo.part === 'controlIn' && !origin.inHandle) return {};\n if (anchorInfo.part === 'controlOut' && !origin.outHandle) return {};\n\n let active = false;\n const preview = pathEditPreview(\n dep, editingId, () => (active ? { path: scratch.currentPose } : null), () => (active ? scratch.others : []),\n );\n if (!preview) return {};\n\n const dragging = draggedByPress(dep, anchorInfo, roleHeld(ctx, 'selection.add'));\n\n const scratch: EditAnchorsScratch = {\n dep,\n id: editingId,\n anchorIndex: anchorInfo.anchorIndex,\n part: anchorInfo.part,\n dragging,\n anchorOrigin: { x: origin.x, y: origin.y },\n handlePress: handlePressOf(origin, anchorInfo.part),\n guide: null,\n drags: startExtensionDrags(dep.extensions, {\n id: editingId,\n node: dep.getNodeShape(editingId),\n path: worldPath,\n anchors: decoded,\n part: anchorInfo.part,\n anchorIndex: anchorInfo.anchorIndex,\n dragging: anchorInfo.part === 'anchor' ? dragging : [],\n scale: ctx.deps.view?.get().scale,\n ...(ctx.deps.snap ? { snap: ctx.deps.snap } : {}),\n }),\n lastMove: null,\n originPose: worldPath,\n currentPose: worldPath,\n symmetries: editedPathSymmetries(ctx.deps.pathSymmetry, editingId, decoded),\n others: [],\n };\n const snapAnchor = anchorDragSnap(ctx.deps.snap, {\n id: editingId, origin: scratch.anchorOrigin, originPose: worldPath, dragging,\n });\n\n const move = (moveCtx: InvocationCtx): void => {\n scratch.lastMove = moveCtx;\n if (scratch.part !== 'anchor') return moveHandle(scratch, moveCtx, paramsOf(opts));\n moveAnchors(scratch, snapAnchor.at(moveCtx.world, snapMods(moveCtx)));\n };\n\n return {\n kind: 'edit-anchors',\n onMove(moveCtx: InvocationCtx): void {\n move(moveCtx);\n active = true;\n },\n onWheel(wheelCtx: InvocationCtx): boolean {\n if (!wheelCtx.wheel || !claimWheel(scratch.drags, wheelCtx.wheel)) return false;\n if (scratch.lastMove) move(scratch.lastMove);\n return true;\n },\n overlay(): readonly OngoingOverlay[] | null {\n if (!active) return null;\n const extra = scratch.drags.length > 0\n ? extensionOverlays(scratch.drags, pathToAnchors(scratch.currentPose) as AnchorSet)\n : [];\n const all = scratch.guide ? [scratch.guide, ...extra] : extra;\n return all.length > 0 ? all : null;\n },\n onEnd(_endCtx: InvocationCtx, reason: 'commit' | 'cancel'): void {\n active = false;\n try {\n if (reason === 'cancel' || scratch.originPose === scratch.currentPose) return;\n scratch.dep.applyEdit(scratch.id, scratch.currentPose, 'Edit anchors', scratch.origins);\n } finally {\n snapAnchor.end();\n endExtensionDrags(scratch.drags);\n }\n },\n previewIds: preview.previewIds,\n previewPose: preview.previewPose,\n previewData: preview.previewData,\n };\n },\n },\n // A path-edit tool has the drag.\n enabled: (deps) => (pathEditToolActive(deps) ? ActionDisabledReason.NotApplicable : true),\n}, editingLayer);\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"extensionDrags.js","names":[],"sources":["../../../../src/interactions/actions/edit-anchors/extensionDrags.ts"],"sourcesContent":["/**\n * The path-edit extensions' share of an anchor or handle drag: the anchors\n * that follow it, where a dragged handle goes, the wheel, and what they draw.\n */\nimport type { OngoingOverlay } from '@weasel-js/routing';\nimport { translateAnchorBy, type AnchorSet } from '@weasel-js/geom';\nimport type {\n PathEditDrag,\n PathEditDragStart,\n PathEditExtension,\n PathEditHandleMove,\n} from 'features/paths/pathEditExtension';\n\ntype Point = { x: number; y: number };\ntype Wheel = { deltaX: number; deltaY: number; deltaZ: number };\n\n/** Each extension's share of the drag `start` describes, in extension order. */\nexport function startExtensionDrags(\n extensions: readonly PathEditExtension[] | undefined,\n start: PathEditDragStart,\n): PathEditDrag[] {\n const out: PathEditDrag[] = [];\n for (const ext of extensions ?? []) {\n const drag = ext.drag?.(start);\n if (drag) out.push(drag);\n }\n return out;\n}\n\n/** The anchors an anchor drag of `dragging` moves and each one's share, the\n * largest any extension gives; null when none gives weights. */\nexport function dragWeights(drags: readonly PathEditDrag[], dragging: readonly number[]): Map<number, number> | null {\n let out: Map<number, number> | null = null;\n for (const drag of drags) {\n const weights = drag.weights?.();\n if (!weights) continue;\n out ??= new Map(dragging.map((i) => [i, 1]));\n for (const [flat, w] of weights) if (w > (out.get(flat) ?? 0)) out.set(flat, w);\n }\n return out;\n}\n\n/** Move every anchor of `set` that `weights` names by its share of `(dx, dy)`. */\nexport function translateWeighted(set: AnchorSet, weights: ReadonlyMap<number, number>, dx: number, dy: number): void {\n for (const [flat, w] of weights) translateAnchorBy(set, flat, dx * w, dy * w);\n}\n\n/** Where a dragged handle goes once every extension has placed it in turn. */\nexport function placeHandle(drags: readonly PathEditDrag[], to: Point, move: PathEditHandleMove): Point {\n let at = to;\n for (const drag of drags) if (drag.handle) at = drag.handle(at, move);\n return at;\n}\n\n/** Offer `wheel` to each extension until one claims it. */\nexport function claimWheel(drags: readonly PathEditDrag[], wheel: Wheel): boolean {\n return drags.some((drag) => drag.wheel?.(wheel) === true);\n}\n\n/** Every extension's overlays for `current`. */\nexport function extensionOverlays(drags: readonly PathEditDrag[], current: AnchorSet): OngoingOverlay[] {\n return drags.flatMap((drag) => drag.overlays?.(current) ?? []);\n}\n\n/** Tell every extension the drag is over. */\nexport function endExtensionDrags(drags: readonly PathEditDrag[]): void {\n for (const drag of drags) drag.end?.();\n}\n"],"mappings":";;;;AAiBA,SAAgB,oBACd,YACA,OACgB;CAChB,MAAM,MAAsB,CAAC;CAC7B,KAAK,MAAM,OAAO,cAAc,CAAC,GAAG;EAClC,MAAM,OAAO,IAAI,OAAO,KAAK;EAC7B,IAAI,MAAM,IAAI,KAAK,IAAI;CACzB;CACA,OAAO;AACT;;;AAIA,SAAgB,YAAY,OAAgC,UAAyD;CACnH,IAAI,MAAkC;CACtC,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,UAAU,KAAK,UAAU;EAC/B,IAAI,CAAC,SAAS;EACd,QAAQ,IAAI,IAAI,SAAS,KAAK,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;EAC3C,KAAK,MAAM,CAAC,MAAM,MAAM,SAAS,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,MAAM,CAAC;CAChF;CACA,OAAO;AACT;;AAGA,SAAgB,kBAAkB,KAAgB,SAAsC,IAAY,IAAkB;CACpH,KAAK,MAAM,CAAC,MAAM,MAAM,SAAS,kBAAkB,KAAK,MAAM,KAAK,GAAG,KAAK,CAAC;AAC9E;;AAGA,SAAgB,YAAY,OAAgC,IAAW,MAAiC;CACtG,IAAI,KAAK;CACT,KAAK,MAAM,QAAQ,OAAO,IAAI,KAAK,QAAQ,KAAK,KAAK,OAAO,IAAI,IAAI;CACpE,OAAO;AACT;;AAGA,SAAgB,WAAW,OAAgC,OAAuB;CAChF,OAAO,MAAM,MAAM,SAAS,KAAK,QAAQ,KAAK,MAAM,IAAI;AAC1D;;AAGA,SAAgB,kBAAkB,OAAgC,SAAsC;CACtG,OAAO,MAAM,SAAS,SAAS,KAAK,WAAW,OAAO,KAAK,CAAC,CAAC;AAC/D;;AAGA,SAAgB,kBAAkB,OAAsC;CACtE,KAAK,MAAM,QAAQ,OAAO,KAAK,MAAM;AACvC"}
1
+ {"version":3,"file":"extensionDrags.js","names":[],"sources":["../../../../src/interactions/actions/edit-anchors/extensionDrags.ts"],"sourcesContent":["/**\n * The path-edit extensions' share of an anchor or handle drag: the anchors\n * that follow it, where a dragged handle goes, the wheel, and what they draw.\n */\nimport type { OngoingOverlay, WorldPoint } from '@weasel-js/routing';\nimport { translateAnchorBy, type AnchorSet } from '@weasel-js/geom';\nimport type {\n PathEditDrag,\n PathEditDragStart,\n PathEditExtension,\n PathEditHandleMove,\n} from 'domains/paths/pathEditExtension';\n\ntype Wheel = { deltaX: number; deltaY: number; deltaZ: number };\n\n/** Each extension's share of the drag `start` describes, in extension order. */\nexport function startExtensionDrags(\n extensions: readonly PathEditExtension[] | undefined,\n start: PathEditDragStart,\n): PathEditDrag[] {\n const out: PathEditDrag[] = [];\n for (const ext of extensions ?? []) {\n const drag = ext.drag?.(start);\n if (drag) out.push(drag);\n }\n return out;\n}\n\n/** The anchors an anchor drag of `dragging` moves and each one's share, the\n * largest any extension gives; null when none gives weights. */\nexport function dragWeights(drags: readonly PathEditDrag[], dragging: readonly number[]): Map<number, number> | null {\n let out: Map<number, number> | null = null;\n for (const drag of drags) {\n const weights = drag.weights?.();\n if (!weights) continue;\n out ??= new Map(dragging.map((i) => [i, 1]));\n for (const [flat, w] of weights) if (w > (out.get(flat) ?? 0)) out.set(flat, w);\n }\n return out;\n}\n\n/** Move every anchor of `set` that `weights` names by its share of `(dx, dy)`. */\nexport function translateWeighted(set: AnchorSet, weights: ReadonlyMap<number, number>, dx: number, dy: number): void {\n for (const [flat, w] of weights) translateAnchorBy(set, flat, dx * w, dy * w);\n}\n\n/** Where a dragged handle goes once every extension has placed it in turn. */\nexport function placeHandle(drags: readonly PathEditDrag[], to: WorldPoint, move: PathEditHandleMove): WorldPoint {\n let at = to;\n for (const drag of drags) if (drag.handle) at = drag.handle(at, move);\n return at;\n}\n\n/** Offer `wheel` to each extension until one claims it. */\nexport function claimWheel(drags: readonly PathEditDrag[], wheel: Wheel): boolean {\n return drags.some((drag) => drag.wheel?.(wheel) === true);\n}\n\n/** Every extension's overlays for `current`. */\nexport function extensionOverlays(drags: readonly PathEditDrag[], current: AnchorSet): OngoingOverlay[] {\n return drags.flatMap((drag) => drag.overlays?.(current) ?? []);\n}\n\n/** Tell every extension the drag is over. */\nexport function endExtensionDrags(drags: readonly PathEditDrag[]): void {\n for (const drag of drags) drag.end?.();\n}\n"],"mappings":";;;;AAgBA,SAAgB,oBACd,YACA,OACgB;CAChB,MAAM,MAAsB,CAAC;CAC7B,KAAK,MAAM,OAAO,cAAc,CAAC,GAAG;EAClC,MAAM,OAAO,IAAI,OAAO,KAAK;EAC7B,IAAI,MAAM,IAAI,KAAK,IAAI;CACzB;CACA,OAAO;AACT;;;AAIA,SAAgB,YAAY,OAAgC,UAAyD;CACnH,IAAI,MAAkC;CACtC,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,UAAU,KAAK,UAAU;EAC/B,IAAI,CAAC,SAAS;EACd,QAAQ,IAAI,IAAI,SAAS,KAAK,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;EAC3C,KAAK,MAAM,CAAC,MAAM,MAAM,SAAS,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,MAAM,CAAC;CAChF;CACA,OAAO;AACT;;AAGA,SAAgB,kBAAkB,KAAgB,SAAsC,IAAY,IAAkB;CACpH,KAAK,MAAM,CAAC,MAAM,MAAM,SAAS,kBAAkB,KAAK,MAAM,KAAK,GAAG,KAAK,CAAC;AAC9E;;AAGA,SAAgB,YAAY,OAAgC,IAAgB,MAAsC;CAChH,IAAI,KAAK;CACT,KAAK,MAAM,QAAQ,OAAO,IAAI,KAAK,QAAQ,KAAK,KAAK,OAAO,IAAI,IAAI;CACpE,OAAO;AACT;;AAGA,SAAgB,WAAW,OAAgC,OAAuB;CAChF,OAAO,MAAM,MAAM,SAAS,KAAK,QAAQ,KAAK,MAAM,IAAI;AAC1D;;AAGA,SAAgB,kBAAkB,OAAgC,SAAsC;CACtG,OAAO,MAAM,SAAS,SAAS,KAAK,WAAW,OAAO,KAAK,CAAC,CAAC;AAC/D;;AAGA,SAAgB,kBAAkB,OAAsC;CACtE,KAAK,MAAM,QAAQ,OAAO,KAAK,MAAM;AACvC"}
@@ -1,4 +1,4 @@
1
- import { PATH_C, PATH_L, PATH_M, PATH_Q, PATH_Z, forEachSegment } from "@weasel-js/geom";
1
+ import { PATH_C, PATH_L, PATH_M, PATH_Q, PATH_Z, forEachSegment, originOf, worldCoords } from "@weasel-js/geom";
2
2
 
3
3
  //#region src/interactions/actions/edit-anchors/geometry.ts
4
4
  /** Walk a PolygonPath's command stream and yield one `PathAnchor` per
@@ -9,7 +9,8 @@ import { PATH_C, PATH_L, PATH_M, PATH_Q, PATH_Z, forEachSegment } from "@weasel-
9
9
  * anchor: its control becomes the first anchor's `controlIn`, the way
10
10
  * `pathToAnchors` reads it, so the two walkers count alike. */
11
11
  function enumerateAnchors(path) {
12
- const { commands, coords } = path;
12
+ const { commands } = path;
13
+ const coords = worldCoords(path);
13
14
  const anchors = [];
14
15
  let lastAnchor = null;
15
16
  let firstInSubpath = null;
@@ -74,12 +75,13 @@ function foldClosingCurve(anchors, first, last) {
74
75
  first.closeCoordIndex = last.coordIndex;
75
76
  first.controlIn = last.controlIn;
76
77
  }
77
- /** Mutate `coords` in-place: write `(x, y)` at `coordIndex`, return a new
78
- * PolygonPath sharing the same commands but with a fresh coords buffer. */
78
+ /** A new PolygonPath sharing `path`'s commands, with the point at `coordIndex`
79
+ * moved to `(x, y)` in the path's own space, in a fresh coords buffer. */
79
80
  function withCoord(path, coordIndex, x, y) {
80
81
  const next = new Float32Array(path.coords);
81
- next[coordIndex] = x;
82
- next[coordIndex + 1] = y;
82
+ const o = originOf(path);
83
+ next[coordIndex] = x - o.x;
84
+ next[coordIndex + 1] = y - o.y;
83
85
  return {
84
86
  ...path,
85
87
  coords: next
@@ -1 +1 @@
1
- {"version":3,"file":"geometry.js","names":[],"sources":["../../../../src/interactions/actions/edit-anchors/geometry.ts"],"sourcesContent":["import { forEachSegment, PATH_C, PATH_L, PATH_M, PATH_Q, PATH_Z, type PolygonPath } from '@weasel-js/geom';\n\n/** A single anchor on a `PolygonPath`, with its incoming/outgoing bezier\n * control points (when present). `coordIndex` points at the anchor's own\n * on-curve x in the `coords` array. `inCoordIndex` / `outCoordIndex` point\n * at the corresponding control x. */\nexport interface PathAnchor {\n /** Sequential anchor index in walk order (0-based). */\n anchorIndex: number;\n /** On-curve point. */\n x: number;\n y: number;\n /** Incoming control (from the previous segment, when it was C/Q). */\n controlIn?: { x: number; y: number; coordIndex: number };\n /** Outgoing control (from the next segment, when it is C/Q). */\n controlOut?: { x: number; y: number; coordIndex: number };\n /** Index of the anchor's on-curve x in `coords`. */\n coordIndex: number;\n /** Set on the first anchor of a closed subpath whose last curve ends\n * exactly on it: the index of that curve's end x, the same point again. */\n closeCoordIndex?: number;\n}\n\n/** Walk a PolygonPath's command stream and yield one `PathAnchor` per\n * on-curve point. Adjacent C/Q control points are attached as\n * `controlIn`/`controlOut`. Subpath boundaries (M after a previous M or Z)\n * start fresh — the new M's anchor has no `controlIn` from a Z. A closing\n * curve that ends exactly on the subpath's first point is not a second\n * anchor: its control becomes the first anchor's `controlIn`, the way\n * `pathToAnchors` reads it, so the two walkers count alike. */\nexport function enumerateAnchors(path: PolygonPath): PathAnchor[] {\n const { commands, coords } = path;\n const anchors: PathAnchor[] = [];\n let lastAnchor: PathAnchor | null = null;\n let firstInSubpath: PathAnchor | null = null;\n\n const push = (coordIndex: number, controlIn?: PathAnchor['controlIn']): void => {\n const a: PathAnchor = {\n anchorIndex: anchors.length,\n x: coords[coordIndex],\n y: coords[coordIndex + 1],\n coordIndex,\n ...(controlIn ? { controlIn } : {}),\n };\n anchors.push(a);\n lastAnchor = a;\n };\n\n forEachSegment(commands, coords, (cmd, ci) => {\n switch (cmd) {\n case PATH_M:\n push(ci);\n firstInSubpath = lastAnchor;\n break;\n case PATH_L:\n push(ci);\n break;\n case PATH_C:\n if (lastAnchor) {\n lastAnchor.controlOut = { x: coords[ci], y: coords[ci + 1], coordIndex: ci };\n }\n push(ci + 4, { x: coords[ci + 2], y: coords[ci + 3], coordIndex: ci + 2 });\n break;\n case PATH_Q:\n if (lastAnchor) {\n lastAnchor.controlOut = { x: coords[ci], y: coords[ci + 1], coordIndex: ci };\n }\n push(ci + 2, { x: coords[ci], y: coords[ci + 1], coordIndex: ci });\n break;\n case PATH_Z:\n foldClosingCurve(anchors, firstInSubpath, lastAnchor);\n lastAnchor = null;\n firstInSubpath = null;\n break;\n default:\n throw new Error(`enumerateAnchors: unknown command ${cmd}`);\n }\n });\n return anchors;\n}\n\nfunction foldClosingCurve(anchors: PathAnchor[], first: PathAnchor | null, last: PathAnchor | null): void {\n if (!first || !last || first === last) return;\n if (!last.controlIn || last.x !== first.x || last.y !== first.y) return;\n anchors.pop();\n first.closeCoordIndex = last.coordIndex;\n first.controlIn = last.controlIn;\n}\n\n/** Mutate `coords` in-place: write `(x, y)` at `coordIndex`, return a new\n * PolygonPath sharing the same commands but with a fresh coords buffer. */\nexport function withCoord(path: PolygonPath, coordIndex: number, x: number, y: number): PolygonPath {\n const next = new Float32Array(path.coords);\n next[coordIndex] = x;\n next[coordIndex + 1] = y;\n return { ...path, coords: next };\n}\n\n/** Translate anchor `anchorIndex` AND its attached control handles\n * (`controlIn` / `controlOut`) by `(dx, dy)`. Used by `editAnchorsAction`\n * when the dragged affordance is the anchor itself — moving the anchor\n * without its handles would shear the surrounding bezier segments. */\nexport function translateAnchor(\n path: PolygonPath,\n anchorIndex: number,\n dx: number,\n dy: number,\n): PolygonPath {\n const anchors = enumerateAnchors(path);\n const a = anchors[anchorIndex];\n if (!a) return path;\n const next = new Float32Array(path.coords);\n // Anchor itself.\n next[a.coordIndex] += dx;\n next[a.coordIndex + 1] += dy;\n if (a.closeCoordIndex !== undefined) {\n next[a.closeCoordIndex] += dx;\n next[a.closeCoordIndex + 1] += dy;\n }\n // In-handle of THIS anchor — the trailing pair of the incoming C/Q.\n if (a.controlIn) {\n next[a.controlIn.coordIndex] += dx;\n next[a.controlIn.coordIndex + 1] += dy;\n }\n // Out-handle: the leading pair of the OUTGOING C/Q. Owned by this\n // anchor's segment.\n if (a.controlOut) {\n next[a.controlOut.coordIndex] += dx;\n next[a.controlOut.coordIndex + 1] += dy;\n }\n return { ...path, coords: next };\n}\n"],"mappings":";;;;;;;;;;AA8BA,SAAgB,iBAAiB,MAAiC;CAChE,MAAM,EAAE,UAAU,WAAW;CAC7B,MAAM,UAAwB,CAAC;CAC/B,IAAI,aAAgC;CACpC,IAAI,iBAAoC;CAExC,MAAM,QAAQ,YAAoB,cAA8C;EAC9E,MAAM,IAAgB;GACpB,aAAa,QAAQ;GACrB,GAAG,OAAO;GACV,GAAG,OAAO,aAAa;GACvB;GACA,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;EACnC;EACA,QAAQ,KAAK,CAAC;EACd,aAAa;CACf;CAEA,eAAe,UAAU,SAAS,KAAK,OAAO;EAC5C,QAAQ,KAAR;GACE,KAAK;IACH,KAAK,EAAE;IACP,iBAAiB;IACjB;GACF,KAAK;IACH,KAAK,EAAE;IACP;GACF,KAAK;IACH,IAAI,YACF,WAAW,aAAa;KAAE,GAAG,OAAO;KAAK,GAAG,OAAO,KAAK;KAAI,YAAY;IAAG;IAE7E,KAAK,KAAK,GAAG;KAAE,GAAG,OAAO,KAAK;KAAI,GAAG,OAAO,KAAK;KAAI,YAAY,KAAK;IAAE,CAAC;IACzE;GACF,KAAK;IACH,IAAI,YACF,WAAW,aAAa;KAAE,GAAG,OAAO;KAAK,GAAG,OAAO,KAAK;KAAI,YAAY;IAAG;IAE7E,KAAK,KAAK,GAAG;KAAE,GAAG,OAAO;KAAK,GAAG,OAAO,KAAK;KAAI,YAAY;IAAG,CAAC;IACjE;GACF,KAAK;IACH,iBAAiB,SAAS,gBAAgB,UAAU;IACpD,aAAa;IACb,iBAAiB;IACjB;GACF,SACE,MAAM,IAAI,MAAM,qCAAqC,KAAK;EAC9D;CACF,CAAC;CACD,OAAO;AACT;AAEA,SAAS,iBAAiB,SAAuB,OAA0B,MAA+B;CACxG,IAAI,CAAC,SAAS,CAAC,QAAQ,UAAU,MAAM;CACvC,IAAI,CAAC,KAAK,aAAa,KAAK,MAAM,MAAM,KAAK,KAAK,MAAM,MAAM,GAAG;CACjE,QAAQ,IAAI;CACZ,MAAM,kBAAkB,KAAK;CAC7B,MAAM,YAAY,KAAK;AACzB;;;AAIA,SAAgB,UAAU,MAAmB,YAAoB,GAAW,GAAwB;CAClG,MAAM,OAAO,IAAI,aAAa,KAAK,MAAM;CACzC,KAAK,cAAc;CACnB,KAAK,aAAa,KAAK;CACvB,OAAO;EAAE,GAAG;EAAM,QAAQ;CAAK;AACjC"}
1
+ {"version":3,"file":"geometry.js","names":[],"sources":["../../../../src/interactions/actions/edit-anchors/geometry.ts"],"sourcesContent":["import { forEachSegment, originOf, PATH_C, PATH_L, PATH_M, PATH_Q, PATH_Z, worldCoords, type PolygonPath } from '@weasel-js/geom';\n\n/** A single anchor on a `PolygonPath`, with its incoming/outgoing bezier\n * control points (when present). `coordIndex` points at the anchor's own\n * on-curve x in the `coords` array. `inCoordIndex` / `outCoordIndex` point\n * at the corresponding control x. */\nexport interface PathAnchor {\n /** Sequential anchor index in walk order (0-based). */\n anchorIndex: number;\n /** On-curve point. */\n x: number;\n y: number;\n /** Incoming control (from the previous segment, when it was C/Q). */\n controlIn?: { x: number; y: number; coordIndex: number };\n /** Outgoing control (from the next segment, when it is C/Q). */\n controlOut?: { x: number; y: number; coordIndex: number };\n /** Index of the anchor's on-curve x in `coords`. */\n coordIndex: number;\n /** Set on the first anchor of a closed subpath whose last curve ends\n * exactly on it: the index of that curve's end x, the same point again. */\n closeCoordIndex?: number;\n}\n\n/** Walk a PolygonPath's command stream and yield one `PathAnchor` per\n * on-curve point. Adjacent C/Q control points are attached as\n * `controlIn`/`controlOut`. Subpath boundaries (M after a previous M or Z)\n * start fresh — the new M's anchor has no `controlIn` from a Z. A closing\n * curve that ends exactly on the subpath's first point is not a second\n * anchor: its control becomes the first anchor's `controlIn`, the way\n * `pathToAnchors` reads it, so the two walkers count alike. */\nexport function enumerateAnchors(path: PolygonPath): PathAnchor[] {\n const { commands } = path;\n const coords = worldCoords(path);\n const anchors: PathAnchor[] = [];\n let lastAnchor: PathAnchor | null = null;\n let firstInSubpath: PathAnchor | null = null;\n\n const push = (coordIndex: number, controlIn?: PathAnchor['controlIn']): void => {\n const a: PathAnchor = {\n anchorIndex: anchors.length,\n x: coords[coordIndex],\n y: coords[coordIndex + 1],\n coordIndex,\n ...(controlIn ? { controlIn } : {}),\n };\n anchors.push(a);\n lastAnchor = a;\n };\n\n forEachSegment(commands, coords, (cmd, ci) => {\n switch (cmd) {\n case PATH_M:\n push(ci);\n firstInSubpath = lastAnchor;\n break;\n case PATH_L:\n push(ci);\n break;\n case PATH_C:\n if (lastAnchor) {\n lastAnchor.controlOut = { x: coords[ci], y: coords[ci + 1], coordIndex: ci };\n }\n push(ci + 4, { x: coords[ci + 2], y: coords[ci + 3], coordIndex: ci + 2 });\n break;\n case PATH_Q:\n if (lastAnchor) {\n lastAnchor.controlOut = { x: coords[ci], y: coords[ci + 1], coordIndex: ci };\n }\n push(ci + 2, { x: coords[ci], y: coords[ci + 1], coordIndex: ci });\n break;\n case PATH_Z:\n foldClosingCurve(anchors, firstInSubpath, lastAnchor);\n lastAnchor = null;\n firstInSubpath = null;\n break;\n default:\n throw new Error(`enumerateAnchors: unknown command ${cmd}`);\n }\n });\n return anchors;\n}\n\nfunction foldClosingCurve(anchors: PathAnchor[], first: PathAnchor | null, last: PathAnchor | null): void {\n if (!first || !last || first === last) return;\n if (!last.controlIn || last.x !== first.x || last.y !== first.y) return;\n anchors.pop();\n first.closeCoordIndex = last.coordIndex;\n first.controlIn = last.controlIn;\n}\n\n/** A new PolygonPath sharing `path`'s commands, with the point at `coordIndex`\n * moved to `(x, y)` in the path's own space, in a fresh coords buffer. */\nexport function withCoord(path: PolygonPath, coordIndex: number, x: number, y: number): PolygonPath {\n const next = new Float32Array(path.coords);\n const o = originOf(path);\n next[coordIndex] = x - o.x;\n next[coordIndex + 1] = y - o.y;\n return { ...path, coords: next };\n}\n\n/** Translate anchor `anchorIndex` AND its attached control handles\n * (`controlIn` / `controlOut`) by `(dx, dy)`. Used by `editAnchorsAction`\n * when the dragged affordance is the anchor itself — moving the anchor\n * without its handles would shear the surrounding bezier segments. */\nexport function translateAnchor(\n path: PolygonPath,\n anchorIndex: number,\n dx: number,\n dy: number,\n): PolygonPath {\n const anchors = enumerateAnchors(path);\n const a = anchors[anchorIndex];\n if (!a) return path;\n const next = new Float32Array(path.coords);\n // Anchor itself.\n next[a.coordIndex] += dx;\n next[a.coordIndex + 1] += dy;\n if (a.closeCoordIndex !== undefined) {\n next[a.closeCoordIndex] += dx;\n next[a.closeCoordIndex + 1] += dy;\n }\n // In-handle of THIS anchor — the trailing pair of the incoming C/Q.\n if (a.controlIn) {\n next[a.controlIn.coordIndex] += dx;\n next[a.controlIn.coordIndex + 1] += dy;\n }\n // Out-handle: the leading pair of the OUTGOING C/Q. Owned by this\n // anchor's segment.\n if (a.controlOut) {\n next[a.controlOut.coordIndex] += dx;\n next[a.controlOut.coordIndex + 1] += dy;\n }\n return { ...path, coords: next };\n}\n"],"mappings":";;;;;;;;;;AA8BA,SAAgB,iBAAiB,MAAiC;CAChE,MAAM,EAAE,aAAa;CACrB,MAAM,SAAS,YAAY,IAAI;CAC/B,MAAM,UAAwB,CAAC;CAC/B,IAAI,aAAgC;CACpC,IAAI,iBAAoC;CAExC,MAAM,QAAQ,YAAoB,cAA8C;EAC9E,MAAM,IAAgB;GACpB,aAAa,QAAQ;GACrB,GAAG,OAAO;GACV,GAAG,OAAO,aAAa;GACvB;GACA,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;EACnC;EACA,QAAQ,KAAK,CAAC;EACd,aAAa;CACf;CAEA,eAAe,UAAU,SAAS,KAAK,OAAO;EAC5C,QAAQ,KAAR;GACE,KAAK;IACH,KAAK,EAAE;IACP,iBAAiB;IACjB;GACF,KAAK;IACH,KAAK,EAAE;IACP;GACF,KAAK;IACH,IAAI,YACF,WAAW,aAAa;KAAE,GAAG,OAAO;KAAK,GAAG,OAAO,KAAK;KAAI,YAAY;IAAG;IAE7E,KAAK,KAAK,GAAG;KAAE,GAAG,OAAO,KAAK;KAAI,GAAG,OAAO,KAAK;KAAI,YAAY,KAAK;IAAE,CAAC;IACzE;GACF,KAAK;IACH,IAAI,YACF,WAAW,aAAa;KAAE,GAAG,OAAO;KAAK,GAAG,OAAO,KAAK;KAAI,YAAY;IAAG;IAE7E,KAAK,KAAK,GAAG;KAAE,GAAG,OAAO;KAAK,GAAG,OAAO,KAAK;KAAI,YAAY;IAAG,CAAC;IACjE;GACF,KAAK;IACH,iBAAiB,SAAS,gBAAgB,UAAU;IACpD,aAAa;IACb,iBAAiB;IACjB;GACF,SACE,MAAM,IAAI,MAAM,qCAAqC,KAAK;EAC9D;CACF,CAAC;CACD,OAAO;AACT;AAEA,SAAS,iBAAiB,SAAuB,OAA0B,MAA+B;CACxG,IAAI,CAAC,SAAS,CAAC,QAAQ,UAAU,MAAM;CACvC,IAAI,CAAC,KAAK,aAAa,KAAK,MAAM,MAAM,KAAK,KAAK,MAAM,MAAM,GAAG;CACjE,QAAQ,IAAI;CACZ,MAAM,kBAAkB,KAAK;CAC7B,MAAM,YAAY,KAAK;AACzB;;;AAIA,SAAgB,UAAU,MAAmB,YAAoB,GAAW,GAAwB;CAClG,MAAM,OAAO,IAAI,aAAa,KAAK,MAAM;CACzC,MAAM,IAAI,SAAS,IAAI;CACvB,KAAK,cAAc,IAAI,EAAE;CACzB,KAAK,aAAa,KAAK,IAAI,EAAE;CAC7B,OAAO;EAAE,GAAG;EAAM,QAAQ;CAAK;AACjC"}
@@ -1 +1 @@
1
- {"version":3,"file":"handleDrag.js","names":[],"sources":["../../../../src/interactions/actions/edit-anchors/handleDrag.ts"],"sourcesContent":["/**\n * The locks on a control-handle drag — its angle, its length, or both — and\n * the guide that shows which one is holding.\n */\nimport { roleReader, type ModifierRoles, type ModifierState, type OngoingOverlay } from '@weasel-js/routing';\nimport { handleAlongItsAngle, handleAtItsLength } from '@weasel-js/geom';\nimport type { HandleLocks } from './params';\n\ntype Point = { x: number; y: number };\n\n/** Where a handle drag's anchor and handle sat at the press. */\nexport interface HandlePress {\n anchor: Point;\n handle: Point;\n}\n\n/** Where the handle goes this move, and the guide its lock draws. */\nexport interface LockedHandle {\n to: Point;\n /** The line or circle the handle is held to; null with no lock held. */\n guide: OngoingOverlay | null;\n}\n\nconst CIRCLE_SEGMENTS = 64;\n/** How far past the handle the angle guide reaches, as a share of its length. */\nconst LINE_OVERSHOOT = 1.25;\n\n/** The modifier roles a handle drag reads: Shift holds its angle, Cmd or\n * Ctrl its length. */\nexport const HANDLE_LOCK_ROLES = {\n 'handle.holdAngle': { label: 'Hold the handle angle', keys: ['shift'] },\n 'handle.holdLength': { label: 'Hold the handle length', keys: ['meta', 'ctrl'] },\n} as const satisfies ModifierRoles;\n\nconst lockHeld = roleReader(HANDLE_LOCK_ROLES);\n\n/** The locks in force: those `held` names, plus those the event's\n * {@link HANDLE_LOCK_ROLES} hold. */\nexport function locksInForce(\n held: HandleLocks,\n ctx: { roles?: Readonly<Record<string, boolean>>; modifiers: ModifierState },\n): HandleLocks {\n return {\n angle: held.angle || lockHeld(ctx, 'handle.holdAngle'),\n length: held.length || lockHeld(ctx, 'handle.holdLength'),\n };\n}\n\n/** The handle dragged toward `pointer` under `locks`. */\nexport function lockHandle(press: HandlePress, pointer: Point, locks: HandleLocks): LockedHandle {\n const { anchor, handle } = press;\n const len = dist(anchor, handle);\n if (locks.angle && locks.length) return { to: { ...handle }, guide: lineGuide(anchor, handle, len) };\n if (locks.angle) {\n const to = handleAlongItsAngle(anchor, handle, pointer);\n return { to, guide: lineGuide(anchor, handle, Math.max(len, dist(anchor, to))) };\n }\n if (locks.length) return { to: handleAtItsLength(anchor, handle, pointer), guide: circleGuide(anchor, len) };\n return { to: pointer, guide: null };\n}\n\n/** The line through `anchor` toward `toward`, reaching past `reach` on both sides. */\nfunction lineGuide(anchor: Point, toward: Point, reach: number): OngoingOverlay {\n const d = dist(anchor, toward);\n if (d === 0) return { kind: 'polyline', points: [anchor, anchor], role: 'lock' };\n const span = reach * LINE_OVERSHOOT;\n const ux = ((toward.x - anchor.x) / d) * span;\n const uy = ((toward.y - anchor.y) / d) * span;\n return {\n kind: 'polyline',\n points: [{ x: anchor.x - ux, y: anchor.y - uy }, { x: anchor.x + ux, y: anchor.y + uy }],\n role: 'lock',\n };\n}\n\nfunction circleGuide(center: Point, r: number): OngoingOverlay {\n const points: Point[] = [];\n for (let i = 0; i <= CIRCLE_SEGMENTS; i++) {\n const a = (i / CIRCLE_SEGMENTS) * Math.PI * 2;\n points.push({ x: center.x + Math.cos(a) * r, y: center.y + Math.sin(a) * r });\n }\n return { kind: 'polyline', points, role: 'lock' };\n}\n\nfunction dist(a: Point, b: Point): number {\n return Math.hypot(b.x - a.x, b.y - a.y);\n}\n"],"mappings":";;;;;;;;AAuBA,IAAM,kBAAkB;;AAExB,IAAM,iBAAiB;;;AAIvB,IAAa,oBAAoB;CAC/B,oBAAoB;EAAE,OAAO;EAAyB,MAAM,CAAC,OAAO;CAAE;CACtE,qBAAqB;EAAE,OAAO;EAA0B,MAAM,CAAC,QAAQ,MAAM;CAAE;AACjF;AAEA,IAAM,WAAW,WAAW,iBAAiB;;;AAI7C,SAAgB,aACd,MACA,KACa;CACb,OAAO;EACL,OAAO,KAAK,SAAS,SAAS,KAAK,kBAAkB;EACrD,QAAQ,KAAK,UAAU,SAAS,KAAK,mBAAmB;CAC1D;AACF;;AAGA,SAAgB,WAAW,OAAoB,SAAgB,OAAkC;CAC/F,MAAM,EAAE,QAAQ,WAAW;CAC3B,MAAM,MAAM,KAAK,QAAQ,MAAM;CAC/B,IAAI,MAAM,SAAS,MAAM,QAAQ,OAAO;EAAE,IAAI,EAAE,GAAG,OAAO;EAAG,OAAO,UAAU,QAAQ,QAAQ,GAAG;CAAE;CACnG,IAAI,MAAM,OAAO;EACf,MAAM,KAAK,oBAAoB,QAAQ,QAAQ,OAAO;EACtD,OAAO;GAAE;GAAI,OAAO,UAAU,QAAQ,QAAQ,KAAK,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC,CAAC;EAAE;CACjF;CACA,IAAI,MAAM,QAAQ,OAAO;EAAE,IAAI,kBAAkB,QAAQ,QAAQ,OAAO;EAAG,OAAO,YAAY,QAAQ,GAAG;CAAE;CAC3G,OAAO;EAAE,IAAI;EAAS,OAAO;CAAK;AACpC;;AAGA,SAAS,UAAU,QAAe,QAAe,OAA+B;CAC9E,MAAM,IAAI,KAAK,QAAQ,MAAM;CAC7B,IAAI,MAAM,GAAG,OAAO;EAAE,MAAM;EAAY,QAAQ,CAAC,QAAQ,MAAM;EAAG,MAAM;CAAO;CAC/E,MAAM,OAAO,QAAQ;CACrB,MAAM,MAAO,OAAO,IAAI,OAAO,KAAK,IAAK;CACzC,MAAM,MAAO,OAAO,IAAI,OAAO,KAAK,IAAK;CACzC,OAAO;EACL,MAAM;EACN,QAAQ,CAAC;GAAE,GAAG,OAAO,IAAI;GAAI,GAAG,OAAO,IAAI;EAAG,GAAG;GAAE,GAAG,OAAO,IAAI;GAAI,GAAG,OAAO,IAAI;EAAG,CAAC;EACvF,MAAM;CACR;AACF;AAEA,SAAS,YAAY,QAAe,GAA2B;CAC7D,MAAM,SAAkB,CAAC;CACzB,KAAK,IAAI,IAAI,GAAG,KAAK,iBAAiB,KAAK;EACzC,MAAM,IAAK,IAAI,kBAAmB,KAAK,KAAK;EAC5C,OAAO,KAAK;GAAE,GAAG,OAAO,IAAI,KAAK,IAAI,CAAC,IAAI;GAAG,GAAG,OAAO,IAAI,KAAK,IAAI,CAAC,IAAI;EAAE,CAAC;CAC9E;CACA,OAAO;EAAE,MAAM;EAAY;EAAQ,MAAM;CAAO;AAClD;AAEA,SAAS,KAAK,GAAU,GAAkB;CACxC,OAAO,KAAK,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;AACxC"}
1
+ {"version":3,"file":"handleDrag.js","names":[],"sources":["../../../../src/interactions/actions/edit-anchors/handleDrag.ts"],"sourcesContent":["/**\n * The locks on a control-handle drag — its angle, its length, or both — and\n * the guide that shows which one is holding.\n */\nimport { roleReader, type ModifierRoles, type ModifierState, type OngoingOverlay, type WorldPoint } from '@weasel-js/routing';\nimport { handleAlongItsAngle, handleAtItsLength, type Point } from '@weasel-js/geom';\nimport type { HandleLocks } from './params';\n\n/** Where a handle drag's anchor and handle sat at the press. */\nexport interface HandlePress {\n anchor: Point;\n handle: Point;\n}\n\n/** Where the handle goes this move, and the guide its lock draws. */\nexport interface LockedHandle {\n to: Point;\n /** The line or circle the handle is held to; null with no lock held. */\n guide: OngoingOverlay | null;\n}\n\nconst CIRCLE_SEGMENTS = 64;\n/** How far past the handle the angle guide reaches, as a share of its length. */\nconst LINE_OVERSHOOT = 1.25;\n\n/** The modifier roles a handle drag reads: Shift holds its angle, Cmd or\n * Ctrl its length. */\nexport const HANDLE_LOCK_ROLES = {\n 'handle.holdAngle': { label: 'Hold the handle angle', keys: ['shift'] },\n 'handle.holdLength': { label: 'Hold the handle length', keys: ['meta', 'ctrl'] },\n} as const satisfies ModifierRoles;\n\nconst lockHeld = roleReader(HANDLE_LOCK_ROLES);\n\n/** The locks in force: those `held` names, plus those the event's\n * {@link HANDLE_LOCK_ROLES} hold. */\nexport function locksInForce(\n held: HandleLocks,\n ctx: { roles?: Readonly<Record<string, boolean>>; modifiers: ModifierState },\n): HandleLocks {\n return {\n angle: held.angle || lockHeld(ctx, 'handle.holdAngle'),\n length: held.length || lockHeld(ctx, 'handle.holdLength'),\n };\n}\n\n/** The handle dragged toward `pointer` under `locks`. */\nexport function lockHandle(press: HandlePress, pointer: WorldPoint, locks: HandleLocks): LockedHandle {\n const { anchor, handle } = press;\n const len = dist(anchor, handle);\n if (locks.angle && locks.length) return { to: { ...handle }, guide: lineGuide(anchor, handle, len) };\n if (locks.angle) {\n const to = handleAlongItsAngle(anchor, handle, pointer);\n return { to, guide: lineGuide(anchor, handle, Math.max(len, dist(anchor, to))) };\n }\n if (locks.length) return { to: handleAtItsLength(anchor, handle, pointer), guide: circleGuide(anchor, len) };\n return { to: pointer, guide: null };\n}\n\n/** The line through `anchor` toward `toward`, reaching past `reach` on both sides. */\nfunction lineGuide(anchor: Point, toward: Point, reach: number): OngoingOverlay {\n const d = dist(anchor, toward);\n if (d === 0) return { kind: 'polyline', points: [anchor, anchor], role: 'lock' };\n const span = reach * LINE_OVERSHOOT;\n const ux = ((toward.x - anchor.x) / d) * span;\n const uy = ((toward.y - anchor.y) / d) * span;\n return {\n kind: 'polyline',\n points: [{ x: anchor.x - ux, y: anchor.y - uy }, { x: anchor.x + ux, y: anchor.y + uy }],\n role: 'lock',\n };\n}\n\nfunction circleGuide(center: Point, r: number): OngoingOverlay {\n const points: Point[] = [];\n for (let i = 0; i <= CIRCLE_SEGMENTS; i++) {\n const a = (i / CIRCLE_SEGMENTS) * Math.PI * 2;\n points.push({ x: center.x + Math.cos(a) * r, y: center.y + Math.sin(a) * r });\n }\n return { kind: 'polyline', points, role: 'lock' };\n}\n\nfunction dist(a: Point, b: Point): number {\n return Math.hypot(b.x - a.x, b.y - a.y);\n}\n"],"mappings":";;;;;;;;AAqBA,IAAM,kBAAkB;;AAExB,IAAM,iBAAiB;;;AAIvB,IAAa,oBAAoB;CAC/B,oBAAoB;EAAE,OAAO;EAAyB,MAAM,CAAC,OAAO;CAAE;CACtE,qBAAqB;EAAE,OAAO;EAA0B,MAAM,CAAC,QAAQ,MAAM;CAAE;AACjF;AAEA,IAAM,WAAW,WAAW,iBAAiB;;;AAI7C,SAAgB,aACd,MACA,KACa;CACb,OAAO;EACL,OAAO,KAAK,SAAS,SAAS,KAAK,kBAAkB;EACrD,QAAQ,KAAK,UAAU,SAAS,KAAK,mBAAmB;CAC1D;AACF;;AAGA,SAAgB,WAAW,OAAoB,SAAqB,OAAkC;CACpG,MAAM,EAAE,QAAQ,WAAW;CAC3B,MAAM,MAAM,KAAK,QAAQ,MAAM;CAC/B,IAAI,MAAM,SAAS,MAAM,QAAQ,OAAO;EAAE,IAAI,EAAE,GAAG,OAAO;EAAG,OAAO,UAAU,QAAQ,QAAQ,GAAG;CAAE;CACnG,IAAI,MAAM,OAAO;EACf,MAAM,KAAK,oBAAoB,QAAQ,QAAQ,OAAO;EACtD,OAAO;GAAE;GAAI,OAAO,UAAU,QAAQ,QAAQ,KAAK,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC,CAAC;EAAE;CACjF;CACA,IAAI,MAAM,QAAQ,OAAO;EAAE,IAAI,kBAAkB,QAAQ,QAAQ,OAAO;EAAG,OAAO,YAAY,QAAQ,GAAG;CAAE;CAC3G,OAAO;EAAE,IAAI;EAAS,OAAO;CAAK;AACpC;;AAGA,SAAS,UAAU,QAAe,QAAe,OAA+B;CAC9E,MAAM,IAAI,KAAK,QAAQ,MAAM;CAC7B,IAAI,MAAM,GAAG,OAAO;EAAE,MAAM;EAAY,QAAQ,CAAC,QAAQ,MAAM;EAAG,MAAM;CAAO;CAC/E,MAAM,OAAO,QAAQ;CACrB,MAAM,MAAO,OAAO,IAAI,OAAO,KAAK,IAAK;CACzC,MAAM,MAAO,OAAO,IAAI,OAAO,KAAK,IAAK;CACzC,OAAO;EACL,MAAM;EACN,QAAQ,CAAC;GAAE,GAAG,OAAO,IAAI;GAAI,GAAG,OAAO,IAAI;EAAG,GAAG;GAAE,GAAG,OAAO,IAAI;GAAI,GAAG,OAAO,IAAI;EAAG,CAAC;EACvF,MAAM;CACR;AACF;AAEA,SAAS,YAAY,QAAe,GAA2B;CAC7D,MAAM,SAAkB,CAAC;CACzB,KAAK,IAAI,IAAI,GAAG,KAAK,iBAAiB,KAAK;EACzC,MAAM,IAAK,IAAI,kBAAmB,KAAK,KAAK;EAC5C,OAAO,KAAK;GAAE,GAAG,OAAO,IAAI,KAAK,IAAI,CAAC,IAAI;GAAG,GAAG,OAAO,IAAI,KAAK,IAAI,CAAC,IAAI;EAAE,CAAC;CAC9E;CACA,OAAO;EAAE,MAAM;EAAY;EAAQ,MAAM;CAAO;AAClD;AAEA,SAAS,KAAK,GAAU,GAAkB;CACxC,OAAO,KAAK,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;AACxC"}
@@ -1,4 +1,4 @@
1
- import { storeEditablePath } from "../../../features/paths/editablePath.js";
1
+ import { storeEditablePath } from "../../../domains/paths/editablePath.js";
2
2
 
3
3
  //#region src/interactions/actions/edit-anchors/preview.ts
4
4
  /** `path`, a frame of an edit of `id`, finished by the dep's `finishEdit` as
@@ -1 +1 @@
1
- {"version":3,"file":"preview.js","names":[],"sources":["../../../../src/interactions/actions/edit-anchors/preview.ts"],"sourcesContent":["import type { OngoingHandle } from '@weasel-js/routing';\nimport type { EditAnchorsDep } from '../depSchema';\nimport type { FinishedEditFrame } from '../editAnchorsDep';\nimport type { PointOrigin, PolygonPath } from '@weasel-js/geom';\nimport { storeEditablePath, type EditableShape } from 'features/paths/editablePath';\n\n/** Where the edited polygon lives on its node: the pose itself, or `data.path`\n * under a rect pose. */\nexport type PathStorageKind = 'pose' | 'data';\n\n/** The preview half of an ongoing path edit, captured at press. */\nexport interface PathEditPreview {\n storageKind: PathStorageKind;\n previewIds: NonNullable<OngoingHandle['previewIds']>;\n previewPose: NonNullable<OngoingHandle['previewPose']>;\n previewData: NonNullable<OngoingHandle['previewData']>;\n}\n\n/** One frame of a drag that reshapes the path in edit mode: the world-space\n * polygon it makes, and where each of its points came from when the drag\n * knows (`editAnchorSetTracked`). */\nexport interface PathEditFrame {\n path: PolygonPath;\n origins?: readonly (PointOrigin | null)[];\n}\n\ntype ShownFrame = { path: PolygonPath; others: readonly { id: string; path: PolygonPath }[] };\n\n/** `path`, a frame of an edit of `id`, finished by the dep's `finishEdit` as\n * committing it would be: its path and the other nodes' that move with it.\n * Null when an extension cannot finish it. */\nexport function finishPathEditFrame(\n dep: EditAnchorsDep,\n id: string,\n path: PolygonPath,\n origins?: readonly (PointOrigin | null)[],\n): FinishedEditFrame | null {\n return dep.finishEdit ? dep.finishEdit(id, path, origins) : { path, others: [] };\n}\n\n/**\n * Preview ghosts for a drag that reshapes the path in edit mode. `current`\n * answers the frame to show, or null while there is nothing to preview yet.\n * Each frame is finished as committing it would be\n * ({@link finishPathEditFrame}), so the node's constraints and the nodes\n * linked to it show as they will commit; a frame that cannot be finished\n * commits nothing, and shows nothing. A drag that finishes its own frames\n * passes `finished`, the world-space paths of the other nodes its frame\n * moves, and its frames are shown as they are. Null when the node stores its\n * path in neither supported place.\n *\n * The ghost is what committing the frame would store (`storeEditablePath`),\n * so a path drawn from a form is drawn again from the edited form on every\n * move.\n */\nexport function pathEditPreview(\n dep: EditAnchorsDep,\n id: string,\n current: () => PathEditFrame | null,\n finished?: () => readonly { id: string; path: PolygonPath }[],\n): PathEditPreview | null {\n const storageKind = dep.getStorageKind(id);\n if (storageKind !== 'pose' && storageKind !== 'data') return null;\n // The node as it was at the press, which every ghost is stored from. A dep\n // that hands out no shape still says a pose-stored polygon is the pose.\n const origin = dep.getNodeShape(id)\n ?? (storageKind === 'pose' ? { pose: dep.getEditablePath(id), data: undefined } : null);\n if (!origin) return null;\n\n // Each frame is finished once, however many times the dispatcher asks for it.\n let lastFrame: PolygonPath | null = null;\n let lastFinished: ShownFrame | null = null;\n const shown = (): ShownFrame | null => {\n const frame = current();\n if (!frame) return null;\n if (finished) return { path: frame.path, others: finished() };\n if (frame.path !== lastFrame) {\n lastFrame = frame.path;\n lastFinished = finishPathEditFrame(dep, id, frame.path, frame.origins) as ShownFrame | null;\n }\n return lastFinished;\n };\n const others = () => shown()?.others ?? [];\n\n // previewPose and previewData are both asked each frame; store once per path.\n let lastPath: PolygonPath | null = null;\n let lastStored: EditableShape | null = null;\n const stored = (pid: string): EditableShape | null => {\n const path = pid === id ? shown()?.path : null;\n if (!path) return null;\n if (path !== lastPath) {\n lastPath = path;\n lastStored = storeEditablePath(origin, path, dep.extensions);\n }\n return lastStored;\n };\n\n // Another node's ghost, stored from that node as the press found it, once per path.\n const otherOrigins = new Map<string, EditableShape | null>();\n const otherStored = new Map<string, { path: PolygonPath; stored: EditableShape | null }>();\n const storedOther = (pid: string): { from: EditableShape; to: EditableShape } | null => {\n const path = others().find((o) => o.id === pid)?.path;\n if (!path) return null;\n if (!otherOrigins.has(pid)) otherOrigins.set(pid, dep.getNodeShape(pid));\n const from = otherOrigins.get(pid);\n if (!from) return null;\n let hit = otherStored.get(pid);\n if (hit?.path !== path) otherStored.set(pid, (hit = { path, stored: storeEditablePath(from, path, dep.extensions) }));\n return hit.stored ? { from, to: hit.stored } : null;\n };\n\n return {\n storageKind,\n previewIds: () => {\n const frame = shown();\n return frame ? [id, ...frame.others.map((o) => o.id)] : null;\n },\n previewPose: (pid: string): unknown | null =>\n (pid === id ? stored(pid)?.pose : storedOther(pid)?.to.pose) ?? null,\n previewData: (pid: string): unknown | null => {\n if (pid !== id) {\n const o = storedOther(pid);\n return o && o.to.data !== o.from.data ? o.to.data : null;\n }\n const next = stored(pid);\n return next && next.data !== origin.data ? next.data : null;\n },\n };\n}\n"],"mappings":";;;;;;AA+BA,SAAgB,oBACd,KACA,IACA,MACA,SAC0B;CAC1B,OAAO,IAAI,aAAa,IAAI,WAAW,IAAI,MAAM,OAAO,IAAI;EAAE;EAAM,QAAQ,CAAC;CAAE;AACjF;;;;;;;;;;;;;;;;AAiBA,SAAgB,gBACd,KACA,IACA,SACA,UACwB;CACxB,MAAM,cAAc,IAAI,eAAe,EAAE;CACzC,IAAI,gBAAgB,UAAU,gBAAgB,QAAQ,OAAO;CAG7D,MAAM,SAAS,IAAI,aAAa,EAAE,MAC5B,gBAAgB,SAAS;EAAE,MAAM,IAAI,gBAAgB,EAAE;EAAG,MAAM;CAAU,IAAI;CACpF,IAAI,CAAC,QAAQ,OAAO;CAGpB,IAAI,YAAgC;CACpC,IAAI,eAAkC;CACtC,MAAM,cAAiC;EACrC,MAAM,QAAQ,QAAQ;EACtB,IAAI,CAAC,OAAO,OAAO;EACnB,IAAI,UAAU,OAAO;GAAE,MAAM,MAAM;GAAM,QAAQ,SAAS;EAAE;EAC5D,IAAI,MAAM,SAAS,WAAW;GAC5B,YAAY,MAAM;GAClB,eAAe,oBAAoB,KAAK,IAAI,MAAM,MAAM,MAAM,OAAO;EACvE;EACA,OAAO;CACT;CACA,MAAM,eAAe,MAAM,CAAC,EAAE,UAAU,CAAC;CAGzC,IAAI,WAA+B;CACnC,IAAI,aAAmC;CACvC,MAAM,UAAU,QAAsC;EACpD,MAAM,OAAO,QAAQ,KAAK,MAAM,CAAC,EAAE,OAAO;EAC1C,IAAI,CAAC,MAAM,OAAO;EAClB,IAAI,SAAS,UAAU;GACrB,WAAW;GACX,aAAa,kBAAkB,QAAQ,MAAM,IAAI,UAAU;EAC7D;EACA,OAAO;CACT;CAGA,MAAM,+BAAe,IAAI,IAAkC;CAC3D,MAAM,8BAAc,IAAI,IAAiE;CACzF,MAAM,eAAe,QAAmE;EACtF,MAAM,OAAO,OAAO,CAAC,CAAC,MAAM,MAAM,EAAE,OAAO,GAAG,CAAC,EAAE;EACjD,IAAI,CAAC,MAAM,OAAO;EAClB,IAAI,CAAC,aAAa,IAAI,GAAG,GAAG,aAAa,IAAI,KAAK,IAAI,aAAa,GAAG,CAAC;EACvE,MAAM,OAAO,aAAa,IAAI,GAAG;EACjC,IAAI,CAAC,MAAM,OAAO;EAClB,IAAI,MAAM,YAAY,IAAI,GAAG;EAC7B,IAAI,KAAK,SAAS,MAAM,YAAY,IAAI,KAAM,MAAM;GAAE;GAAM,QAAQ,kBAAkB,MAAM,MAAM,IAAI,UAAU;EAAE,CAAE;EACpH,OAAO,IAAI,SAAS;GAAE;GAAM,IAAI,IAAI;EAAO,IAAI;CACjD;CAEA,OAAO;EACL;EACA,kBAAkB;GAChB,MAAM,QAAQ,MAAM;GACpB,OAAO,QAAQ,CAAC,IAAI,GAAG,MAAM,OAAO,KAAK,MAAM,EAAE,EAAE,CAAC,IAAI;EAC1D;EACA,cAAc,SACX,QAAQ,KAAK,OAAO,GAAG,CAAC,EAAE,OAAO,YAAY,GAAG,CAAC,EAAE,GAAG,SAAS;EAClE,cAAc,QAAgC;GAC5C,IAAI,QAAQ,IAAI;IACd,MAAM,IAAI,YAAY,GAAG;IACzB,OAAO,KAAK,EAAE,GAAG,SAAS,EAAE,KAAK,OAAO,EAAE,GAAG,OAAO;GACtD;GACA,MAAM,OAAO,OAAO,GAAG;GACvB,OAAO,QAAQ,KAAK,SAAS,OAAO,OAAO,KAAK,OAAO;EACzD;CACF;AACF"}
1
+ {"version":3,"file":"preview.js","names":[],"sources":["../../../../src/interactions/actions/edit-anchors/preview.ts"],"sourcesContent":["import type { OngoingHandle } from '@weasel-js/routing';\nimport type { EditAnchorsDep } from '../depSchema';\nimport type { FinishedEditFrame } from '../editAnchorsDep';\nimport type { PointOrigin, PolygonPath } from '@weasel-js/geom';\nimport { storeEditablePath, type EditableShape } from 'domains/paths/editablePath';\n\n/** Where the edited polygon lives on its node: the pose itself, or `data.path`\n * under a rect pose. */\nexport type PathStorageKind = 'pose' | 'data';\n\n/** The preview half of an ongoing path edit, captured at press. */\nexport interface PathEditPreview {\n storageKind: PathStorageKind;\n previewIds: NonNullable<OngoingHandle['previewIds']>;\n previewPose: NonNullable<OngoingHandle['previewPose']>;\n previewData: NonNullable<OngoingHandle['previewData']>;\n}\n\n/** One frame of a drag that reshapes the path in edit mode: the world-space\n * polygon it makes, and where each of its points came from when the drag\n * knows (`editAnchorSetTracked`). */\nexport interface PathEditFrame {\n path: PolygonPath;\n origins?: readonly (PointOrigin | null)[];\n}\n\ntype ShownFrame = { path: PolygonPath; others: readonly { id: string; path: PolygonPath }[] };\n\n/** `path`, a frame of an edit of `id`, finished by the dep's `finishEdit` as\n * committing it would be: its path and the other nodes' that move with it.\n * Null when an extension cannot finish it. */\nexport function finishPathEditFrame(\n dep: EditAnchorsDep,\n id: string,\n path: PolygonPath,\n origins?: readonly (PointOrigin | null)[],\n): FinishedEditFrame | null {\n return dep.finishEdit ? dep.finishEdit(id, path, origins) : { path, others: [] };\n}\n\n/**\n * Preview ghosts for a drag that reshapes the path in edit mode. `current`\n * answers the frame to show, or null while there is nothing to preview yet.\n * Each frame is finished as committing it would be\n * ({@link finishPathEditFrame}), so the node's constraints and the nodes\n * linked to it show as they will commit; a frame that cannot be finished\n * commits nothing, and shows nothing. A drag that finishes its own frames\n * passes `finished`, the world-space paths of the other nodes its frame\n * moves, and its frames are shown as they are. Null when the node stores its\n * path in neither supported place.\n *\n * The ghost is what committing the frame would store (`storeEditablePath`),\n * so a path drawn from a form is drawn again from the edited form on every\n * move.\n */\nexport function pathEditPreview(\n dep: EditAnchorsDep,\n id: string,\n current: () => PathEditFrame | null,\n finished?: () => readonly { id: string; path: PolygonPath }[],\n): PathEditPreview | null {\n const storageKind = dep.getStorageKind(id);\n if (storageKind !== 'pose' && storageKind !== 'data') return null;\n // The node as it was at the press, which every ghost is stored from. A dep\n // that hands out no shape still says a pose-stored polygon is the pose.\n const origin = dep.getNodeShape(id)\n ?? (storageKind === 'pose' ? { pose: dep.getEditablePath(id), data: undefined } : null);\n if (!origin) return null;\n\n // Each frame is finished once, however many times the dispatcher asks for it.\n let lastFrame: PolygonPath | null = null;\n let lastFinished: ShownFrame | null = null;\n const shown = (): ShownFrame | null => {\n const frame = current();\n if (!frame) return null;\n if (finished) return { path: frame.path, others: finished() };\n if (frame.path !== lastFrame) {\n lastFrame = frame.path;\n lastFinished = finishPathEditFrame(dep, id, frame.path, frame.origins) as ShownFrame | null;\n }\n return lastFinished;\n };\n const others = () => shown()?.others ?? [];\n\n // previewPose and previewData are both asked each frame; store once per path.\n let lastPath: PolygonPath | null = null;\n let lastStored: EditableShape | null = null;\n const stored = (pid: string): EditableShape | null => {\n const path = pid === id ? shown()?.path : null;\n if (!path) return null;\n if (path !== lastPath) {\n lastPath = path;\n lastStored = storeEditablePath(origin, path, dep.extensions);\n }\n return lastStored;\n };\n\n // Another node's ghost, stored from that node as the press found it, once per path.\n const otherOrigins = new Map<string, EditableShape | null>();\n const otherStored = new Map<string, { path: PolygonPath; stored: EditableShape | null }>();\n const storedOther = (pid: string): { from: EditableShape; to: EditableShape } | null => {\n const path = others().find((o) => o.id === pid)?.path;\n if (!path) return null;\n if (!otherOrigins.has(pid)) otherOrigins.set(pid, dep.getNodeShape(pid));\n const from = otherOrigins.get(pid);\n if (!from) return null;\n let hit = otherStored.get(pid);\n if (hit?.path !== path) otherStored.set(pid, (hit = { path, stored: storeEditablePath(from, path, dep.extensions) }));\n return hit.stored ? { from, to: hit.stored } : null;\n };\n\n return {\n storageKind,\n previewIds: () => {\n const frame = shown();\n return frame ? [id, ...frame.others.map((o) => o.id)] : null;\n },\n previewPose: (pid: string): unknown | null =>\n (pid === id ? stored(pid)?.pose : storedOther(pid)?.to.pose) ?? null,\n previewData: (pid: string): unknown | null => {\n if (pid !== id) {\n const o = storedOther(pid);\n return o && o.to.data !== o.from.data ? o.to.data : null;\n }\n const next = stored(pid);\n return next && next.data !== origin.data ? next.data : null;\n },\n };\n}\n"],"mappings":";;;;;;AA+BA,SAAgB,oBACd,KACA,IACA,MACA,SAC0B;CAC1B,OAAO,IAAI,aAAa,IAAI,WAAW,IAAI,MAAM,OAAO,IAAI;EAAE;EAAM,QAAQ,CAAC;CAAE;AACjF;;;;;;;;;;;;;;;;AAiBA,SAAgB,gBACd,KACA,IACA,SACA,UACwB;CACxB,MAAM,cAAc,IAAI,eAAe,EAAE;CACzC,IAAI,gBAAgB,UAAU,gBAAgB,QAAQ,OAAO;CAG7D,MAAM,SAAS,IAAI,aAAa,EAAE,MAC5B,gBAAgB,SAAS;EAAE,MAAM,IAAI,gBAAgB,EAAE;EAAG,MAAM;CAAU,IAAI;CACpF,IAAI,CAAC,QAAQ,OAAO;CAGpB,IAAI,YAAgC;CACpC,IAAI,eAAkC;CACtC,MAAM,cAAiC;EACrC,MAAM,QAAQ,QAAQ;EACtB,IAAI,CAAC,OAAO,OAAO;EACnB,IAAI,UAAU,OAAO;GAAE,MAAM,MAAM;GAAM,QAAQ,SAAS;EAAE;EAC5D,IAAI,MAAM,SAAS,WAAW;GAC5B,YAAY,MAAM;GAClB,eAAe,oBAAoB,KAAK,IAAI,MAAM,MAAM,MAAM,OAAO;EACvE;EACA,OAAO;CACT;CACA,MAAM,eAAe,MAAM,CAAC,EAAE,UAAU,CAAC;CAGzC,IAAI,WAA+B;CACnC,IAAI,aAAmC;CACvC,MAAM,UAAU,QAAsC;EACpD,MAAM,OAAO,QAAQ,KAAK,MAAM,CAAC,EAAE,OAAO;EAC1C,IAAI,CAAC,MAAM,OAAO;EAClB,IAAI,SAAS,UAAU;GACrB,WAAW;GACX,aAAa,kBAAkB,QAAQ,MAAM,IAAI,UAAU;EAC7D;EACA,OAAO;CACT;CAGA,MAAM,+BAAe,IAAI,IAAkC;CAC3D,MAAM,8BAAc,IAAI,IAAiE;CACzF,MAAM,eAAe,QAAmE;EACtF,MAAM,OAAO,OAAO,CAAC,CAAC,MAAM,MAAM,EAAE,OAAO,GAAG,CAAC,EAAE;EACjD,IAAI,CAAC,MAAM,OAAO;EAClB,IAAI,CAAC,aAAa,IAAI,GAAG,GAAG,aAAa,IAAI,KAAK,IAAI,aAAa,GAAG,CAAC;EACvE,MAAM,OAAO,aAAa,IAAI,GAAG;EACjC,IAAI,CAAC,MAAM,OAAO;EAClB,IAAI,MAAM,YAAY,IAAI,GAAG;EAC7B,IAAI,KAAK,SAAS,MAAM,YAAY,IAAI,KAAM,MAAM;GAAE;GAAM,QAAQ,kBAAkB,MAAM,MAAM,IAAI,UAAU;EAAE,CAAE;EACpH,OAAO,IAAI,SAAS;GAAE;GAAM,IAAI,IAAI;EAAO,IAAI;CACjD;CAEA,OAAO;EACL;EACA,kBAAkB;GAChB,MAAM,QAAQ,MAAM;GACpB,OAAO,QAAQ,CAAC,IAAI,GAAG,MAAM,OAAO,KAAK,MAAM,EAAE,EAAE,CAAC,IAAI;EAC1D;EACA,cAAc,SACX,QAAQ,KAAK,OAAO,GAAG,CAAC,EAAE,OAAO,YAAY,GAAG,CAAC,EAAE,GAAG,SAAS;EAClE,cAAc,QAAgC;GAC5C,IAAI,QAAQ,IAAI;IACd,MAAM,IAAI,YAAY,GAAG;IACzB,OAAO,KAAK,EAAE,GAAG,SAAS,EAAE,KAAK,OAAO,EAAE,GAAG,OAAO;GACtD;GACA,MAAM,OAAO,OAAO,GAAG;GACvB,OAAO,QAAQ,KAAK,SAAS,OAAO,OAAO,KAAK,OAAO;EACzD;CACF;AACF"}
@@ -97,10 +97,11 @@ function flipSelection(selection, scene, axis, pivot, applyOps, geometryProjecti
97
97
  const world = worlds.get(id);
98
98
  const about = sharedPivot ?? ownPivot.get(id) ?? null;
99
99
  const flipped = about ? flipPoseAboutBounds(world, axis, geom, about) : flipPoseViaDescriptor(world, axis, geom);
100
+ const to = frame.local(id, flipped);
100
101
  ops.push(createTransformOp({
101
102
  id,
102
103
  from: node.pose,
103
- to: frame.local(id, flipped),
104
+ to,
104
105
  label: "Flip"
105
106
  }));
106
107
  const g = about ?? geom.getBounds(world);
@@ -108,7 +109,7 @@ function flipSelection(selection, scene, axis, pivot, applyOps, geometryProjecti
108
109
  id,
109
110
  data: node.data,
110
111
  pose: node.pose
111
- }, localMirror(axis, frame, id, g), "Flip");
112
+ }, localMirror(axis, frame, id, g), to, "Flip");
112
113
  if (dataOp) ops.push(dataOp);
113
114
  }
114
115
  if (ops.length === 0) return;