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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (488) hide show
  1. package/CHANGELOG.md +275 -0
  2. package/dist/WeaselProvider.js +1 -1
  3. package/dist/affordances/pathAnchors.js +10 -6
  4. package/dist/affordances/pathAnchors.js.map +1 -1
  5. package/dist/animation/poseTarget.js +46 -15
  6. package/dist/animation/poseTarget.js.map +1 -1
  7. package/dist/animation/poseTransition.js +3 -3
  8. package/dist/animation/poseTransition.js.map +1 -1
  9. package/dist/animation/reflow.js +3 -6
  10. package/dist/animation/reflow.js.map +1 -1
  11. package/dist/animation/rig/bindRig.js +2 -2
  12. package/dist/animation/rig/bindRig.js.map +1 -1
  13. package/dist/animation/rig/skinBinding.js +2 -2
  14. package/dist/animation/rig/skinBinding.js.map +1 -1
  15. package/dist/barrel/canvas.js +5 -1
  16. package/dist/barrel/chrome.js +3 -1
  17. package/dist/barrel/paint.js +4 -4
  18. package/dist/barrel/paths.js +9 -4
  19. package/dist/barrel/renderer.js +3 -2
  20. package/dist/barrel/routing.js +4 -4
  21. package/dist/barrel/scene.js +7 -1
  22. package/dist/barrel/text.js +2 -2
  23. package/dist/barrel/viewport.js +6 -2
  24. package/dist/canvas/Canvas.js +2 -1
  25. package/dist/canvas/Canvas.js.map +1 -1
  26. package/dist/canvas/CanvasView.js +23 -34
  27. package/dist/canvas/CanvasView.js.map +1 -1
  28. package/dist/canvas/ModalityHud.js +1 -1
  29. package/dist/canvas/NodeShape.js +5 -1
  30. package/dist/canvas/NodeShape.js.map +1 -1
  31. package/dist/canvas/SceneCanvas/ActionsProviderIfRoot.js +1 -1
  32. package/dist/canvas/SceneCanvas/DepRegistryProviderIfRoot.js +1 -1
  33. package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js +1 -1
  34. package/dist/canvas/SceneCanvas/SceneCanvasInput.js +2 -0
  35. package/dist/canvas/SceneCanvas/SceneCanvasInput.js.map +1 -1
  36. package/dist/canvas/SceneCanvas/SurfaceDeclarations.js +23 -0
  37. package/dist/canvas/SceneCanvas/SurfaceDeclarations.js.map +1 -0
  38. package/dist/canvas/SceneCanvas/canvasPassThrough.js +2 -1
  39. package/dist/canvas/SceneCanvas/canvasPassThrough.js.map +1 -1
  40. package/dist/canvas/SceneCanvas/defaultNodeRouting.js +2 -2
  41. package/dist/canvas/SceneCanvas/defaultNodeRouting.js.map +1 -1
  42. package/dist/canvas/SceneCanvas/props.js.map +1 -1
  43. package/dist/canvas/SceneCanvas/scenePointQuery.js +27 -5
  44. package/dist/canvas/SceneCanvas/scenePointQuery.js.map +1 -1
  45. package/dist/canvas/SceneCanvas/useActionTunings.js +6 -1
  46. package/dist/canvas/SceneCanvas/useActionTunings.js.map +1 -1
  47. package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js +1 -0
  48. package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js.map +1 -1
  49. package/dist/canvas/SceneCanvas/useCanvasTools.js +10 -2
  50. package/dist/canvas/SceneCanvas/useCanvasTools.js.map +1 -1
  51. package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js +25 -19
  52. package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js.map +1 -1
  53. package/dist/canvas/SceneCanvas/useSceneCanvasHandle.js +9 -3
  54. package/dist/canvas/SceneCanvas/useSceneCanvasHandle.js.map +1 -1
  55. package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js +17 -2
  56. package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js.map +1 -1
  57. package/dist/canvas/SceneCanvas/useSceneCanvasTools.js +2 -0
  58. package/dist/canvas/SceneCanvas/useSceneCanvasTools.js.map +1 -1
  59. package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js +19 -4
  60. package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js.map +1 -1
  61. package/dist/canvas/SceneCanvas/useScenePicking.js +31 -6
  62. package/dist/canvas/SceneCanvas/useScenePicking.js.map +1 -1
  63. package/dist/canvas/SceneCanvas/useSceneSelectTool.js +4 -5
  64. package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
  65. package/dist/canvas/SceneCanvas/useSceneStandardActions.js +7 -4
  66. package/dist/canvas/SceneCanvas/useSceneStandardActions.js.map +1 -1
  67. package/dist/canvas/SceneCanvas.js +30 -12
  68. package/dist/canvas/SceneCanvas.js.map +1 -1
  69. package/dist/canvas/ViewMarker.js +35 -0
  70. package/dist/canvas/ViewMarker.js.map +1 -0
  71. package/dist/canvas/accentColor.js +26 -3
  72. package/dist/canvas/accentColor.js.map +1 -1
  73. package/dist/canvas/buildSceneTree.js +14 -20
  74. package/dist/canvas/buildSceneTree.js.map +1 -1
  75. package/dist/canvas/copiesAbove.js +1 -1
  76. package/dist/canvas/copiesAbove.js.map +1 -1
  77. package/dist/canvas/defaultDrawOne.js +11 -6
  78. package/dist/canvas/defaultDrawOne.js.map +1 -1
  79. package/dist/canvas/deps/areaSelect.js +5 -2
  80. package/dist/canvas/deps/areaSelect.js.map +1 -1
  81. package/dist/canvas/deps/booleans.js +3 -2
  82. package/dist/canvas/deps/booleans.js.map +1 -1
  83. package/dist/canvas/deps/enteredGroup.js +81 -0
  84. package/dist/canvas/deps/enteredGroup.js.map +1 -0
  85. package/dist/canvas/deps/hitTestArea.js +28 -16
  86. package/dist/canvas/deps/hitTestArea.js.map +1 -1
  87. package/dist/canvas/deps/insert.js +10 -38
  88. package/dist/canvas/deps/insert.js.map +1 -1
  89. package/dist/canvas/deps/insertSite.js +39 -0
  90. package/dist/canvas/deps/insertSite.js.map +1 -0
  91. package/dist/canvas/deps/lassoSelect.js +6 -3
  92. package/dist/canvas/deps/lassoSelect.js.map +1 -1
  93. package/dist/canvas/deps/pathEditCommits.js +3 -7
  94. package/dist/canvas/deps/pathEditCommits.js.map +1 -1
  95. package/dist/canvas/deps/pathLeaf.js +1 -1
  96. package/dist/canvas/deps/pathLeaf.js.map +1 -1
  97. package/dist/canvas/deps/slice.js +2 -1
  98. package/dist/canvas/deps/slice.js.map +1 -1
  99. package/dist/canvas/derivedPath.js +14 -6
  100. package/dist/canvas/derivedPath.js.map +1 -1
  101. package/dist/canvas/doubleClickTarget.js +9 -11
  102. package/dist/canvas/doubleClickTarget.js.map +1 -1
  103. package/dist/canvas/drawnBounds.js +26 -9
  104. package/dist/canvas/drawnBounds.js.map +1 -1
  105. package/dist/canvas/inheritedPaints.js +55 -0
  106. package/dist/canvas/inheritedPaints.js.map +1 -0
  107. package/dist/canvas/nodeShapes/derivedPainter.js +15 -16
  108. package/dist/canvas/nodeShapes/derivedPainter.js.map +1 -1
  109. package/dist/canvas/nodeShapes/nodePaint.js +7 -34
  110. package/dist/canvas/nodeShapes/nodePaint.js.map +1 -1
  111. package/dist/canvas/nodeShapes/pathPainter.js +7 -7
  112. package/dist/canvas/nodeShapes/pathPainter.js.map +1 -1
  113. package/dist/canvas/nodeShapes/rectFallbackPainter.js +4 -5
  114. package/dist/canvas/nodeShapes/rectFallbackPainter.js.map +1 -1
  115. package/dist/canvas/nodeShapes/shapePainter.js +9 -9
  116. package/dist/canvas/nodeShapes/shapePainter.js.map +1 -1
  117. package/dist/canvas/nodeShapes/textPainter.js +13 -12
  118. package/dist/canvas/nodeShapes/textPainter.js.map +1 -1
  119. package/dist/canvas/ownClip.js +24 -0
  120. package/dist/canvas/ownClip.js.map +1 -0
  121. package/dist/canvas/paintCull.js.map +1 -1
  122. package/dist/canvas/pickIndex/nodeBroadBox.js +86 -0
  123. package/dist/canvas/pickIndex/nodeBroadBox.js.map +1 -0
  124. package/dist/canvas/pickIndex/scenePickIndex.js +208 -0
  125. package/dist/canvas/pickIndex/scenePickIndex.js.map +1 -0
  126. package/dist/canvas/pickReach.js +3 -3
  127. package/dist/canvas/pickReach.js.map +1 -1
  128. package/dist/canvas/pickWalk.js +17 -91
  129. package/dist/canvas/pickWalk.js.map +1 -1
  130. package/dist/canvas/poseCascade.js +1 -1
  131. package/dist/canvas/poseCascade.js.map +1 -1
  132. package/dist/canvas/publishPreviews.js +97 -0
  133. package/dist/canvas/publishPreviews.js.map +1 -0
  134. package/dist/canvas/renderSceneToPixels.js +1 -1
  135. package/dist/canvas/renderSceneToPixels.js.map +1 -1
  136. package/dist/canvas/sceneAdapter.js +7 -5
  137. package/dist/canvas/sceneAdapter.js.map +1 -1
  138. package/dist/canvas/sceneClipboard.js +17 -10
  139. package/dist/canvas/sceneClipboard.js.map +1 -1
  140. package/dist/canvas/scenePickReads.js +37 -0
  141. package/dist/canvas/scenePickReads.js.map +1 -0
  142. package/dist/canvas/scenePickSource.js +74 -0
  143. package/dist/canvas/scenePickSource.js.map +1 -0
  144. package/dist/canvas/sceneSlotWiring.js +9 -3
  145. package/dist/canvas/sceneSlotWiring.js.map +1 -1
  146. package/dist/canvas/sceneViewRender.js +9 -4
  147. package/dist/canvas/sceneViewRender.js.map +1 -1
  148. package/dist/canvas/shownNodes.js +15 -0
  149. package/dist/canvas/shownNodes.js.map +1 -0
  150. package/dist/canvas/surfaceContribution.js +12 -8
  151. package/dist/canvas/surfaceContribution.js.map +1 -1
  152. package/dist/canvas/useCanvasView.js +4 -2
  153. package/dist/canvas/useCanvasView.js.map +1 -1
  154. package/dist/canvas/useLayerStack.js +6 -2
  155. package/dist/canvas/useLayerStack.js.map +1 -1
  156. package/dist/canvas/useSceneAdapter.js +5 -3
  157. package/dist/canvas/useSceneAdapter.js.map +1 -1
  158. package/dist/canvas/useViewAim.js +121 -0
  159. package/dist/canvas/useViewAim.js.map +1 -0
  160. package/dist/canvas/useViewNesting.js +3 -2
  161. package/dist/canvas/useViewNesting.js.map +1 -1
  162. package/dist/canvas/useViewRegistrySurface.js +4 -2
  163. package/dist/canvas/useViewRegistrySurface.js.map +1 -1
  164. package/dist/canvas/viewMarkers.js +63 -0
  165. package/dist/canvas/viewMarkers.js.map +1 -0
  166. package/dist/canvas/viewRegistry.js +27 -1
  167. package/dist/canvas/viewRegistry.js.map +1 -1
  168. package/dist/clipboard.d.ts +2 -2
  169. package/dist/{clone-C3e0OTuF.d.ts → clone-qb2e1F5p.d.ts} +2 -2
  170. package/dist/clone.d.ts +3 -3
  171. package/dist/core/isPlainObject.js +2 -2
  172. package/dist/core/layers/layerGroup.js.map +1 -1
  173. package/dist/core/pages/pageEdits.js +2 -3
  174. package/dist/core/pages/pageEdits.js.map +1 -1
  175. package/dist/core/paint/meshKindLazy.js +59 -2
  176. package/dist/core/paint/meshKindLazy.js.map +1 -1
  177. package/dist/core/scene/NodeRouting.js.map +1 -1
  178. package/dist/core/scene/arrivalWindow.js +1 -1
  179. package/dist/core/scene/arrivalWindow.js.map +1 -1
  180. package/dist/core/scene/clipNothing.js +10 -0
  181. package/dist/core/scene/clipNothing.js.map +1 -0
  182. package/dist/core/scene/declaredFieldSetters.js +44 -0
  183. package/dist/core/scene/declaredFieldSetters.js.map +1 -0
  184. package/dist/core/scene/depMemo.js +14 -3
  185. package/dist/core/scene/depMemo.js.map +1 -1
  186. package/dist/core/scene/dependents.js +9 -1
  187. package/dist/core/scene/dependents.js.map +1 -1
  188. package/dist/core/scene/derivedParts.js +10 -11
  189. package/dist/core/scene/derivedParts.js.map +1 -1
  190. package/dist/core/scene/documentOf.js +43 -0
  191. package/dist/core/scene/documentOf.js.map +1 -0
  192. package/dist/core/scene/effectivePose.js +44 -15
  193. package/dist/core/scene/effectivePose.js.map +1 -1
  194. package/dist/core/scene/fieldEdits.js +17 -14
  195. package/dist/core/scene/fieldEdits.js.map +1 -1
  196. package/dist/core/scene/fontReads.js +37 -0
  197. package/dist/core/scene/fontReads.js.map +1 -0
  198. package/dist/core/scene/historyForm.js +4 -4
  199. package/dist/core/scene/historyForm.js.map +1 -1
  200. package/dist/core/scene/inheritedPaint.js +97 -0
  201. package/dist/core/scene/inheritedPaint.js.map +1 -0
  202. package/dist/core/scene/kitOps.js +3 -23
  203. package/dist/core/scene/kitOps.js.map +1 -1
  204. package/dist/core/scene/kitRegistry.js +5 -0
  205. package/dist/core/scene/kitRegistry.js.map +1 -1
  206. package/dist/core/scene/layoutPass.js +1 -2
  207. package/dist/core/scene/layoutPass.js.map +1 -1
  208. package/dist/core/scene/nodeAsShown.js +72 -0
  209. package/dist/core/scene/nodeAsShown.js.map +1 -0
  210. package/dist/core/scene/nodeFnFields.js +2 -10
  211. package/dist/core/scene/nodeFnFields.js.map +1 -1
  212. package/dist/core/scene/nodeMemo.js +21 -8
  213. package/dist/core/scene/nodeMemo.js.map +1 -1
  214. package/dist/core/scene/nodeStore.js +2 -4
  215. package/dist/core/scene/nodeStore.js.map +1 -1
  216. package/dist/core/scene/pathEffects/blend.js.map +1 -1
  217. package/dist/core/scene/pathEffects/derive.js +10 -4
  218. package/dist/core/scene/pathEffects/derive.js.map +1 -1
  219. package/dist/core/scene/pathEffects/effects.js +3 -1
  220. package/dist/core/scene/pathEffects/effects.js.map +1 -1
  221. package/dist/core/scene/pathEffects/foundHidden.js +35 -0
  222. package/dist/core/scene/pathEffects/foundHidden.js.map +1 -0
  223. package/dist/core/scene/pathEffects/textAlong.js +95 -0
  224. package/dist/core/scene/pathEffects/textAlong.js.map +1 -0
  225. package/dist/core/scene/plyStacks.js +89 -0
  226. package/dist/core/scene/plyStacks.js.map +1 -0
  227. package/dist/core/scene/poseHold.js +42 -24
  228. package/dist/core/scene/poseHold.js.map +1 -1
  229. package/dist/core/scene/poseOverrides.js +35 -38
  230. package/dist/core/scene/poseOverrides.js.map +1 -1
  231. package/dist/core/scene/posePlies.js +59 -0
  232. package/dist/core/scene/posePlies.js.map +1 -0
  233. package/dist/core/scene/reconcileSpecs.js +6 -15
  234. package/dist/core/scene/reconcileSpecs.js.map +1 -1
  235. package/dist/core/scene/renderOrder.js +10 -4
  236. package/dist/core/scene/renderOrder.js.map +1 -1
  237. package/dist/core/scene/sameValue.js +17 -0
  238. package/dist/core/scene/sameValue.js.map +1 -0
  239. package/dist/core/scene/scene.js +3 -3
  240. package/dist/core/scene/scene.js.map +1 -1
  241. package/dist/core/scene/sceneEdits.js +8 -0
  242. package/dist/core/scene/sceneEdits.js.map +1 -1
  243. package/dist/core/scene/sceneState.js +1 -2
  244. package/dist/core/scene/sceneState.js.map +1 -1
  245. package/dist/core/scene/serialization.js +45 -9
  246. package/dist/core/scene/serialization.js.map +1 -1
  247. package/dist/core/scene/staleReplay.js +5 -2
  248. package/dist/core/scene/staleReplay.js.map +1 -1
  249. package/dist/core/scene/statedFields.js +4 -2
  250. package/dist/core/scene/statedFields.js.map +1 -1
  251. package/dist/core/scene/treeEdits.js +2 -4
  252. package/dist/core/scene/treeEdits.js.map +1 -1
  253. package/dist/core/scene/treeOrder.js +3 -1
  254. package/dist/core/scene/treeOrder.js.map +1 -1
  255. package/dist/core/scene/treeReads.js +1 -2
  256. package/dist/core/scene/treeReads.js.map +1 -1
  257. package/dist/core/scene/treeShape.js +13 -0
  258. package/dist/core/scene/treeShape.js.map +1 -0
  259. package/dist/core/scene/types.js.map +1 -1
  260. package/dist/core/scene/useScene.js +1 -1
  261. package/dist/core/scene/useScene.js.map +1 -1
  262. package/dist/{cornerHandles-BHuoMs1o.d.ts → cornerHandles-CNCb7aTY.d.ts} +3 -2
  263. package/dist/{depSchema-BpZNOv7q.d.ts → depSchema-B1RS-IJx.d.ts} +39 -18
  264. package/dist/devtools/handlers.js +1 -1
  265. package/dist/devtools/handlers.js.map +1 -1
  266. package/dist/devtools/protocol.js.map +1 -1
  267. package/dist/devtools/registry.js +4 -4
  268. package/dist/devtools/registry.js.map +1 -1
  269. package/dist/devtools-protocol.d.ts +4 -3
  270. package/dist/devtools.d.ts +3 -2
  271. package/dist/features/measure/measureContribution.js +4 -2
  272. package/dist/features/measure/measureContribution.js.map +1 -1
  273. package/dist/features/measure/measureHit.js +5 -3
  274. package/dist/features/measure/measureHit.js.map +1 -1
  275. package/dist/features/measure/measurement.js +1 -1
  276. package/dist/features/measure/measurement.js.map +1 -1
  277. package/dist/features/modes/modeLayers.js +14 -7
  278. package/dist/features/modes/modeLayers.js.map +1 -1
  279. package/dist/features/modes/modeTriggers.js +10 -6
  280. package/dist/features/modes/modeTriggers.js.map +1 -1
  281. package/dist/features/pathEffects/effectSources.js +69 -0
  282. package/dist/features/pathEffects/effectSources.js.map +1 -0
  283. package/dist/features/pathEffects/readSource.js +18 -2
  284. package/dist/features/pathEffects/readSource.js.map +1 -1
  285. package/dist/features/pathEffects/textItems.js +53 -0
  286. package/dist/features/pathEffects/textItems.js.map +1 -0
  287. package/dist/features/pathEffects/useEffectSourcesLayer.js +53 -0
  288. package/dist/features/pathEffects/useEffectSourcesLayer.js.map +1 -0
  289. package/dist/features/paths/editablePath.js +1 -1
  290. package/dist/features/paths/editablePath.js.map +1 -1
  291. package/dist/features/paths/pathEditState.js +5 -2
  292. package/dist/features/paths/pathEditState.js.map +1 -1
  293. package/dist/features/paths/pathSnap/sceneIndex.js +1 -1
  294. package/dist/features/paths/pathSnap/sceneIndex.js.map +1 -1
  295. package/dist/features/selection/enteredGroupFrame.js +65 -0
  296. package/dist/features/selection/enteredGroupFrame.js.map +1 -0
  297. package/dist/features/selection/enteredGroupScope.js +44 -0
  298. package/dist/features/selection/enteredGroupScope.js.map +1 -0
  299. package/dist/features/text/sceneTextDrafts.js +64 -0
  300. package/dist/features/text/sceneTextDrafts.js.map +1 -0
  301. package/dist/features/text/textHitAt.js +34 -3
  302. package/dist/features/text/textHitAt.js.map +1 -1
  303. package/dist/features/text/useSceneTextEdit.js +23 -19
  304. package/dist/features/text/useSceneTextEdit.js.map +1 -1
  305. package/dist/features/viewports/aim.js +105 -0
  306. package/dist/features/viewports/aim.js.map +1 -0
  307. package/dist/features/viewports/viewMarker.js +82 -0
  308. package/dist/features/viewports/viewMarker.js.map +1 -0
  309. package/dist/features/viewports/viewMarkerDrag.js +55 -0
  310. package/dist/features/viewports/viewMarkerDrag.js.map +1 -0
  311. package/dist/index.d.ts +709 -258
  312. package/dist/index.js +33 -12
  313. package/dist/interactions/actions/admitsChild.js +11 -0
  314. package/dist/interactions/actions/admitsChild.js.map +1 -0
  315. package/dist/interactions/actions/booleans/booleans.js +2 -1
  316. package/dist/interactions/actions/booleans/booleans.js.map +1 -1
  317. package/dist/interactions/actions/booleans/operands.js +6 -5
  318. package/dist/interactions/actions/booleans/operands.js.map +1 -1
  319. package/dist/interactions/actions/clipboard/action.js +27 -17
  320. package/dist/interactions/actions/clipboard/action.js.map +1 -1
  321. package/dist/interactions/actions/defaults/anchorEditing.js +44 -23
  322. package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
  323. package/dist/interactions/actions/defaults/anchorJoin.js +12 -6
  324. package/dist/interactions/actions/defaults/anchorJoin.js.map +1 -1
  325. package/dist/interactions/actions/defaults/bendSegment.js +7 -4
  326. package/dist/interactions/actions/defaults/bendSegment.js.map +1 -1
  327. package/dist/interactions/actions/defaults/cancelGesture.js +11 -8
  328. package/dist/interactions/actions/defaults/cancelGesture.js.map +1 -1
  329. package/dist/interactions/actions/defaults/contextMenu.js +33 -11
  330. package/dist/interactions/actions/defaults/contextMenu.js.map +1 -1
  331. package/dist/interactions/actions/defaults/createPaintAction.js +5 -2
  332. package/dist/interactions/actions/defaults/createPaintAction.js.map +1 -1
  333. package/dist/interactions/actions/defaults/delete.js +28 -14
  334. package/dist/interactions/actions/defaults/delete.js.map +1 -1
  335. package/dist/interactions/actions/defaults/duplicate.js +11 -8
  336. package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
  337. package/dist/interactions/actions/defaults/enterGroup.js +88 -0
  338. package/dist/interactions/actions/defaults/enterGroup.js.map +1 -0
  339. package/dist/interactions/actions/defaults/enterPathEdit.js +20 -10
  340. package/dist/interactions/actions/defaults/enterPathEdit.js.map +1 -1
  341. package/dist/interactions/actions/defaults/enterTextEdit.js +1 -1
  342. package/dist/interactions/actions/defaults/enterTextEdit.js.map +1 -1
  343. package/dist/interactions/actions/defaults/escape.js +11 -8
  344. package/dist/interactions/actions/defaults/escape.js.map +1 -1
  345. package/dist/interactions/actions/defaults/exitPathEdit.js +7 -4
  346. package/dist/interactions/actions/defaults/exitPathEdit.js.map +1 -1
  347. package/dist/interactions/actions/defaults/group.js +26 -20
  348. package/dist/interactions/actions/defaults/group.js.map +1 -1
  349. package/dist/interactions/actions/defaults/ingest.js +10 -4
  350. package/dist/interactions/actions/defaults/ingest.js.map +1 -1
  351. package/dist/interactions/actions/defaults/insertPathAnchor.js +7 -4
  352. package/dist/interactions/actions/defaults/insertPathAnchor.js.map +1 -1
  353. package/dist/interactions/actions/defaults/nudge.js +8 -2
  354. package/dist/interactions/actions/defaults/nudge.js.map +1 -1
  355. package/dist/interactions/actions/defaults/pathEditTools.js +11 -8
  356. package/dist/interactions/actions/defaults/pathEditTools.js.map +1 -1
  357. package/dist/interactions/actions/defaults/pinchZoom.js +7 -4
  358. package/dist/interactions/actions/defaults/pinchZoom.js.map +1 -1
  359. package/dist/interactions/actions/defaults/reorder.js +24 -6
  360. package/dist/interactions/actions/defaults/reorder.js.map +1 -1
  361. package/dist/interactions/actions/defaults/selectAll.js +15 -8
  362. package/dist/interactions/actions/defaults/selectAll.js.map +1 -1
  363. package/dist/interactions/actions/defaults/selectDeep.js +35 -0
  364. package/dist/interactions/actions/defaults/selectDeep.js.map +1 -0
  365. package/dist/interactions/actions/defaults/setFill.js +7 -3
  366. package/dist/interactions/actions/defaults/setFill.js.map +1 -1
  367. package/dist/interactions/actions/defaults/setFillOpacity.js +1 -1
  368. package/dist/interactions/actions/defaults/setStroke.js +12 -11
  369. package/dist/interactions/actions/defaults/setStroke.js.map +1 -1
  370. package/dist/interactions/actions/defaults/setStrokeOpacity.js +1 -1
  371. package/dist/interactions/actions/defaults/toolActivate.js +7 -1
  372. package/dist/interactions/actions/defaults/toolActivate.js.map +1 -1
  373. package/dist/interactions/actions/defaults/toolResetToDefault.js +7 -4
  374. package/dist/interactions/actions/defaults/toolResetToDefault.js.map +1 -1
  375. package/dist/interactions/actions/defaults/undoRedo.js +19 -13
  376. package/dist/interactions/actions/defaults/undoRedo.js.map +1 -1
  377. package/dist/interactions/actions/defaults/viewportWheelPan.js +16 -7
  378. package/dist/interactions/actions/defaults/viewportWheelPan.js.map +1 -1
  379. package/dist/interactions/actions/defaults/viewportZoom.js +24 -6
  380. package/dist/interactions/actions/defaults/viewportZoom.js.map +1 -1
  381. package/dist/interactions/actions/drawnBoxes.js +2 -1
  382. package/dist/interactions/actions/drawnBoxes.js.map +1 -1
  383. package/dist/interactions/actions/edit-anchors/action.js +7 -4
  384. package/dist/interactions/actions/edit-anchors/action.js.map +1 -1
  385. package/dist/interactions/actions/flip/action.js +8 -2
  386. package/dist/interactions/actions/flip/action.js.map +1 -1
  387. package/dist/interactions/actions/insert/action.js +1 -0
  388. package/dist/interactions/actions/insert/action.js.map +1 -1
  389. package/dist/interactions/actions/insert/parent.js +7 -5
  390. package/dist/interactions/actions/insert/parent.js.map +1 -1
  391. package/dist/interactions/actions/insert/placeAction.js +3 -2
  392. package/dist/interactions/actions/insert/placeAction.js.map +1 -1
  393. package/dist/interactions/actions/insert/repositionAction.js +2 -1
  394. package/dist/interactions/actions/insert/repositionAction.js.map +1 -1
  395. package/dist/interactions/actions/insert/session.js +2 -2
  396. package/dist/interactions/actions/insert/session.js.map +1 -1
  397. package/dist/interactions/actions/move/action.js +7 -4
  398. package/dist/interactions/actions/move/action.js.map +1 -1
  399. package/dist/interactions/actions/move/commit/reparent.js +1 -1
  400. package/dist/interactions/actions/move/commit/reparent.js.map +1 -1
  401. package/dist/interactions/actions/move/gestureAdapter.js +6 -7
  402. package/dist/interactions/actions/move/gestureAdapter.js.map +1 -1
  403. package/dist/interactions/actions/move/layout/exits.js +1 -1
  404. package/dist/interactions/actions/move/layout/exits.js.map +1 -1
  405. package/dist/interactions/actions/move/session.js +4 -4
  406. package/dist/interactions/actions/move/session.js.map +1 -1
  407. package/dist/interactions/actions/outlines/createOutlinesAction.js +8 -5
  408. package/dist/interactions/actions/outlines/createOutlinesAction.js.map +1 -1
  409. package/dist/interactions/actions/outlines/outlineStroke.js +5 -3
  410. package/dist/interactions/actions/outlines/outlineStroke.js.map +1 -1
  411. package/dist/interactions/actions/pathCommands/cutJoin.js +8 -5
  412. package/dist/interactions/actions/pathCommands/cutJoin.js.map +1 -1
  413. package/dist/interactions/actions/pathCommands/pathNodes.js +14 -5
  414. package/dist/interactions/actions/pathCommands/pathNodes.js.map +1 -1
  415. package/dist/interactions/actions/resize/cornerHandles.js +2 -1
  416. package/dist/interactions/actions/resize/cornerHandles.js.map +1 -1
  417. package/dist/interactions/actions/selectionSubtree.js +12 -7
  418. package/dist/interactions/actions/selectionSubtree.js.map +1 -1
  419. package/dist/interactions/actions/transform-again/action.js +8 -5
  420. package/dist/interactions/actions/transform-again/action.js.map +1 -1
  421. package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js +14 -2
  422. package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js.map +1 -1
  423. package/dist/json.d.ts +4 -0
  424. package/dist/json.js +3 -0
  425. package/dist/kitSettingsSchema.js +8 -1
  426. package/dist/kitSettingsSchema.js.map +1 -1
  427. package/dist/{math-CrJULaZI.d.ts → math-C_elShP_.d.ts} +7 -1
  428. package/dist/math.d.ts +1 -1
  429. package/dist/math.js +2 -1
  430. package/dist/move.d.ts +4 -4
  431. package/dist/{options-DBxEryYq.d.ts → options-ClEm3SEl.d.ts} +1 -1
  432. package/dist/resize.d.ts +4 -4
  433. package/dist/test-seams.d.ts +6 -6
  434. package/dist/tools/builtin/ellipse/useEllipseTool.js +1 -0
  435. package/dist/tools/builtin/ellipse/useEllipseTool.js.map +1 -1
  436. package/dist/tools/builtin/eyedropper/useEyedropperTool.js +2 -1
  437. package/dist/tools/builtin/eyedropper/useEyedropperTool.js.map +1 -1
  438. package/dist/tools/builtin/hand/dragPanContribution.js +4 -1
  439. package/dist/tools/builtin/hand/dragPanContribution.js.map +1 -1
  440. package/dist/tools/builtin/hand/useHandTool.js +7 -4
  441. package/dist/tools/builtin/hand/useHandTool.js.map +1 -1
  442. package/dist/tools/builtin/image/useImageTool.js +1 -0
  443. package/dist/tools/builtin/image/useImageTool.js.map +1 -1
  444. package/dist/tools/builtin/lasso/useLassoTool.js +1 -0
  445. package/dist/tools/builtin/lasso/useLassoTool.js.map +1 -1
  446. package/dist/tools/builtin/line/useLineTool.js +1 -0
  447. package/dist/tools/builtin/line/useLineTool.js.map +1 -1
  448. package/dist/tools/builtin/measure/useMeasureTool.js +12 -5
  449. package/dist/tools/builtin/measure/useMeasureTool.js.map +1 -1
  450. package/dist/tools/builtin/pen/usePenTool.js +14 -6
  451. package/dist/tools/builtin/pen/usePenTool.js.map +1 -1
  452. package/dist/tools/builtin/pencil/usePencilTool.js +1 -0
  453. package/dist/tools/builtin/pencil/usePencilTool.js.map +1 -1
  454. package/dist/tools/builtin/polygon/usePolygonTool.js +13 -4
  455. package/dist/tools/builtin/polygon/usePolygonTool.js.map +1 -1
  456. package/dist/tools/builtin/pressTarget.js +22 -0
  457. package/dist/tools/builtin/pressTarget.js.map +1 -0
  458. package/dist/tools/builtin/rect/useRectTool.js +1 -0
  459. package/dist/tools/builtin/rect/useRectTool.js.map +1 -1
  460. package/dist/tools/builtin/select/adapterPointQuery.js +6 -5
  461. package/dist/tools/builtin/select/adapterPointQuery.js.map +1 -1
  462. package/dist/tools/builtin/select/collapseDeferred.js +40 -0
  463. package/dist/tools/builtin/select/collapseDeferred.js.map +1 -0
  464. package/dist/tools/builtin/select/groupEntry.js +82 -0
  465. package/dist/tools/builtin/select/groupEntry.js.map +1 -0
  466. package/dist/tools/builtin/select/pressDecision.js +32 -0
  467. package/dist/tools/builtin/select/pressDecision.js.map +1 -0
  468. package/dist/tools/builtin/select/selectionContributions.js +22 -13
  469. package/dist/tools/builtin/select/selectionContributions.js.map +1 -1
  470. package/dist/tools/builtin/select/useSelectTool.js +74 -55
  471. package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
  472. package/dist/tools/builtin/slice/useSliceTool.js +22 -8
  473. package/dist/tools/builtin/slice/useSliceTool.js.map +1 -1
  474. package/dist/tools/builtin/star/useStarTool.js +13 -4
  475. package/dist/tools/builtin/star/useStarTool.js.map +1 -1
  476. package/dist/tools/builtin/sticky/stickyNode.js +4 -1
  477. package/dist/tools/builtin/sticky/stickyNode.js.map +1 -1
  478. package/dist/tools/builtin/sticky/useStickyTool.js +10 -5
  479. package/dist/tools/builtin/sticky/useStickyTool.js.map +1 -1
  480. package/dist/tools/builtin/text/useTextTool.js +3 -1
  481. package/dist/tools/builtin/text/useTextTool.js.map +1 -1
  482. package/dist/{types-7puAuVD2.d.ts → types-49uYJN3j.d.ts} +1 -1
  483. package/dist/{types-CniVctmk.d.ts → types-BEdRIhNc.d.ts} +272 -109
  484. package/dist/{types-oFeQ5VjV.d.ts → types-p2alu-R1.d.ts} +1 -1
  485. package/dist/util/paint.js +1 -3
  486. package/dist/util/paint.js.map +1 -1
  487. package/dist/version.js +1 -1
  488. package/package.json +29 -25
