@weasel-js/core 1.11.0 → 1.12.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 (586) hide show
  1. package/CHANGELOG.md +611 -0
  2. package/README.md +1 -1
  3. package/dist/WeaselProvider.js +1 -1
  4. package/dist/affordances/pathAnchors.js +10 -6
  5. package/dist/affordances/pathAnchors.js.map +1 -1
  6. package/dist/animation/index.js +3 -3
  7. package/dist/animation/rig/bindRig.js +1 -1
  8. package/dist/animation/rig/rigNode.js.map +1 -1
  9. package/dist/barrel/canvas.js +9 -3
  10. package/dist/barrel/interactions.js +5 -4
  11. package/dist/barrel/paint.js +4 -4
  12. package/dist/barrel/paths.js +10 -6
  13. package/dist/barrel/renderer.js +5 -3
  14. package/dist/barrel/routing.js +7 -12
  15. package/dist/barrel/scene.js +9 -5
  16. package/dist/barrel/text.js +2 -2
  17. package/dist/barrel/viewport.js +9 -4
  18. package/dist/canvas/Canvas.js +5 -2
  19. package/dist/canvas/Canvas.js.map +1 -1
  20. package/dist/canvas/CanvasView.js +23 -34
  21. package/dist/canvas/CanvasView.js.map +1 -1
  22. package/dist/canvas/ModalityHud.js +1 -1
  23. package/dist/canvas/NodeShape.js +4 -1
  24. package/dist/canvas/NodeShape.js.map +1 -1
  25. package/dist/canvas/SceneCanvas/ActionsProviderIfRoot.js +1 -1
  26. package/dist/canvas/SceneCanvas/DepRegistryProviderIfRoot.js +1 -1
  27. package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js +1 -1
  28. package/dist/canvas/SceneCanvas/SceneCanvasInput.js +17 -3
  29. package/dist/canvas/SceneCanvas/SceneCanvasInput.js.map +1 -1
  30. package/dist/canvas/SceneCanvas/SurfaceDeclarations.js +23 -0
  31. package/dist/canvas/SceneCanvas/SurfaceDeclarations.js.map +1 -0
  32. package/dist/canvas/SceneCanvas/builtinToolSets.js +30 -0
  33. package/dist/canvas/SceneCanvas/builtinToolSets.js.map +1 -0
  34. package/dist/canvas/SceneCanvas/canvasPassThrough.js +2 -0
  35. package/dist/canvas/SceneCanvas/canvasPassThrough.js.map +1 -1
  36. package/dist/canvas/SceneCanvas/coordTrace.js +5 -16
  37. package/dist/canvas/SceneCanvas/coordTrace.js.map +1 -1
  38. package/dist/canvas/SceneCanvas/defaultNodeProperties.js +5 -0
  39. package/dist/canvas/SceneCanvas/defaultNodeProperties.js.map +1 -1
  40. package/dist/canvas/SceneCanvas/features.js +45 -5
  41. package/dist/canvas/SceneCanvas/features.js.map +1 -1
  42. package/dist/canvas/SceneCanvas/props.js +1 -11
  43. package/dist/canvas/SceneCanvas/props.js.map +1 -1
  44. package/dist/canvas/SceneCanvas/scenePointQuery.js +7 -3
  45. package/dist/canvas/SceneCanvas/scenePointQuery.js.map +1 -1
  46. package/dist/canvas/SceneCanvas/toolRegistry.js +8 -4
  47. package/dist/canvas/SceneCanvas/toolRegistry.js.map +1 -1
  48. package/dist/canvas/SceneCanvas/useActionTunings.js +6 -1
  49. package/dist/canvas/SceneCanvas/useActionTunings.js.map +1 -1
  50. package/dist/canvas/SceneCanvas/useActionsPropResolver.js +1 -11
  51. package/dist/canvas/SceneCanvas/useActionsPropResolver.js.map +1 -1
  52. package/dist/canvas/SceneCanvas/useBuiltinShapeTools.js.map +1 -1
  53. package/dist/canvas/SceneCanvas/useBuiltinTools.js +2 -9
  54. package/dist/canvas/SceneCanvas/useBuiltinTools.js.map +1 -1
  55. package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js +94 -0
  56. package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js.map +1 -0
  57. package/dist/canvas/SceneCanvas/useCanvasSelection.js +7 -2
  58. package/dist/canvas/SceneCanvas/useCanvasSelection.js.map +1 -1
  59. package/dist/canvas/SceneCanvas/useCanvasTools.js +15 -10
  60. package/dist/canvas/SceneCanvas/useCanvasTools.js.map +1 -1
  61. package/dist/canvas/SceneCanvas/useNodeKinds.js +1 -1
  62. package/dist/canvas/SceneCanvas/useNodeKinds.js.map +1 -1
  63. package/dist/canvas/SceneCanvas/useSceneCanvasHandle.js +9 -3
  64. package/dist/canvas/SceneCanvas/useSceneCanvasHandle.js.map +1 -1
  65. package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js +23 -4
  66. package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js.map +1 -1
  67. package/dist/canvas/SceneCanvas/useSceneCanvasTools.js +2 -3
  68. package/dist/canvas/SceneCanvas/useSceneCanvasTools.js.map +1 -1
  69. package/dist/canvas/SceneCanvas/useSceneChrome.js +6 -7
  70. package/dist/canvas/SceneCanvas/useSceneChrome.js.map +1 -1
  71. package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js +24 -7
  72. package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js.map +1 -1
  73. package/dist/canvas/SceneCanvas/useScenePicking.js +2 -3
  74. package/dist/canvas/SceneCanvas/useScenePicking.js.map +1 -1
  75. package/dist/canvas/SceneCanvas/useSceneSelectTool.js +2 -1
  76. package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
  77. package/dist/canvas/SceneCanvas/useSceneStandardActions.js +5 -5
  78. package/dist/canvas/SceneCanvas/useSceneStandardActions.js.map +1 -1
  79. package/dist/canvas/SceneCanvas.js +27 -24
  80. package/dist/canvas/SceneCanvas.js.map +1 -1
  81. package/dist/canvas/SurfaceCanvas.js +5 -22
  82. package/dist/canvas/SurfaceCanvas.js.map +1 -1
  83. package/dist/canvas/ViewMarker.js +35 -0
  84. package/dist/canvas/ViewMarker.js.map +1 -0
  85. package/dist/canvas/canvasRenderer.js +9 -5
  86. package/dist/canvas/canvasRenderer.js.map +1 -1
  87. package/dist/canvas/contextAttributes.js +14 -0
  88. package/dist/canvas/contextAttributes.js.map +1 -0
  89. package/dist/canvas/defaultDrawOne.js +2 -0
  90. package/dist/canvas/defaultDrawOne.js.map +1 -1
  91. package/dist/canvas/deps/booleans.js +2 -1
  92. package/dist/canvas/deps/booleans.js.map +1 -1
  93. package/dist/canvas/deps/editAnchors.js +9 -113
  94. package/dist/canvas/deps/editAnchors.js.map +1 -1
  95. package/dist/canvas/deps/hitTestArea.js +2 -2
  96. package/dist/canvas/deps/hitTestArea.js.map +1 -1
  97. package/dist/canvas/deps/insert.js +10 -4
  98. package/dist/canvas/deps/insert.js.map +1 -1
  99. package/dist/canvas/deps/pathEditCommits.js +189 -0
  100. package/dist/canvas/deps/pathEditCommits.js.map +1 -0
  101. package/dist/canvas/deps/snap.js +12 -9
  102. package/dist/canvas/deps/snap.js.map +1 -1
  103. package/dist/canvas/deps/transformFollowers.js +20 -0
  104. package/dist/canvas/deps/transformFollowers.js.map +1 -0
  105. package/dist/canvas/derivedPath.js +20 -1
  106. package/dist/canvas/derivedPath.js.map +1 -1
  107. package/dist/canvas/drawnBounds.js +38 -4
  108. package/dist/canvas/drawnBounds.js.map +1 -1
  109. package/dist/canvas/inheritedPaints.js +44 -0
  110. package/dist/canvas/inheritedPaints.js.map +1 -0
  111. package/dist/canvas/nodeShapes/derivedPainter.js +23 -16
  112. package/dist/canvas/nodeShapes/derivedPainter.js.map +1 -1
  113. package/dist/canvas/nodeShapes/nodePaint.js +13 -34
  114. package/dist/canvas/nodeShapes/nodePaint.js.map +1 -1
  115. package/dist/canvas/nodeShapes/pathPainter.js +12 -10
  116. package/dist/canvas/nodeShapes/pathPainter.js.map +1 -1
  117. package/dist/canvas/nodeShapes/rectFallbackPainter.js +4 -5
  118. package/dist/canvas/nodeShapes/rectFallbackPainter.js.map +1 -1
  119. package/dist/canvas/nodeShapes/shapePainter.js +12 -11
  120. package/dist/canvas/nodeShapes/shapePainter.js.map +1 -1
  121. package/dist/canvas/nodeShapes/textPainter.js +13 -12
  122. package/dist/canvas/nodeShapes/textPainter.js.map +1 -1
  123. package/dist/canvas/overlayRoleStrokes.js +12 -0
  124. package/dist/canvas/overlayRoleStrokes.js.map +1 -1
  125. package/dist/canvas/pathOverlay.js +27 -7
  126. package/dist/canvas/pathOverlay.js.map +1 -1
  127. package/dist/canvas/pickReach.js +3 -3
  128. package/dist/canvas/pickReach.js.map +1 -1
  129. package/dist/canvas/pickWalk.js +3 -1
  130. package/dist/canvas/pickWalk.js.map +1 -1
  131. package/dist/canvas/renderDebugSnapshot.js +1 -0
  132. package/dist/canvas/renderDebugSnapshot.js.map +1 -1
  133. package/dist/canvas/renderSceneToPixels.js +7 -6
  134. package/dist/canvas/renderSceneToPixels.js.map +1 -1
  135. package/dist/canvas/sceneAdapter.js.map +1 -1
  136. package/dist/canvas/sceneSlotWiring.js +2 -1
  137. package/dist/canvas/sceneSlotWiring.js.map +1 -1
  138. package/dist/canvas/sceneViewRender.js +2 -1
  139. package/dist/canvas/sceneViewRender.js.map +1 -1
  140. package/dist/canvas/surfaceContribution.js +12 -8
  141. package/dist/canvas/surfaceContribution.js.map +1 -1
  142. package/dist/canvas/useCanvasPaint.js +2 -1
  143. package/dist/canvas/useCanvasPaint.js.map +1 -1
  144. package/dist/canvas/useCanvasView.js +4 -2
  145. package/dist/canvas/useCanvasView.js.map +1 -1
  146. package/dist/canvas/useInFlightBounds.js +52 -0
  147. package/dist/canvas/useInFlightBounds.js.map +1 -0
  148. package/dist/canvas/useLayerStack.js +6 -2
  149. package/dist/canvas/useLayerStack.js.map +1 -1
  150. package/dist/canvas/useViewAim.js +121 -0
  151. package/dist/canvas/useViewAim.js.map +1 -0
  152. package/dist/canvas/useViewHelpers.js +7 -9
  153. package/dist/canvas/useViewHelpers.js.map +1 -1
  154. package/dist/canvas/useViewNesting.js +3 -2
  155. package/dist/canvas/useViewNesting.js.map +1 -1
  156. package/dist/canvas/useViewRegistrySurface.js +4 -2
  157. package/dist/canvas/useViewRegistrySurface.js.map +1 -1
  158. package/dist/canvas/viewMarkers.js +63 -0
  159. package/dist/canvas/viewMarkers.js.map +1 -0
  160. package/dist/canvas/viewRegistry.js +27 -1
  161. package/dist/canvas/viewRegistry.js.map +1 -1
  162. package/dist/clipboard.d.ts +3 -2
  163. package/dist/{clone-DZDrfIlU.d.ts → clone-C3e0OTuF.d.ts} +8 -21
  164. package/dist/clone.d.ts +4 -3
  165. package/dist/core/geometry/autoPoseDescriptor.js +1 -1
  166. package/dist/core/ops/setDependsOn.js +37 -0
  167. package/dist/core/ops/setDependsOn.js.map +1 -0
  168. package/dist/core/ops/writtenBy.js +53 -0
  169. package/dist/core/ops/writtenBy.js.map +1 -0
  170. package/dist/core/pages/fitViewToPage.js +12 -1
  171. package/dist/core/pages/fitViewToPage.js.map +1 -1
  172. package/dist/core/pages/pageEdits.js +163 -0
  173. package/dist/core/pages/pageEdits.js.map +1 -0
  174. package/dist/core/pages/pages.js +30 -1
  175. package/dist/core/pages/pages.js.map +1 -1
  176. package/dist/core/paint/meshKindLazy.js +59 -2
  177. package/dist/core/paint/meshKindLazy.js.map +1 -1
  178. package/dist/core/scene/NodeRouting.js +8 -3
  179. package/dist/core/scene/NodeRouting.js.map +1 -1
  180. package/dist/core/scene/arrivalWindow.js.map +1 -1
  181. package/dist/core/scene/carryDescendants.js +51 -0
  182. package/dist/core/scene/carryDescendants.js.map +1 -0
  183. package/dist/core/scene/clipNothing.js +13 -0
  184. package/dist/core/scene/clipNothing.js.map +1 -0
  185. package/dist/core/scene/depMemo.js +11 -3
  186. package/dist/core/scene/depMemo.js.map +1 -1
  187. package/dist/core/scene/dependents.js +7 -1
  188. package/dist/core/scene/dependents.js.map +1 -1
  189. package/dist/core/scene/derivedParts.js +10 -11
  190. package/dist/core/scene/derivedParts.js.map +1 -1
  191. package/dist/core/scene/docValues.js +57 -0
  192. package/dist/core/scene/docValues.js.map +1 -0
  193. package/dist/core/scene/editPass.js +111 -0
  194. package/dist/core/scene/editPass.js.map +1 -0
  195. package/dist/core/scene/effectivePose.js +13 -2
  196. package/dist/core/scene/effectivePose.js.map +1 -1
  197. package/dist/core/scene/fieldEdits.js +4 -1
  198. package/dist/core/scene/fieldEdits.js.map +1 -1
  199. package/dist/core/scene/fontReads.js +37 -0
  200. package/dist/core/scene/fontReads.js.map +1 -0
  201. package/dist/core/scene/historyFacade.js +1 -0
  202. package/dist/core/scene/historyFacade.js.map +1 -1
  203. package/dist/core/scene/inheritedPaint.js +75 -0
  204. package/dist/core/scene/inheritedPaint.js.map +1 -0
  205. package/dist/core/scene/kitRegistry.js +5 -0
  206. package/dist/core/scene/kitRegistry.js.map +1 -1
  207. package/dist/core/scene/layoutPass.js +28 -3
  208. package/dist/core/scene/layoutPass.js.map +1 -1
  209. package/dist/core/scene/nodeMemo.js +15 -7
  210. package/dist/core/scene/nodeMemo.js.map +1 -1
  211. package/dist/core/scene/pathEffects/blend.js.map +1 -1
  212. package/dist/core/scene/pathEffects/derive.js +7 -2
  213. package/dist/core/scene/pathEffects/derive.js.map +1 -1
  214. package/dist/core/scene/pathEffects/effects.js +3 -1
  215. package/dist/core/scene/pathEffects/effects.js.map +1 -1
  216. package/dist/core/scene/pathEffects/textAlong.js +95 -0
  217. package/dist/core/scene/pathEffects/textAlong.js.map +1 -0
  218. package/dist/core/scene/recorder.js +28 -19
  219. package/dist/core/scene/recorder.js.map +1 -1
  220. package/dist/core/scene/scene.js +15 -9
  221. package/dist/core/scene/scene.js.map +1 -1
  222. package/dist/core/scene/sceneChanges.js +13 -0
  223. package/dist/core/scene/sceneChanges.js.map +1 -0
  224. package/dist/core/scene/sceneEdits.js +8 -1
  225. package/dist/core/scene/sceneEdits.js.map +1 -1
  226. package/dist/core/scene/sceneMutators.js +1 -0
  227. package/dist/core/scene/sceneMutators.js.map +1 -1
  228. package/dist/core/scene/sceneRecording.js +20 -3
  229. package/dist/core/scene/sceneRecording.js.map +1 -1
  230. package/dist/core/scene/sceneSelection.js +4 -1
  231. package/dist/core/scene/sceneSelection.js.map +1 -1
  232. package/dist/core/scene/sceneState.js +11 -5
  233. package/dist/core/scene/sceneState.js.map +1 -1
  234. package/dist/core/scene/serialization.js +5 -3
  235. package/dist/core/scene/serialization.js.map +1 -1
  236. package/dist/core/scene/types.js.map +1 -1
  237. package/dist/core/scene/unitScopes.js +19 -1
  238. package/dist/core/scene/unitScopes.js.map +1 -1
  239. package/dist/core/selection/usePublishSceneSelection.js +6 -12
  240. package/dist/core/selection/usePublishSceneSelection.js.map +1 -1
  241. package/dist/core/shapeKinds.js +25 -11
  242. package/dist/core/shapeKinds.js.map +1 -1
  243. package/dist/{cornerHandles-Camw_yeu.d.ts → cornerHandles-p8fDIAKt.d.ts} +3 -2
  244. package/dist/debug/createDebugOverlayLayer.js +16 -111
  245. package/dist/debug/createDebugOverlayLayer.js.map +1 -1
  246. package/dist/debug/createDebugSink.js +2 -1
  247. package/dist/debug/createDebugSink.js.map +1 -1
  248. package/dist/debug/defaultTheme.js +4 -2
  249. package/dist/debug/defaultTheme.js.map +1 -1
  250. package/dist/debug/features.js +17 -0
  251. package/dist/debug/features.js.map +1 -0
  252. package/dist/debug/overlayPanels.js +117 -0
  253. package/dist/debug/overlayPanels.js.map +1 -0
  254. package/dist/debug/parseDebugFlags.js +4 -13
  255. package/dist/debug/parseDebugFlags.js.map +1 -1
  256. package/dist/{depSchema-Cgfu_G9s.d.ts → depSchema-mG19aEdD.d.ts} +174 -904
  257. package/dist/devtools/handlers.js +112 -0
  258. package/dist/devtools/handlers.js.map +1 -0
  259. package/dist/devtools/history.js +71 -0
  260. package/dist/devtools/history.js.map +1 -0
  261. package/dist/devtools/hook.js +74 -0
  262. package/dist/devtools/hook.js.map +1 -0
  263. package/dist/devtools/label.js +25 -0
  264. package/dist/devtools/label.js.map +1 -0
  265. package/dist/devtools/notices.js +58 -0
  266. package/dist/devtools/notices.js.map +1 -0
  267. package/dist/devtools/protocol.js +7 -0
  268. package/dist/devtools/protocol.js.map +1 -0
  269. package/dist/devtools/registry.js +90 -0
  270. package/dist/devtools/registry.js.map +1 -0
  271. package/dist/devtools/root.js +80 -0
  272. package/dist/devtools/root.js.map +1 -0
  273. package/dist/devtools/serialize.js +86 -0
  274. package/dist/devtools/serialize.js.map +1 -0
  275. package/dist/devtools/session.js +111 -0
  276. package/dist/devtools/session.js.map +1 -0
  277. package/dist/devtools/transport.js +67 -0
  278. package/dist/devtools/transport.js.map +1 -0
  279. package/dist/devtools/useDevtoolsRoot.js +81 -0
  280. package/dist/devtools/useDevtoolsRoot.js.map +1 -0
  281. package/dist/devtools-protocol.d.ts +435 -0
  282. package/dist/devtools-protocol.js +7 -0
  283. package/dist/devtools.d.ts +8 -0
  284. package/dist/devtools.js +12 -0
  285. package/dist/devtools.js.map +1 -0
  286. package/dist/features/chrome-caps/useHoverTracking.js +16 -10
  287. package/dist/features/chrome-caps/useHoverTracking.js.map +1 -1
  288. package/dist/features/ingestion/contentHandlers.js +25 -2
  289. package/dist/features/ingestion/contentHandlers.js.map +1 -1
  290. package/dist/features/measure/measureCommands.js +7 -5
  291. package/dist/features/measure/measureCommands.js.map +1 -1
  292. package/dist/features/measure/measureContribution.js +4 -2
  293. package/dist/features/measure/measureContribution.js.map +1 -1
  294. package/dist/features/modes/modeTriggers.js +7 -3
  295. package/dist/features/modes/modeTriggers.js.map +1 -1
  296. package/dist/features/pathEffects/effectSources.js +60 -0
  297. package/dist/features/pathEffects/effectSources.js.map +1 -0
  298. package/dist/features/pathEffects/readSource.js +17 -1
  299. package/dist/features/pathEffects/readSource.js.map +1 -1
  300. package/dist/features/pathEffects/textItems.js +53 -0
  301. package/dist/features/pathEffects/textItems.js.map +1 -0
  302. package/dist/features/pathEffects/useEffectSourcesLayer.js +57 -0
  303. package/dist/features/pathEffects/useEffectSourcesLayer.js.map +1 -0
  304. package/dist/features/paths/index.js +2 -2
  305. package/dist/features/paths/pathEditExtension.js.map +1 -1
  306. package/dist/features/paths/pathEditState.js +5 -2
  307. package/dist/features/paths/pathEditState.js.map +1 -1
  308. package/dist/features/paths/pathSnap/pathSnapSource.js +5 -5
  309. package/dist/features/paths/pathSnap/pathSnapSource.js.map +1 -1
  310. package/dist/features/paths/pathSnap/sceneIndex.js +10 -3
  311. package/dist/features/paths/pathSnap/sceneIndex.js.map +1 -1
  312. package/dist/features/selection/selectionPose.js +4 -1
  313. package/dist/features/selection/selectionPose.js.map +1 -1
  314. package/dist/features/snap/gather.js +9 -1
  315. package/dist/features/snap/gather.js.map +1 -1
  316. package/dist/features/snap/lazySession.js +17 -0
  317. package/dist/features/snap/lazySession.js.map +1 -0
  318. package/dist/features/snap/marks.js.map +1 -1
  319. package/dist/features/snap/probes2d.js +27 -19
  320. package/dist/features/snap/probes2d.js.map +1 -1
  321. package/dist/features/snap/readoutLayer.js.map +1 -1
  322. package/dist/features/snap/roles.js +2 -1
  323. package/dist/features/snap/roles.js.map +1 -1
  324. package/dist/features/snap/rotationSource.js +25 -8
  325. package/dist/features/snap/rotationSource.js.map +1 -1
  326. package/dist/features/snap/sceneSession2d.js +6 -4
  327. package/dist/features/snap/sceneSession2d.js.map +1 -1
  328. package/dist/features/snap/session2d.js +37 -23
  329. package/dist/features/snap/session2d.js.map +1 -1
  330. package/dist/features/snap/spanSnap.js +5 -2
  331. package/dist/features/snap/spanSnap.js.map +1 -1
  332. package/dist/features/text/sceneTextDrafts.js +65 -0
  333. package/dist/features/text/sceneTextDrafts.js.map +1 -0
  334. package/dist/features/text/textHitAt.js +31 -2
  335. package/dist/features/text/textHitAt.js.map +1 -1
  336. package/dist/features/text/useSceneTextEdit.js +22 -18
  337. package/dist/features/text/useSceneTextEdit.js.map +1 -1
  338. package/dist/features/viewports/aim.js +105 -0
  339. package/dist/features/viewports/aim.js.map +1 -0
  340. package/dist/features/viewports/viewMarker.js +82 -0
  341. package/dist/features/viewports/viewMarker.js.map +1 -0
  342. package/dist/features/viewports/viewMarkerDrag.js +55 -0
  343. package/dist/features/viewports/viewMarkerDrag.js.map +1 -0
  344. package/dist/icons.table.js +1 -1
  345. package/dist/icons.table.js.map +1 -1
  346. package/dist/icons.table.json +0 -10
  347. package/dist/index.d.ts +1198 -894
  348. package/dist/index.js +55 -38
  349. package/dist/interactions/actions/align/action.js +6 -4
  350. package/dist/interactions/actions/align/action.js.map +1 -1
  351. package/dist/interactions/actions/align/align.js.map +1 -1
  352. package/dist/interactions/actions/booleans/action.js +7 -1
  353. package/dist/interactions/actions/booleans/action.js.map +1 -1
  354. package/dist/interactions/actions/booleans/booleans.js +2 -1
  355. package/dist/interactions/actions/booleans/booleans.js.map +1 -1
  356. package/dist/interactions/actions/booleans/operands.js +6 -5
  357. package/dist/interactions/actions/booleans/operands.js.map +1 -1
  358. package/dist/interactions/actions/clipboard/action.js +27 -17
  359. package/dist/interactions/actions/clipboard/action.js.map +1 -1
  360. package/dist/interactions/actions/clone/action.js +1 -1
  361. package/dist/interactions/actions/clone/behaviors.js +4 -4
  362. package/dist/interactions/actions/clone/behaviors.js.map +1 -1
  363. package/dist/interactions/actions/defaults/anchorEditing.js +44 -23
  364. package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
  365. package/dist/interactions/actions/defaults/anchorJoin.js +12 -6
  366. package/dist/interactions/actions/defaults/anchorJoin.js.map +1 -1
  367. package/dist/interactions/actions/defaults/bendSegment.js +8 -5
  368. package/dist/interactions/actions/defaults/bendSegment.js.map +1 -1
  369. package/dist/interactions/actions/defaults/cancelGesture.js +11 -8
  370. package/dist/interactions/actions/defaults/cancelGesture.js.map +1 -1
  371. package/dist/interactions/actions/defaults/contextMenu.js +17 -8
  372. package/dist/interactions/actions/defaults/contextMenu.js.map +1 -1
  373. package/dist/interactions/actions/defaults/createPaintAction.js +2 -2
  374. package/dist/interactions/actions/defaults/createPaintAction.js.map +1 -1
  375. package/dist/interactions/actions/defaults/delete.js +64 -41
  376. package/dist/interactions/actions/defaults/delete.js.map +1 -1
  377. package/dist/interactions/actions/defaults/duplicate.js +12 -9
  378. package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
  379. package/dist/interactions/actions/defaults/enterPathEdit.js +19 -9
  380. package/dist/interactions/actions/defaults/enterPathEdit.js.map +1 -1
  381. package/dist/interactions/actions/defaults/escape.js +11 -8
  382. package/dist/interactions/actions/defaults/escape.js.map +1 -1
  383. package/dist/interactions/actions/defaults/exitPathEdit.js +7 -4
  384. package/dist/interactions/actions/defaults/exitPathEdit.js.map +1 -1
  385. package/dist/interactions/actions/defaults/group.js +21 -15
  386. package/dist/interactions/actions/defaults/group.js.map +1 -1
  387. package/dist/interactions/actions/defaults/ingest.js +17 -9
  388. package/dist/interactions/actions/defaults/ingest.js.map +1 -1
  389. package/dist/interactions/actions/defaults/insertPathAnchor.js +7 -4
  390. package/dist/interactions/actions/defaults/insertPathAnchor.js.map +1 -1
  391. package/dist/interactions/actions/defaults/nudge.js +21 -40
  392. package/dist/interactions/actions/defaults/nudge.js.map +1 -1
  393. package/dist/interactions/actions/defaults/pathEditTools.js +11 -8
  394. package/dist/interactions/actions/defaults/pathEditTools.js.map +1 -1
  395. package/dist/interactions/actions/defaults/pinchZoom.js +7 -4
  396. package/dist/interactions/actions/defaults/pinchZoom.js.map +1 -1
  397. package/dist/interactions/actions/defaults/reorder.js +24 -6
  398. package/dist/interactions/actions/defaults/reorder.js.map +1 -1
  399. package/dist/interactions/actions/defaults/selectAll.js +8 -5
  400. package/dist/interactions/actions/defaults/selectAll.js.map +1 -1
  401. package/dist/interactions/actions/defaults/setFill.js +7 -3
  402. package/dist/interactions/actions/defaults/setFill.js.map +1 -1
  403. package/dist/interactions/actions/defaults/setFillOpacity.js +1 -1
  404. package/dist/interactions/actions/defaults/setStroke.js +12 -11
  405. package/dist/interactions/actions/defaults/setStroke.js.map +1 -1
  406. package/dist/interactions/actions/defaults/setStrokeOpacity.js +1 -1
  407. package/dist/interactions/actions/defaults/toolActivate.js +7 -1
  408. package/dist/interactions/actions/defaults/toolActivate.js.map +1 -1
  409. package/dist/interactions/actions/defaults/toolResetToDefault.js +7 -4
  410. package/dist/interactions/actions/defaults/toolResetToDefault.js.map +1 -1
  411. package/dist/interactions/actions/defaults/undoRedo.js +19 -13
  412. package/dist/interactions/actions/defaults/undoRedo.js.map +1 -1
  413. package/dist/interactions/actions/defaults/viewportWheelPan.js +16 -7
  414. package/dist/interactions/actions/defaults/viewportWheelPan.js.map +1 -1
  415. package/dist/interactions/actions/defaults/viewportZoom.js +24 -6
  416. package/dist/interactions/actions/defaults/viewportZoom.js.map +1 -1
  417. package/dist/interactions/actions/distribute/action.js +6 -4
  418. package/dist/interactions/actions/distribute/action.js.map +1 -1
  419. package/dist/interactions/actions/drawnBoxes.js +2 -1
  420. package/dist/interactions/actions/drawnBoxes.js.map +1 -1
  421. package/dist/interactions/actions/edit-anchors/action.js +10 -10
  422. package/dist/interactions/actions/edit-anchors/action.js.map +1 -1
  423. package/dist/interactions/actions/edit-anchors/preview.js +41 -8
  424. package/dist/interactions/actions/edit-anchors/preview.js.map +1 -1
  425. package/dist/interactions/actions/flip/action.js +14 -6
  426. package/dist/interactions/actions/flip/action.js.map +1 -1
  427. package/dist/interactions/actions/gestureLifecycle.js +5 -2
  428. package/dist/interactions/actions/gestureLifecycle.js.map +1 -1
  429. package/dist/interactions/actions/insert/action.js +1 -2
  430. package/dist/interactions/actions/insert/action.js.map +1 -1
  431. package/dist/interactions/actions/insert/commit.js +1 -1
  432. package/dist/interactions/actions/insert/parent.js +11 -0
  433. package/dist/interactions/actions/insert/parent.js.map +1 -0
  434. package/dist/interactions/actions/insert/placeAction.js +7 -2
  435. package/dist/interactions/actions/insert/placeAction.js.map +1 -1
  436. package/dist/interactions/actions/insert/repositionAction.js +2 -1
  437. package/dist/interactions/actions/insert/repositionAction.js.map +1 -1
  438. package/dist/interactions/actions/insert/session.js +5 -1
  439. package/dist/interactions/actions/insert/session.js.map +1 -1
  440. package/dist/interactions/actions/move/action.js +15 -8
  441. package/dist/interactions/actions/move/action.js.map +1 -1
  442. package/dist/interactions/actions/move/behaviors/layoutDrop.js +0 -1
  443. package/dist/interactions/actions/move/behaviors/layoutDrop.js.map +1 -1
  444. package/dist/interactions/actions/move/behaviors/snapToContainer.js +1 -1
  445. package/dist/interactions/actions/move/capture.js +1 -1
  446. package/dist/interactions/actions/move/commit/index.js +0 -2
  447. package/dist/interactions/actions/move/commit/index.js.map +1 -1
  448. package/dist/interactions/actions/move/commit/layoutDrop.js +2 -2
  449. package/dist/interactions/actions/move/commit/layoutRelease.js +2 -2
  450. package/dist/interactions/actions/move/commit/reparent.js +11 -5
  451. package/dist/interactions/actions/move/commit/reparent.js.map +1 -1
  452. package/dist/interactions/actions/move/frame.js +4 -10
  453. package/dist/interactions/actions/move/frame.js.map +1 -1
  454. package/dist/interactions/actions/move/landedDescendants.js +20 -17
  455. package/dist/interactions/actions/move/landedDescendants.js.map +1 -1
  456. package/dist/interactions/actions/move/layout/container.js +2 -1
  457. package/dist/interactions/actions/move/layout/container.js.map +1 -1
  458. package/dist/interactions/actions/move/layout/dragged.js +1 -1
  459. package/dist/interactions/actions/move/layout/dropPass.js +18 -1
  460. package/dist/interactions/actions/move/layout/dropPass.js.map +1 -1
  461. package/dist/interactions/actions/move/layout/exits.js +5 -5
  462. package/dist/interactions/actions/move/layout/exits.js.map +1 -1
  463. package/dist/interactions/actions/move/session.js +14 -3
  464. package/dist/interactions/actions/move/session.js.map +1 -1
  465. package/dist/interactions/actions/move/translate.js +8 -37
  466. package/dist/interactions/actions/move/translate.js.map +1 -1
  467. package/dist/interactions/actions/moveNodes.js +87 -0
  468. package/dist/interactions/actions/moveNodes.js.map +1 -0
  469. package/dist/interactions/actions/outlines/createOutlinesAction.js +8 -5
  470. package/dist/interactions/actions/outlines/createOutlinesAction.js.map +1 -1
  471. package/dist/interactions/actions/outlines/outlineStroke.js +5 -3
  472. package/dist/interactions/actions/outlines/outlineStroke.js.map +1 -1
  473. package/dist/interactions/actions/pathCommands/cutJoin.js +8 -5
  474. package/dist/interactions/actions/pathCommands/cutJoin.js.map +1 -1
  475. package/dist/interactions/actions/planeInput.js.map +1 -1
  476. package/dist/interactions/actions/resize/action.js +83 -72
  477. package/dist/interactions/actions/resize/action.js.map +1 -1
  478. package/dist/interactions/actions/resize/commit.js +2 -1
  479. package/dist/interactions/actions/resize/commit.js.map +1 -1
  480. package/dist/interactions/actions/resize/cornerHandles.js +2 -1
  481. package/dist/interactions/actions/resize/cornerHandles.js.map +1 -1
  482. package/dist/interactions/actions/resize/session.js +2 -0
  483. package/dist/interactions/actions/resize/session.js.map +1 -1
  484. package/dist/interactions/actions/resolvePreviews.js +3 -3
  485. package/dist/interactions/actions/resolvePreviews.js.map +1 -1
  486. package/dist/interactions/actions/rotate/action.js +7 -4
  487. package/dist/interactions/actions/rotate/action.js.map +1 -1
  488. package/dist/interactions/actions/rotate/commit.js +2 -1
  489. package/dist/interactions/actions/rotate/commit.js.map +1 -1
  490. package/dist/interactions/actions/rotate/session.js +2 -0
  491. package/dist/interactions/actions/rotate/session.js.map +1 -1
  492. package/dist/interactions/actions/selectionSubtree.js +25 -9
  493. package/dist/interactions/actions/selectionSubtree.js.map +1 -1
  494. package/dist/interactions/actions/testUtils.js.map +1 -1
  495. package/dist/interactions/actions/transform-again/action.js +13 -11
  496. package/dist/interactions/actions/transform-again/action.js.map +1 -1
  497. package/dist/interactions/actions/transformFinish.js +106 -0
  498. package/dist/interactions/actions/transformFinish.js.map +1 -0
  499. package/dist/json.d.ts +4 -0
  500. package/dist/json.js +3 -0
  501. package/dist/kitSettingsSchema.js +8 -5
  502. package/dist/kitSettingsSchema.js.map +1 -1
  503. package/dist/{math-CrJULaZI.d.ts → math-B56zxVs5.d.ts} +10 -1
  504. package/dist/math.d.ts +1 -1
  505. package/dist/math.js +2 -1
  506. package/dist/move.d.ts +5 -4
  507. package/dist/{options-p9GZMXbY.d.ts → options-DBxEryYq.d.ts} +1 -1
  508. package/dist/resize.d.ts +4 -3
  509. package/dist/routing.d.ts +1 -1
  510. package/dist/routing.js +2 -2
  511. package/dist/test-hook/createTestHook.js +1 -1
  512. package/dist/test-hook/createTestHook.js.map +1 -1
  513. package/dist/test-hook/useTestHook.js +2 -1
  514. package/dist/test-hook/useTestHook.js.map +1 -1
  515. package/dist/test-seams.d.ts +9 -5
  516. package/dist/tools/builtin/ellipse/useEllipseTool.js +1 -0
  517. package/dist/tools/builtin/ellipse/useEllipseTool.js.map +1 -1
  518. package/dist/tools/builtin/eyedropper/useEyedropperTool.js +2 -1
  519. package/dist/tools/builtin/eyedropper/useEyedropperTool.js.map +1 -1
  520. package/dist/tools/builtin/hand/dragPanContribution.js +4 -1
  521. package/dist/tools/builtin/hand/dragPanContribution.js.map +1 -1
  522. package/dist/tools/builtin/hand/useHandTool.js +7 -4
  523. package/dist/tools/builtin/hand/useHandTool.js.map +1 -1
  524. package/dist/tools/builtin/image/useImageTool.js +1 -0
  525. package/dist/tools/builtin/image/useImageTool.js.map +1 -1
  526. package/dist/tools/builtin/lasso/useLassoTool.js +1 -0
  527. package/dist/tools/builtin/lasso/useLassoTool.js.map +1 -1
  528. package/dist/tools/builtin/line/useLineTool.js +1 -0
  529. package/dist/tools/builtin/line/useLineTool.js.map +1 -1
  530. package/dist/tools/builtin/measure/useMeasureTool.js +12 -5
  531. package/dist/tools/builtin/measure/useMeasureTool.js.map +1 -1
  532. package/dist/tools/builtin/pen/penHandle.js +3 -5
  533. package/dist/tools/builtin/pen/penHandle.js.map +1 -1
  534. package/dist/tools/builtin/pen/usePenTool.js +14 -6
  535. package/dist/tools/builtin/pen/usePenTool.js.map +1 -1
  536. package/dist/tools/builtin/pencil/usePencilTool.js +1 -0
  537. package/dist/tools/builtin/pencil/usePencilTool.js.map +1 -1
  538. package/dist/tools/builtin/polygon/usePolygonTool.js +13 -4
  539. package/dist/tools/builtin/polygon/usePolygonTool.js.map +1 -1
  540. package/dist/tools/builtin/rect/useRectTool.js +1 -0
  541. package/dist/tools/builtin/rect/useRectTool.js.map +1 -1
  542. package/dist/tools/builtin/select/adapterPointQuery.js +3 -2
  543. package/dist/tools/builtin/select/adapterPointQuery.js.map +1 -1
  544. package/dist/tools/builtin/select/pressDecision.js +32 -0
  545. package/dist/tools/builtin/select/pressDecision.js.map +1 -0
  546. package/dist/tools/builtin/select/selectionContributions.js +22 -13
  547. package/dist/tools/builtin/select/selectionContributions.js.map +1 -1
  548. package/dist/tools/builtin/select/useSelectTool.js +13 -10
  549. package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
  550. package/dist/tools/builtin/slice/useSliceTool.js +22 -8
  551. package/dist/tools/builtin/slice/useSliceTool.js.map +1 -1
  552. package/dist/tools/builtin/star/useStarTool.js +13 -4
  553. package/dist/tools/builtin/star/useStarTool.js.map +1 -1
  554. package/dist/tools/builtin/sticky/stickyNode.js +4 -1
  555. package/dist/tools/builtin/sticky/stickyNode.js.map +1 -1
  556. package/dist/tools/builtin/sticky/useStickyTool.js +10 -5
  557. package/dist/tools/builtin/sticky/useStickyTool.js.map +1 -1
  558. package/dist/tools/builtin/text/useTextTool.js +3 -1
  559. package/dist/tools/builtin/text/useTextTool.js.map +1 -1
  560. package/dist/tools/index.js +26 -0
  561. package/dist/{types-CHVXGfYm.d.ts → types-7puAuVD2.d.ts} +3 -262
  562. package/dist/types-BN9hbdMI.d.ts +184 -0
  563. package/dist/types-CniVctmk.d.ts +949 -0
  564. package/dist/types-oFeQ5VjV.d.ts +264 -0
  565. package/dist/util/paint.js +1 -3
  566. package/dist/util/paint.js.map +1 -1
  567. package/dist/version.js +1 -1
  568. package/package.json +39 -25
  569. package/dist/canvas/SceneCanvas/useBuiltinRigTools.js +0 -65
  570. package/dist/canvas/SceneCanvas/useBuiltinRigTools.js.map +0 -1
  571. package/dist/tools/builtin/bone/boneActions.js +0 -156
  572. package/dist/tools/builtin/bone/boneActions.js.map +0 -1
  573. package/dist/tools/builtin/bone/boneOverlay.js +0 -71
  574. package/dist/tools/builtin/bone/boneOverlay.js.map +0 -1
  575. package/dist/tools/builtin/bone/placedRig.js +0 -28
  576. package/dist/tools/builtin/bone/placedRig.js.map +0 -1
  577. package/dist/tools/builtin/bone/rigEditState.js +0 -30
  578. package/dist/tools/builtin/bone/rigEditState.js.map +0 -1
  579. package/dist/tools/builtin/bone/useBoneTool.js +0 -125
  580. package/dist/tools/builtin/bone/useBoneTool.js.map +0 -1
  581. package/dist/tools/builtin/weight/useWeightTool.js +0 -101
  582. package/dist/tools/builtin/weight/useWeightTool.js.map +0 -1
  583. package/dist/tools/builtin/weight/weightActions.js +0 -140
  584. package/dist/tools/builtin/weight/weightActions.js.map +0 -1
  585. package/dist/tools/builtin/weight/weightOverlay.js +0 -78
  586. package/dist/tools/builtin/weight/weightOverlay.js.map +0 -1
