@weasel-js/core 1.11.1 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (297) hide show
  1. package/CHANGELOG.md +184 -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/barrel/canvas.js +3 -1
  6. package/dist/barrel/paint.js +4 -4
  7. package/dist/barrel/paths.js +8 -4
  8. package/dist/barrel/renderer.js +3 -2
  9. package/dist/barrel/routing.js +4 -4
  10. package/dist/barrel/scene.js +2 -1
  11. package/dist/barrel/text.js +2 -2
  12. package/dist/barrel/viewport.js +6 -2
  13. package/dist/canvas/Canvas.js +2 -1
  14. package/dist/canvas/Canvas.js.map +1 -1
  15. package/dist/canvas/CanvasView.js +23 -34
  16. package/dist/canvas/CanvasView.js.map +1 -1
  17. package/dist/canvas/ModalityHud.js +1 -1
  18. package/dist/canvas/NodeShape.js +4 -1
  19. package/dist/canvas/NodeShape.js.map +1 -1
  20. package/dist/canvas/SceneCanvas/ActionsProviderIfRoot.js +1 -1
  21. package/dist/canvas/SceneCanvas/DepRegistryProviderIfRoot.js +1 -1
  22. package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js +1 -1
  23. package/dist/canvas/SceneCanvas/SurfaceDeclarations.js +23 -0
  24. package/dist/canvas/SceneCanvas/SurfaceDeclarations.js.map +1 -0
  25. package/dist/canvas/SceneCanvas/canvasPassThrough.js +1 -0
  26. package/dist/canvas/SceneCanvas/canvasPassThrough.js.map +1 -1
  27. package/dist/canvas/SceneCanvas/props.js.map +1 -1
  28. package/dist/canvas/SceneCanvas/scenePointQuery.js +7 -3
  29. package/dist/canvas/SceneCanvas/scenePointQuery.js.map +1 -1
  30. package/dist/canvas/SceneCanvas/useActionTunings.js +6 -1
  31. package/dist/canvas/SceneCanvas/useActionTunings.js.map +1 -1
  32. package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js +1 -0
  33. package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js.map +1 -1
  34. package/dist/canvas/SceneCanvas/useCanvasTools.js +7 -2
  35. package/dist/canvas/SceneCanvas/useCanvasTools.js.map +1 -1
  36. package/dist/canvas/SceneCanvas/useSceneCanvasHandle.js +9 -3
  37. package/dist/canvas/SceneCanvas/useSceneCanvasHandle.js.map +1 -1
  38. package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js +10 -2
  39. package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js.map +1 -1
  40. package/dist/canvas/SceneCanvas/useSceneCanvasTools.js +1 -0
  41. package/dist/canvas/SceneCanvas/useSceneCanvasTools.js.map +1 -1
  42. package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js +19 -4
  43. package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js.map +1 -1
  44. package/dist/canvas/SceneCanvas/useSceneStandardActions.js +2 -1
  45. package/dist/canvas/SceneCanvas/useSceneStandardActions.js.map +1 -1
  46. package/dist/canvas/SceneCanvas.js +11 -10
  47. package/dist/canvas/SceneCanvas.js.map +1 -1
  48. package/dist/canvas/ViewMarker.js +35 -0
  49. package/dist/canvas/ViewMarker.js.map +1 -0
  50. package/dist/canvas/defaultDrawOne.js +2 -0
  51. package/dist/canvas/defaultDrawOne.js.map +1 -1
  52. package/dist/canvas/deps/booleans.js +2 -1
  53. package/dist/canvas/deps/booleans.js.map +1 -1
  54. package/dist/canvas/deps/hitTestArea.js +2 -2
  55. package/dist/canvas/deps/hitTestArea.js.map +1 -1
  56. package/dist/canvas/deps/insert.js +6 -2
  57. package/dist/canvas/deps/insert.js.map +1 -1
  58. package/dist/canvas/deps/pathEditCommits.js +3 -7
  59. package/dist/canvas/deps/pathEditCommits.js.map +1 -1
  60. package/dist/canvas/drawnBounds.js +11 -3
  61. package/dist/canvas/drawnBounds.js.map +1 -1
  62. package/dist/canvas/inheritedPaints.js +44 -0
  63. package/dist/canvas/inheritedPaints.js.map +1 -0
  64. package/dist/canvas/nodeShapes/derivedPainter.js +15 -16
  65. package/dist/canvas/nodeShapes/derivedPainter.js.map +1 -1
  66. package/dist/canvas/nodeShapes/nodePaint.js +13 -34
  67. package/dist/canvas/nodeShapes/nodePaint.js.map +1 -1
  68. package/dist/canvas/nodeShapes/pathPainter.js +9 -8
  69. package/dist/canvas/nodeShapes/pathPainter.js.map +1 -1
  70. package/dist/canvas/nodeShapes/rectFallbackPainter.js +4 -5
  71. package/dist/canvas/nodeShapes/rectFallbackPainter.js.map +1 -1
  72. package/dist/canvas/nodeShapes/shapePainter.js +11 -10
  73. package/dist/canvas/nodeShapes/shapePainter.js.map +1 -1
  74. package/dist/canvas/nodeShapes/textPainter.js +13 -12
  75. package/dist/canvas/nodeShapes/textPainter.js.map +1 -1
  76. package/dist/canvas/pickReach.js +3 -3
  77. package/dist/canvas/pickReach.js.map +1 -1
  78. package/dist/canvas/pickWalk.js +3 -1
  79. package/dist/canvas/pickWalk.js.map +1 -1
  80. package/dist/canvas/renderSceneToPixels.js +1 -1
  81. package/dist/canvas/renderSceneToPixels.js.map +1 -1
  82. package/dist/canvas/sceneSlotWiring.js +2 -1
  83. package/dist/canvas/sceneSlotWiring.js.map +1 -1
  84. package/dist/canvas/sceneViewRender.js +2 -1
  85. package/dist/canvas/sceneViewRender.js.map +1 -1
  86. package/dist/canvas/surfaceContribution.js +12 -8
  87. package/dist/canvas/surfaceContribution.js.map +1 -1
  88. package/dist/canvas/useCanvasView.js +4 -2
  89. package/dist/canvas/useCanvasView.js.map +1 -1
  90. package/dist/canvas/useLayerStack.js +6 -2
  91. package/dist/canvas/useLayerStack.js.map +1 -1
  92. package/dist/canvas/useViewAim.js +121 -0
  93. package/dist/canvas/useViewAim.js.map +1 -0
  94. package/dist/canvas/useViewNesting.js +3 -2
  95. package/dist/canvas/useViewNesting.js.map +1 -1
  96. package/dist/canvas/useViewRegistrySurface.js +4 -2
  97. package/dist/canvas/useViewRegistrySurface.js.map +1 -1
  98. package/dist/canvas/viewMarkers.js +63 -0
  99. package/dist/canvas/viewMarkers.js.map +1 -0
  100. package/dist/canvas/viewRegistry.js +27 -1
  101. package/dist/canvas/viewRegistry.js.map +1 -1
  102. package/dist/core/pages/pageEdits.js +2 -3
  103. package/dist/core/pages/pageEdits.js.map +1 -1
  104. package/dist/core/paint/meshKindLazy.js +59 -2
  105. package/dist/core/paint/meshKindLazy.js.map +1 -1
  106. package/dist/core/scene/clipNothing.js +13 -0
  107. package/dist/core/scene/clipNothing.js.map +1 -0
  108. package/dist/core/scene/depMemo.js +11 -3
  109. package/dist/core/scene/depMemo.js.map +1 -1
  110. package/dist/core/scene/dependents.js +7 -1
  111. package/dist/core/scene/dependents.js.map +1 -1
  112. package/dist/core/scene/derivedParts.js +10 -11
  113. package/dist/core/scene/derivedParts.js.map +1 -1
  114. package/dist/core/scene/effectivePose.js +7 -2
  115. package/dist/core/scene/effectivePose.js.map +1 -1
  116. package/dist/core/scene/fontReads.js +37 -0
  117. package/dist/core/scene/fontReads.js.map +1 -0
  118. package/dist/core/scene/inheritedPaint.js +75 -0
  119. package/dist/core/scene/inheritedPaint.js.map +1 -0
  120. package/dist/core/scene/kitRegistry.js +5 -0
  121. package/dist/core/scene/kitRegistry.js.map +1 -1
  122. package/dist/core/scene/nodeMemo.js +15 -7
  123. package/dist/core/scene/nodeMemo.js.map +1 -1
  124. package/dist/core/scene/pathEffects/blend.js.map +1 -1
  125. package/dist/core/scene/pathEffects/derive.js +7 -2
  126. package/dist/core/scene/pathEffects/derive.js.map +1 -1
  127. package/dist/core/scene/pathEffects/effects.js +3 -1
  128. package/dist/core/scene/pathEffects/effects.js.map +1 -1
  129. package/dist/core/scene/pathEffects/textAlong.js +95 -0
  130. package/dist/core/scene/pathEffects/textAlong.js.map +1 -0
  131. package/dist/{cornerHandles-BHuoMs1o.d.ts → cornerHandles-p8fDIAKt.d.ts} +2 -1
  132. package/dist/{depSchema-BpZNOv7q.d.ts → depSchema-mG19aEdD.d.ts} +61 -13
  133. package/dist/devtools/registry.js +4 -4
  134. package/dist/devtools/registry.js.map +1 -1
  135. package/dist/features/measure/measureContribution.js +4 -2
  136. package/dist/features/measure/measureContribution.js.map +1 -1
  137. package/dist/features/modes/modeTriggers.js +7 -3
  138. package/dist/features/modes/modeTriggers.js.map +1 -1
  139. package/dist/features/pathEffects/effectSources.js +60 -0
  140. package/dist/features/pathEffects/effectSources.js.map +1 -0
  141. package/dist/features/pathEffects/readSource.js +17 -1
  142. package/dist/features/pathEffects/readSource.js.map +1 -1
  143. package/dist/features/pathEffects/textItems.js +53 -0
  144. package/dist/features/pathEffects/textItems.js.map +1 -0
  145. package/dist/features/pathEffects/useEffectSourcesLayer.js +57 -0
  146. package/dist/features/pathEffects/useEffectSourcesLayer.js.map +1 -0
  147. package/dist/features/paths/pathEditState.js +5 -2
  148. package/dist/features/paths/pathEditState.js.map +1 -1
  149. package/dist/features/text/sceneTextDrafts.js +65 -0
  150. package/dist/features/text/sceneTextDrafts.js.map +1 -0
  151. package/dist/features/text/textHitAt.js +31 -2
  152. package/dist/features/text/textHitAt.js.map +1 -1
  153. package/dist/features/text/useSceneTextEdit.js +22 -18
  154. package/dist/features/text/useSceneTextEdit.js.map +1 -1
  155. package/dist/features/viewports/aim.js +105 -0
  156. package/dist/features/viewports/aim.js.map +1 -0
  157. package/dist/features/viewports/viewMarker.js +82 -0
  158. package/dist/features/viewports/viewMarker.js.map +1 -0
  159. package/dist/features/viewports/viewMarkerDrag.js +55 -0
  160. package/dist/features/viewports/viewMarkerDrag.js.map +1 -0
  161. package/dist/index.d.ts +500 -201
  162. package/dist/index.js +23 -12
  163. package/dist/interactions/actions/booleans/booleans.js +2 -1
  164. package/dist/interactions/actions/booleans/booleans.js.map +1 -1
  165. package/dist/interactions/actions/booleans/operands.js +6 -5
  166. package/dist/interactions/actions/booleans/operands.js.map +1 -1
  167. package/dist/interactions/actions/clipboard/action.js +27 -17
  168. package/dist/interactions/actions/clipboard/action.js.map +1 -1
  169. package/dist/interactions/actions/defaults/anchorEditing.js +44 -23
  170. package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
  171. package/dist/interactions/actions/defaults/anchorJoin.js +12 -6
  172. package/dist/interactions/actions/defaults/anchorJoin.js.map +1 -1
  173. package/dist/interactions/actions/defaults/bendSegment.js +7 -4
  174. package/dist/interactions/actions/defaults/bendSegment.js.map +1 -1
  175. package/dist/interactions/actions/defaults/cancelGesture.js +11 -8
  176. package/dist/interactions/actions/defaults/cancelGesture.js.map +1 -1
  177. package/dist/interactions/actions/defaults/contextMenu.js +17 -8
  178. package/dist/interactions/actions/defaults/contextMenu.js.map +1 -1
  179. package/dist/interactions/actions/defaults/createPaintAction.js +1 -1
  180. package/dist/interactions/actions/defaults/createPaintAction.js.map +1 -1
  181. package/dist/interactions/actions/defaults/delete.js +28 -14
  182. package/dist/interactions/actions/defaults/delete.js.map +1 -1
  183. package/dist/interactions/actions/defaults/duplicate.js +11 -8
  184. package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
  185. package/dist/interactions/actions/defaults/enterPathEdit.js +19 -9
  186. package/dist/interactions/actions/defaults/enterPathEdit.js.map +1 -1
  187. package/dist/interactions/actions/defaults/escape.js +11 -8
  188. package/dist/interactions/actions/defaults/escape.js.map +1 -1
  189. package/dist/interactions/actions/defaults/exitPathEdit.js +7 -4
  190. package/dist/interactions/actions/defaults/exitPathEdit.js.map +1 -1
  191. package/dist/interactions/actions/defaults/group.js +19 -13
  192. package/dist/interactions/actions/defaults/group.js.map +1 -1
  193. package/dist/interactions/actions/defaults/ingest.js +10 -4
  194. package/dist/interactions/actions/defaults/ingest.js.map +1 -1
  195. package/dist/interactions/actions/defaults/insertPathAnchor.js +7 -4
  196. package/dist/interactions/actions/defaults/insertPathAnchor.js.map +1 -1
  197. package/dist/interactions/actions/defaults/nudge.js +8 -2
  198. package/dist/interactions/actions/defaults/nudge.js.map +1 -1
  199. package/dist/interactions/actions/defaults/pathEditTools.js +11 -8
  200. package/dist/interactions/actions/defaults/pathEditTools.js.map +1 -1
  201. package/dist/interactions/actions/defaults/pinchZoom.js +7 -4
  202. package/dist/interactions/actions/defaults/pinchZoom.js.map +1 -1
  203. package/dist/interactions/actions/defaults/reorder.js +24 -6
  204. package/dist/interactions/actions/defaults/reorder.js.map +1 -1
  205. package/dist/interactions/actions/defaults/selectAll.js +8 -5
  206. package/dist/interactions/actions/defaults/selectAll.js.map +1 -1
  207. package/dist/interactions/actions/defaults/setFill.js +7 -3
  208. package/dist/interactions/actions/defaults/setFill.js.map +1 -1
  209. package/dist/interactions/actions/defaults/setFillOpacity.js +1 -1
  210. package/dist/interactions/actions/defaults/setStroke.js +12 -11
  211. package/dist/interactions/actions/defaults/setStroke.js.map +1 -1
  212. package/dist/interactions/actions/defaults/setStrokeOpacity.js +1 -1
  213. package/dist/interactions/actions/defaults/toolActivate.js +7 -1
  214. package/dist/interactions/actions/defaults/toolActivate.js.map +1 -1
  215. package/dist/interactions/actions/defaults/toolResetToDefault.js +7 -4
  216. package/dist/interactions/actions/defaults/toolResetToDefault.js.map +1 -1
  217. package/dist/interactions/actions/defaults/undoRedo.js +19 -13
  218. package/dist/interactions/actions/defaults/undoRedo.js.map +1 -1
  219. package/dist/interactions/actions/defaults/viewportWheelPan.js +16 -7
  220. package/dist/interactions/actions/defaults/viewportWheelPan.js.map +1 -1
  221. package/dist/interactions/actions/defaults/viewportZoom.js +24 -6
  222. package/dist/interactions/actions/defaults/viewportZoom.js.map +1 -1
  223. package/dist/interactions/actions/edit-anchors/action.js +7 -4
  224. package/dist/interactions/actions/edit-anchors/action.js.map +1 -1
  225. package/dist/interactions/actions/flip/action.js +8 -2
  226. package/dist/interactions/actions/flip/action.js.map +1 -1
  227. package/dist/interactions/actions/insert/repositionAction.js +2 -1
  228. package/dist/interactions/actions/insert/repositionAction.js.map +1 -1
  229. package/dist/interactions/actions/move/action.js +7 -4
  230. package/dist/interactions/actions/move/action.js.map +1 -1
  231. package/dist/interactions/actions/outlines/createOutlinesAction.js +8 -5
  232. package/dist/interactions/actions/outlines/createOutlinesAction.js.map +1 -1
  233. package/dist/interactions/actions/outlines/outlineStroke.js +5 -3
  234. package/dist/interactions/actions/outlines/outlineStroke.js.map +1 -1
  235. package/dist/interactions/actions/pathCommands/cutJoin.js +8 -5
  236. package/dist/interactions/actions/pathCommands/cutJoin.js.map +1 -1
  237. package/dist/interactions/actions/resize/cornerHandles.js +2 -1
  238. package/dist/interactions/actions/resize/cornerHandles.js.map +1 -1
  239. package/dist/interactions/actions/selectionSubtree.js +12 -7
  240. package/dist/interactions/actions/selectionSubtree.js.map +1 -1
  241. package/dist/interactions/actions/transform-again/action.js +8 -5
  242. package/dist/interactions/actions/transform-again/action.js.map +1 -1
  243. package/dist/json.d.ts +4 -0
  244. package/dist/json.js +3 -0
  245. package/dist/kitSettingsSchema.js +8 -1
  246. package/dist/kitSettingsSchema.js.map +1 -1
  247. package/dist/{math-CrJULaZI.d.ts → math-B56zxVs5.d.ts} +10 -1
  248. package/dist/math.d.ts +1 -1
  249. package/dist/math.js +2 -1
  250. package/dist/resize.d.ts +1 -1
  251. package/dist/test-seams.d.ts +2 -2
  252. package/dist/tools/builtin/ellipse/useEllipseTool.js +1 -0
  253. package/dist/tools/builtin/ellipse/useEllipseTool.js.map +1 -1
  254. package/dist/tools/builtin/eyedropper/useEyedropperTool.js +2 -1
  255. package/dist/tools/builtin/eyedropper/useEyedropperTool.js.map +1 -1
  256. package/dist/tools/builtin/hand/dragPanContribution.js +4 -1
  257. package/dist/tools/builtin/hand/dragPanContribution.js.map +1 -1
  258. package/dist/tools/builtin/hand/useHandTool.js +7 -4
  259. package/dist/tools/builtin/hand/useHandTool.js.map +1 -1
  260. package/dist/tools/builtin/image/useImageTool.js +1 -0
  261. package/dist/tools/builtin/image/useImageTool.js.map +1 -1
  262. package/dist/tools/builtin/lasso/useLassoTool.js +1 -0
  263. package/dist/tools/builtin/lasso/useLassoTool.js.map +1 -1
  264. package/dist/tools/builtin/line/useLineTool.js +1 -0
  265. package/dist/tools/builtin/line/useLineTool.js.map +1 -1
  266. package/dist/tools/builtin/measure/useMeasureTool.js +12 -5
  267. package/dist/tools/builtin/measure/useMeasureTool.js.map +1 -1
  268. package/dist/tools/builtin/pen/usePenTool.js +14 -6
  269. package/dist/tools/builtin/pen/usePenTool.js.map +1 -1
  270. package/dist/tools/builtin/pencil/usePencilTool.js +1 -0
  271. package/dist/tools/builtin/pencil/usePencilTool.js.map +1 -1
  272. package/dist/tools/builtin/polygon/usePolygonTool.js +13 -4
  273. package/dist/tools/builtin/polygon/usePolygonTool.js.map +1 -1
  274. package/dist/tools/builtin/rect/useRectTool.js +1 -0
  275. package/dist/tools/builtin/rect/useRectTool.js.map +1 -1
  276. package/dist/tools/builtin/select/adapterPointQuery.js +3 -2
  277. package/dist/tools/builtin/select/adapterPointQuery.js.map +1 -1
  278. package/dist/tools/builtin/select/pressDecision.js +32 -0
  279. package/dist/tools/builtin/select/pressDecision.js.map +1 -0
  280. package/dist/tools/builtin/select/selectionContributions.js +22 -13
  281. package/dist/tools/builtin/select/selectionContributions.js.map +1 -1
  282. package/dist/tools/builtin/select/useSelectTool.js +13 -10
  283. package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
  284. package/dist/tools/builtin/slice/useSliceTool.js +22 -8
  285. package/dist/tools/builtin/slice/useSliceTool.js.map +1 -1
  286. package/dist/tools/builtin/star/useStarTool.js +13 -4
  287. package/dist/tools/builtin/star/useStarTool.js.map +1 -1
  288. package/dist/tools/builtin/sticky/stickyNode.js +4 -1
  289. package/dist/tools/builtin/sticky/stickyNode.js.map +1 -1
  290. package/dist/tools/builtin/sticky/useStickyTool.js +10 -5
  291. package/dist/tools/builtin/sticky/useStickyTool.js.map +1 -1
  292. package/dist/tools/builtin/text/useTextTool.js +3 -1
  293. package/dist/tools/builtin/text/useTextTool.js.map +1 -1
  294. package/dist/util/paint.js +1 -3
  295. package/dist/util/paint.js.map +1 -1
  296. package/dist/version.js +1 -1
  297. package/package.json +28 -24