@@ -1 +1 @@
1
- {"version":3,"file":"viewportWheelPan.js","names":[],"sources":["../../../../src/interactions/actions/defaults/viewportWheelPan.ts"],"sourcesContent":["import type { Action } from '@weasel-js/routing';\nimport { wheelPan } from 'core/viewport/wheelHandler';\n\n/**\n * Wheel and trackpad pan. Shift+wheel pans horizontally, by routing deltaY\n * into the x axis (`params.swapAxis`). `params.axis` locks the pan to one\n * axis, and outranks `swapAxis`: a shift-wheel routed into a barred axis\n * moves nothing.\n */\nexport const viewportWheelPanAction: Action & { requires: string[] } = {\n id: 'viewport.wheelPan',\n label: 'Pan (wheel)',\n group: 'viewport',\n /**\n * The matcher's strict modifier check keeps the bindings exclusive, and\n * leaves Cmd+wheel to `viewportZoomAction`: omitting `mods` forbids any\n * modifier, and `mods: { shift: true }` forbids the others.\n */\n defaultBinding: [\n { spec: { kind: 'wheel' }, opts: {} },\n { spec: { kind: 'wheel', mods: { shift: true } }, opts: { params: { swapAxis: true } } },\n ],\n requires: ['view', 'debug'],\n invoker: {\n timing: 'immediate',\n run(deps, params) {\n const view = deps.view;\n if (!view) return;\n const axis = (params?.axis as 'both' | 'x' | 'y' | undefined) ?? 'both';\n const deltaX = (params?.deltaX as number | undefined) ?? 0;\n const deltaY = (params?.deltaY as number | undefined) ?? 0;\n const from = view.get();\n const next = wheelPan(from, { deltaX, deltaY }, { axis, swapAxis: params?.swapAxis === true });\n view.set(next);\n deps.debug?.recordViewport('pan', from, next);\n },\n },\n enabled: () => true,\n};\n\n/** Tuning for {@link makeViewportWheelPanAction}. */\nexport interface WheelPanOptions {\n /** Lock the pan to one axis. Default `'both'`. */\n axis?: 'both' | 'x' | 'y';\n}\n\n/** Builds a `viewport.wheelPan` descriptor with `opts` baked into both of its\n * default bindings. `viewportWheelPanAction` is the unconfigured one. */\nexport function makeViewportWheelPanAction(\n opts: WheelPanOptions = {},\n): Action & { requires: string[] } {\n const axis = opts.axis ?? 'both';\n return {\n ...viewportWheelPanAction,\n defaultBinding: [\n { spec: { kind: 'wheel' }, opts: { params: { axis } } },\n { spec: { kind: 'wheel', mods: { shift: true } }, opts: { params: { swapAxis: true, axis } } },\n ],\n };\n}\n"],"mappings":";;;;;;;;;AASA,IAAa,yBAA0D;CACrE,IAAI;CACJ,OAAO;CACP,OAAO;;;;;;CAMP,gBAAgB,CACd;EAAE,MAAM,EAAE,MAAM,QAAQ;EAAG,MAAM,CAAC;CAAE,GACpC;EAAE,MAAM;GAAE,MAAM;GAAS,MAAM,EAAE,OAAO,KAAK;EAAE;EAAG,MAAM,EAAE,QAAQ,EAAE,UAAU,KAAK,EAAE;CAAE,CACzF;CACA,UAAU,CAAC,QAAQ,OAAO;CAC1B,SAAS;EACP,QAAQ;EACR,IAAI,MAAM,QAAQ;GAChB,MAAM,OAAO,KAAK;GAClB,IAAI,CAAC,MAAM;GACX,MAAM,OAAQ,QAAQ,QAA2C;GACjE,MAAM,SAAU,QAAQ,UAAiC;GACzD,MAAM,SAAU,QAAQ,UAAiC;GACzD,MAAM,OAAO,KAAK,IAAI;GACtB,MAAM,OAAO,SAAS,MAAM;IAAE;IAAQ;GAAO,GAAG;IAAE;IAAM,UAAU,QAAQ,aAAa;GAAK,CAAC;GAC7F,KAAK,IAAI,IAAI;GACb,KAAK,OAAO,eAAe,OAAO,MAAM,IAAI;EAC9C;CACF;CACA,eAAe;AACjB;;;AAUA,SAAgB,2BACd,OAAwB,CAAC,GACQ;CACjC,MAAM,OAAO,KAAK,QAAQ;CAC1B,OAAO;EACL,GAAG;EACH,gBAAgB,CACd;GAAE,MAAM,EAAE,MAAM,QAAQ;GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;EAAE,GACtD;GAAE,MAAM;IAAE,MAAM;IAAS,MAAM,EAAE,OAAO,KAAK;GAAE;GAAG,MAAM,EAAE,QAAQ;IAAE,UAAU;IAAM;GAAK,EAAE;EAAE,CAC/F;CACF;AACF"}
1
+ {"version":3,"file":"viewportWheelPan.js","names":[],"sources":["../../../../src/interactions/actions/defaults/viewportWheelPan.ts"],"sourcesContent":["import type { Action } from '@weasel-js/routing';\nimport { wheelPan } from 'core/viewport/wheelHandler';\n\n/**\n * Wheel and trackpad pan. Shift+wheel pans horizontally, by routing deltaY\n * into the x axis (`params.swapAxis`). `params.axis` locks the pan to one\n * axis, and outranks `swapAxis`: a shift-wheel routed into a barred axis\n * moves nothing.\n */\nexport const viewportWheelPanAction: Action & { requires: string[] } = {\n id: 'viewport.wheelPan',\n label: 'Pan (wheel)',\n group: 'viewport',\n /**\n * The matcher's strict modifier check keeps the bindings exclusive, and\n * leaves Cmd+wheel to `viewportZoomAction`: omitting `mods` forbids any\n * modifier, and `mods: { shift: true }` forbids the others.\n */\n defaultBinding: [\n { spec: { kind: 'wheel' }, opts: { id: 'viewport.wheelPan.wheel' } },\n { spec: { kind: 'wheel', mods: { shift: true } }, opts: { id: 'viewport.wheelPan.swapAxis', params: { swapAxis: true } } },\n ],\n requires: ['view', 'debug'],\n invoker: {\n timing: 'immediate',\n run(deps, params) {\n const view = deps.view;\n if (!view) return;\n const axis = (params?.axis as 'both' | 'x' | 'y' | undefined) ?? 'both';\n const deltaX = (params?.deltaX as number | undefined) ?? 0;\n const deltaY = (params?.deltaY as number | undefined) ?? 0;\n const from = view.get();\n const next = wheelPan(from, { deltaX, deltaY }, { axis, swapAxis: params?.swapAxis === true });\n view.set(next);\n deps.debug?.recordViewport('pan', from, next);\n },\n },\n enabled: () => true,\n};\n\n/** Tuning for {@link makeViewportWheelPanAction}. */\nexport interface WheelPanOptions {\n /** Lock the pan to one axis. Default `'both'`. */\n axis?: 'both' | 'x' | 'y';\n}\n\n/** Builds a `viewport.wheelPan` descriptor with `opts` baked into both of its\n * default bindings. `viewportWheelPanAction` is the unconfigured one. */\nexport function makeViewportWheelPanAction(\n opts: WheelPanOptions = {},\n): Action & { requires: string[] } {\n const axis = opts.axis ?? 'both';\n return {\n ...viewportWheelPanAction,\n defaultBinding: [\n { spec: { kind: 'wheel' }, opts: { id: 'viewport.wheelPan.wheel', params: { axis } } },\n { spec: { kind: 'wheel', mods: { shift: true } }, opts: { id: 'viewport.wheelPan.swapAxis', params: { swapAxis: true, axis } } },\n ],\n };\n}\n"],"mappings":";;;;;;;;;AASA,IAAa,yBAA0D;CACrE,IAAI;CACJ,OAAO;CACP,OAAO;;;;;;CAMP,gBAAgB,CACd;EAAE,MAAM,EAAE,MAAM,QAAQ;EAAG,MAAM,EAAE,IAAI,0BAA0B;CAAE,GACnE;EAAE,MAAM;GAAE,MAAM;GAAS,MAAM,EAAE,OAAO,KAAK;EAAE;EAAG,MAAM;GAAE,IAAI;GAA8B,QAAQ,EAAE,UAAU,KAAK;EAAE;CAAE,CAC3H;CACA,UAAU,CAAC,QAAQ,OAAO;CAC1B,SAAS;EACP,QAAQ;EACR,IAAI,MAAM,QAAQ;GAChB,MAAM,OAAO,KAAK;GAClB,IAAI,CAAC,MAAM;GACX,MAAM,OAAQ,QAAQ,QAA2C;GACjE,MAAM,SAAU,QAAQ,UAAiC;GACzD,MAAM,SAAU,QAAQ,UAAiC;GACzD,MAAM,OAAO,KAAK,IAAI;GACtB,MAAM,OAAO,SAAS,MAAM;IAAE;IAAQ;GAAO,GAAG;IAAE;IAAM,UAAU,QAAQ,aAAa;GAAK,CAAC;GAC7F,KAAK,IAAI,IAAI;GACb,KAAK,OAAO,eAAe,OAAO,MAAM,IAAI;EAC9C;CACF;CACA,eAAe;AACjB;;;AAUA,SAAgB,2BACd,OAAwB,CAAC,GACQ;CACjC,MAAM,OAAO,KAAK,QAAQ;CAC1B,OAAO;EACL,GAAG;EACH,gBAAgB,CACd;GAAE,MAAM,EAAE,MAAM,QAAQ;GAAG,MAAM;IAAE,IAAI;IAA2B,QAAQ,EAAE,KAAK;GAAE;EAAE,GACrF;GAAE,MAAM;IAAE,MAAM;IAAS,MAAM,EAAE,OAAO,KAAK;GAAE;GAAG,MAAM;IAAE,IAAI;IAA8B,QAAQ;KAAE,UAAU;KAAM;IAAK;GAAE;EAAE,CACjI;CACF;AACF"}
@@ -27,18 +27,27 @@ function zoomBindings(wheel) {
27
27
  kind: "wheel",
28
28
  mods: { mod: true }
29
29
  },