@@ -1,5 +1,6 @@
1
1
  import { scenePoseFrame } from "../poseFrame.js";
2
2
  import { poseDescriptorOf } from "../poseDescriptorDep.js";
3
+ import { transformFinisher } from "../transformFinish.js";
3
4
  import { translateEach } from "../selectionSubtree.js";
4
5
  import { drawnBoxes } from "../drawnBoxes.js";
5
6
  import { planDistribute } from "./plan.js";
@@ -27,7 +28,7 @@ var ICON_FOR = {
27
28
  * translated in world and each result is stored back in its own parent's
28
29
  * frame.
29
30
  */
30
- function distributeSelection(selection, scene, axis, mode, geom, boxesOver) {
31
+ function distributeSelection(selection, scene, axis, mode, geom, boxesOver, finish) {
31
32
  const ids = selection.get();
32
33
  if (ids.length < 3) return;
33
34
  const frame = scenePoseFrame(scene);
@@ -49,7 +50,7 @@ function distributeSelection(selection, scene, axis, mode, geom, boxesOver) {
49
50
  dy: axis === "y" ? delta : 0
50
51
  };
51
52
  });
52
- scene.batch("Distribute", () => translateEach(scene, frame, geom, moves));
53
+ scene.batch("Distribute", () => translateEach(scene, frame, geom, moves, finish, "Distribute"));
53
54
  }