@@ -0,0 +1,105 @@
1
+ import { ZOOM_FLOOR } from "../../core/viewport/zoomBounds.js";
2
+ import { normalizeView } from "../../core/viewport/view.js";
3
+ import { fitViewToBounds } from "../../core/viewport/fitViewToBounds.js";
4
+
5
+ //#region src/features/viewports/aim.ts
6
+ /** The aim of view `viewId` (`null`: a surface's root) showing world rect `rect`. */
7
+ function rectAim(viewId, rect) {
8
+ return {
9
+ viewId,
10
+ kind: "rect",
11
+ extent: rect,
12
+ outlineIn(space) {
13
+ const { x, y, width, height } = rect;
14
+ const out = [];
15
+ for (const corner of [
16
+ {
17
+ x,
18
+ y
19
+ },
20
+ {
21
+ x: x + width,
22
+ y
23
+ },
24
+ {
25
+ x: x + width,
26
+ y: y + height
27
+ },
28
+ {
29
+ x,
30
+ y: y + height
31
+ }
32
+ ]) {
33
+ const p = space.project(corner);
34
+ if (!p) return null;
35
+ out.push(p);
36
+ }
37
+ return out;
38
+ },
39
+ pannedIn(space, from, to) {
40
+ const a = space.unproject(from);
41
+ const b = space.unproject(to);
42
+ if (!a || !b) return null;
43
+ return rectAim(viewId, {
44
+ ...rect,
45
+ x: rect.x + (b.x - a.x),
46
+ y: rect.y + (b.y - a.y)
47
+ });
48
+ }
49
+ };
50
+ }
51
+ /** Whether `aim` is core's 2D rect aim, so its `extent` is a world rect. */
52
+ function isRectAim(aim) {
53
+ return aim.kind === "rect";
54
+ }
55
+ /** A 2D view's frame: `view` is its camera and `size` its frame; frame px are measured from the frame's top-left. */
56
+ function aimSpace2d(viewId, view, size) {
57
+ return {
58
+ viewId,
59
+ kind: "2d",
60
+ size: {
61
+ width: size.width,
62
+ height: size.height
63
+ },
64
+ project: (p) => ({
65
+ x: (p.x - view.x) * view.scale.x,
66
+ y: (p.y - view.y) * view.scale.y
67
+ }),
68
+ unproject: (px) => ({
69
+ x: px.x / view.scale.x + view.x,
70
+ y: px.y / view.scale.y + view.y
71
+ })
72
+ };
73
+ }
74
+ var UNUSED_CURRENT = {
75
+ x: 0,
76
+ y: 0,
77
+ scale: {
78
+ x: 1,
79
+ y: 1
80
+ }
81
+ };
82
+ /**
83
+ * The camera that shows `rect` in a frame of `size`: `'exact'` per axis (the inverse of `viewportWorldRect`),
84
+ * `'contain'` uniform and centered. Not clamped to a zoom range, and finite for an empty rect or frame. Normalized.
85
+ */
86
+ function viewForAim(rect, size, fit) {
87
+ if (!(rect.width > 0 && rect.height > 0 && size.width > 0 && size.height > 0)) return normalizeView({
88
+ x: rect.x,
89
+ y: rect.y,
90
+ scale: {
91
+ x: ZOOM_FLOOR,
92
+ y: ZOOM_FLOOR
93
+ }
94
+ });
95
+ return normalizeView(fitViewToBounds(rect, size, UNUSED_CURRENT, {
96
+ mode: fit === "exact" ? "stretch" : "contain",
97
+ padding: 0,
98
+ minScale: 0,
99
+ maxScale: Infinity
100
+ }));
101
+ }
102
+
103
+ //#endregion
104
+ export { aimSpace2d, isRectAim, rectAim, viewForAim };
105
+ //# sourceMappingURL=aim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aim.js","names":[],"sources":["../../../src/features/viewports/aim.ts"],"sourcesContent":["import { fitViewToBounds, type Bounds } from 'core/viewport/fitViewToBounds';\nimport type { Dims } from 'core/layers/render';\nimport { normalizeView, type View } from 'core/viewport/view';\nimport { ZOOM_FLOOR } from 'core/viewport/zoomBounds';\n\n/** A point in a view's world. A 2D world is the plane z = 0, so `z` is absent there. */\nexport interface AimPoint {\n x: number;\n y: number;\n z?: number;\n}\n\n/** A closed outline in a frame's CSS px, from the frame's top-left; at least 3 points. */\nexport type AimOutline = readonly { x: number; y: number }[];\n\n/** A target view, as an aim is drawn into it: its world, and its frame. */\nexport interface AimSpace {\n /** The view whose frame this is; `null` for a surface's root view. */\n readonly viewId: string | null;\n /** `'2d'` for core's views; any other value is its owner's (kernel3d: `'3d'`). */\n readonly kind: string;\n readonly size: { width: number; height: number };\n /** World point to frame px; `null` where the point does not draw (behind a 3D camera). */\n project(p: AimPoint): { x: number; y: number } | null;\n /** Frame px to the world point under it; `null` where there is none (a 3D ray missing its plane). */\n unproject(px: { x: number; y: number }): AimPoint | null;\n}\n\n/** What a viewport shows, read in the world of the view it names. */\nexport interface ViewAim<TExtent = unknown> {\n readonly viewId: string | null;\n /** Discriminates `extent`: `'rect'` for core's 2D views. */\n readonly kind: string;\n /** Where the aim lies in its world, in its owner's own terms. */\n readonly extent: TExtent;\n /** The aim's outline in `space`'s frame, or `null` when it cannot be drawn there. */\n outlineIn(space: AimSpace): AimOutline | null;\n /** This aim moved so the world point under frame px `from` in `space` ends up under `to`; `null` when either misses. */\n pannedIn(space: AimSpace, from: { x: number; y: number }, to: { x: number; y: number }): ViewAim<TExtent> | null;\n}\n\n/** Core's 2D aim: a world rect a view shows. */\nexport type RectAim = ViewAim<Bounds> & { readonly kind: 'rect' };\n\n/** The aim of view `viewId` (`null`: a surface's root) showing world rect `rect`. */\nexport function rectAim(viewId: string | null, rect: Bounds): RectAim {\n return {\n viewId,\n kind: 'rect',\n extent: rect,\n outlineIn(space) {\n const { x, y, width, height } = rect;\n const out: { x: number; y: number }[] = [];\n for (const corner of [\n { x, y },\n { x: x + width, y },\n { x: x + width, y: y + height },\n { x, y: y + height },\n ]) {\n const p = space.project(corner);\n if (!p) return null;\n out.push(p);\n }\n return out;\n },\n pannedIn(space, from, to) {\n const a = space.unproject(from);\n const b = space.unproject(to);\n if (!a || !b) return null;\n return rectAim(viewId, { ...rect, x: rect.x + (b.x - a.x), y: rect.y + (b.y - a.y) });\n },\n };\n}\n\n/** Whether `aim` is core's 2D rect aim, so its `extent` is a world rect. */\nexport function isRectAim(aim: ViewAim): aim is RectAim {\n return aim.kind === 'rect';\n}\n\n/** A 2D view's frame: `view` is its camera and `size` its frame; frame px are measured from the frame's top-left. */\nexport function aimSpace2d(viewId: string | null, view: View, size: Dims): AimSpace {\n return {\n viewId,\n kind: '2d',\n size: { width: size.width, height: size.height },\n project: (p) => ({ x: (p.x - view.x) * view.scale.x, y: (p.y - view.y) * view.scale.y }),\n unproject: (px) => ({ x: px.x / view.scale.x + view.x, y: px.y / view.scale.y + view.y }),\n };\n}\n\nconst UNUSED_CURRENT: View = { x: 0, y: 0, scale: { x: 1, y: 1 } };\n\n/**\n * The camera that shows `rect` in a frame of `size`: `'exact'` per axis (the inverse of `viewportWorldRect`),\n * `'contain'` uniform and centered. Not clamped to a zoom range, and finite for an empty rect or frame. Normalized.\n */\nexport function viewForAim(rect: Bounds, size: Dims, fit: 'exact' | 'contain'): View {\n if (!(rect.width > 0 && rect.height > 0 && size.width > 0 && size.height > 0)) {\n return normalizeView({ x: rect.x, y: rect.y, scale: { x: ZOOM_FLOOR, y: ZOOM_FLOOR } });\n }\n return normalizeView(fitViewToBounds(rect, size, UNUSED_CURRENT, {\n mode: fit === 'exact' ? 'stretch' : 'contain', padding: 0, minScale: 0, maxScale: Infinity,\n }));\n}\n"],"mappings":";;;;;;AA6CA,SAAgB,QAAQ,QAAuB,MAAuB;CACpE,OAAO;EACL;EACA,MAAM;EACN,QAAQ;EACR,UAAU,OAAO;GACf,MAAM,EAAE,GAAG,GAAG,OAAO,WAAW;GAChC,MAAM,MAAkC,CAAC;GACzC,KAAK,MAAM,UAAU;IACnB;KAAE;KAAG;IAAE;IACP;KAAE,GAAG,IAAI;KAAO;IAAE;IAClB;KAAE,GAAG,IAAI;KAAO,GAAG,IAAI;IAAO;IAC9B;KAAE;KAAG,GAAG,IAAI;IAAO;GACrB,GAAG;IACD,MAAM,IAAI,MAAM,QAAQ,MAAM;IAC9B,IAAI,CAAC,GAAG,OAAO;IACf,IAAI,KAAK,CAAC;GACZ;GACA,OAAO;EACT;EACA,SAAS,OAAO,MAAM,IAAI;GACxB,MAAM,IAAI,MAAM,UAAU,IAAI;GAC9B,MAAM,IAAI,MAAM,UAAU,EAAE;GAC5B,IAAI,CAAC,KAAK,CAAC,GAAG,OAAO;GACrB,OAAO,QAAQ,QAAQ;IAAE,GAAG;IAAM,GAAG,KAAK,KAAK,EAAE,IAAI,EAAE;IAAI,GAAG,KAAK,KAAK,EAAE,IAAI,EAAE;GAAG,CAAC;EACtF;CACF;AACF;;AAGA,SAAgB,UAAU,KAA8B;CACtD,OAAO,IAAI,SAAS;AACtB;;AAGA,SAAgB,WAAW,QAAuB,MAAY,MAAsB;CAClF,OAAO;EACL;EACA,MAAM;EACN,MAAM;GAAE,OAAO,KAAK;GAAO,QAAQ,KAAK;EAAO;EAC/C,UAAU,OAAO;GAAE,IAAI,EAAE,IAAI,KAAK,KAAK,KAAK,MAAM;GAAG,IAAI,EAAE,IAAI,KAAK,KAAK,KAAK,MAAM;EAAE;EACtF,YAAY,QAAQ;GAAE,GAAG,GAAG,IAAI,KAAK,MAAM,IAAI,KAAK;GAAG,GAAG,GAAG,IAAI,KAAK,MAAM,IAAI,KAAK;EAAE;CACzF;AACF;AAEA,IAAM,iBAAuB;CAAE,GAAG;CAAG,GAAG;CAAG,OAAO;EAAE,GAAG;EAAG,GAAG;CAAE;AAAE;;;;;AAMjE,SAAgB,WAAW,MAAc,MAAY,KAAgC;CACnF,IAAI,EAAE,KAAK,QAAQ,KAAK,KAAK,SAAS,KAAK,KAAK,QAAQ,KAAK,KAAK,SAAS,IACzE,OAAO,cAAc;EAAE,GAAG,KAAK;EAAG,GAAG,KAAK;EAAG,OAAO;GAAE,GAAG;GAAY,GAAG;EAAW;CAAE,CAAC;CAExF,OAAO,cAAc,gBAAgB,MAAM,MAAM,gBAAgB;EAC/D,MAAM,QAAQ,UAAU,YAAY;EAAW,SAAS;EAAG,UAAU;EAAG,UAAU;CACpF,CAAC,CAAC;AACJ"}
@@ -0,0 +1,82 @@
1
+ import { aimSpace2d } from "./aim.js";
2
+ import { pointInPolygon, pointSegmentDist2, polygonFromPoints } from "@weasel-js/geom";
3
+
4
+ //#region src/features/viewports/viewMarker.ts
5
+ /** The marker style a {@link ViewMarkerProps.style} fills in from: the minimap indicator's. */
6
+ var VIEW_MARKER_DEFAULT_STYLE = {
7
+ stroke: "#ffffff",
8
+ width: 1,
9
+ dash: [2, 3]
10
+ };
11
+ /** The aim `source` names: a view's through `host`, or one handed in by value. */
12
+ function markerSourceAim(source, host) {
13
+ return source === null || typeof source === "string" ? host.aimOf(source) : source.aim();
14
+ }
15
+ /** A screen-space layer, id `view-marker:<id>`, drawing `props`'s source aim in the frame it is handed. */
16
+ function createViewMarkerLayer(props, host) {
17
+ const { id } = props();
18
+ return {
19
+ id: `view-marker:${id}`,
20
+ label: `View marker ${id}`,
21
+ space: "screen",
22
+ draw: (_data, view, dims) => {
23
+ const { source, target, style } = props();
24
+ if (source === target) return [];
25
+ const outline = markerSourceAim(source, host)?.outlineIn(aimSpace2d(target, view, dims));
26
+ if (!outline) return [];
27
+ const d = VIEW_MARKER_DEFAULT_STYLE;
28
+ return [{
29
+ kind: "path",
30
+ path: polygonFromPoints(outline),
31
+ stroke: {
32
+ paint: {
33
+ kind: "solid",
34
+ color: style?.stroke ?? d.stroke
35
+ },
36
+ width: style?.width ?? d.width,
37
+ dash: [...style?.dash ?? d.dash]
38
+ }
39
+ }];
40
+ }
41
+ };
42
+ }
43
+ /** A press this many screen px outside the outline still counts. */
44
+ var VIEW_MARKER_HIT_SLOP_PX = 4;
45
+ /**
46
+ * The grab for a press at frame px `at` in `space`, the marker's target, or
47
+ * `null` when the marker is not interactive, its source has no writer or no
48
+ * aim, or `at` is not on its outline or within {@link VIEW_MARKER_HIT_SLOP_PX} of it. A surface with views
49
+ * calls this itself; a canvas with none, such as a detached minimap, calls it to route a press to its marker.
50
+ */
51
+ function grabViewMarker(props, host, space, at) {
52
+ const { id, source, target, interactive } = props;
53
+ if (interactive === false || source === target) return null;
54
+ const write = source === null || typeof source === "string" ? (aim) => {
55
+ host.setAim(source, aim);
56
+ } : source.setAim?.bind(source);
57
+ if (!write) return null;
58
+ const startAim = markerSourceAim(source, host);
59
+ const outline = startAim?.outlineIn(space);
60
+ if (!startAim || !outline || !outlineHolds(outline, at, VIEW_MARKER_HIT_SLOP_PX)) return null;
61
+ return {
62
+ markerId: id,
63
+ moveTo: (world) => {
64
+ const to = space.project(world);
65
+ const next = to && startAim.pannedIn(space, at, to);
66
+ if (next) write(next);
67
+ }
68
+ };
69
+ }
70
+ function outlineHolds(outline, p, slop) {
71
+ if (pointInPolygon(outline.flatMap((q) => [q.x, q.y]), p.x, p.y)) return true;
72
+ for (let i = 0; i < outline.length; i++) {
73
+ const a = outline[i];
74
+ const b = outline[(i + 1) % outline.length];
75
+ if (pointSegmentDist2(p.x, p.y, a.x, a.y, b.x, b.y) <= slop * slop) return true;
76
+ }
77
+ return false;
78
+ }
79
+
80
+ //#endregion
81
+ export { VIEW_MARKER_DEFAULT_STYLE, VIEW_MARKER_HIT_SLOP_PX, createViewMarkerLayer, grabViewMarker, markerSourceAim };
82
+ //# sourceMappingURL=viewMarker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewMarker.js","names":[],"sources":["../../../src/features/viewports/viewMarker.ts"],"sourcesContent":["import { pointInPolygon, pointSegmentDist2, polygonFromPoints } from '@weasel-js/geom';\nimport type { DrawCommand } from '@weasel-js/renderer';\nimport type { RenderLayer } from 'core/layers/render';\nimport { aimSpace2d, type AimOutline, type AimSpace, type ViewAim } from './aim';\n\n/** An aim handed in by value (another surface's view, a 3D pane), with the writer that moves it. */\nexport interface AimSource {\n aim(): ViewAim | null;\n setAim?(aim: ViewAim): void;\n}\n\n/** How a marker's outline is stroked, in screen px. */\nexport interface ViewMarkerStyle {\n stroke?: string;\n width?: number;\n dash?: readonly number[];\n}\n\n/** One view's aim, drawn inside another view's frame. */\nexport interface ViewMarkerProps {\n /** Unique among the markers on one surface. */\n id: string;\n /** Whose aim: a view id on this surface, `null` for the root view, or an aim by value. */\n source: string | null | AimSource;\n /** Whose frame it is drawn in, after that view's layers: a view id, or `null` for the root. */\n target: string | null;\n /** Screen px. Default: stroke `#ffffff`, width 1, dash [2, 3]. */\n style?: ViewMarkerStyle;\n /** Default true: dragging the marker edits the source's aim. A source with no writer is never interactive.\n * A marker grabs its whole interior plus {@link VIEW_MARKER_HIT_SLOP_PX}, so one covering much of its\n * frame takes every press there; `false` opts out. */\n interactive?: boolean;\n}\n\n/** The marker style a {@link ViewMarkerProps.style} fills in from: the minimap indicator's. */\nexport const VIEW_MARKER_DEFAULT_STYLE: Required<ViewMarkerStyle> = { stroke: '#ffffff', width: 1, dash: [2, 3] };\n\n/** What a marker resolves aims through: the surface's views. */\nexport interface ViewMarkerHost {\n aimOf(id: string | null): ViewAim | null;\n setAim(id: string | null, aim: ViewAim): boolean;\n}\n\n/** The aim `source` names: a view's through `host`, or one handed in by value. */\nexport function markerSourceAim(source: ViewMarkerProps['source'], host: ViewMarkerHost): ViewAim | null {\n return source === null || typeof source === 'string' ? host.aimOf(source) : source.aim();\n}\n\n/** A screen-space layer, id `view-marker:<id>`, drawing `props`'s source aim in the frame it is handed. */\nexport function createViewMarkerLayer(props: () => ViewMarkerProps, host: ViewMarkerHost): RenderLayer<unknown> {\n const { id } = props();\n return {\n id: `view-marker:${id}`,\n label: `View marker ${id}`,\n space: 'screen',\n draw: (_data, view, dims): DrawCommand[] => {\n const { source, target, style } = props();\n if (source === target) return [];\n const aim = markerSourceAim(source, host);\n const outline = aim?.outlineIn(aimSpace2d(target, view, dims));\n if (!outline) return [];\n const d = VIEW_MARKER_DEFAULT_STYLE;\n return [{\n kind: 'path',\n path: polygonFromPoints(outline),\n stroke: {\n paint: { kind: 'solid', color: style?.stroke ?? d.stroke },\n width: style?.width ?? d.width,\n dash: [...(style?.dash ?? d.dash)],\n },\n }];\n },\n };\n}\n\n/** A press on a marker, held for the drag: where it started, and the aim it started from. */\nexport interface ViewMarkerGrab {\n readonly markerId: string;\n /** Pan the source so the world point under the press ends up under `world`, a point in the target's world. */\n moveTo(world: { x: number; y: number }): void;\n}\n\n/** A press this many screen px outside the outline still counts. */\nexport const VIEW_MARKER_HIT_SLOP_PX = 4;\n\n/**\n * The grab for a press at frame px `at` in `space`, the marker's target, or\n * `null` when the marker is not interactive, its source has no writer or no\n * aim, or `at` is not on its outline or within {@link VIEW_MARKER_HIT_SLOP_PX} of it. A surface with views\n * calls this itself; a canvas with none, such as a detached minimap, calls it to route a press to its marker.\n */\nexport function grabViewMarker(\n props: ViewMarkerProps,\n host: ViewMarkerHost,\n space: AimSpace,\n at: { x: number; y: number },\n): ViewMarkerGrab | null {\n const { id, source, target, interactive } = props;\n if (interactive === false || source === target) return null;\n const write = source === null || typeof source === 'string'\n ? (aim: ViewAim) => { host.setAim(source, aim); }\n : source.setAim?.bind(source);\n if (!write) return null;\n const startAim = markerSourceAim(source, host);\n const outline = startAim?.outlineIn(space);\n if (!startAim || !outline || !outlineHolds(outline, at, VIEW_MARKER_HIT_SLOP_PX)) return null;\n return {\n markerId: id,\n moveTo: (world) => {\n const to = space.project(world);\n const next = to && startAim.pannedIn(space, at, to);\n if (next) write(next);\n },\n };\n}\n\nfunction outlineHolds(outline: AimOutline, p: { x: number; y: number }, slop: number): boolean {\n if (pointInPolygon(outline.flatMap((q) => [q.x, q.y]), p.x, p.y)) return true;\n for (let i = 0; i < outline.length; i++) {\n const a = outline[i]!;\n const b = outline[(i + 1) % outline.length]!;\n if (pointSegmentDist2(p.x, p.y, a.x, a.y, b.x, b.y) <= slop * slop) return true;\n }\n return false;\n}\n"],"mappings":";;;;;AAmCA,IAAa,4BAAuD;CAAE,QAAQ;CAAW,OAAO;CAAG,MAAM,CAAC,GAAG,CAAC;AAAE;;AAShH,SAAgB,gBAAgB,QAAmC,MAAsC;CACvG,OAAO,WAAW,QAAQ,OAAO,WAAW,WAAW,KAAK,MAAM,MAAM,IAAI,OAAO,IAAI;AACzF;;AAGA,SAAgB,sBAAsB,OAA8B,MAA4C;CAC9G,MAAM,EAAE,OAAO,MAAM;CACrB,OAAO;EACL,IAAI,eAAe;EACnB,OAAO,eAAe;EACtB,OAAO;EACP,OAAO,OAAO,MAAM,SAAwB;GAC1C,MAAM,EAAE,QAAQ,QAAQ,UAAU,MAAM;GACxC,IAAI,WAAW,QAAQ,OAAO,CAAC;GAE/B,MAAM,UADM,gBAAgB,QAAQ,IACpB,CAAA,EAAK,UAAU,WAAW,QAAQ,MAAM,IAAI,CAAC;GAC7D,IAAI,CAAC,SAAS,OAAO,CAAC;GACtB,MAAM,IAAI;GACV,OAAO,CAAC;IACN,MAAM;IACN,MAAM,kBAAkB,OAAO;IAC/B,QAAQ;KACN,OAAO;MAAE,MAAM;MAAS,OAAO,OAAO,UAAU,EAAE;KAAO;KACzD,OAAO,OAAO,SAAS,EAAE;KACzB,MAAM,CAAC,GAAI,OAAO,QAAQ,EAAE,IAAK;IACnC;GACF,CAAC;EACH;CACF;AACF;;AAUA,IAAa,0BAA0B;;;;;;;AAQvC,SAAgB,eACd,OACA,MACA,OACA,IACuB;CACvB,MAAM,EAAE,IAAI,QAAQ,QAAQ,gBAAgB;CAC5C,IAAI,gBAAgB,SAAS,WAAW,QAAQ,OAAO;CACvD,MAAM,QAAQ,WAAW,QAAQ,OAAO,WAAW,YAC9C,QAAiB;EAAE,KAAK,OAAO,QAAQ,GAAG;CAAG,IAC9C,OAAO,QAAQ,KAAK,MAAM;CAC9B,IAAI,CAAC,OAAO,OAAO;CACnB,MAAM,WAAW,gBAAgB,QAAQ,IAAI;CAC7C,MAAM,UAAU,UAAU,UAAU,KAAK;CACzC,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,aAAa,SAAS,IAAI,uBAAuB,GAAG,OAAO;CACzF,OAAO;EACL,UAAU;EACV,SAAS,UAAU;GACjB,MAAM,KAAK,MAAM,QAAQ,KAAK;GAC9B,MAAM,OAAO,MAAM,SAAS,SAAS,OAAO,IAAI,EAAE;GAClD,IAAI,MAAM,MAAM,IAAI;EACtB;CACF;AACF;AAEA,SAAS,aAAa,SAAqB,GAA6B,MAAuB;CAC7F,IAAI,eAAe,QAAQ,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,GAAG,OAAO;CACzE,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;EACvC,MAAM,IAAI,QAAQ;EAClB,MAAM,IAAI,SAAS,IAAI,KAAK,QAAQ;EACpC,IAAI,kBAAkB,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,KAAK,OAAO,MAAM,OAAO;CAC7E;CACA,OAAO;AACT"}
@@ -0,0 +1,55 @@
1
+ import { hitPredicate } from "@weasel-js/gestures";
2
+
3
+ //#region src/features/viewports/viewMarkerDrag.ts
4
+ /** The action id a drag on a view marker routes to. */
5
+ var VIEW_MARKER_DRAG = "view.marker.drag";
6
+ /** The id of the entry {@link createViewMarkerContribution} builds. */
7
+ var VIEW_MARKER_ENTRY_ID = "view-marker";
8
+ var MARKER_KIND_PREFIX = "layer:view-marker:";
9
+ function grabIn(affordance) {
10
+ return affordance?.payload ?? null;
11
+ }
12
+ /** Drags the grabbed marker. There is nothing to commit, so a cancel ends where a release does. */
13
+ function viewMarkerDragAction() {
14
+ return {
15
+ id: VIEW_MARKER_DRAG,
16
+ label: "View marker — drag",
17
+ invoker: {
18
+ timing: "ongoing",
19
+ start: (ctx) => {
20
+ const grab = grabIn(ctx.drag?.affordance);
21
+ if (!grab) return {};
22
+ return {
23
+ kind: VIEW_MARKER_DRAG,
24
+ onMove: (m) => {
25
+ grab.moveTo(m.drag?.current ?? m.world);
26
+ }
27
+ };
28
+ }
29
+ }
30
+ };
31
+ }
32
+ /**
33
+ * The always-on entry (id `'view-marker'`) that binds a drag on any marker to {@link VIEW_MARKER_DRAG}.
34
+ * A `<SceneCanvas>` installs it unless `actions={null}` or a `tools` takeover leaves it out; to turn marker
35
+ * dragging back on there, pass it in `ambient`, or in the takeover's own `useTools` ambient list.
36
+ */
37
+ function createViewMarkerContribution() {
38
+ return {
39
+ id: VIEW_MARKER_ENTRY_ID,
40
+ eligibility: { always: true },
41
+ actions: [viewMarkerDragAction()],
42
+ bindings: [{
43
+ spec: {
44
+ kind: "drag",
45
+ target: { kindOf: hitPredicate((hit) => hit?.kind?.startsWith(MARKER_KIND_PREFIX) ?? false) }
46
+ },
47
+ actionId: VIEW_MARKER_DRAG,
48
+ opts: { id: "view-marker.drag" }
49
+ }]
50
+ };
51
+ }
52
+
53
+ //#endregion
54
+ export { VIEW_MARKER_DRAG, VIEW_MARKER_ENTRY_ID, createViewMarkerContribution, viewMarkerDragAction };
55
+ //# sourceMappingURL=viewMarkerDrag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewMarkerDrag.js","names":[],"sources":["../../../src/features/viewports/viewMarkerDrag.ts"],"sourcesContent":["import { hitPredicate } from '@weasel-js/gestures';\nimport type { Action, InvocationCtx } from '@weasel-js/routing';\nimport type { SurfaceContribution } from 'canvas/surfaceContribution';\nimport type { ViewMarkerGrab } from './viewMarker';\n\n/** The action id a drag on a view marker routes to. */\nexport const VIEW_MARKER_DRAG = 'view.marker.drag';\n\n/** The id of the entry {@link createViewMarkerContribution} builds. */\nexport const VIEW_MARKER_ENTRY_ID = 'view-marker';\n\nconst MARKER_KIND_PREFIX = 'layer:view-marker:';\n\nfunction grabIn(affordance: unknown): ViewMarkerGrab | null {\n return (affordance as { payload?: ViewMarkerGrab } | null | undefined)?.payload ?? null;\n}\n\n/** Drags the grabbed marker. There is nothing to commit, so a cancel ends where a release does. */\nexport function viewMarkerDragAction(): Action {\n return {\n id: VIEW_MARKER_DRAG,\n label: 'View marker — drag',\n invoker: {\n timing: 'ongoing',\n start: (ctx: InvocationCtx) => {\n const grab = grabIn(ctx.drag?.affordance);\n if (!grab) return {};\n return {\n kind: VIEW_MARKER_DRAG,\n onMove: (m: InvocationCtx) => { grab.moveTo(m.drag?.current ?? m.world); },\n };\n },\n },\n };\n}\n\n/**\n * The always-on entry (id `'view-marker'`) that binds a drag on any marker to {@link VIEW_MARKER_DRAG}.\n * A `<SceneCanvas>` installs it unless `actions={null}` or a `tools` takeover leaves it out; to turn marker\n * dragging back on there, pass it in `ambient`, or in the takeover's own `useTools` ambient list.\n */\nexport function createViewMarkerContribution(): SurfaceContribution {\n return {\n id: VIEW_MARKER_ENTRY_ID,\n eligibility: { always: true },\n actions: [viewMarkerDragAction()],\n bindings: [{\n spec: {\n kind: 'drag',\n target: { kindOf: hitPredicate((hit) => (hit as { kind?: string } | null | undefined)?.kind?.startsWith(MARKER_KIND_PREFIX) ?? false) },\n },\n actionId: VIEW_MARKER_DRAG,\n opts: { id: 'view-marker.drag' },\n }],\n };\n}\n"],"mappings":";;;;AAMA,IAAa,mBAAmB;;AAGhC,IAAa,uBAAuB;AAEpC,IAAM,qBAAqB;AAE3B,SAAS,OAAO,YAA4C;CAC1D,OAAQ,YAAgE,WAAW;AACrF;;AAGA,SAAgB,uBAA+B;CAC7C,OAAO;EACL,IAAI;EACJ,OAAO;EACP,SAAS;GACP,QAAQ;GACR,QAAQ,QAAuB;IAC7B,MAAM,OAAO,OAAO,IAAI,MAAM,UAAU;IACxC,IAAI,CAAC,MAAM,OAAO,CAAC;IACnB,OAAO;KACL,MAAM;KACN,SAAS,MAAqB;MAAE,KAAK,OAAO,EAAE,MAAM,WAAW,EAAE,KAAK;KAAG;IAC3E;GACF;EACF;CACF;AACF;;;;;;AAOA,SAAgB,+BAAoD;CAClE,OAAO;EACL,IAAI;EACJ,aAAa,EAAE,QAAQ,KAAK;EAC5B,SAAS,CAAC,qBAAqB,CAAC;EAChC,UAAU,CAAC;GACT,MAAM;IACJ,MAAM;IACN,QAAQ,EAAE,QAAQ,cAAc,QAAS,KAA8C,MAAM,WAAW,kBAAkB,KAAK,KAAK,EAAE;GACxI;GACA,UAAU;GACV,MAAM,EAAE,IAAI,mBAAmB;EACjC,CAAC;CACH;AACF"}