30
- opts: { params: { kind: "wheel" } }
30
+ opts: {
31
+ id: "viewport.zoom.wheel",
32
+ params: { kind: "wheel" }
33
+ }
31
34
  },
32
35
  {
33
36
  spec: {
34
37
  kind: "wheel",
35
38
  mods: { ctrl: true }
36
39
  },
37
- opts: { params: { kind: "wheel" } }
40
+ opts: {
41
+ id: "viewport.zoom.trackpad",
42
+ params: { kind: "wheel" }
43
+ }
38
44
  },
39
45
  {
40
46
  spec: { kind: "pinch" },
41
- opts: { params: { kind: "pinch" } }
47
+ opts: {
48
+ id: "viewport.zoom.pinch",
49
+ params: { kind: "pinch" }
50
+ }
42
51
  },
43
52
  {
44
53
  spec: {
@@ -49,7 +58,10 @@ function zoomBindings(wheel) {
49
58
  shift: "optional"
50
59
  }
51
60
  },
52
- opts: { params: { kind: "in" } }
61
+ opts: {
62
+ id: "viewport.zoom.in",
63
+ params: { kind: "in" }
64
+ }
53
65
  },
54
66
  {
55
67
  spec: {
@@ -57,7 +69,10 @@ function zoomBindings(wheel) {
57
69
  key: "-",
58
70
  mods: { mod: true }
59
71
  },
60
- opts: { params: { kind: "out" } }
72
+ opts: {
73
+ id: "viewport.zoom.out",
74
+ params: { kind: "out" }
75
+ }
61
76
  },
62
77
  {
63
78
  spec: {
@@ -65,7 +80,10 @@ function zoomBindings(wheel) {
65
80
  key: "0",
66
81
  mods: { mod: true }
67
82
  },
68
- opts: { params: { kind: "reset" } }
83
+ opts: {
84
+ id: "viewport.zoom.reset",
85
+ params: { kind: "reset" }
86
+ }
69
87
  }
70
88
  ];
71
89
  }