54
55
  function makeDistributeAction(axis) {
55
56
  return {
@@ -63,7 +64,8 @@ function makeDistributeAction(axis) {
63
64
  "scene",
64
65
  "poseDescriptor",
65
66
  "view",
66
- "boundsIncludeStroke"
67
+ "boundsIncludeStroke",
68
+ "transformFollowers"
67
69
  ],
68
70
  invoker: {
69
71
  timing: "immediate",
@@ -71,7 +73,7 @@ function makeDistributeAction(axis) {
71
73
  const selection = deps.selection;
72
74
  const scene = deps.scene;
73
75
  if (!selection || !scene) return;
74
- distributeSelection(selection, scene, axis, params?.mode ?? "centers", poseDescriptorOf(deps.poseDescriptor), (frame) => drawnBoxes(deps, frame));
76
+ distributeSelection(selection, scene, axis, params?.mode ?? "centers", poseDescriptorOf(deps.poseDescriptor), (frame) => drawnBoxes(deps, frame), transformFinisher(deps));
75
77
  }
76
78
  },
77
79
  enabled: (deps) => {
@@ -1 +1 @@
1
- {"version":3,"file":"action.js","names":[],"sources":["../../../../src/interactions/actions/distribute/action.tsx"],"sourcesContent":["import type { IconRef } from '@weasel-js/icons';\nimport type { Scene } from 'core/scene/types';\nimport type { PoseDescriptor } from '../resize/geometry';\nimport { poseDescriptorOf } from '../poseDescriptorDep';\nimport { drawnBoxes, type DrawnBoxes } from '../drawnBoxes';\nimport type { DistributeAxis, DistributeMode } from './distribute';\nimport { planDistribute } from './plan';\nimport { scenePoseFrame, type PoseFrame } from '../poseFrame';\nimport { translateEach } from '../selectionSubtree';\nimport type { Action } from '@weasel-js/routing';\nimport { ActionDisabledReason } from '@weasel-js/routing';\nimport type { SelectionApi } from 'core/selection/useSelection';\nimport type { ImmediateInvoker } from '@weasel-js/routing';\nimport { icon } from '../../../icons.gen';\n\nconst ID_FOR: Record<DistributeAxis, string> = { x: 'distribute.horizontal', y: 'distribute.vertical' };\nconst LABEL_FOR: Record<DistributeAxis, string> = { x: 'Distribute Horizontally', y: 'Distribute Vertically' };\nconst ICON_FOR: Record<DistributeAxis, IconRef> = {\n x: icon('distributeHorizontal'),\n y: icon('distributeVertical'),\n};\n\n// ---------------------------------------------------------------------------\n// Shared helper for descriptor invokers\n// ---------------------------------------------------------------------------\n\n/**\n * Apply a distribute operation to the current selection via the Scene API.\n * Reads poses through the `poseDescriptor` dep; `mode` comes from the\n * binding's `params.mode`.\n *\n * The span being divided is a world span, so the bounds are read and\n * translated in world and each result is stored back in its own parent's\n * frame.\n */\nfunction distributeSelection(\n selection: SelectionApi,\n scene: Scene<unknown, string, unknown>,\n axis: DistributeAxis,\n mode: DistributeMode,\n geom: PoseDescriptor<unknown>,\n boxesOver: (frame: PoseFrame<unknown>) => DrawnBoxes,\n): void {\n const ids = selection.get();\n if (ids.length < 3) return;\n const frame = scenePoseFrame(scene);\n const boxes = boxesOver(frame);\n\n const items = ids.map((id) => {\n const pose = frame.world(id);\n return { id, pose, b: boxes.aabb(id, pose) };\n });\n const targets = planDistribute(items.map((it) => it.b), axis, mode);\n\n const moves = items.map((it, i) => {\n const delta = targets[i] - (axis === 'x' ? it.b.x : it.b.y);\n return { id: it.id, dx: axis === 'x' ? delta : 0, dy: axis === 'y' ? delta : 0 };\n });\n scene.batch('Distribute', () => translateEach(scene, frame, geom, moves));\n}\n\n// ---------------------------------------------------------------------------\n// Static descriptors\n// ---------------------------------------------------------------------------\n\nfunction makeDistributeAction(axis: DistributeAxis): Action {\n return {\n id: ID_FOR[axis],\n label: LABEL_FOR[axis],\n icon: ICON_FOR[axis],\n group: 'distribute',\n eligible: { capability: 'transforms-selection' },\n requires: ['selection', 'scene', 'poseDescriptor', 'view', 'boundsIncludeStroke'],\n // No default keybindings. Wire bindings explicitly via the actions registry.\n invoker: {\n timing: 'immediate',\n run: (deps, params) => {\n const selection = deps.selection;\n const scene = deps.scene;\n if (!selection || !scene) return;\n const mode = (params?.mode as DistributeMode | undefined) ?? 'centers';\n distributeSelection(\n selection, scene, axis, mode, poseDescriptorOf(deps.poseDescriptor), (frame) => drawnBoxes(deps, frame),\n );\n },\n } satisfies ImmediateInvoker,\n // Deps-aware, matching `distributeSelection`'s own `ids.length < 3` guard:\n // a constant disabled reason greys the entry out forever (see\n // `requiresSelection`).\n enabled: (deps) => {\n const selection = deps?.selection;\n const count = selection?.get().length ?? 0;\n return count >= 3 ? true : ActionDisabledReason.SelectionRequired;\n },\n };\n}\n\n/** Static descriptor for distribute-horizontal. */\nexport const distributeHorizontalAction = makeDistributeAction('x');\n/** Static descriptor for distribute-vertical. */\nexport const distributeVerticalAction = makeDistributeAction('y');\n\n"],"mappings":"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"}
1
+ {"version":3,"file":"action.js","names":[],"sources":["../../../../src/interactions/actions/distribute/action.tsx"],"sourcesContent":["import type { IconRef } from '@weasel-js/icons';\nimport type { Scene } from 'core/scene/types';\nimport type { PoseDescriptor } from '../resize/geometry';\nimport { poseDescriptorOf } from '../poseDescriptorDep';\nimport { drawnBoxes, type DrawnBoxes } from '../drawnBoxes';\nimport type { DistributeAxis, DistributeMode } from './distribute';\nimport { planDistribute } from './plan';\nimport { scenePoseFrame, type PoseFrame } from '../poseFrame';\nimport { translateEach } from '../selectionSubtree';\nimport { transformFinisher, type TransformFinisher } from '../transformFinish';\nimport type { Action } from '@weasel-js/routing';\nimport { ActionDisabledReason } from '@weasel-js/routing';\nimport type { SelectionApi } from 'core/selection/useSelection';\nimport type { ImmediateInvoker } from '@weasel-js/routing';\nimport { icon } from '../../../icons.gen';\n\nconst ID_FOR: Record<DistributeAxis, string> = { x: 'distribute.horizontal', y: 'distribute.vertical' };\nconst LABEL_FOR: Record<DistributeAxis, string> = { x: 'Distribute Horizontally', y: 'Distribute Vertically' };\nconst ICON_FOR: Record<DistributeAxis, IconRef> = {\n x: icon('distributeHorizontal'),\n y: icon('distributeVertical'),\n};\n\n// ---------------------------------------------------------------------------\n// Shared helper for descriptor invokers\n// ---------------------------------------------------------------------------\n\n/**\n * Apply a distribute operation to the current selection via the Scene API.\n * Reads poses through the `poseDescriptor` dep; `mode` comes from the\n * binding's `params.mode`.\n *\n * The span being divided is a world span, so the bounds are read and\n * translated in world and each result is stored back in its own parent's\n * frame.\n */\nfunction distributeSelection(\n selection: SelectionApi,\n scene: Scene<unknown, string, unknown>,\n axis: DistributeAxis,\n mode: DistributeMode,\n geom: PoseDescriptor<unknown>,\n boxesOver: (frame: PoseFrame<unknown>) => DrawnBoxes,\n finish?: TransformFinisher,\n): void {\n const ids = selection.get();\n if (ids.length < 3) return;\n const frame = scenePoseFrame(scene);\n const boxes = boxesOver(frame);\n\n const items = ids.map((id) => {\n const pose = frame.world(id);\n return { id, pose, b: boxes.aabb(id, pose) };\n });\n const targets = planDistribute(items.map((it) => it.b), axis, mode);\n\n const moves = items.map((it, i) => {\n const delta = targets[i] - (axis === 'x' ? it.b.x : it.b.y);\n return { id: it.id, dx: axis === 'x' ? delta : 0, dy: axis === 'y' ? delta : 0 };\n });\n scene.batch('Distribute', () => translateEach(scene, frame, geom, moves, finish, 'Distribute'));\n}\n\n// ---------------------------------------------------------------------------\n// Static descriptors\n// ---------------------------------------------------------------------------\n\nfunction makeDistributeAction(axis: DistributeAxis): Action {\n return {\n id: ID_FOR[axis],\n label: LABEL_FOR[axis],\n icon: ICON_FOR[axis],\n group: 'distribute',\n eligible: { capability: 'transforms-selection' },\n requires: ['selection', 'scene', 'poseDescriptor', 'view', 'boundsIncludeStroke', 'transformFollowers'],\n // No default keybindings. Wire bindings explicitly via the actions registry.\n invoker: {\n timing: 'immediate',\n run: (deps, params) => {\n const selection = deps.selection;\n const scene = deps.scene;\n if (!selection || !scene) return;\n const mode = (params?.mode as DistributeMode | undefined) ?? 'centers';\n distributeSelection(\n selection, scene, axis, mode, poseDescriptorOf(deps.poseDescriptor), (frame) => drawnBoxes(deps, frame),\n transformFinisher(deps),\n );\n },\n } satisfies ImmediateInvoker,\n // Deps-aware, matching `distributeSelection`'s own `ids.length < 3` guard:\n // a constant disabled reason greys the entry out forever (see\n // `requiresSelection`).\n enabled: (deps) => {\n const selection = deps?.selection;\n const count = selection?.get().length ?? 0;\n return count >= 3 ? true : ActionDisabledReason.SelectionRequired;\n },\n };\n}\n\n/** Static descriptor for distribute-horizontal. */\nexport const distributeHorizontalAction = makeDistributeAction('x');\n/** Static descriptor for distribute-vertical. */\nexport const distributeVerticalAction = makeDistributeAction('y');\n\n"],"mappings":"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CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAqB,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AAClD,CAAA,CAAA;AACF,CAAA,CAAA;AACF;;AAGA,CAAA,CAAA,CAAA,CAAa,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAA6B,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAqB,CAAA,CAAA,CAAG,CAAA;;AAElE,CAAA,CAAA,CAAA,CAAa,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAA6B,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAqB,CAAA,CAAA,CAAG,CAAA"}
@@ -1,5 +1,6 @@
1
1
  import { axisAlignedBounds } from "../../core/geometry/unionBounds.js";
2
2
  import { drawnBoundsOf, inkExtentsOf } from "../../canvas/drawnBounds.js";
3
+ import { drawnBoundsScene } from "../../canvas/derivedPath.js";
3
4
  import { scenePoseFrame } from "./poseFrame.js";
4
5
  import { poseDescriptorOf } from "./poseDescriptorDep.js";
5
6
 
@@ -8,7 +9,7 @@ var NO_SCENE = { get: () => void 0 };
8
9
  /** The {@link DrawnBoxes} over `deps.scene`. Pass the action's own `frame`
9
10
  * when it has one, so container members are read the way it reads them. */
10
11
  function drawnBoxes(deps, frame) {
11
- const scene = deps.scene ?? NO_SCENE;
12
+ const scene = deps.scene ? drawnBoundsScene(deps.scene) : NO_SCENE;
12
13
  const descriptor = poseDescriptorOf(deps.poseDescriptor);
13
14
  const view = deps.view?.get();
14
15
  let worlds = frame;
@@ -1 +1 @@
1
- {"version":3,"file":"drawnBoxes.js","names":[],"sources":["../../../src/interactions/actions/drawnBoxes.ts"],"sourcesContent":["import type { ActionDeps } from '@weasel-js/routing';\nimport { drawnBoundsOf, inkExtentsOf, type DrawnBoundsOptions, type DrawnBoundsScene } from 'canvas/drawnBounds';\nimport type { InkExtents } from 'core/geometry/inkExtents';\nimport { axisAlignedBounds } from 'core/geometry/unionBounds';\nimport type { NodeId } from 'core/scene/types';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport { poseDescriptorOf } from './poseDescriptorDep';\nimport { scenePoseFrame, type PoseFrame } from './poseFrame';\n\nconst NO_SCENE: DrawnBoundsScene = { get: () => undefined };\n\n/** `drawnBoundsOf` bound to an action's deps: the box each id is shown with\n * at a world pose, stroke included unless the canvas opted out. */\nexport interface DrawnBoxes {\n /** In the frame the pose is drawn in. */\n framed(id: NodeId | string, pose: unknown): Bounds;\n /** The axis-aligned box a turned node covers. */\n aabb(id: NodeId | string, pose: unknown): Bounds;\n /** How far the ink reaches past the pose box, in the box's own frame. */\n extents(id: NodeId | string, pose: unknown): InkExtents;\n}\n\n/** The {@link DrawnBoxes} over `deps.scene`. Pass the action's own `frame`\n * when it has one, so container members are read the way it reads them. */\nexport function drawnBoxes(\n deps: Pick<ActionDeps, 'scene' | 'poseDescriptor' | 'view' | 'boundsIncludeStroke'>,\n frame?: PoseFrame<unknown>,\n): DrawnBoxes {\n const scene: DrawnBoundsScene = deps.scene ?? NO_SCENE;\n const descriptor = poseDescriptorOf(deps.poseDescriptor);\n const view = deps.view?.get();\n let worlds = frame;\n const opts: DrawnBoundsOptions = {\n descriptor,\n worldPoseOf: (id) => (worlds ??= scenePoseFrame(deps.scene!)).world(id),\n includeStroke: deps.boundsIncludeStroke ?? true,\n ...(view ? { scale: view.scale } : {}),\n };\n const framed = (id: NodeId | string, pose: unknown): Bounds =>\n drawnBoundsOf(scene, id as NodeId, pose, opts);\n return {\n framed,\n aabb: (id, pose) => axisAlignedBounds(framed(id, pose)),\n extents: (id, pose) => inkExtentsOf(scene, id as NodeId, pose, opts),\n };\n}\n"],"mappings":";;;;;;AASA,IAAM,WAA6B,EAAE,WAAW,OAAU;;;AAe1D,SAAgB,WACd,MACA,OACY;CACZ,MAAM,QAA0B,KAAK,SAAS;CAC9C,MAAM,aAAa,iBAAiB,KAAK,cAAc;CACvD,MAAM,OAAO,KAAK,MAAM,IAAI;CAC5B,IAAI,SAAS;CACb,MAAM,OAA2B;EAC/B;EACA,cAAc,QAAQ,WAAW,eAAe,KAAK,KAAM,EAAA,CAAG,MAAM,EAAE;EACtE,eAAe,KAAK,uBAAuB;EAC3C,GAAI,OAAO,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;CACtC;CACA,MAAM,UAAU,IAAqB,SACnC,cAAc,OAAO,IAAc,MAAM,IAAI;CAC/C,OAAO;EACL;EACA,OAAO,IAAI,SAAS,kBAAkB,OAAO,IAAI,IAAI,CAAC;EACtD,UAAU,IAAI,SAAS,aAAa,OAAO,IAAc,MAAM,IAAI;CACrE;AACF"}
1
+ {"version":3,"file":"drawnBoxes.js","names":[],"sources":["../../../src/interactions/actions/drawnBoxes.ts"],"sourcesContent":["import type { ActionDeps } from '@weasel-js/routing';\nimport { drawnBoundsOf, inkExtentsOf, type DrawnBoundsOptions, type DrawnBoundsScene } from 'canvas/drawnBounds';\nimport { drawnBoundsScene } from 'canvas/derivedPath';\nimport type { InkExtents } from 'core/geometry/inkExtents';\nimport { axisAlignedBounds } from 'core/geometry/unionBounds';\nimport type { NodeId } from 'core/scene/types';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport { poseDescriptorOf } from './poseDescriptorDep';\nimport { scenePoseFrame, type PoseFrame } from './poseFrame';\n\nconst NO_SCENE: DrawnBoundsScene = { get: () => undefined };\n\n/** `drawnBoundsOf` bound to an action's deps: the box each id is shown with\n * at a world pose, stroke included unless the canvas opted out. */\nexport interface DrawnBoxes {\n /** In the frame the pose is drawn in. */\n framed(id: NodeId | string, pose: unknown): Bounds;\n /** The axis-aligned box a turned node covers. */\n aabb(id: NodeId | string, pose: unknown): Bounds;\n /** How far the ink reaches past the pose box, in the box's own frame. */\n extents(id: NodeId | string, pose: unknown): InkExtents;\n}\n\n/** The {@link DrawnBoxes} over `deps.scene`. Pass the action's own `frame`\n * when it has one, so container members are read the way it reads them. */\nexport function drawnBoxes(\n deps: Pick<ActionDeps, 'scene' | 'poseDescriptor' | 'view' | 'boundsIncludeStroke'>,\n frame?: PoseFrame<unknown>,\n): DrawnBoxes {\n const scene: DrawnBoundsScene = deps.scene ? drawnBoundsScene(deps.scene) : NO_SCENE;\n const descriptor = poseDescriptorOf(deps.poseDescriptor);\n const view = deps.view?.get();\n let worlds = frame;\n const opts: DrawnBoundsOptions = {\n descriptor,\n worldPoseOf: (id) => (worlds ??= scenePoseFrame(deps.scene!)).world(id),\n includeStroke: deps.boundsIncludeStroke ?? true,\n ...(view ? { scale: view.scale } : {}),\n };\n const framed = (id: NodeId | string, pose: unknown): Bounds =>\n drawnBoundsOf(scene, id as NodeId, pose, opts);\n return {\n framed,\n aabb: (id, pose) => axisAlignedBounds(framed(id, pose)),\n extents: (id, pose) => inkExtentsOf(scene, id as NodeId, pose, opts),\n };\n}\n"],"mappings":";;;;;;;AAUA,IAAM,WAA6B,EAAE,WAAW,OAAU;;;AAe1D,SAAgB,WACd,MACA,OACY;CACZ,MAAM,QAA0B,KAAK,QAAQ,iBAAiB,KAAK,KAAK,IAAI;CAC5E,MAAM,aAAa,iBAAiB,KAAK,cAAc;CACvD,MAAM,OAAO,KAAK,MAAM,IAAI;CAC5B,IAAI,SAAS;CACb,MAAM,OAA2B;EAC/B;EACA,cAAc,QAAQ,WAAW,eAAe,KAAK,KAAM,EAAA,CAAG,MAAM,EAAE;EACtE,eAAe,KAAK,uBAAuB;EAC3C,GAAI,OAAO,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;CACtC;CACA,MAAM,UAAU,IAAqB,SACnC,cAAc,OAAO,IAAc,MAAM,IAAI;CAC/C,OAAO;EACL;EACA,OAAO,IAAI,SAAS,kBAAkB,OAAO,IAAI,IAAI,CAAC;EACtD,UAAU,IAAI,SAAS,aAAa,OAAO,IAAc,MAAM,IAAI;CACrE;AACF"}
@@ -5,7 +5,7 @@ import { inputSession } from "../../../features/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";
8
- import { pathEditPreview } from "./preview.js";
8
+ import { finishPathEditFrame, pathEditPreview } from "./preview.js";
9
9
  import { claimWheel, dragWeights, endExtensionDrags, extensionOverlays, placeHandle, startExtensionDrags, translateWeighted } from "./extensionDrags.js";
10
10
  import { editedPathSymmetries } from "./symmetry.js";
11
11
  import { anchorAt, editAnchorSetTracked, moveHandleSymmetric, moveHandleTo, pathAwayFromAnchors, pathToAnchors, translateAnchorsSymmetric } from "@weasel-js/geom";
@@ -80,10 +80,7 @@ var paramsOf = (opts) => resolveParams(opts?.params) ?? {};
80
80
  function track(scratch, next) {
81
81
  if (!next) return;
82
82
  const { dep, id } = scratch;
83
- const done = dep.finishEdit ? dep.finishEdit(id, next.path, next.origins) : {
84
- path: next.path,
85
- others: []
86
- };
83
+ const done = finishPathEditFrame(dep, id, next.path, next.origins);
87
84
  if (!done) return;
88
85
  scratch.currentPose = done.path;
89
86
  scratch.others = done.others;
@@ -163,10 +160,13 @@ var editAnchorsAction = inPlane({
163
160
  id: "editAnchors",
164
161
  label: "Edit Anchors",
165
162
  tags: pathOperationTags("drag"),
166
- defaultBinding: {
167
- kind: "drag",
168
- target: { kindOf: isAnchorOrControl }
169
- },
163
+ defaultBinding: [{
164
+ spec: {
165
+ kind: "drag",
166
+ target: { kindOf: isAnchorOrControl }
167
+ },
168
+ opts: { id: "editAnchors.drag" }
169
+ }],
170
170
  eligible: { capability: "edits-anchors" },
171
171
  modifierRoles: EDIT_ANCHORS_ROLES,
172
172
  requires: [
@@ -193,7 +193,7 @@ var editAnchorsAction = inPlane({
193
193
  if (anchorInfo.part === "controlIn" && !origin.inHandle) return {};
194
194
  if (anchorInfo.part === "controlOut" && !origin.outHandle) return {};
195
195
  let active = false;
196
- const preview = pathEditPreview(dep, editingId, () => active ? scratch.currentPose : null, () => active ? scratch.others : []);
196
+ const preview = pathEditPreview(dep, editingId, () => active ? { path: scratch.currentPose } : null, () => active ? scratch.others : []);
197
197
  if (!preview) return {};
198
198
  const dragging = draggedByPress(dep, anchorInfo, roleHeld(ctx, "selection.add"));
199
199
  const scratch = {
@@ -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 { 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 = dep.finishEdit ? dep.finishEdit(id, next.path, next.origins) : { path: next.path, others: [] };\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 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 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 ? 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 } 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,17 +1,31 @@
1
1
  import { storeEditablePath } from "../../../features/paths/editablePath.js";
2
2
 
3
3
  //#region src/interactions/actions/edit-anchors/preview.ts
4
+ /** `path`, a frame of an edit of `id`, finished by the dep's `finishEdit` as
5
+ * committing it would be: its path and the other nodes' that move with it.
6
+ * Null when an extension cannot finish it. */
7
+ function finishPathEditFrame(dep, id, path, origins) {
8
+ return dep.finishEdit ? dep.finishEdit(id, path, origins) : {
9
+ path,
10
+ others: []
11
+ };
12
+ }
4
13
  /**
5
14
  * Preview ghosts for a drag that reshapes the path in edit mode. `current`
6
- * answers the world-space polygon to show, or null while there is nothing to
7
- * preview yet; `others`, the world-space paths of other nodes the edit moves
8
- * with it. Null when the node stores its path in neither supported place.
15
+ * answers the frame to show, or null while there is nothing to preview yet.
16
+ * Each frame is finished as committing it would be
17
+ * ({@link finishPathEditFrame}), so the node's constraints and the nodes
18
+ * linked to it show as they will commit; a frame that cannot be finished
19
+ * commits nothing, and shows nothing. A drag that finishes its own frames
20
+ * passes `finished`, the world-space paths of the other nodes its frame
21
+ * moves, and its frames are shown as they are. Null when the node stores its
22
+ * path in neither supported place.
9
23
  *
10
- * The ghost is what committing `current` would store (`storeEditablePath`),
24
+ * The ghost is what committing the frame would store (`storeEditablePath`),
11
25
  * so a path drawn from a form is drawn again from the edited form on every
12
26
  * move.
13
27
  */
14
- function pathEditPreview(dep, id, current, others = () => []) {
28
+ function pathEditPreview(dep, id, current, finished) {
15
29
  const storageKind = dep.getStorageKind(id);
16
30
  if (storageKind !== "pose" && storageKind !== "data") return null;
17
31
  const origin = dep.getNodeShape(id) ?? (storageKind === "pose" ? {
@@ -19,10 +33,26 @@ function pathEditPreview(dep, id, current, others = () => []) {
19
33
  data: void 0
20
34
  } : null);
21
35
  if (!origin) return null;
36
+ let lastFrame = null;
37
+ let lastFinished = null;
38
+ const shown = () => {
39
+ const frame = current();
40
+ if (!frame) return null;
41
+ if (finished) return {
42
+ path: frame.path,
43
+ others: finished()
44
+ };
45
+ if (frame.path !== lastFrame) {
46
+ lastFrame = frame.path;
47
+ lastFinished = finishPathEditFrame(dep, id, frame.path, frame.origins);
48
+ }
49
+ return lastFinished;
50
+ };
51
+ const others = () => shown()?.others ?? [];
22
52
  let lastPath = null;
23
53
  let lastStored = null;
24
54
  const stored = (pid) => {
25
- const path = pid === id ? current() : null;
55
+ const path = pid === id ? shown()?.path : null;
26
56
  if (!path) return null;
27
57
  if (path !== lastPath) {
28
58
  lastPath = path;
@@ -50,7 +80,10 @@ function pathEditPreview(dep, id, current, others = () => []) {
50
80
  };
51
81
  return {
52
82
  storageKind,
53
- previewIds: () => current() ? [id, ...others().map((o) => o.id)] : null,
83
+ previewIds: () => {
84
+ const frame = shown();
85
+ return frame ? [id, ...frame.others.map((o) => o.id)] : null;
86
+ },
54
87
  previewPose: (pid) => (pid === id ? stored(pid)?.pose : storedOther(pid)?.to.pose) ?? null,
55
88
  previewData: (pid) => {
56
89
  if (pid !== id) {
@@ -64,5 +97,5 @@ function pathEditPreview(dep, id, current, others = () => []) {
64
97
  }
65
98
 
66
99
  //#endregion
67
- export { pathEditPreview };
100
+ export { finishPathEditFrame, pathEditPreview };
68
101
  //# sourceMappingURL=preview.js.map
@@ -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 { 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/**\n * Preview ghosts for a drag that reshapes the path in edit mode. `current`\n * answers the world-space polygon to show, or null while there is nothing to\n * preview yet; `others`, the world-space paths of other nodes the edit moves\n * with it. Null when the node stores its path in neither supported place.\n *\n * The ghost is what committing `current` 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: () => PolygonPath | null,\n others: () => 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 // 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 ? current() : 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: () => (current() ? [id, ...others().map((o) => o.id)] : null),\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":";;;;;;;;;;;;;AA2BA,SAAgB,gBACd,KACA,IACA,SACA,eAAmE,CAAC,GAC5C;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,WAA+B;CACnC,IAAI,aAAmC;CACvC,MAAM,UAAU,QAAsC;EACpD,MAAM,OAAO,QAAQ,KAAK,QAAQ,IAAI;EACtC,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,kBAAmB,QAAQ,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,CAAC,KAAK,MAAM,EAAE,EAAE,CAAC,IAAI;EACpE,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 '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"}
@@ -2,11 +2,12 @@ import { unionAABB } from "../../../core/geometry/unionBounds.js";
2
2
  import { isNoInkExtents } from "../../../core/geometry/inkExtents.js";
3
3
  import { scenePoseFrame } from "../poseFrame.js";
4
4
  import { poseDescriptorOf } from "../poseDescriptorDep.js";
5
- import { shapeSources } from "../selectionSubtree.js";
5
+ import { defaultCommitAdapter } from "../defaultCommitAdapter.js";
6
+ import { transformApplyOps } from "../transformFinish.js";
6
7
  import { createTransformOp } from "../../../core/ops/transform.js";
8
+ import { shapeSources } from "../selectionSubtree.js";
7
9
  import { geometryDataOp } from "../geometryProjection.js";
8
10
  import { drawnBoxes } from "../drawnBoxes.js";
9
- import { defaultCommitAdapter } from "../defaultCommitAdapter.js";
10
11
  import { flipPoseAboutBounds, flipPoseViaDescriptor } from "./helpers.js";
11
12
  import { actionReferenceSources, resolveSpatialReference } from "../spatialReference.js";
12
13
  import { requiresSelection } from "../defaults/requiresSelection.js";
@@ -141,14 +142,20 @@ var flipAction = {
141
142
  key: ["h", "H"],
142
143
  mods: { shift: true }
143
144
  },
144
- opts: { params: { axis: "x" } }
145
+ opts: {
146
+ id: "flip.x",
147
+ params: { axis: "x" }
148
+ }
145
149
  }, {
146
150
  spec: {
147
151
  kind: "key",
148
152
  key: ["v", "V"],
149
153
  mods: { shift: true }
150
154
  },
151
- opts: { params: { axis: "y" } }
155
+ opts: {
156
+ id: "flip.y",
157
+ params: { axis: "y" }
158
+ }
152
159
  }],
153
160
  eligible: { capability: "transforms-selection" },
154
161
  requires: [
@@ -159,7 +166,8 @@ var flipAction = {
159
166
  "poseDescriptor",
160
167
  "pointer",
161
168
  "view",
162
- "boundsIncludeStroke"
169
+ "boundsIncludeStroke",
170
+ "transformFollowers"
163
171
  ],
164
172
  invoker: {
165
173
  timing: "immediate",
@@ -168,7 +176,7 @@ var flipAction = {
168
176
  const pivot = params?.pivot ?? "each";
169
177
  const selection = deps.selection;
170
178
  const scene = deps.scene;
171
- const applyOps = deps.applyOps;
179
+ const applyOps = transformApplyOps(deps);
172
180
  const geometryProjection = deps.geometryProjection;
173
181
  if (!selection || !scene) return;
174
182
  flipSelection(selection, scene, axis, pivot, applyOps, geometryProjection, poseDescriptorOf(deps.poseDescriptor), params, deps.pointer, (frame) => drawnBoxes(deps, frame));