@@ -1 +1 @@
1
- {"version":3,"file":"viewportZoom.js","names":[],"sources":["../../../../src/interactions/actions/defaults/viewportZoom.ts"],"sourcesContent":["import type { Action, BoundGesture } from '@weasel-js/routing';\nimport type { ViewApi } from '../depSchema';\nimport { axisZoomFactor, zoomAt, type ZoomAxis } from 'core/viewport/zoomAt';\nimport { wheelZoomFactor } from 'core/viewport/wheelHandler';\nimport { DEFAULT_MIN_ZOOM, DEFAULT_MAX_ZOOM } from 'core/viewport/zoomBounds';\nimport { viewToTransform, type View } from 'core/viewport/view';\nimport { screenToWorld } from 'core/viewport/viewTransform';\nimport type { ViewAnimationOptions } from 'core/viewport/useViewAnimation';\n\n// Multiplicative step for keyboard zoom (matches useKeyboardZoomTool default).\nconst KEY_STEP = 1.25;\n\n/** Keyboard-zoom anchor: the host center when the view dep can measure it,\n * else the canvas top-left origin. */\nfunction keyAnchor(view: ViewApi): { x: number; y: number } {\n const size = view.hostSize?.();\n return size ? { x: size.width / 2, y: size.height / 2 } : { x: 0, y: 0 };\n}\n\n/**\n * Tuning for the animated form of the discrete zoom steps.\n */\nexport interface ViewportZoomAnimateOptions extends ViewAnimationOptions {\n /** Duration for the Cmd+0 reset branch. Defaults to `ms`. */\n resetMs?: number;\n}\n\n/**\n * Tuning for {@link makeViewportZoomAction}.\n */\nexport interface ViewportZoomOptions {\n /**\n * Which wheel gesture triggers zoom.\n * - `'mod'` (default): Cmd/Ctrl+wheel — coexists with plain-wheel pan\n * (`viewport.wheelPan`).\n * - `'plain'`: bare wheel, no modifier. Pair with `viewport.pan: false`,\n * since plain wheel otherwise drives pan and the two would compete.\n */\n wheel?: 'plain' | 'mod';\n /** Lower clamp on the resulting view scale, forwarded to `zoomAt`. Default 0.1. */\n min?: number;\n /** Upper clamp on the resulting view scale, forwarded to `zoomAt`. Default 8. */\n max?: number;\n /** Hold wheel, trackpad-pinch, and key zoom to one axis; the other keeps its\n * scale and its translation. Default `'both'`. Cmd+0 still resets both. */\n axis?: ZoomAxis;\n /**\n * Glide the discrete steps (Cmd+=, Cmd+-, Cmd+0) instead of jumping.\n * `true` uses the kit defaults; an object tunes them. Wheel and pinch never\n * animate — their input already samples every frame. Requires a `view` dep\n * that implements `animate`; without one this is ignored.\n */\n animate?: boolean | ViewportZoomAnimateOptions;\n}\n\n/** The gestures `viewport.zoom` answers: the configurable wheel trigger, then\n * the fixed pinch and key bindings. */\nfunction zoomBindings(wheel: ViewportZoomOptions['wheel']): BoundGesture[] {\n // 'plain': bare wheel (omitting `mods` forbids any modifier — see the\n // matcher's strict modifier check). 'mod': Cmd/Ctrl+wheel.\n const wheelSpec =\n wheel === 'plain'\n ? { kind: 'wheel' as const }\n : { kind: 'wheel' as const, mods: { mod: true } };\n return [\n {\n spec: wheelSpec,\n opts: { params: { kind: 'wheel' } },\n },\n // Trackpad pinch, which the browser delivers as ctrl+wheel. Distinct from\n // the `mod` binding on mac (`mod` is metaKey there), so without its own\n // binding nothing claims it and the page zooms instead. Elsewhere it\n // duplicates `mod`, and `matchBest` picks a single winner.\n {\n spec: { kind: 'wheel' as const, mods: { ctrl: true } },\n opts: { params: { kind: 'wheel' } },\n },\n // Safari's trackpad pinch, reported as a scale rather than a wheel. Safari\n // may send the same pinch as ctrl+wheel too; the dispatcher swallows that\n // copy while a claimed gesture is live.\n {\n spec: { kind: 'pinch' as const },\n opts: { params: { kind: 'pinch' } },\n },\n // Cmd+= → zoom in (also accepts Cmd+Shift+= which is Cmd++ on many keyboards)\n {\n spec: { kind: 'key', key: '=', mods: { mod: true, shift: 'optional' } },\n opts: { params: { kind: 'in' } },\n },\n {\n spec: { kind: 'key', key: '-', mods: { mod: true } },\n opts: { params: { kind: 'out' } },\n },\n {\n spec: { kind: 'key', key: '0', mods: { mod: true } },\n opts: { params: { kind: 'reset' } },\n },\n ];\n}\n\n/**\n * Build a `viewport.zoom` Action descriptor with a configurable wheel trigger\n * and scale clamp. The keyboard bindings (Cmd+=/-/0) and the two trackpad\n * pinch bindings are fixed. A wheel or pinch sample zooms about its focal\n * point, which `zoomAt` expects in canvas-local pixels.\n */\nexport function makeViewportZoomAction(\n opts: ViewportZoomOptions = {},\n): Action & { requires: string[] } {\n const clamp = { min: opts.min ?? DEFAULT_MIN_ZOOM, max: opts.max ?? DEFAULT_MAX_ZOOM };\n const axis = opts.axis ?? 'both';\n\n const rawAnimate: ViewportZoomAnimateOptions | null =\n opts.animate === true ? {} : (opts.animate || null);\n const { resetMs, ...animateRest } = rawAnimate ?? {};\n const tweenOpts: ViewAnimationOptions | null = rawAnimate ? animateRest : null;\n\n return {\n id: 'viewport.zoom',\n label: 'Zoom',\n group: 'viewport',\n variants: [\n { key: 'in', label: 'Zoom In', params: { kind: 'in' } },\n { key: 'out', label: 'Zoom Out', params: { kind: 'out' } },\n { key: 'reset', label: 'Reset Zoom', params: { kind: 'reset' } },\n ],\n defaultBinding: zoomBindings(opts.wheel),\n requires: ['view', 'debug'],\n invoker: {\n timing: 'immediate',\n run(deps, params) {\n const view = deps.view;\n if (!view) return;\n const debug = deps.debug;\n const current = view.get();\n // Report a zoom about `anchor` (canvas-local px) from `from` to `to`.\n const record = (from: View, to: View, anchor: { x: number; y: number }) => {\n if (!debug) return;\n const [wx, wy] = screenToWorld(anchor.x, anchor.y, viewToTransform(from));\n debug.recordViewport('zoom', from, to, { x: wx, y: wy });\n };\n const kind = params?.kind as string | undefined;\n\n const canAnimate = tweenOpts !== null && typeof view.animate === 'function';\n const stepTo = (target: View, ms?: number) => {\n if (!canAnimate) { view.set(target); return; }\n view.animate!(target, { ...tweenOpts!, ms: ms ?? tweenOpts!.ms });\n };\n // Successive presses compound off where the camera is heading, not off\n // whichever frame the tween happens to be on.\n const stepFrom = (): View => (canAnimate && view.animationTarget?.()) || current;\n\n const focal = () => ({\n x: (params?.clientX as number | undefined) ?? 0,\n y: (params?.clientY as number | undefined) ?? 0,\n });\n const scaleBy = (factor: number) => {\n if (!(factor > 0) || !Number.isFinite(factor)) return;\n const next = zoomAt(current, focal(), axisZoomFactor(factor, axis), clamp);\n view.set(next);\n record(current, next, focal());\n };\n\n switch (kind) {\n case 'wheel':\n scaleBy(wheelZoomFactor((params?.deltaY as number | undefined) ?? 0));\n break;\n case 'pinch':\n scaleBy((params?.scale as number | undefined) ?? Number.NaN);\n break;\n case 'in':\n case 'out': {\n const from = stepFrom();\n const step = axisZoomFactor(kind === 'in' ? KEY_STEP : 1 / KEY_STEP, axis);\n const target = zoomAt(from, keyAnchor(view), step, clamp);\n stepTo(target);\n record(from, target, keyAnchor(view));\n break;\n }\n case 'reset': {\n // A recenter that returns its target can be animated; one that\n // returns nothing dispatched the view itself and is already done.\n const target = view.recenter\n ? view.recenter()\n : { x: 0, y: 0, scale: { x: 1, y: 1 } };\n if (target) stepTo(target, resetMs);\n break;\n }\n default:\n // Unknown kind — no-op. Legacy bridge calls with params=undefined;\n // default to zoom-in as a sensible fallback.\n if (params === undefined) {\n view.set(zoomAt(current, { x: 0, y: 0 }, axisZoomFactor(KEY_STEP, axis), clamp));\n }\n break;\n }\n },\n },\n enabled: () => true,\n };\n}\n\n/**\n * Default `viewport.zoom` descriptor: Cmd/Ctrl+wheel zoom with the kit's\n * default 0.1–8 scale clamp. Equivalent to `makeViewportZoomAction()`.\n */\nexport const viewportZoomAction: Action & { requires: string[] } = makeViewportZoomAction();\n"],"mappings":"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"}
1
+ {"version":3,"file":"viewportZoom.js","names":[],"sources":["../../../../src/interactions/actions/defaults/viewportZoom.ts"],"sourcesContent":["import type { Action, BoundGesture } from '@weasel-js/routing';\nimport type { ViewApi } from '../depSchema';\nimport { axisZoomFactor, zoomAt, type ZoomAxis } from 'core/viewport/zoomAt';\nimport { wheelZoomFactor } from 'core/viewport/wheelHandler';\nimport { DEFAULT_MIN_ZOOM, DEFAULT_MAX_ZOOM } from 'core/viewport/zoomBounds';\nimport { viewToTransform, type View } from 'core/viewport/view';\nimport { screenToWorld } from 'core/viewport/viewTransform';\nimport type { ViewAnimationOptions } from 'core/viewport/useViewAnimation';\n\n// Multiplicative step for keyboard zoom (matches useKeyboardZoomTool default).\nconst KEY_STEP = 1.25;\n\n/** Keyboard-zoom anchor: the host center when the view dep can measure it,\n * else the canvas top-left origin. */\nfunction keyAnchor(view: ViewApi): { x: number; y: number } {\n const size = view.hostSize?.();\n return size ? { x: size.width / 2, y: size.height / 2 } : { x: 0, y: 0 };\n}\n\n/**\n * Tuning for the animated form of the discrete zoom steps.\n */\nexport interface ViewportZoomAnimateOptions extends ViewAnimationOptions {\n /** Duration for the Cmd+0 reset branch. Defaults to `ms`. */\n resetMs?: number;\n}\n\n/**\n * Tuning for {@link makeViewportZoomAction}.\n */\nexport interface ViewportZoomOptions {\n /**\n * Which wheel gesture triggers zoom.\n * - `'mod'` (default): Cmd/Ctrl+wheel — coexists with plain-wheel pan\n * (`viewport.wheelPan`).\n * - `'plain'`: bare wheel, no modifier. Pair with `viewport.pan: false`,\n * since plain wheel otherwise drives pan and the two would compete.\n */\n wheel?: 'plain' | 'mod';\n /** Lower clamp on the resulting view scale, forwarded to `zoomAt`. Default 0.1. */\n min?: number;\n /** Upper clamp on the resulting view scale, forwarded to `zoomAt`. Default 8. */\n max?: number;\n /** Hold wheel, trackpad-pinch, and key zoom to one axis; the other keeps its\n * scale and its translation. Default `'both'`. Cmd+0 still resets both. */\n axis?: ZoomAxis;\n /**\n * Glide the discrete steps (Cmd+=, Cmd+-, Cmd+0) instead of jumping.\n * `true` uses the kit defaults; an object tunes them. Wheel and pinch never\n * animate — their input already samples every frame. Requires a `view` dep\n * that implements `animate`; without one this is ignored.\n */\n animate?: boolean | ViewportZoomAnimateOptions;\n}\n\n/** The gestures `viewport.zoom` answers: the configurable wheel trigger, then\n * the fixed pinch and key bindings. */\nfunction zoomBindings(wheel: ViewportZoomOptions['wheel']): BoundGesture[] {\n // 'plain': bare wheel (omitting `mods` forbids any modifier — see the\n // matcher's strict modifier check). 'mod': Cmd/Ctrl+wheel.\n const wheelSpec =\n wheel === 'plain'\n ? { kind: 'wheel' as const }\n : { kind: 'wheel' as const, mods: { mod: true } };\n return [\n {\n spec: wheelSpec,\n opts: { id: 'viewport.zoom.wheel', params: { kind: 'wheel' } },\n },\n // Trackpad pinch, which the browser delivers as ctrl+wheel. Distinct from\n // the `mod` binding on mac (`mod` is metaKey there), so without its own\n // binding nothing claims it and the page zooms instead. Elsewhere it\n // duplicates `mod`, and `matchBest` picks a single winner.\n {\n spec: { kind: 'wheel' as const, mods: { ctrl: true } },\n opts: { id: 'viewport.zoom.trackpad', params: { kind: 'wheel' } },\n },\n // Safari's trackpad pinch, reported as a scale rather than a wheel. Safari\n // may send the same pinch as ctrl+wheel too; the dispatcher swallows that\n // copy while a claimed gesture is live.\n {\n spec: { kind: 'pinch' as const },\n opts: { id: 'viewport.zoom.pinch', params: { kind: 'pinch' } },\n },\n // Cmd+= → zoom in (also accepts Cmd+Shift+= which is Cmd++ on many keyboards)\n {\n spec: { kind: 'key', key: '=', mods: { mod: true, shift: 'optional' } },\n opts: { id: 'viewport.zoom.in', params: { kind: 'in' } },\n },\n {\n spec: { kind: 'key', key: '-', mods: { mod: true } },\n opts: { id: 'viewport.zoom.out', params: { kind: 'out' } },\n },\n {\n spec: { kind: 'key', key: '0', mods: { mod: true } },\n opts: { id: 'viewport.zoom.reset', params: { kind: 'reset' } },\n },\n ];\n}\n\n/**\n * Build a `viewport.zoom` Action descriptor with a configurable wheel trigger\n * and scale clamp. The keyboard bindings (Cmd+=/-/0) and the two trackpad\n * pinch bindings are fixed. A wheel or pinch sample zooms about its focal\n * point, which `zoomAt` expects in canvas-local pixels.\n */\nexport function makeViewportZoomAction(\n opts: ViewportZoomOptions = {},\n): Action & { requires: string[] } {\n const clamp = { min: opts.min ?? DEFAULT_MIN_ZOOM, max: opts.max ?? DEFAULT_MAX_ZOOM };\n const axis = opts.axis ?? 'both';\n\n const rawAnimate: ViewportZoomAnimateOptions | null =\n opts.animate === true ? {} : (opts.animate || null);\n const { resetMs, ...animateRest } = rawAnimate ?? {};\n const tweenOpts: ViewAnimationOptions | null = rawAnimate ? animateRest : null;\n\n return {\n id: 'viewport.zoom',\n label: 'Zoom',\n group: 'viewport',\n variants: [\n { key: 'in', label: 'Zoom In', params: { kind: 'in' } },\n { key: 'out', label: 'Zoom Out', params: { kind: 'out' } },\n { key: 'reset', label: 'Reset Zoom', params: { kind: 'reset' } },\n ],\n defaultBinding: zoomBindings(opts.wheel),\n requires: ['view', 'debug'],\n invoker: {\n timing: 'immediate',\n run(deps, params) {\n const view = deps.view;\n if (!view) return;\n const debug = deps.debug;\n const current = view.get();\n // Report a zoom about `anchor` (canvas-local px) from `from` to `to`.\n const record = (from: View, to: View, anchor: { x: number; y: number }) => {\n if (!debug) return;\n const [wx, wy] = screenToWorld(anchor.x, anchor.y, viewToTransform(from));\n debug.recordViewport('zoom', from, to, { x: wx, y: wy });\n };\n const kind = params?.kind as string | undefined;\n\n const canAnimate = tweenOpts !== null && typeof view.animate === 'function';\n const stepTo = (target: View, ms?: number) => {\n if (!canAnimate) { view.set(target); return; }\n view.animate!(target, { ...tweenOpts!, ms: ms ?? tweenOpts!.ms });\n };\n // Successive presses compound off where the camera is heading, not off\n // whichever frame the tween happens to be on.\n const stepFrom = (): View => (canAnimate && view.animationTarget?.()) || current;\n\n const focal = () => ({\n x: (params?.clientX as number | undefined) ?? 0,\n y: (params?.clientY as number | undefined) ?? 0,\n });\n const scaleBy = (factor: number) => {\n if (!(factor > 0) || !Number.isFinite(factor)) return;\n const next = zoomAt(current, focal(), axisZoomFactor(factor, axis), clamp);\n view.set(next);\n record(current, next, focal());\n };\n\n switch (kind) {\n case 'wheel':\n scaleBy(wheelZoomFactor((params?.deltaY as number | undefined) ?? 0));\n break;\n case 'pinch':\n scaleBy((params?.scale as number | undefined) ?? Number.NaN);\n break;\n case 'in':\n case 'out': {\n const from = stepFrom();\n const step = axisZoomFactor(kind === 'in' ? KEY_STEP : 1 / KEY_STEP, axis);\n const target = zoomAt(from, keyAnchor(view), step, clamp);\n stepTo(target);\n record(from, target, keyAnchor(view));\n break;\n }\n case 'reset': {\n // A recenter that returns its target can be animated; one that\n // returns nothing dispatched the view itself and is already done.\n const target = view.recenter\n ? view.recenter()\n : { x: 0, y: 0, scale: { x: 1, y: 1 } };\n if (target) stepTo(target, resetMs);\n break;\n }\n default:\n // Unknown kind — no-op. Legacy bridge calls with params=undefined;\n // default to zoom-in as a sensible fallback.\n if (params === undefined) {\n view.set(zoomAt(current, { x: 0, y: 0 }, axisZoomFactor(KEY_STEP, axis), clamp));\n }\n break;\n }\n },\n },\n enabled: () => true,\n };\n}\n\n/**\n * Default `viewport.zoom` descriptor: Cmd/Ctrl+wheel zoom with the kit's\n * default 0.1–8 scale clamp. Equivalent to `makeViewportZoomAction()`.\n */\nexport const viewportZoomAction: Action & { requires: string[] } = makeViewportZoomAction();\n"],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { documentOf } from "../../core/scene/documentOf.js";
1
2
  import { axisAlignedBounds } from "../../core/geometry/unionBounds.js";
2
3
  import { drawnBoundsOf, inkExtentsOf } from "../../canvas/drawnBounds.js";
3
4
  import { drawnBoundsScene } from "../../canvas/derivedPath.js";
@@ -9,7 +10,7 @@ var NO_SCENE = { get: () => void 0 };
9
10
  /** The {@link DrawnBoxes} over `deps.scene`. Pass the action's own `frame`
10
11
  * when it has one, so container members are read the way it reads them. */
11
12
  function drawnBoxes(deps, frame) {
12
- const scene = deps.scene ? drawnBoundsScene(deps.scene) : NO_SCENE;
13
+ const scene = deps.scene ? drawnBoundsScene(documentOf(deps.scene)) : NO_SCENE;
13
14
  const descriptor = poseDescriptorOf(deps.poseDescriptor);
14
15
  const view = deps.view?.get();
15
16
  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 { 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"}
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 { documentOf } from 'core/scene/documentOf';\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 // A command measures what the document holds, not what an open gesture or edit is showing.\n const scene: DrawnBoundsScene = deps.scene ? drawnBoundsScene(documentOf(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":";;;;;;;;AAWA,IAAM,WAA6B,EAAE,WAAW,OAAU;;;AAe1D,SAAgB,WACd,MACA,OACY;CAEZ,MAAM,QAA0B,KAAK,QAAQ,iBAAiB,WAAW,KAAK,KAAK,CAAC,IAAI;CACxF,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"}
@@ -160,10 +160,13 @@ var editAnchorsAction = inPlane({
160
160
  id: "editAnchors",
161
161
  label: "Edit Anchors",
162
162
  tags: pathOperationTags("drag"),
163
- defaultBinding: {
164
- kind: "drag",
165
- target: { kindOf: isAnchorOrControl }
166
- },
163
+ defaultBinding: [{
164
+ spec: {
165
+ kind: "drag",
166
+ target: { kindOf: isAnchorOrControl }
167
+ },
168
+ opts: { id: "editAnchors.drag" }
169
+ }],
167
170
  eligible: { capability: "edits-anchors" },
168
171
  modifierRoles: EDIT_ANCHORS_ROLES,
169
172
  requires: [
@@ -1 +1 @@
1
- {"version":3,"file":"action.js","names":[],"sources":["../../../../src/interactions/actions/edit-anchors/action.ts"],"sourcesContent":["/**\n * `editAnchorsAction` — ongoing Action descriptor for editing polygon anchors.\n *\n * - **Press** selects. Bare press replaces the anchor selection; Shift\n * toggles the pressed anchor in or out of it. Pressing an anchor that\n * is already part of a multi-anchor selection leaves the selection\n * alone, so you can grab a set and move it.\n * - **Drag** then moves whatever the press left selected — one anchor,\n * or every selected anchor together. The pressed anchor snaps through\n * the `snap` dep's `point` session, to what the drag leaves still of the\n * path and to the canvas's targets; Shift holds it to the axes and\n * diagonals through its press, and Ctrl suspends the snap.\n * - **Dragging a control handle** moves just that handle, mirroring the\n * opposite one while the anchor is smooth. Alt breaks the mirror; Shift\n * holds the handle to the angle it had at the press, changing only its\n * length; Mod (Cmd or Ctrl) holds its length, changing only its angle;\n * both freeze it. The `editAnchors` dep's `handleLocks`, or else the\n * `handleLock` param ({@link EditAnchorsParams}), holds a lock with no\n * modifier. The lock in force draws its guide as a `polyline` overlay\n * of role `lock`: the line, or the circle round the anchor.\n * - **Path-edit extensions shape the drag** through their `drag`: anchors\n * that follow an anchor drag by a share of it, where a dragged handle\n * goes after the locks, the wheel mid-drag, and overlays of their own.\n * - **Every frame is finished** by the dep's `finishEdit`, so what a\n * path-edit extension keeps true holds while the drag runs.\n * - **Under a `pathSymmetry` dep**, on a path that is its own image under\n * some of its transforms, every image of what is dragged follows it.\n *\n * Selection lives on the `editAnchors` dep (flat anchor indices) rather\n * than in this handle's scratch, because the keyboard actions\n * (`nudgeAnchors`, `deleteAnchors`) and the overlay all need to read it\n * between gestures.\n *\n * Operational when the consumer wires the `editAnchors` dep, and\n * `buildAffordanceAt` with a `getAnchorState` thunk so the dispatcher\n * classifies anchor/control-handle hits as `anchor:N`, `controlIn:N`, or\n * `controlOut:N` affordances on pointerdown. On any other affordance `start`\n * returns `{}` so other bindings can handle the drag.\n */\n\nimport type { Action } from '@weasel-js/routing';\nimport type { BindingOpts, InvocationCtx, OngoingHandle, OngoingOverlay } from '@weasel-js/routing';\nimport type { EditAnchorsDep, SnapDep } from '../depSchema';\nimport type { SnapBegin, SnapMods, SnapSession } from '../snapDep';\nimport { inputSession } from 'features/snap/session2d';\nimport { SNAP_ROLES, snapMods } from 'features/snap/roles';\nimport { ActionDisabledReason, isAnchorOrControl, resolveParams, roleReader, type ModifierRoles } from '@weasel-js/routing';\nimport {\n anchorAt,\n editAnchorSetTracked,\n moveHandleSymmetric,\n moveHandleTo,\n pathAwayFromAnchors,\n translateAnchorsSymmetric,\n type AnchorSet,\n type AnchorSymmetry,\n type PenAnchor,\n type PointOrigin,\n} from '@weasel-js/geom';\nimport { pathToAnchors, type PolygonPath } from '@weasel-js/geom';\nimport { inPlane, editingLayer } from '../planeInput';\nimport { HANDLE_LOCK_ROLES, lockHandle, locksInForce, type HandlePress } from './handleDrag';\nimport { locksOf, type EditAnchorsParams } from './params';\nimport { finishPathEditFrame, pathEditPreview } from './preview';\nimport {\n claimWheel, dragWeights, endExtensionDrags, extensionOverlays, placeHandle, startExtensionDrags, translateWeighted,\n} from './extensionDrags';\nimport type { PathEditDrag } from 'features/paths/pathEditExtension';\nimport { pathEditToolActive } from '../defaults/pathEditTools';\nimport { editedPathSymmetries } from './symmetry';\nimport { pathOperationTags } from '../pathGestureTags';\n\n/**\n * Parse an anchor affordance kind — `'anchor:N'` | `'controlIn:N'` |\n * `'controlOut:N'`, where N is the flat anchor index — or `null` when the\n * affordance is not an anchor kind.\n */\nfunction parseAnchorAffordance(\n kind: string,\n): { anchorIndex: number; part: 'anchor' | 'controlIn' | 'controlOut' } | null {\n const m = kind.match(/^(anchor|controlIn|controlOut):(\\d+)$/);\n if (!m) return null;\n return {\n anchorIndex: parseInt(m[2], 10),\n part: m[1] as 'anchor' | 'controlIn' | 'controlOut',\n };\n}\n\n/**\n * Resolve what a press on `anchorIndex` should leave selected.\n *\n * Shift toggles. A bare press on an anchor that's already part of the\n * selection keeps the selection intact (so the drag moves the whole set);\n * a bare press anywhere else collapses to just that anchor.\n */\nexport function selectionAfterAnchorPress(\n current: ReadonlySet<number>,\n anchorIndex: number,\n additive: boolean,\n): Set<number> {\n if (additive) {\n const next = new Set(current);\n if (next.has(anchorIndex)) next.delete(anchorIndex);\n else next.add(anchorIndex);\n return next;\n }\n if (current.has(anchorIndex) && current.size > 1) return new Set(current);\n return new Set([anchorIndex]);\n}\n\ninterface EditAnchorsScratch {\n dep: EditAnchorsDep;\n id: string;\n anchorIndex: number;\n /** `'anchor'` drags on-curve points (and their handles) by a delta.\n * `'controlIn'` / `'controlOut'` drag one control point to an absolute\n * world coord. */\n part: 'anchor' | 'controlIn' | 'controlOut';\n /** Anchors this drag moves — the pressed one, or the whole anchor\n * selection when the press landed inside it. Flat indices. */\n dragging: readonly number[];\n /** World position of the pressed anchor at drag start; the drag delta is\n * measured from here rather than accumulated per move, so the geometry\n * is always derived from `originPose` in one step. */\n anchorOrigin: { x: number; y: number };\n /** The anchor and the dragged control handle at the press, for a handle drag. */\n handlePress: HandlePress | null;\n /** The guide the lock in force drew at the last move. */\n guide: OngoingOverlay | null;\n /** The path-edit extensions' shares of this drag. */\n drags: readonly PathEditDrag[];\n /** The last move, to redo when an extension claims the wheel. */\n lastMove: InvocationCtx | null;\n originPose: PolygonPath;\n currentPose: PolygonPath;\n /** The transforms `originPose` is its own image under, read at the press. */\n symmetries: readonly AnchorSymmetry[];\n /** Where each point of `currentPose` came from in `originPose`. */\n origins?: readonly (PointOrigin | null)[];\n /** Other nodes' world paths the finished edit moves with it. */\n others: readonly { id: string; path: PolygonPath }[];\n}\n\ntype Point = { x: number; y: number };\n\n/**\n * An anchor drag's snap: a `point` session, begun on the first move, for\n * where the pressed anchor goes. The edited path offers no targets but what\n * the drag leaves still of it, and Shift holds the anchor to the axes and\n * diagonals through its press.\n */\nfunction anchorDragSnap(\n snap: SnapDep | undefined,\n press: { id: string; origin: Point; originPose: PolygonPath; dragging: readonly number[] },\n): { at(world: Point, mods: SnapMods): Point; end(): void } {\n let session: SnapSession | null = null;\n return {\n at(world, mods) {\n if (!session) {\n const { path, anchors } = pathAwayFromAnchors(press.originPose, press.dragging);\n const req: SnapBegin = {\n gesture: 'point', press: press.origin, ids: [press.id],\n also: { paths: [path], anchors }, constrain: 'axis',\n };\n session = snap?.begin(req) ?? inputSession(req);\n }\n return session.at(world, mods).point;\n },\n end() {\n session?.end();\n session = null;\n },\n };\n}\n\nconst paramsOf = (opts: BindingOpts | undefined): EditAnchorsParams =>\n (resolveParams(opts?.params) ?? {}) as EditAnchorsParams;\n\n/** Take `next` as the frame to show, finished by the dep. A frame it cannot\n * finish is dropped, so the path stays at the last one it could. */\nfunction track(scratch: EditAnchorsScratch, next: ReturnType<typeof editAnchorSetTracked>): void {\n if (!next) return;\n const { dep, id } = scratch;\n const done = finishPathEditFrame(dep, id, next.path, next.origins);\n if (!done) return;\n scratch.currentPose = done.path as PolygonPath;\n scratch.others = done.others as EditAnchorsScratch['others'];\n scratch.origins = next.origins;\n}\n\n/** The anchor and the handle a press on `part` grabbed; null for an anchor press. */\nfunction handlePressOf(\n origin: PenAnchor,\n part: EditAnchorsScratch['part'],\n): HandlePress | null {\n const handle = part === 'controlIn' ? origin.inHandle : part === 'controlOut' ? origin.outHandle : undefined;\n return handle ? { anchor: { x: origin.x, y: origin.y }, handle: { ...handle } } : null;\n}\n\n/** Move the dragged anchors to follow `to`, and the anchors the extensions\n * weight by their shares. A weighted drag moves no symmetric images. */\nfunction moveAnchors(scratch: EditAnchorsScratch, to: Point): void {\n if (scratch.dragging.length === 0) return;\n const dx = to.x - scratch.anchorOrigin.x;\n const dy = to.y - scratch.anchorOrigin.y;\n const weights = dragWeights(scratch.drags, scratch.dragging);\n track(scratch, editAnchorSetTracked(scratch.originPose, (set) => {\n if (weights) translateWeighted(set, weights, dx, dy);\n else translateAnchorsSymmetric(set, scratch.dragging, dx, dy, scratch.symmetries);\n }));\n}\n\n/** The modifiers `editAnchors` reads while it runs: Shift on the press adds\n * to the anchor selection, Alt breaks a smooth anchor's mirror, the handle\n * locks, and Shift holds an anchor drag to the axes and diagonals. An anchor\n * drag also reads `snap.suspend` at its default key, Ctrl, which is not\n * declared here because on a handle Ctrl holds the length. */\nexport const EDIT_ANCHORS_ROLES = {\n 'selection.add': { label: 'Add to the anchor selection', keys: ['shift'] },\n 'mirror.break': { label: \"Break the handles' mirror\", keys: ['alt'] },\n ...HANDLE_LOCK_ROLES,\n 'snap.constrain': SNAP_ROLES['snap.constrain'],\n} as const satisfies ModifierRoles;\n\nconst roleHeld = roleReader(EDIT_ANCHORS_ROLES);\n\n/** Move the dragged handle toward the pointer, under the locks in force, then\n * wherever the extensions place it. */\nfunction moveHandle(scratch: EditAnchorsScratch, ctx: InvocationCtx, params: EditAnchorsParams): void {\n const side = scratch.part === 'controlIn' ? 'in' : 'out';\n // Modifiers and held locks are read live, so either can change mid-drag.\n const held = (ctx.deps.editAnchors ?? scratch.dep).handleLocks ?? locksOf(params.handleLock);\n const locks = locksInForce(held, ctx);\n const locked = scratch.handlePress ? lockHandle(scratch.handlePress, ctx.world, locks) : { to: ctx.world, guide: null };\n scratch.guide = locked.guide;\n const to = placeHandle(scratch.drags, locked.to, { locks });\n const alt = roleHeld(ctx, 'mirror.break');\n const move = scratch.symmetries.length > 0\n ? (set: AnchorSet) => moveHandleSymmetric(set, scratch.anchorIndex, side, to.x, to.y, alt, scratch.symmetries)\n : (set: AnchorSet) => moveHandleTo(set, scratch.anchorIndex, side, to.x, to.y, alt);\n track(scratch, editAnchorSetTracked(scratch.originPose, move));\n}\n\n/**\n * What a press drags, the selection updated first, before any move: a handle\n * press drags its anchor's handle; an anchor press selects as\n * {@link selectionAfterAnchorPress} says and drags what it left selected —\n * nothing when Shift took the pressed anchor out of it.\n */\nfunction draggedByPress(\n dep: EditAnchorsDep,\n press: { anchorIndex: number; part: EditAnchorsScratch['part'] },\n shift: boolean,\n): readonly number[] {\n if (press.part !== 'anchor') return [press.anchorIndex];\n const next = selectionAfterAnchorPress(dep.selectedAnchors, press.anchorIndex, shift);\n dep.setSelectedAnchors(next);\n return next.has(press.anchorIndex) ? [...next] : [];\n}\n\n/** Static descriptor for the `editAnchors` Action. */\nexport const editAnchorsAction: Action & { requires: string[] } = inPlane({\n id: 'editAnchors',\n label: 'Edit Anchors',\n tags: pathOperationTags('drag'),\n defaultBinding: {\n 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 ? { path: scratch.currentPose } : null), () => (active ? scratch.others : []),\n );\n if (!preview) return {};\n\n const dragging = draggedByPress(dep, anchorInfo, roleHeld(ctx, 'selection.add'));\n\n const scratch: EditAnchorsScratch = {\n dep,\n id: editingId,\n anchorIndex: anchorInfo.anchorIndex,\n part: anchorInfo.part,\n dragging,\n anchorOrigin: { x: origin.x, y: origin.y },\n handlePress: handlePressOf(origin, anchorInfo.part),\n guide: null,\n drags: startExtensionDrags(dep.extensions, {\n id: editingId,\n node: dep.getNodeShape(editingId),\n path: worldPath,\n anchors: decoded,\n part: anchorInfo.part,\n anchorIndex: anchorInfo.anchorIndex,\n dragging: anchorInfo.part === 'anchor' ? dragging : [],\n scale: ctx.deps.view?.get().scale,\n ...(ctx.deps.snap ? { snap: ctx.deps.snap } : {}),\n }),\n lastMove: null,\n originPose: worldPath,\n currentPose: worldPath,\n symmetries: editedPathSymmetries(ctx.deps.pathSymmetry, editingId, decoded),\n others: [],\n };\n const snapAnchor = anchorDragSnap(ctx.deps.snap, {\n id: editingId, origin: scratch.anchorOrigin, originPose: worldPath, dragging,\n });\n\n const move = (moveCtx: InvocationCtx): void => {\n scratch.lastMove = moveCtx;\n if (scratch.part !== 'anchor') return moveHandle(scratch, moveCtx, paramsOf(opts));\n moveAnchors(scratch, snapAnchor.at(moveCtx.world, snapMods(moveCtx)));\n };\n\n return {\n kind: 'edit-anchors',\n onMove(moveCtx: InvocationCtx): void {\n move(moveCtx);\n active = true;\n },\n onWheel(wheelCtx: InvocationCtx): boolean {\n if (!wheelCtx.wheel || !claimWheel(scratch.drags, wheelCtx.wheel)) return false;\n if (scratch.lastMove) move(scratch.lastMove);\n return true;\n },\n overlay(): readonly OngoingOverlay[] | null {\n if (!active) return null;\n const extra = scratch.drags.length > 0\n ? extensionOverlays(scratch.drags, pathToAnchors(scratch.currentPose) as AnchorSet)\n : [];\n const all = scratch.guide ? [scratch.guide, ...extra] : extra;\n return all.length > 0 ? all : null;\n },\n onEnd(_endCtx: InvocationCtx, reason: 'commit' | 'cancel'): void {\n active = false;\n try {\n if (reason === 'cancel' || scratch.originPose === scratch.currentPose) return;\n scratch.dep.applyEdit(scratch.id, scratch.currentPose, 'Edit anchors', scratch.origins);\n } finally {\n snapAnchor.end();\n endExtensionDrags(scratch.drags);\n }\n },\n previewIds: preview.previewIds,\n previewPose: preview.previewPose,\n previewData: preview.previewData,\n };\n },\n },\n // A path-edit tool has the drag.\n enabled: (deps) => (pathEditToolActive(deps) ? ActionDisabledReason.NotApplicable : true),\n}, editingLayer);\n"],"mappings":"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"}
1
+ {"version":3,"file":"action.js","names":[],"sources":["../../../../src/interactions/actions/edit-anchors/action.ts"],"sourcesContent":["/**\n * `editAnchorsAction` — ongoing Action descriptor for editing polygon anchors.\n *\n * - **Press** selects. Bare press replaces the anchor selection; Shift\n * toggles the pressed anchor in or out of it. Pressing an anchor that\n * is already part of a multi-anchor selection leaves the selection\n * alone, so you can grab a set and move it.\n * - **Drag** then moves whatever the press left selected — one anchor,\n * or every selected anchor together. The pressed anchor snaps through\n * the `snap` dep's `point` session, to what the drag leaves still of the\n * path and to the canvas's targets; Shift holds it to the axes and\n * diagonals through its press, and Ctrl suspends the snap.\n * - **Dragging a control handle** moves just that handle, mirroring the\n * opposite one while the anchor is smooth. Alt breaks the mirror; Shift\n * holds the handle to the angle it had at the press, changing only its\n * length; Mod (Cmd or Ctrl) holds its length, changing only its angle;\n * both freeze it. The `editAnchors` dep's `handleLocks`, or else the\n * `handleLock` param ({@link EditAnchorsParams}), holds a lock with no\n * modifier. The lock in force draws its guide as a `polyline` overlay\n * of role `lock`: the line, or the circle round the anchor.\n * - **Path-edit extensions shape the drag** through their `drag`: anchors\n * that follow an anchor drag by a share of it, where a dragged handle\n * goes after the locks, the wheel mid-drag, and overlays of their own.\n * - **Every frame is finished** by the dep's `finishEdit`, so what a\n * path-edit extension keeps true holds while the drag runs.\n * - **Under a `pathSymmetry` dep**, on a path that is its own image under\n * some of its transforms, every image of what is dragged follows it.\n *\n * Selection lives on the `editAnchors` dep (flat anchor indices) rather\n * than in this handle's scratch, because the keyboard actions\n * (`nudgeAnchors`, `deleteAnchors`) and the overlay all need to read it\n * between gestures.\n *\n * Operational when the consumer wires the `editAnchors` dep, and\n * `buildAffordanceAt` with a `getAnchorState` thunk so the dispatcher\n * classifies anchor/control-handle hits as `anchor:N`, `controlIn:N`, or\n * `controlOut:N` affordances on pointerdown. On any other affordance `start`\n * returns `{}` so other bindings can handle the drag.\n */\n\nimport type { Action } from '@weasel-js/routing';\nimport type { BindingOpts, InvocationCtx, OngoingHandle, OngoingOverlay } 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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"}
@@ -142,14 +142,20 @@ var flipAction = {
142
142
  key: ["h", "H"],
143
143
  mods: { shift: true }
144
144
  },
145
- opts: { params: { axis: "x" } }
145
+ opts: {
146
+ id: "flip.x",
147
+ params: { axis: "x" }
148
+ }
146
149
  }, {
147
150
  spec: {
148
151
  kind: "key",
149
152
  key: ["v", "V"],
150
153
  mods: { shift: true }
151
154
  },
152
- opts: { params: { axis: "y" } }
155
+ opts: {
156
+ id: "flip.y",
157
+ params: { axis: "y" }
158
+ }
153
159
  }],
154
160
  eligible: { capability: "transforms-selection" },
155
161
  requires: [
@@ -1 +1 @@
1
- {"version":3,"file":"action.js","names":[],"sources":["../../../../src/interactions/actions/flip/action.ts"],"sourcesContent":["import type { NodeId, Scene } from 'core/scene/types';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport { shapeSources } from '../selectionSubtree';\nimport type { Op } from 'core/ops/types';\nimport { multiply, rotate, translate, type Affine } from '@weasel-js/geom';\nimport { createTransformOp } from 'core/ops/transform';\nimport type { PoseDescriptor } from '../resize/geometry';\nimport { poseDescriptorOf } from '../poseDescriptorDep';\nimport {\n flipPoseAboutBounds,\n flipPoseViaDescriptor,\n type FlipAxis,\n type FlipPivot,\n} from './helpers';\nimport { unionAABB } from 'core/geometry/unionBounds';\nimport { drawnBoxes, type DrawnBoxes } from '../drawnBoxes';\nimport { isNoInkExtents } from 'core/geometry/inkExtents';\nimport { scenePoseFrame, type PoseFrame } from '../poseFrame';\nimport { actionReferenceSources, resolveSpatialReference } from '../spatialReference';\nimport type { PointerContextValue } from '@weasel-js/routing/react';\nimport type { RectPose } from 'core/scene/types';\nimport type { Action } from '@weasel-js/routing';\nimport { defaultCommitAdapter } from '../defaultCommitAdapter';\nimport { transformApplyOps } from '../transformFinish';\nimport { requiresSelection } from '../defaults/requiresSelection';\nimport type { SelectionApi } from 'core/selection/useSelection';\nimport { geometryDataOp, type GeometryProjection } from '../geometryProjection';\nimport { icon } from '../../../icons.gen';\n\n/**\n * The world mirror about `axis` through the center of `pivotWorld`, written in\n * the frame `id` stores its data in. `geometryProjection.transform` is given\n * the affine the node's own pose underwent, and data-held geometry is local —\n * so a world mirror has to be conjugated by the frame or an asymmetric shape\n * reflects across the wrong line while its pose lands correctly.\n *\n * The frame's turn comes back as the rotation on a rebased zero-size pose at\n * the pivot point, so no composition strategy has to expose one.\n */\nfunction localMirror(\n axis: FlipAxis,\n frame: PoseFrame<unknown>,\n id: Parameters<PoseFrame<unknown>['local']>[0],\n pivotWorld: { x: number; y: number; width: number; height: number },\n): Affine {\n const at = frame.local(id, {\n x: pivotWorld.x + pivotWorld.width / 2,\n y: pivotWorld.y + pivotWorld.height / 2,\n width: 0,\n height: 0,\n rotation: 0,\n }) as RectPose;\n const theta = at.rotation ?? 0;\n const mirror: Affine = axis === 'x' ? [-1, 0, 0, 1, 0, 0] : [1, 0, 0, -1, 0, 0];\n const turned = multiply(multiply(rotate(theta), mirror), rotate(-theta));\n return multiply(translate(at.x, at.y), multiply(turned, translate(-at.x, -at.y)));\n}\n\n/**\n * Flip the current selection in `scene` along `axis`, as one undo entry.\n *\n * Pivot: `'each'` (per-item own AABB) unless the caller passes `'union'`,\n * which mirrors every pose about the selection's *visual* union AABB — the\n * rotated ink extents — so items swap sides as well as reflect, or a\n * `SpatialReference`, which mirrors about the center of what it names. A rotated\n * member's own mirrored position needs no expansion: `axisAlignedBounds`\n * keeps the stored box's center, so mirroring either box lands the same place.\n */\nfunction flipSelection(\n selection: SelectionApi,\n scene: Scene<unknown, string, unknown>,\n axis: FlipAxis,\n pivot: FlipPivot,\n applyOps: ((ops: Op[], label: string) => void) | undefined,\n geometryProjection: GeometryProjection | undefined,\n geom: PoseDescriptor<unknown>,\n params: Record<string, unknown> | undefined,\n pointer: PointerContextValue | undefined,\n boxesOver: (frame: PoseFrame<unknown>) => DrawnBoxes,\n): void {\n const selected = selection.get();\n if (selected.length === 0) return;\n const frame = scenePoseFrame(scene);\n const boxes = boxesOver(frame);\n\n // A group whose pose derives from its children mirrors by mirroring them,\n // about the group's own box where each selected node flips in place.\n const ids: NodeId[] = [];\n const ownPivot = new Map<string, Bounds>();\n for (const id of selected) {\n const members = shapeSources(scene, [id]) as NodeId[];\n const stoodIn = members.length !== 1 || members[0] !== id;\n const box = stoodIn ? boxes.aabb(id, frame.world(id)) : null;\n for (const member of members) {\n if (ids.includes(member)) continue;\n ids.push(member);\n if (box) ownPivot.set(member, box);\n }\n }\n\n const worlds = new Map<string, unknown>();\n for (const id of ids) {\n if (scene.get(id) === undefined) continue;\n const world = frame.world(id);\n worlds.set(id as string, world);\n // A shape whose ink reaches past its box unevenly flips in place about\n // the box it is drawn with, so that box stays put.\n if (!ownPivot.has(id as string) && !isNoInkExtents(boxes.extents(id, world))) {\n ownPivot.set(id as string, boxes.aabb(id, world));\n }\n }\n let sharedPivot: ReturnType<typeof unionAABB> = null;\n if (pivot === 'union') {\n sharedPivot = unionAABB([...worlds].map(([id, p]) => boxes.aabb(id, p)));\n } else if (pivot !== 'each') {\n sharedPivot = resolveSpatialReference(pivot, actionReferenceSources(params, pointer, scene, frame, boxes));\n if (sharedPivot === null) return;\n }\n\n const ops: Op[] = [];\n for (const id of ids) {\n const node = scene.get(id);\n if (!node) continue;\n const world = worlds.get(id as string)!;\n const about = sharedPivot ?? ownPivot.get(id as string) ?? null;\n const flipped = about\n ? flipPoseAboutBounds(world, axis, geom, about)\n : flipPoseViaDescriptor(world, axis, geom);\n ops.push(createTransformOp<unknown>({\n id: id as string,\n from: node.pose,\n to: frame.local(id, flipped),\n label: 'Flip',\n }));\n // Mirror the data-held geometry about the SAME pivot the pose flip uses.\n // A pose-only flip of a bare-AABB pose is identity, so this data op is\n // the only way an asymmetric shape actually mirrors.\n const g = about ?? geom.getBounds(world);\n const dataOp = geometryDataOp(\n geometryProjection,\n { id: id as string, data: node.data, pose: node.pose },\n localMirror(axis, frame, id, g),\n 'Flip',\n );\n if (dataOp) ops.push(dataOp);\n }\n if (ops.length === 0) return;\n\n if (applyOps) applyOps(ops, 'Flip');\n else scene.applyBatch(ops, 'Flip', defaultCommitAdapter(scene, selection.adapterMethods));\n}\n\n/**\n * Flips the selection. `params.axis` (`'x'` | `'y'`, default `'x'`) picks the\n * direction, and the optional `params.pivot` (a `FlipPivot`, default `'each'`)\n * picks what it mirrors about.\n */\nexport const flipAction: Action & { requires: string[] } = {\n id: 'flip',\n label: 'Flip',\n group: 'flip',\n tags: ['mirror'],\n variants: [\n { key: 'x', label: 'Flip Horizontal', icon: icon('flipX'), params: { axis: 'x' } },\n { key: 'y', label: 'Flip Vertical', icon: icon('flipY'), params: { axis: 'y' } },\n ],\n defaultBinding: [\n { spec: { kind: 'key', key: ['h', 'H'], mods: { shift: true } }, opts: { params: { axis: 'x' } } },\n { spec: { kind: 'key', key: ['v', 'V'], mods: { shift: true } }, opts: { params: { axis: 'y' } } },\n ],\n eligible: { capability: 'transforms-selection' },\n requires: [\n 'selection', 'scene', 'applyOps', 'geometryProjection', 'poseDescriptor', 'pointer', 'view', 'boundsIncludeStroke',\n 'transformFollowers',\n ],\n invoker: {\n timing: 'immediate',\n run: (deps, params) => {\n const axis = (params?.axis as FlipAxis | undefined) ?? 'x';\n const pivot = (params?.pivot as FlipPivot | undefined) ?? 'each';\n const selection = deps.selection;\n const scene = deps.scene;\n const applyOps = transformApplyOps(deps);\n const geometryProjection = deps.geometryProjection;\n if (!selection || !scene) return;\n flipSelection(\n selection, scene, axis, pivot, applyOps, geometryProjection,\n poseDescriptorOf(deps.poseDescriptor),\n params, deps.pointer,\n (frame) => drawnBoxes(deps, frame),\n );\n },\n },\n enabled: requiresSelection,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"action.js","names":[],"sources":["../../../../src/interactions/actions/flip/action.ts"],"sourcesContent":["import type { NodeId, Scene } from 'core/scene/types';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport { shapeSources } from '../selectionSubtree';\nimport type { Op } from 'core/ops/types';\nimport { multiply, rotate, translate, type Affine } from '@weasel-js/geom';\nimport { createTransformOp } from 'core/ops/transform';\nimport type { PoseDescriptor } from '../resize/geometry';\nimport { poseDescriptorOf } from '../poseDescriptorDep';\nimport {\n flipPoseAboutBounds,\n flipPoseViaDescriptor,\n type FlipAxis,\n type FlipPivot,\n} from './helpers';\nimport { unionAABB } from 'core/geometry/unionBounds';\nimport { drawnBoxes, type DrawnBoxes } from '../drawnBoxes';\nimport { isNoInkExtents } from 'core/geometry/inkExtents';\nimport { scenePoseFrame, type PoseFrame } from '../poseFrame';\nimport { actionReferenceSources, resolveSpatialReference } from '../spatialReference';\nimport type { PointerContextValue } from '@weasel-js/routing/react';\nimport type { RectPose } from 'core/scene/types';\nimport type { Action } from '@weasel-js/routing';\nimport { defaultCommitAdapter } from '../defaultCommitAdapter';\nimport { transformApplyOps } from '../transformFinish';\nimport { requiresSelection } from '../defaults/requiresSelection';\nimport type { SelectionApi } from 'core/selection/useSelection';\nimport { geometryDataOp, type GeometryProjection } from '../geometryProjection';\nimport { icon } from '../../../icons.gen';\n\n/**\n * The world mirror about `axis` through the center of `pivotWorld`, written in\n * the frame `id` stores its data in. `geometryProjection.transform` is given\n * the affine the node's own pose underwent, and data-held geometry is local —\n * so a world mirror has to be conjugated by the frame or an asymmetric shape\n * reflects across the wrong line while its pose lands correctly.\n *\n * The frame's turn comes back as the rotation on a rebased zero-size pose at\n * the pivot point, so no composition strategy has to expose one.\n */\nfunction localMirror(\n axis: FlipAxis,\n frame: PoseFrame<unknown>,\n id: Parameters<PoseFrame<unknown>['local']>[0],\n pivotWorld: { x: number; y: number; width: number; height: number },\n): Affine {\n const at = frame.local(id, {\n x: pivotWorld.x + pivotWorld.width / 2,\n y: pivotWorld.y + pivotWorld.height / 2,\n width: 0,\n height: 0,\n rotation: 0,\n }) as RectPose;\n const theta = at.rotation ?? 0;\n const mirror: Affine = axis === 'x' ? [-1, 0, 0, 1, 0, 0] : [1, 0, 0, -1, 0, 0];\n const turned = multiply(multiply(rotate(theta), mirror), rotate(-theta));\n return multiply(translate(at.x, at.y), multiply(turned, translate(-at.x, -at.y)));\n}\n\n/**\n * Flip the current selection in `scene` along `axis`, as one undo entry.\n *\n * Pivot: `'each'` (per-item own AABB) unless the caller passes `'union'`,\n * which mirrors every pose about the selection's *visual* union AABB — the\n * rotated ink extents — so items swap sides as well as reflect, or a\n * `SpatialReference`, which mirrors about the center of what it names. A rotated\n * member's own mirrored position needs no expansion: `axisAlignedBounds`\n * keeps the stored box's center, so mirroring either box lands the same place.\n */\nfunction flipSelection(\n selection: SelectionApi,\n scene: Scene<unknown, string, unknown>,\n axis: FlipAxis,\n pivot: FlipPivot,\n applyOps: ((ops: Op[], label: string) => void) | undefined,\n geometryProjection: GeometryProjection | undefined,\n geom: PoseDescriptor<unknown>,\n params: Record<string, unknown> | undefined,\n pointer: PointerContextValue | undefined,\n boxesOver: (frame: PoseFrame<unknown>) => DrawnBoxes,\n): void {\n const selected = selection.get();\n if (selected.length === 0) return;\n const frame = scenePoseFrame(scene);\n const boxes = boxesOver(frame);\n\n // A group whose pose derives from its children mirrors by mirroring them,\n // about the group's own box where each selected node flips in place.\n const ids: NodeId[] = [];\n const ownPivot = new Map<string, Bounds>();\n for (const id of selected) {\n const members = shapeSources(scene, [id]) as NodeId[];\n const stoodIn = members.length !== 1 || members[0] !== id;\n const box = stoodIn ? boxes.aabb(id, frame.world(id)) : null;\n for (const member of members) {\n if (ids.includes(member)) continue;\n ids.push(member);\n if (box) ownPivot.set(member, box);\n }\n }\n\n const worlds = new Map<string, unknown>();\n for (const id of ids) {\n if (scene.get(id) === undefined) continue;\n const world = frame.world(id);\n worlds.set(id as string, world);\n // A shape whose ink reaches past its box unevenly flips in place about\n // the box it is drawn with, so that box stays put.\n if (!ownPivot.has(id as string) && !isNoInkExtents(boxes.extents(id, world))) {\n ownPivot.set(id as string, boxes.aabb(id, world));\n }\n }\n let sharedPivot: ReturnType<typeof unionAABB> = null;\n if (pivot === 'union') {\n sharedPivot = unionAABB([...worlds].map(([id, p]) => boxes.aabb(id, p)));\n } else if (pivot !== 'each') {\n sharedPivot = resolveSpatialReference(pivot, actionReferenceSources(params, pointer, scene, frame, boxes));\n if (sharedPivot === null) return;\n }\n\n const ops: Op[] = [];\n for (const id of ids) {\n const node = scene.get(id);\n if (!node) continue;\n const world = worlds.get(id as string)!;\n const about = sharedPivot ?? ownPivot.get(id as string) ?? null;\n const flipped = about\n ? flipPoseAboutBounds(world, axis, geom, about)\n : flipPoseViaDescriptor(world, axis, geom);\n ops.push(createTransformOp<unknown>({\n id: id as string,\n from: node.pose,\n to: frame.local(id, flipped),\n label: 'Flip',\n }));\n // Mirror the data-held geometry about the SAME pivot the pose flip uses.\n // A pose-only flip of a bare-AABB pose is identity, so this data op is\n // the only way an asymmetric shape actually mirrors.\n const g = about ?? geom.getBounds(world);\n const dataOp = geometryDataOp(\n geometryProjection,\n { id: id as string, data: node.data, pose: node.pose },\n localMirror(axis, frame, id, g),\n 'Flip',\n );\n if (dataOp) ops.push(dataOp);\n }\n if (ops.length === 0) return;\n\n if (applyOps) applyOps(ops, 'Flip');\n else scene.applyBatch(ops, 'Flip', defaultCommitAdapter(scene, selection.adapterMethods));\n}\n\n/**\n * Flips the selection. `params.axis` (`'x'` | `'y'`, default `'x'`) picks the\n * direction, and the optional `params.pivot` (a `FlipPivot`, default `'each'`)\n * picks what it mirrors about.\n */\nexport const flipAction: Action & { requires: string[] } = {\n id: 'flip',\n label: 'Flip',\n group: 'flip',\n tags: ['mirror'],\n variants: [\n { key: 'x', label: 'Flip Horizontal', icon: icon('flipX'), params: { axis: 'x' } },\n { key: 'y', label: 'Flip Vertical', icon: icon('flipY'), params: { axis: 'y' } },\n ],\n defaultBinding: [\n { spec: { kind: 'key', key: ['h', 'H'], mods: { shift: true } }, opts: { id: 'flip.x', params: { axis: 'x' } } },\n { spec: { kind: 'key', key: ['v', 'V'], mods: { shift: true } }, opts: { id: 'flip.y', params: { axis: 'y' } } },\n ],\n eligible: { capability: 'transforms-selection' },\n requires: [\n 'selection', 'scene', 'applyOps', 'geometryProjection', 'poseDescriptor', 'pointer', 'view', 'boundsIncludeStroke',\n 'transformFollowers',\n ],\n invoker: {\n timing: 'immediate',\n run: (deps, params) => {\n const axis = (params?.axis as FlipAxis | undefined) ?? 'x';\n const pivot = (params?.pivot as FlipPivot | undefined) ?? 'each';\n const selection = deps.selection;\n const scene = deps.scene;\n const applyOps = transformApplyOps(deps);\n const geometryProjection = deps.geometryProjection;\n if (!selection || !scene) return;\n flipSelection(\n selection, scene, axis, pivot, applyOps, geometryProjection,\n poseDescriptorOf(deps.poseDescriptor),\n params, deps.pointer,\n (frame) => drawnBoxes(deps, frame),\n );\n },\n },\n enabled: requiresSelection,\n};\n"],"mappings":"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"}
@@ -36,6 +36,7 @@ var insertAction = inPlane({
36
36
  "insert",
37
37
  "insertLive",
38
38
  "insertInto",
39
+ "canDropInto",
39
40
  "selection",
40
41
  "snap",
41
42
  "textEdit",
@@ -1 +1 @@
1
- {"version":3,"file":"action.js","names":[],"sources":["../../../../src/interactions/actions/insert/action.ts"],"sourcesContent":["import type { Action } from '@weasel-js/routing';\nimport type { InvocationCtx, OngoingHandle, BindingOpts, OngoingOverlay } from '@weasel-js/routing';\nimport { resolveParams } from '@weasel-js/routing';\nimport { shapeKindInfo } from 'core/shapeKinds';\nimport type { KitInsertShape } from 'core/shapeKinds';\nimport { SNAP_ROLES } from 'features/snap/roles';\nimport { runBehaviorSettled } from '../gestureLifecycle';\nimport { inPlane, insertLayer } from '../planeInput';\nimport { copiesWithin } from 'canvas/copiesAbove';\nimport { scenePaintedPoseOf } from 'canvas/derivedPath';\nimport { cancelInsert, commitInsert } from './commit';\nimport { carryStart, nextFrame } from './frame';\nimport type { InsertLiveGesture } from './live';\nimport { beginInsert } from './session';\nimport { insertKind, insertShape } from './shape';\n\n/**\n * Drag-to-insert. The node `kind` is read from `opts.params.kind`.\n *\n * The drag's start and current point snap through the `snap` dep's `point`\n * sessions (never freehand pencil samples). While `snap.constrain` (Shift) is\n * held, a line's end stays on the 15° line through the snapped start nearest\n * the pointer, and lands where that line meets a target. Behaviors from the\n * binding's `opts.behaviors` (`InsertBehavior[]`) run over the two points\n * after that; on release the first answer wins (`null` aborts, `Op[]` commits\n * in place of the insert).\n *\n * The new node has no scene id until commit, so the live preview is the\n * `overlay()` surface with `kind: 'insertPreview'`. A consumer-defined kind\n * the kit cannot draw skips the preview and still commits on release.\n */\nexport const insertAction: Action & { requires: string[] } = inPlane({\n id: 'insert',\n label: 'Insert',\n group: 'insert',\n eligible: { capability: 'creates-shapes' },\n modifierRoles: SNAP_ROLES,\n requires: ['insert', 'insertLive', 'insertInto', 'selection', 'snap', 'textEdit', 'scene', 'applyOps', 'view', 'kitSettings'],\n invoker: {\n timing: 'ongoing',\n start(ctx: InvocationCtx, opts?: BindingOpts): OngoingHandle {\n const begun = beginInsert(ctx, opts);\n if (!begun) return {};\n const { session, readers } = begun;\n let frame = begun.frame;\n const gesture: InsertLiveGesture = { open: true, userRotation: 0, repositioning: false };\n const live = ctx.deps.insertLive;\n if (live) live.gesture = gesture;\n // Where the shape will land decides the copies it will be drawn in.\n const scene = ctx.deps.scene;\n const copies = scene ? copiesWithin(scene, session.parent, scenePaintedPoseOf(scene)) : [];\n\n return {\n kind: 'insert',\n onMove(moveCtx: InvocationCtx): void {\n if (gesture.repositioning) carryStart(session, frame.pointer, moveCtx.world, moveCtx);\n frame = nextFrame(session, readers, frame, moveCtx);\n },\n overlay(): OngoingOverlay | null {\n if (!gesture.open) return null;\n // Resolve params on every overlay() so thunked params (polygon\n // sides ticking via ArrowUp mid-drag) reflect in the live preview.\n const resolved = resolveParams(session.opts?.params);\n if (!shapeKindInfo(insertKind(resolved))?.insertPreview) return null;\n const { bounds, extras, start } = insertShape(resolved, frame, frame.points, gesture.userRotation);\n return {\n kind: 'insertPreview',\n shape: extras.kind as KitInsertShape,\n bounds,\n extras,\n // Anchor dot at the click point: radial shapes have no vertex\n // there, and in center mode it marks the growth axis.\n anchorPoint: { x: start.x, y: start.y },\n ...(copies.length > 0 ? { copies } : {}),\n };\n },\n onEnd(endCtx: InvocationCtx, reason: 'commit' | 'cancel'): void {\n gesture.open = false;\n if (live?.gesture === gesture) live.gesture = null;\n let committed = false;\n try {\n if (reason === 'cancel') cancelInsert(session);\n else committed = commitInsert(session, frame, endCtx.drag?.points ?? frame.points, gesture.userRotation);\n } finally {\n session.snap.close();\n runBehaviorSettled(session.behaviors, session.gestureCtx, committed);\n }\n },\n };\n },\n },\n /** Insert needs no selection; anything but `true` blocks dispatcher-routed inserts. */\n enabled: () => true as const,\n}, insertLayer);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,IAAa,eAAgD,QAAQ;CACnE,IAAI;CACJ,OAAO;CACP,OAAO;CACP,UAAU,EAAE,YAAY,iBAAiB;CACzC,eAAe;CACf,UAAU;EAAC;EAAU;EAAc;EAAc;EAAa;EAAQ;EAAY;EAAS;EAAY;EAAQ;CAAa;CAC5H,SAAS;EACP,QAAQ;EACR,MAAM,KAAoB,MAAmC;GAC3D,MAAM,QAAQ,YAAY,KAAK,IAAI;GACnC,IAAI,CAAC,OAAO,OAAO,CAAC;GACpB,MAAM,EAAE,SAAS,YAAY;GAC7B,IAAI,QAAQ,MAAM;GAClB,MAAM,UAA6B;IAAE,MAAM;IAAM,cAAc;IAAG,eAAe;GAAM;GACvF,MAAM,OAAO,IAAI,KAAK;GACtB,IAAI,MAAM,KAAK,UAAU;GAEzB,MAAM,QAAQ,IAAI,KAAK;GACvB,MAAM,SAAS,QAAQ,aAAa,OAAO,QAAQ,QAAQ,mBAAmB,KAAK,CAAC,IAAI,CAAC;GAEzF,OAAO;IACL,MAAM;IACN,OAAO,SAA8B;KACnC,IAAI,QAAQ,eAAe,WAAW,SAAS,MAAM,SAAS,QAAQ,OAAO,OAAO;KACpF,QAAQ,UAAU,SAAS,SAAS,OAAO,OAAO;IACpD;IACA,UAAiC;KAC/B,IAAI,CAAC,QAAQ,MAAM,OAAO;KAG1B,MAAM,WAAW,cAAc,QAAQ,MAAM,MAAM;KACnD,IAAI,CAAC,cAAc,WAAW,QAAQ,CAAC,CAAC,EAAE,eAAe,OAAO;KAChE,MAAM,EAAE,QAAQ,QAAQ,UAAU,YAAY,UAAU,OAAO,MAAM,QAAQ,QAAQ,YAAY;KACjG,OAAO;MACL,MAAM;MACN,OAAO,OAAO;MACd;MACA;MAGA,aAAa;OAAE,GAAG,MAAM;OAAG,GAAG,MAAM;MAAE;MACtC,GAAI,OAAO,SAAS,IAAI,EAAE,OAAO,IAAI,CAAC;KACxC;IACF;IACA,MAAM,QAAuB,QAAmC;KAC9D,QAAQ,OAAO;KACf,IAAI,MAAM,YAAY,SAAS,KAAK,UAAU;KAC9C,IAAI,YAAY;KAChB,IAAI;MACF,IAAI,WAAW,UAAU,aAAa,OAAO;WACxC,YAAY,aAAa,SAAS,OAAO,OAAO,MAAM,UAAU,MAAM,QAAQ,QAAQ,YAAY;KACzG,UAAU;MACR,QAAQ,KAAK,MAAM;MACnB,mBAAmB,QAAQ,WAAW,QAAQ,YAAY,SAAS;KACrE;IACF;GACF;EACF;CACF;;CAEA,eAAe;AACjB,GAAG,WAAW"}
1
+ {"version":3,"file":"action.js","names":[],"sources":["../../../../src/interactions/actions/insert/action.ts"],"sourcesContent":["import type { Action } from '@weasel-js/routing';\nimport type { InvocationCtx, OngoingHandle, BindingOpts, OngoingOverlay } from '@weasel-js/routing';\nimport { resolveParams } from '@weasel-js/routing';\nimport { shapeKindInfo } from 'core/shapeKinds';\nimport type { KitInsertShape } from 'core/shapeKinds';\nimport { SNAP_ROLES } from 'features/snap/roles';\nimport { runBehaviorSettled } from '../gestureLifecycle';\nimport { inPlane, insertLayer } from '../planeInput';\nimport { copiesWithin } from 'canvas/copiesAbove';\nimport { scenePaintedPoseOf } from 'canvas/derivedPath';\nimport { cancelInsert, commitInsert } from './commit';\nimport { carryStart, nextFrame } from './frame';\nimport type { InsertLiveGesture } from './live';\nimport { beginInsert } from './session';\nimport { insertKind, insertShape } from './shape';\n\n/**\n * Drag-to-insert. The node `kind` is read from `opts.params.kind`.\n *\n * The drag's start and current point snap through the `snap` dep's `point`\n * sessions (never freehand pencil samples). While `snap.constrain` (Shift) is\n * held, a line's end stays on the 15° line through the snapped start nearest\n * the pointer, and lands where that line meets a target. Behaviors from the\n * binding's `opts.behaviors` (`InsertBehavior[]`) run over the two points\n * after that; on release the first answer wins (`null` aborts, `Op[]` commits\n * in place of the insert).\n *\n * The new node has no scene id until commit, so the live preview is the\n * `overlay()` surface with `kind: 'insertPreview'`. A consumer-defined kind\n * the kit cannot draw skips the preview and still commits on release.\n */\nexport const insertAction: Action & { requires: string[] } = inPlane({\n id: 'insert',\n label: 'Insert',\n group: 'insert',\n eligible: { capability: 'creates-shapes' },\n modifierRoles: SNAP_ROLES,\n requires: ['insert', 'insertLive', 'insertInto', 'canDropInto', 'selection', 'snap', 'textEdit', 'scene', 'applyOps', 'view', 'kitSettings'],\n invoker: {\n timing: 'ongoing',\n start(ctx: InvocationCtx, opts?: BindingOpts): OngoingHandle {\n const begun = beginInsert(ctx, opts);\n if (!begun) return {};\n const { session, readers } = begun;\n let frame = begun.frame;\n const gesture: InsertLiveGesture = { open: true, userRotation: 0, repositioning: false };\n const live = ctx.deps.insertLive;\n if (live) live.gesture = gesture;\n // Where the shape will land decides the copies it will be drawn in.\n const scene = ctx.deps.scene;\n const copies = scene ? copiesWithin(scene, session.parent, scenePaintedPoseOf(scene)) : [];\n\n return {\n kind: 'insert',\n onMove(moveCtx: InvocationCtx): void {\n if (gesture.repositioning) carryStart(session, frame.pointer, moveCtx.world, moveCtx);\n frame = nextFrame(session, readers, frame, moveCtx);\n },\n overlay(): OngoingOverlay | null {\n if (!gesture.open) return null;\n // Resolve params on every overlay() so thunked params (polygon\n // sides ticking via ArrowUp mid-drag) reflect in the live preview.\n const resolved = resolveParams(session.opts?.params);\n if (!shapeKindInfo(insertKind(resolved))?.insertPreview) return null;\n const { bounds, extras, start } = insertShape(resolved, frame, frame.points, gesture.userRotation);\n return {\n kind: 'insertPreview',\n shape: extras.kind as KitInsertShape,\n bounds,\n extras,\n // Anchor dot at the click point: radial shapes have no vertex\n // there, and in center mode it marks the growth axis.\n anchorPoint: { x: start.x, y: start.y },\n ...(copies.length > 0 ? { copies } : {}),\n };\n },\n onEnd(endCtx: InvocationCtx, reason: 'commit' | 'cancel'): void {\n gesture.open = false;\n if (live?.gesture === gesture) live.gesture = null;\n let committed = false;\n try {\n if (reason === 'cancel') cancelInsert(session);\n else committed = commitInsert(session, frame, endCtx.drag?.points ?? frame.points, gesture.userRotation);\n } finally {\n session.snap.close();\n runBehaviorSettled(session.behaviors, session.gestureCtx, committed);\n }\n },\n };\n },\n },\n /** Insert needs no selection; anything but `true` blocks dispatcher-routed inserts. */\n enabled: () => true as const,\n}, insertLayer);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,IAAa,eAAgD,QAAQ;CACnE,IAAI;CACJ,OAAO;CACP,OAAO;CACP,UAAU,EAAE,YAAY,iBAAiB;CACzC,eAAe;CACf,UAAU;EAAC;EAAU;EAAc;EAAc;EAAe;EAAa;EAAQ;EAAY;EAAS;EAAY;EAAQ;CAAa;CAC3I,SAAS;EACP,QAAQ;EACR,MAAM,KAAoB,MAAmC;GAC3D,MAAM,QAAQ,YAAY,KAAK,IAAI;GACnC,IAAI,CAAC,OAAO,OAAO,CAAC;GACpB,MAAM,EAAE,SAAS,YAAY;GAC7B,IAAI,QAAQ,MAAM;GAClB,MAAM,UAA6B;IAAE,MAAM;IAAM,cAAc;IAAG,eAAe;GAAM;GACvF,MAAM,OAAO,IAAI,KAAK;GACtB,IAAI,MAAM,KAAK,UAAU;GAEzB,MAAM,QAAQ,IAAI,KAAK;GACvB,MAAM,SAAS,QAAQ,aAAa,OAAO,QAAQ,QAAQ,mBAAmB,KAAK,CAAC,IAAI,CAAC;GAEzF,OAAO;IACL,MAAM;IACN,OAAO,SAA8B;KACnC,IAAI,QAAQ,eAAe,WAAW,SAAS,MAAM,SAAS,QAAQ,OAAO,OAAO;KACpF,QAAQ,UAAU,SAAS,SAAS,OAAO,OAAO;IACpD;IACA,UAAiC;KAC/B,IAAI,CAAC,QAAQ,MAAM,OAAO;KAG1B,MAAM,WAAW,cAAc,QAAQ,MAAM,MAAM;KACnD,IAAI,CAAC,cAAc,WAAW,QAAQ,CAAC,CAAC,EAAE,eAAe,OAAO;KAChE,MAAM,EAAE,QAAQ,QAAQ,UAAU,YAAY,UAAU,OAAO,MAAM,QAAQ,QAAQ,YAAY;KACjG,OAAO;MACL,MAAM;MACN,OAAO,OAAO;MACd;MACA;MAGA,aAAa;OAAE,GAAG,MAAM;OAAG,GAAG,MAAM;MAAE;MACtC,GAAI,OAAO,SAAS,IAAI,EAAE,OAAO,IAAI,CAAC;KACxC;IACF;IACA,MAAM,QAAuB,QAAmC;KAC9D,QAAQ,OAAO;KACf,IAAI,MAAM,YAAY,SAAS,KAAK,UAAU;KAC9C,IAAI,YAAY;KAChB,IAAI;MACF,IAAI,WAAW,UAAU,aAAa,OAAO;WACxC,YAAY,aAAa,SAAS,OAAO,OAAO,MAAM,UAAU,MAAM,QAAQ,QAAQ,YAAY;KACzG,UAAU;MACR,QAAQ,KAAK,MAAM;MACnB,mBAAmB,QAAQ,WAAW,QAAQ,YAAY,SAAS;KACrE;IACF;GACF;EACF;CACF;;CAEA,eAAe;AACjB,GAAG,WAAW"}
@@ -1,9 +1,11 @@
1
+ import { admitsChild } from "../admitsChild.js";
2
+
1
3
  //#region src/interactions/actions/insert/parent.ts
2
- /** The container an insert lands in: the one the `insertInto` dep's `into`
3
- * names, or null, the top of the tree, when it names no container. */
4
- function insertParent(scene, into) {
5
- if (into == null || !scene) return null;
6
- return scene.get(into)?.kind === "container" ? into : null;
4
+ /** The node an insert lands in: the one the `insertInto` dep's `into` names when
5
+ * it exists and admits the insert, or null, the top of the tree. */
6
+ function insertParent(scene, into, canDropInto) {
7
+ if (into == null || !scene || scene.get(into) === void 0) return null;
8
+ return admitsChild(scene, into, [], canDropInto) ? into : null;
7
9
  }
8
10
 
9
11
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"parent.js","names":[],"sources":["../../../../src/interactions/actions/insert/parent.ts"],"sourcesContent":["import type { NodeId } from 'core/scene/types';\n\n/** What finding an insert's container reads of a scene. A `Scene` satisfies it. */\ninterface ContainerReader {\n get(id: NodeId): { kind: string } | undefined;\n}\n\n/** The container an insert lands in: the one the `insertInto` dep's `into`\n * names, or null, the top of the tree, when it names no container. */\nexport function insertParent(scene: ContainerReader | undefined, into: string | null | undefined): NodeId | null {\n if (into == null || !scene) return null;\n return scene.get(into as NodeId)?.kind === 'container' ? (into as NodeId) : null;\n}\n"],"mappings":";;;AASA,SAAgB,aAAa,OAAoC,MAAgD;CAC/G,IAAI,QAAQ,QAAQ,CAAC,OAAO,OAAO;CACnC,OAAO,MAAM,IAAI,IAAc,CAAC,EAAE,SAAS,cAAe,OAAkB;AAC9E"}
1
+ {"version":3,"file":"parent.js","names":[],"sources":["../../../../src/interactions/actions/insert/parent.ts"],"sourcesContent":["import type { NodeId } from 'core/scene/types';\nimport { admitsChild } from '../admitsChild';\nimport type { CanDropIntoDep } from '../layoutDeps';\n\n/** What finding an insert's parent reads of a scene. A `Scene` satisfies it. */\ninterface ParentReader {\n get(id: NodeId): { kind: string } | undefined;\n}\n\n/** The node an insert lands in: the one the `insertInto` dep's `into` names when\n * it exists and admits the insert, or null, the top of the tree. */\nexport function insertParent(\n scene: ParentReader | undefined,\n into: string | null | undefined,\n canDropInto?: CanDropIntoDep,\n): NodeId | null {\n if (into == null || !scene || scene.get(into as NodeId) === undefined) return null;\n return admitsChild(scene, into as NodeId, [], canDropInto) ? (into as NodeId) : null;\n}\n"],"mappings":";;;;;AAWA,SAAgB,aACd,OACA,MACA,aACe;CACf,IAAI,QAAQ,QAAQ,CAAC,SAAS,MAAM,IAAI,IAAc,MAAM,QAAW,OAAO;CAC9E,OAAO,YAAY,OAAO,MAAgB,CAAC,GAAG,WAAW,IAAK,OAAkB;AAClF"}
@@ -1,9 +1,9 @@
1
+ import { insertParent } from "./parent.js";
1
2
  import { inPlane, insertLayer } from "../planeInput.js";
2
3
  import { SNAP_ROLES, snapMods } from "../../../features/snap/roles.js";
3
4
  import { minSizeOf } from "../../../core/scene/minSize.js";
4
5
  import { insertKind } from "./shape.js";
5
6
  import { enterInsertedText } from "./commit.js";
6
- import { insertParent } from "./parent.js";
7
7
 
8
8
  //#region src/interactions/actions/insert/placeAction.ts
9
9
  /** What the dispatcher lays under an immediate action's params for a click. */
@@ -43,6 +43,7 @@ var placeAction = inPlane({
43
43
  requires: [
44
44
  "insert",
45
45
  "insertInto",
46
+ "canDropInto",
46
47
  "selection",
47
48
  "snap",
48
49
  "textEdit",
@@ -63,7 +64,7 @@ var placeAction = inPlane({
63
64
  if (!dep || !size || typeof x !== "number" || typeof y !== "number") return;
64
65
  if (!(size.width > 0 && size.height > 0)) return;
65
66
  deps.selection?.clear();
66
- const parent = insertParent(deps.scene, deps.insertInto?.get());
67
+ const parent = insertParent(deps.scene, deps.insertInto?.get(), deps.canDropInto);
67
68
  const snap = deps.snap?.begin({
68
69
  gesture: "point",
69
70
  press: {
@@ -1 +1 @@
1
- {"version":3,"file":"placeAction.js","names":[],"sources":["../../../../src/interactions/actions/insert/placeAction.ts"],"sourcesContent":["import type { Action, ActionDeps, ModifierState } from '@weasel-js/routing';\nimport { SNAP_ROLES, snapMods } from 'features/snap/roles';\nimport type { InsertExtras } from '../depSchema';\nimport { inPlane, insertLayer } from '../planeInput';\nimport { enterInsertedText } from './commit';\nimport { insertParent } from './parent';\nimport { insertKind } from './shape';\nimport { minSizeOf } from 'core/scene/minSize';\n\n/** What the dispatcher lays under an immediate action's params for a click. */\nconst CLICK_FIELDS = ['worldX', 'worldY', 'pressX', 'pressY', 'affordance', 'mods'] as const;\n\nconst NO_KEYS: ModifierState = { alt: false, ctrl: false, meta: false, shift: false };\n\n/**\n * Click-to-insert: the companion to `insertAction` for a click, which draws\n * no box. It commits a node of `params.kind` at `params.size`, centered on the\n * click, snapped through a `point` session of the `snap` dep unless the\n * click held `snap.suspend` (Ctrl), and commits\n * through the same `insert` dep a drag commits through. The rest of the\n * binding's params are the insert's extras, as a drag's are.\n *\n * A `minSize` in the params floors the size, and goes on to the node.\n *\n * A binding with no `size` places nothing: the kind's extent is the tool's to\n * name, and a click has none of its own.\n */\nexport const placeAction: Action & { requires: string[] } = inPlane({\n id: 'place',\n label: 'Place',\n group: 'insert',\n eligible: { capability: 'creates-shapes' },\n modifierRoles: SNAP_ROLES,\n requires: ['insert', 'insertInto', 'selection', 'snap', 'textEdit', 'scene'],\n invoker: {\n timing: 'immediate',\n run(deps: ActionDeps, params?: Record<string, unknown>): void {\n const dep = deps.insert;\n const asked = params?.['size'] as { width: number; height: number } | undefined;\n const min = minSizeOf(params);\n const size = asked && min\n ? { width: Math.max(asked.width, min.width), height: Math.max(asked.height, min.height) }\n : asked;\n const x = params?.['worldX'];\n const y = params?.['worldY'];\n if (!dep || !size || typeof x !== 'number' || typeof y !== 'number') return;\n if (!(size.width > 0 && size.height > 0)) return;\n\n deps.selection?.clear();\n const parent = insertParent(deps.scene, deps.insertInto?.get());\n const snap = deps.snap?.begin({ gesture: 'point', press: { x, y }, ...(parent === null ? {} : { parent }) });\n const mods = snapMods({ modifiers: (params?.['mods'] as ModifierState | undefined) ?? NO_KEYS });\n const at = snap ? snap.at({ x, y }, mods).point : { x, y };\n snap?.end();\n const extras: Record<string, unknown> = { ...params, kind: insertKind(params) };\n for (const k of [...CLICK_FIELDS, 'size']) delete extras[k];\n const bounds = { x: at.x - size.width / 2, y: at.y - size.height / 2, width: size.width, height: size.height };\n const ends = { start: at, end: { x: at.x + size.width / 2, y: at.y + size.height / 2 }, fromCenter: true };\n const id = dep.commit(bounds, extras as InsertExtras, ends);\n enterInsertedText(deps.textEdit, id === null ? null : String(id), extras.kind as string);\n },\n },\n /** Like `insert`, needs no selection. */\n enabled: () => true as const,\n}, insertLayer);\n"],"mappings":";;;;;;;;;AAUA,IAAM,eAAe;CAAC;CAAU;CAAU;CAAU;CAAU;CAAc;AAAM;AAElF,IAAM,UAAyB;CAAE,KAAK;CAAO,MAAM;CAAO,MAAM;CAAO,OAAO;AAAM;;;;;;;;;;;;;;AAepF,IAAa,cAA+C,QAAQ;CAClE,IAAI;CACJ,OAAO;CACP,OAAO;CACP,UAAU,EAAE,YAAY,iBAAiB;CACzC,eAAe;CACf,UAAU;EAAC;EAAU;EAAc;EAAa;EAAQ;EAAY;CAAO;CAC3E,SAAS;EACP,QAAQ;EACR,IAAI,MAAkB,QAAwC;GAC5D,MAAM,MAAM,KAAK;GACjB,MAAM,QAAQ,SAAS;GACvB,MAAM,MAAM,UAAU,MAAM;GAC5B,MAAM,OAAO,SAAS,MAClB;IAAE,OAAO,KAAK,IAAI,MAAM,OAAO,IAAI,KAAK;IAAG,QAAQ,KAAK,IAAI,MAAM,QAAQ,IAAI,MAAM;GAAE,IACtF;GACJ,MAAM,IAAI,SAAS;GACnB,MAAM,IAAI,SAAS;GACnB,IAAI,CAAC,OAAO,CAAC,QAAQ,OAAO,MAAM,YAAY,OAAO,MAAM,UAAU;GACrE,IAAI,EAAE,KAAK,QAAQ,KAAK,KAAK,SAAS,IAAI;GAE1C,KAAK,WAAW,MAAM;GACtB,MAAM,SAAS,aAAa,KAAK,OAAO,KAAK,YAAY,IAAI,CAAC;GAC9D,MAAM,OAAO,KAAK,MAAM,MAAM;IAAE,SAAS;IAAS,OAAO;KAAE;KAAG;IAAE;IAAG,GAAI,WAAW,OAAO,CAAC,IAAI,EAAE,OAAO;GAAG,CAAC;GAC3G,MAAM,OAAO,SAAS,EAAE,WAAY,SAAS,WAAyC,QAAQ,CAAC;GAC/F,MAAM,KAAK,OAAO,KAAK,GAAG;IAAE;IAAG;GAAE,GAAG,IAAI,CAAC,CAAC,QAAQ;IAAE;IAAG;GAAE;GACzD,MAAM,IAAI;GACV,MAAM,SAAkC;IAAE,GAAG;IAAQ,MAAM,WAAW,MAAM;GAAE;GAC9E,KAAK,MAAM,KAAK,CAAC,GAAG,cAAc,MAAM,GAAG,OAAO,OAAO;GACzD,MAAM,SAAS;IAAE,GAAG,GAAG,IAAI,KAAK,QAAQ;IAAG,GAAG,GAAG,IAAI,KAAK,SAAS;IAAG,OAAO,KAAK;IAAO,QAAQ,KAAK;GAAO;GAC7G,MAAM,OAAO;IAAE,OAAO;IAAI,KAAK;KAAE,GAAG,GAAG,IAAI,KAAK,QAAQ;KAAG,GAAG,GAAG,IAAI,KAAK,SAAS;IAAE;IAAG,YAAY;GAAK;GACzG,MAAM,KAAK,IAAI,OAAO,QAAQ,QAAwB,IAAI;GAC1D,kBAAkB,KAAK,UAAU,OAAO,OAAO,OAAO,OAAO,EAAE,GAAG,OAAO,IAAc;EACzF;CACF;;CAEA,eAAe;AACjB,GAAG,WAAW"}
1
+ {"version":3,"file":"placeAction.js","names":[],"sources":["../../../../src/interactions/actions/insert/placeAction.ts"],"sourcesContent":["import type { Action, ActionDeps, ModifierState } from '@weasel-js/routing';\nimport { SNAP_ROLES, snapMods } from 'features/snap/roles';\nimport type { InsertExtras } from '../depSchema';\nimport { inPlane, insertLayer } from '../planeInput';\nimport { enterInsertedText } from './commit';\nimport { insertParent } from './parent';\nimport { insertKind } from './shape';\nimport { minSizeOf } from 'core/scene/minSize';\n\n/** What the dispatcher lays under an immediate action's params for a click. */\nconst CLICK_FIELDS = ['worldX', 'worldY', 'pressX', 'pressY', 'affordance', 'mods'] as const;\n\nconst NO_KEYS: ModifierState = { alt: false, ctrl: false, meta: false, shift: false };\n\n/**\n * Click-to-insert: the companion to `insertAction` for a click, which draws\n * no box. It commits a node of `params.kind` at `params.size`, centered on the\n * click, snapped through a `point` session of the `snap` dep unless the\n * click held `snap.suspend` (Ctrl), and commits\n * through the same `insert` dep a drag commits through. The rest of the\n * binding's params are the insert's extras, as a drag's are.\n *\n * A `minSize` in the params floors the size, and goes on to the node.\n *\n * A binding with no `size` places nothing: the kind's extent is the tool's to\n * name, and a click has none of its own.\n */\nexport const placeAction: Action & { requires: string[] } = inPlane({\n id: 'place',\n label: 'Place',\n group: 'insert',\n eligible: { capability: 'creates-shapes' },\n modifierRoles: SNAP_ROLES,\n requires: ['insert', 'insertInto', 'canDropInto', 'selection', 'snap', 'textEdit', 'scene'],\n invoker: {\n timing: 'immediate',\n run(deps: ActionDeps, params?: Record<string, unknown>): void {\n const dep = deps.insert;\n const asked = params?.['size'] as { width: number; height: number } | undefined;\n const min = minSizeOf(params);\n const size = asked && min\n ? { width: Math.max(asked.width, min.width), height: Math.max(asked.height, min.height) }\n : asked;\n const x = params?.['worldX'];\n const y = params?.['worldY'];\n if (!dep || !size || typeof x !== 'number' || typeof y !== 'number') return;\n if (!(size.width > 0 && size.height > 0)) return;\n\n deps.selection?.clear();\n const parent = insertParent(deps.scene, deps.insertInto?.get(), deps.canDropInto);\n const snap = deps.snap?.begin({ gesture: 'point', press: { x, y }, ...(parent === null ? {} : { parent }) });\n const mods = snapMods({ modifiers: (params?.['mods'] as ModifierState | undefined) ?? NO_KEYS });\n const at = snap ? snap.at({ x, y }, mods).point : { x, y };\n snap?.end();\n const extras: Record<string, unknown> = { ...params, kind: insertKind(params) };\n for (const k of [...CLICK_FIELDS, 'size']) delete extras[k];\n const bounds = { x: at.x - size.width / 2, y: at.y - size.height / 2, width: size.width, height: size.height };\n const ends = { start: at, end: { x: at.x + size.width / 2, y: at.y + size.height / 2 }, fromCenter: true };\n const id = dep.commit(bounds, extras as InsertExtras, ends);\n enterInsertedText(deps.textEdit, id === null ? null : String(id), extras.kind as string);\n },\n },\n /** Like `insert`, needs no selection. */\n enabled: () => true as const,\n}, insertLayer);\n"],"mappings":";;;;;;;;;AAUA,IAAM,eAAe;CAAC;CAAU;CAAU;CAAU;CAAU;CAAc;AAAM;AAElF,IAAM,UAAyB;CAAE,KAAK;CAAO,MAAM;CAAO,MAAM;CAAO,OAAO;AAAM;;;;;;;;;;;;;;AAepF,IAAa,cAA+C,QAAQ;CAClE,IAAI;CACJ,OAAO;CACP,OAAO;CACP,UAAU,EAAE,YAAY,iBAAiB;CACzC,eAAe;CACf,UAAU;EAAC;EAAU;EAAc;EAAe;EAAa;EAAQ;EAAY;CAAO;CAC1F,SAAS;EACP,QAAQ;EACR,IAAI,MAAkB,QAAwC;GAC5D,MAAM,MAAM,KAAK;GACjB,MAAM,QAAQ,SAAS;GACvB,MAAM,MAAM,UAAU,MAAM;GAC5B,MAAM,OAAO,SAAS,MAClB;IAAE,OAAO,KAAK,IAAI,MAAM,OAAO,IAAI,KAAK;IAAG,QAAQ,KAAK,IAAI,MAAM,QAAQ,IAAI,MAAM;GAAE,IACtF;GACJ,MAAM,IAAI,SAAS;GACnB,MAAM,IAAI,SAAS;GACnB,IAAI,CAAC,OAAO,CAAC,QAAQ,OAAO,MAAM,YAAY,OAAO,MAAM,UAAU;GACrE,IAAI,EAAE,KAAK,QAAQ,KAAK,KAAK,SAAS,IAAI;GAE1C,KAAK,WAAW,MAAM;GACtB,MAAM,SAAS,aAAa,KAAK,OAAO,KAAK,YAAY,IAAI,GAAG,KAAK,WAAW;GAChF,MAAM,OAAO,KAAK,MAAM,MAAM;IAAE,SAAS;IAAS,OAAO;KAAE;KAAG;IAAE;IAAG,GAAI,WAAW,OAAO,CAAC,IAAI,EAAE,OAAO;GAAG,CAAC;GAC3G,MAAM,OAAO,SAAS,EAAE,WAAY,SAAS,WAAyC,QAAQ,CAAC;GAC/F,MAAM,KAAK,OAAO,KAAK,GAAG;IAAE;IAAG;GAAE,GAAG,IAAI,CAAC,CAAC,QAAQ;IAAE;IAAG;GAAE;GACzD,MAAM,IAAI;GACV,MAAM,SAAkC;IAAE,GAAG;IAAQ,MAAM,WAAW,MAAM;GAAE;GAC9E,KAAK,MAAM,KAAK,CAAC,GAAG,cAAc,MAAM,GAAG,OAAO,OAAO;GACzD,MAAM,SAAS;IAAE,GAAG,GAAG,IAAI,KAAK,QAAQ;IAAG,GAAG,GAAG,IAAI,KAAK,SAAS;IAAG,OAAO,KAAK;IAAO,QAAQ,KAAK;GAAO;GAC7G,MAAM,OAAO;IAAE,OAAO;IAAI,KAAK;KAAE,GAAG,GAAG,IAAI,KAAK,QAAQ;KAAG,GAAG,GAAG,IAAI,KAAK,SAAS;IAAE;IAAG,YAAY;GAAK;GACzG,MAAM,KAAK,IAAI,OAAO,QAAQ,QAAwB,IAAI;GAC1D,kBAAkB,KAAK,UAAU,OAAO,OAAO,OAAO,OAAO,EAAE,GAAG,OAAO,IAAc;EACzF;CACF;;CAEA,eAAe;AACjB,GAAG,WAAW"}