@weasel-js/core 1.12.0 → 1.13.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 (304) hide show
  1. package/CHANGELOG.md +199 -0
  2. package/dist/animation/poseTarget.js +46 -15
  3. package/dist/animation/poseTarget.js.map +1 -1
  4. package/dist/animation/poseTransition.js +3 -3
  5. package/dist/animation/poseTransition.js.map +1 -1
  6. package/dist/animation/reflow.js +3 -6
  7. package/dist/animation/reflow.js.map +1 -1
  8. package/dist/animation/rig/bindRig.js +2 -2
  9. package/dist/animation/rig/bindRig.js.map +1 -1
  10. package/dist/animation/rig/skinBinding.js +2 -2
  11. package/dist/animation/rig/skinBinding.js.map +1 -1
  12. package/dist/barrel/canvas.js +5 -3
  13. package/dist/barrel/chrome.js +3 -1
  14. package/dist/barrel/paths.js +3 -2
  15. package/dist/barrel/routing.js +2 -2
  16. package/dist/barrel/scene.js +6 -1
  17. package/dist/barrel/text.js +2 -2
  18. package/dist/canvas/NodeShape.js +1 -0
  19. package/dist/canvas/NodeShape.js.map +1 -1
  20. package/dist/canvas/SceneCanvas/SceneCanvasInput.js +2 -0
  21. package/dist/canvas/SceneCanvas/SceneCanvasInput.js.map +1 -1
  22. package/dist/canvas/SceneCanvas/canvasPassThrough.js +1 -1
  23. package/dist/canvas/SceneCanvas/canvasPassThrough.js.map +1 -1
  24. package/dist/canvas/SceneCanvas/defaultNodeRouting.js +2 -2
  25. package/dist/canvas/SceneCanvas/defaultNodeRouting.js.map +1 -1
  26. package/dist/canvas/SceneCanvas/scenePointQuery.js +20 -2
  27. package/dist/canvas/SceneCanvas/scenePointQuery.js.map +1 -1
  28. package/dist/canvas/SceneCanvas/useCanvasTools.js +3 -0
  29. package/dist/canvas/SceneCanvas/useCanvasTools.js.map +1 -1
  30. package/dist/canvas/SceneCanvas/useContributionRoles.js +2 -0
  31. package/dist/canvas/SceneCanvas/useContributionRoles.js.map +1 -1
  32. package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js +25 -19
  33. package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js.map +1 -1
  34. package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js +7 -0
  35. package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js.map +1 -1
  36. package/dist/canvas/SceneCanvas/useSceneCanvasTools.js +1 -0
  37. package/dist/canvas/SceneCanvas/useSceneCanvasTools.js.map +1 -1
  38. package/dist/canvas/SceneCanvas/useScenePicking.js +31 -6
  39. package/dist/canvas/SceneCanvas/useScenePicking.js.map +1 -1
  40. package/dist/canvas/SceneCanvas/useSceneSelectTool.js +4 -5
  41. package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
  42. package/dist/canvas/SceneCanvas/useSceneStandardActions.js +5 -3
  43. package/dist/canvas/SceneCanvas/useSceneStandardActions.js.map +1 -1
  44. package/dist/canvas/SceneCanvas.js +19 -2
  45. package/dist/canvas/SceneCanvas.js.map +1 -1
  46. package/dist/canvas/accentColor.js +26 -3
  47. package/dist/canvas/accentColor.js.map +1 -1
  48. package/dist/canvas/buildSceneLayer.js +1 -1
  49. package/dist/canvas/buildSceneLayer.js.map +1 -1
  50. package/dist/canvas/buildSceneTree.js +14 -20
  51. package/dist/canvas/buildSceneTree.js.map +1 -1
  52. package/dist/canvas/canvasRenderer.js +6 -2
  53. package/dist/canvas/canvasRenderer.js.map +1 -1
  54. package/dist/canvas/copiesAbove.js +1 -1
  55. package/dist/canvas/copiesAbove.js.map +1 -1
  56. package/dist/canvas/defaultDrawOne.js +10 -7
  57. package/dist/canvas/defaultDrawOne.js.map +1 -1
  58. package/dist/canvas/deps/areaSelect.js +5 -2
  59. package/dist/canvas/deps/areaSelect.js.map +1 -1
  60. package/dist/canvas/deps/booleans.js +1 -1
  61. package/dist/canvas/deps/booleans.js.map +1 -1
  62. package/dist/canvas/deps/enteredGroup.js +81 -0
  63. package/dist/canvas/deps/enteredGroup.js.map +1 -0
  64. package/dist/canvas/deps/hitTestArea.js +27 -15
  65. package/dist/canvas/deps/hitTestArea.js.map +1 -1
  66. package/dist/canvas/deps/insert.js +4 -36
  67. package/dist/canvas/deps/insert.js.map +1 -1
  68. package/dist/canvas/deps/insertSite.js +39 -0
  69. package/dist/canvas/deps/insertSite.js.map +1 -0
  70. package/dist/canvas/deps/lassoSelect.js +6 -3
  71. package/dist/canvas/deps/lassoSelect.js.map +1 -1
  72. package/dist/canvas/deps/pathLeaf.js +1 -1
  73. package/dist/canvas/deps/pathLeaf.js.map +1 -1
  74. package/dist/canvas/deps/slice.js +2 -1
  75. package/dist/canvas/deps/slice.js.map +1 -1
  76. package/dist/canvas/derivedPath.js +14 -6
  77. package/dist/canvas/derivedPath.js.map +1 -1
  78. package/dist/canvas/doubleClickTarget.js +9 -11
  79. package/dist/canvas/doubleClickTarget.js.map +1 -1
  80. package/dist/canvas/drawnBounds.js +16 -7
  81. package/dist/canvas/drawnBounds.js.map +1 -1
  82. package/dist/canvas/inheritedPaints.js +22 -11
  83. package/dist/canvas/inheritedPaints.js.map +1 -1
  84. package/dist/canvas/nodeShapes/nodePaint.js +1 -7
  85. package/dist/canvas/nodeShapes/nodePaint.js.map +1 -1
  86. package/dist/canvas/nodeShapes/pathPainter.js +2 -3
  87. package/dist/canvas/nodeShapes/pathPainter.js.map +1 -1
  88. package/dist/canvas/nodeShapes/rectFallbackPainter.js +1 -1
  89. package/dist/canvas/nodeShapes/rectFallbackPainter.js.map +1 -1
  90. package/dist/canvas/nodeShapes/shapePainter.js +2 -3
  91. package/dist/canvas/nodeShapes/shapePainter.js.map +1 -1
  92. package/dist/canvas/ownClip.js +24 -0
  93. package/dist/canvas/ownClip.js.map +1 -0
  94. package/dist/canvas/paintCull.js.map +1 -1
  95. package/dist/canvas/pickIndex/nodeBroadBox.js +86 -0
  96. package/dist/canvas/pickIndex/nodeBroadBox.js.map +1 -0
  97. package/dist/canvas/pickIndex/scenePickIndex.js +208 -0
  98. package/dist/canvas/pickIndex/scenePickIndex.js.map +1 -0
  99. package/dist/canvas/pickWalk.js +14 -90
  100. package/dist/canvas/pickWalk.js.map +1 -1
  101. package/dist/canvas/poseCascade.js +1 -1
  102. package/dist/canvas/poseCascade.js.map +1 -1
  103. package/dist/canvas/publishPreviews.js +97 -0
  104. package/dist/canvas/publishPreviews.js.map +1 -0
  105. package/dist/canvas/sceneAdapter.js +7 -5
  106. package/dist/canvas/sceneAdapter.js.map +1 -1
  107. package/dist/canvas/sceneClipboard.js +17 -10
  108. package/dist/canvas/sceneClipboard.js.map +1 -1
  109. package/dist/canvas/scenePickReads.js +37 -0
  110. package/dist/canvas/scenePickReads.js.map +1 -0
  111. package/dist/canvas/scenePickSource.js +74 -0
  112. package/dist/canvas/scenePickSource.js.map +1 -0
  113. package/dist/canvas/sceneSlotWiring.js +9 -4
  114. package/dist/canvas/sceneSlotWiring.js.map +1 -1
  115. package/dist/canvas/sceneViewRender.js +13 -8
  116. package/dist/canvas/sceneViewRender.js.map +1 -1
  117. package/dist/canvas/shownNodes.js +15 -0
  118. package/dist/canvas/shownNodes.js.map +1 -0
  119. package/dist/canvas/useCanvasPaint.js +3 -1
  120. package/dist/canvas/useCanvasPaint.js.map +1 -1
  121. package/dist/canvas/useSceneAdapter.js +5 -3
  122. package/dist/canvas/useSceneAdapter.js.map +1 -1
  123. package/dist/clipboard.d.ts +2 -2
  124. package/dist/{clone-C3e0OTuF.d.ts → clone-qb2e1F5p.d.ts} +2 -2
  125. package/dist/clone.d.ts +3 -3
  126. package/dist/core/isPlainObject.js +2 -2
  127. package/dist/core/layers/layerGroup.js.map +1 -1
  128. package/dist/core/layers/render.js.map +1 -1
  129. package/dist/core/scene/NodeRouting.js.map +1 -1
  130. package/dist/core/scene/arrivalWindow.js +1 -1
  131. package/dist/core/scene/arrivalWindow.js.map +1 -1
  132. package/dist/core/scene/clipNothing.js +2 -5
  133. package/dist/core/scene/clipNothing.js.map +1 -1
  134. package/dist/core/scene/declaredFieldSetters.js +44 -0
  135. package/dist/core/scene/declaredFieldSetters.js.map +1 -0
  136. package/dist/core/scene/depMemo.js +3 -0
  137. package/dist/core/scene/depMemo.js.map +1 -1
  138. package/dist/core/scene/dependents.js +3 -1
  139. package/dist/core/scene/dependents.js.map +1 -1
  140. package/dist/core/scene/documentOf.js +43 -0
  141. package/dist/core/scene/documentOf.js.map +1 -0
  142. package/dist/core/scene/effectivePose.js +37 -13
  143. package/dist/core/scene/effectivePose.js.map +1 -1
  144. package/dist/core/scene/fieldEdits.js +17 -14
  145. package/dist/core/scene/fieldEdits.js.map +1 -1
  146. package/dist/core/scene/historyForm.js +4 -4
  147. package/dist/core/scene/historyForm.js.map +1 -1
  148. package/dist/core/scene/inheritedPaint.js +27 -5
  149. package/dist/core/scene/inheritedPaint.js.map +1 -1
  150. package/dist/core/scene/kitOps.js +3 -23
  151. package/dist/core/scene/kitOps.js.map +1 -1
  152. package/dist/core/scene/layoutPass.js +1 -2
  153. package/dist/core/scene/layoutPass.js.map +1 -1
  154. package/dist/core/scene/nodeAsShown.js +72 -0
  155. package/dist/core/scene/nodeAsShown.js.map +1 -0
  156. package/dist/core/scene/nodeFnFields.js +2 -10
  157. package/dist/core/scene/nodeFnFields.js.map +1 -1
  158. package/dist/core/scene/nodeMemo.js +6 -1
  159. package/dist/core/scene/nodeMemo.js.map +1 -1
  160. package/dist/core/scene/nodeStore.js +2 -4
  161. package/dist/core/scene/nodeStore.js.map +1 -1
  162. package/dist/core/scene/pathEffects/derive.js +3 -2
  163. package/dist/core/scene/pathEffects/derive.js.map +1 -1
  164. package/dist/core/scene/pathEffects/foundHidden.js +35 -0
  165. package/dist/core/scene/pathEffects/foundHidden.js.map +1 -0
  166. package/dist/core/scene/plyStacks.js +89 -0
  167. package/dist/core/scene/plyStacks.js.map +1 -0
  168. package/dist/core/scene/poseHold.js +42 -24
  169. package/dist/core/scene/poseHold.js.map +1 -1
  170. package/dist/core/scene/poseOverrides.js +35 -38
  171. package/dist/core/scene/poseOverrides.js.map +1 -1
  172. package/dist/core/scene/posePlies.js +59 -0
  173. package/dist/core/scene/posePlies.js.map +1 -0
  174. package/dist/core/scene/reconcileSpecs.js +6 -15
  175. package/dist/core/scene/reconcileSpecs.js.map +1 -1
  176. package/dist/core/scene/renderOrder.js +10 -4
  177. package/dist/core/scene/renderOrder.js.map +1 -1
  178. package/dist/core/scene/sameValue.js +17 -0
  179. package/dist/core/scene/sameValue.js.map +1 -0
  180. package/dist/core/scene/scene.js +3 -3
  181. package/dist/core/scene/scene.js.map +1 -1
  182. package/dist/core/scene/sceneEdits.js +8 -0
  183. package/dist/core/scene/sceneEdits.js.map +1 -1
  184. package/dist/core/scene/sceneState.js +1 -2
  185. package/dist/core/scene/sceneState.js.map +1 -1
  186. package/dist/core/scene/serialization.js +45 -9
  187. package/dist/core/scene/serialization.js.map +1 -1
  188. package/dist/core/scene/staleReplay.js +5 -2
  189. package/dist/core/scene/staleReplay.js.map +1 -1
  190. package/dist/core/scene/statedFields.js +4 -2
  191. package/dist/core/scene/statedFields.js.map +1 -1
  192. package/dist/core/scene/treeEdits.js +2 -4
  193. package/dist/core/scene/treeEdits.js.map +1 -1
  194. package/dist/core/scene/treeOrder.js +3 -1
  195. package/dist/core/scene/treeOrder.js.map +1 -1
  196. package/dist/core/scene/treeReads.js +1 -2
  197. package/dist/core/scene/treeReads.js.map +1 -1
  198. package/dist/core/scene/treeShape.js +13 -0
  199. package/dist/core/scene/treeShape.js.map +1 -0
  200. package/dist/core/scene/types.js.map +1 -1
  201. package/dist/core/scene/useScene.js +1 -1
  202. package/dist/core/scene/useScene.js.map +1 -1
  203. package/dist/{cornerHandles-p8fDIAKt.d.ts → cornerHandles-CNCb7aTY.d.ts} +1 -1
  204. package/dist/{depSchema-mG19aEdD.d.ts → depSchema-CpIQzKWQ.d.ts} +38 -59
  205. package/dist/devtools/handlers.js +1 -1
  206. package/dist/devtools/handlers.js.map +1 -1
  207. package/dist/devtools/protocol.js.map +1 -1
  208. package/dist/devtools-protocol.d.ts +4 -3
  209. package/dist/devtools.d.ts +3 -2
  210. package/dist/features/measure/measureHit.js +5 -3
  211. package/dist/features/measure/measureHit.js.map +1 -1
  212. package/dist/features/measure/measurement.js +1 -1
  213. package/dist/features/measure/measurement.js.map +1 -1
  214. package/dist/features/modes/modeLayers.js +14 -7
  215. package/dist/features/modes/modeLayers.js.map +1 -1
  216. package/dist/features/modes/modeTriggers.js +3 -3
  217. package/dist/features/modes/modeTriggers.js.map +1 -1
  218. package/dist/features/pathEffects/effectSources.js +25 -16
  219. package/dist/features/pathEffects/effectSources.js.map +1 -1
  220. package/dist/features/pathEffects/readSource.js +1 -1
  221. package/dist/features/pathEffects/readSource.js.map +1 -1
  222. package/dist/features/pathEffects/useEffectSourcesLayer.js +15 -19
  223. package/dist/features/pathEffects/useEffectSourcesLayer.js.map +1 -1
  224. package/dist/features/paths/editablePath.js +1 -1
  225. package/dist/features/paths/editablePath.js.map +1 -1
  226. package/dist/features/paths/pathSnap/sceneIndex.js +1 -1
  227. package/dist/features/paths/pathSnap/sceneIndex.js.map +1 -1
  228. package/dist/features/selection/enteredGroupFrame.js +65 -0
  229. package/dist/features/selection/enteredGroupFrame.js.map +1 -0
  230. package/dist/features/selection/enteredGroupScope.js +44 -0
  231. package/dist/features/selection/enteredGroupScope.js.map +1 -0
  232. package/dist/features/text/sceneTextDrafts.js +26 -27
  233. package/dist/features/text/sceneTextDrafts.js.map +1 -1
  234. package/dist/features/text/textHitAt.js +4 -2
  235. package/dist/features/text/textHitAt.js.map +1 -1
  236. package/dist/features/text/useSceneTextEdit.js +3 -3
  237. package/dist/features/text/useSceneTextEdit.js.map +1 -1
  238. package/dist/features/viewports/viewportLayer.js +2 -1
  239. package/dist/features/viewports/viewportLayer.js.map +1 -1
  240. package/dist/index.d.ts +230 -77
  241. package/dist/index.js +16 -6
  242. package/dist/interactions/actions/admitsChild.js +11 -0
  243. package/dist/interactions/actions/admitsChild.js.map +1 -0
  244. package/dist/interactions/actions/defaults/contextMenu.js +16 -3
  245. package/dist/interactions/actions/defaults/contextMenu.js.map +1 -1
  246. package/dist/interactions/actions/defaults/createPaintAction.js +4 -1
  247. package/dist/interactions/actions/defaults/createPaintAction.js.map +1 -1
  248. package/dist/interactions/actions/defaults/enterGroup.js +88 -0
  249. package/dist/interactions/actions/defaults/enterGroup.js.map +1 -0
  250. package/dist/interactions/actions/defaults/enterPathEdit.js +1 -1
  251. package/dist/interactions/actions/defaults/enterPathEdit.js.map +1 -1
  252. package/dist/interactions/actions/defaults/enterTextEdit.js +1 -1
  253. package/dist/interactions/actions/defaults/enterTextEdit.js.map +1 -1
  254. package/dist/interactions/actions/defaults/group.js +7 -7
  255. package/dist/interactions/actions/defaults/group.js.map +1 -1
  256. package/dist/interactions/actions/defaults/selectAll.js +7 -3
  257. package/dist/interactions/actions/defaults/selectAll.js.map +1 -1
  258. package/dist/interactions/actions/defaults/selectDeep.js +35 -0
  259. package/dist/interactions/actions/defaults/selectDeep.js.map +1 -0
  260. package/dist/interactions/actions/drawnBoxes.js +2 -1
  261. package/dist/interactions/actions/drawnBoxes.js.map +1 -1
  262. package/dist/interactions/actions/insert/action.js +1 -0
  263. package/dist/interactions/actions/insert/action.js.map +1 -1
  264. package/dist/interactions/actions/insert/parent.js +7 -5
  265. package/dist/interactions/actions/insert/parent.js.map +1 -1
  266. package/dist/interactions/actions/insert/placeAction.js +3 -2
  267. package/dist/interactions/actions/insert/placeAction.js.map +1 -1
  268. package/dist/interactions/actions/insert/session.js +2 -2
  269. package/dist/interactions/actions/insert/session.js.map +1 -1
  270. package/dist/interactions/actions/move/commit/reparent.js +1 -1
  271. package/dist/interactions/actions/move/commit/reparent.js.map +1 -1
  272. package/dist/interactions/actions/move/gestureAdapter.js +6 -7
  273. package/dist/interactions/actions/move/gestureAdapter.js.map +1 -1
  274. package/dist/interactions/actions/move/layout/exits.js +1 -1
  275. package/dist/interactions/actions/move/layout/exits.js.map +1 -1
  276. package/dist/interactions/actions/move/session.js +4 -4
  277. package/dist/interactions/actions/move/session.js.map +1 -1
  278. package/dist/interactions/actions/pathCommands/pathNodes.js +14 -5
  279. package/dist/interactions/actions/pathCommands/pathNodes.js.map +1 -1
  280. package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js +14 -2
  281. package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js.map +1 -1
  282. package/dist/{math-B56zxVs5.d.ts → math-C_elShP_.d.ts} +2 -5
  283. package/dist/math.d.ts +1 -1
  284. package/dist/move.d.ts +4 -4
  285. package/dist/{options-DBxEryYq.d.ts → options-ClEm3SEl.d.ts} +1 -1
  286. package/dist/resize.d.ts +4 -4
  287. package/dist/test-seams.d.ts +6 -6
  288. package/dist/tools/builtin/pressTarget.js +22 -0
  289. package/dist/tools/builtin/pressTarget.js.map +1 -0
  290. package/dist/tools/builtin/select/adapterPointQuery.js +3 -3
  291. package/dist/tools/builtin/select/adapterPointQuery.js.map +1 -1
  292. package/dist/tools/builtin/select/collapseDeferred.js +40 -0
  293. package/dist/tools/builtin/select/collapseDeferred.js.map +1 -0
  294. package/dist/tools/builtin/select/groupEntry.js +82 -0
  295. package/dist/tools/builtin/select/groupEntry.js.map +1 -0
  296. package/dist/tools/builtin/select/pressDecision.js +1 -1
  297. package/dist/tools/builtin/select/pressDecision.js.map +1 -1
  298. package/dist/tools/builtin/select/useSelectTool.js +61 -45
  299. package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
  300. package/dist/{types-7puAuVD2.d.ts → types-49uYJN3j.d.ts} +1 -1
  301. package/dist/{types-CniVctmk.d.ts → types-BEdRIhNc.d.ts} +272 -109
  302. package/dist/{types-oFeQ5VjV.d.ts → types-p2alu-R1.d.ts} +1 -1
  303. package/dist/version.js +1 -1
  304. package/package.json +25 -25
@@ -1,5 +1,5 @@
1
1
  import * as _weasel_js_paint from '@weasel-js/paint';
2
- import { TileKind, Paint, GradStop, TilePaint, PaintedFill, PaintedStroke, Inherit, Fill, Stroke, CommandFill, CommandStroke, CommandPaint } from '@weasel-js/paint';
2
+ import { TileKind, Paint, GradStop, TilePaint, Fill, Stroke, Inherit, CommandFill, CommandStroke, CommandPaint } from '@weasel-js/paint';
3
3
  import { Reflection } from '@weasel-js/registry';
4
4
  import { ComponentType } from 'react';
5
5
  import { IconRef } from '@weasel-js/icons';
@@ -7,18 +7,18 @@ import { PaintDrawKind, DrawCommand, Effect, CompositeOp, BlendFactors } from '@
7
7
  import * as _weasel_js_routing_react from '@weasel-js/routing/react';
8
8
  import * as _weasel_js_geom from '@weasel-js/geom';
9
9
  import { FramedBox, View, Path, Affine, Rect, Scale2, PolygonPath, PathFillRule, AnchorSet, PointOrigin } from '@weasel-js/geom';
10
+ import { I as InheritedPaint, S as Scene, R as RectPose } from './types-BEdRIhNc.js';
10
11
  import { Op, History } from '@weasel-js/history';
11
12
  import { NodeId, ModifierState, OverlayRole, ResizeAnchor, ActionDeps, OngoingHandle, OngoingOverlay, SelectionApi, IngestItem, Point2, DragSample, DebugSink, LongPressState } from '@weasel-js/routing';
12
13
  import { TextNodeSource, TextOutlinesError, TextStyle, StyledRun, TextVerticalAlign } from '@weasel-js/text';
13
14
  import { CursorSpec } from '@weasel-js/cursor';
14
15
  import { ClaimableGesture } from '@weasel-js/gestures';
15
16
  import { SnapTier, ProbeRole } from '@weasel-js/geom/nd';
16
- import { S as Scene, R as RectPose } from './types-CniVctmk.js';
17
- import { a as PlaneMap, L as LayoutStrategy, b as LayoutDropTargetMode, R as ReflowTransition } from './types-7puAuVD2.js';
17
+ import { a as PlaneMap, L as LayoutStrategy, b as LayoutDropTargetMode, R as ReflowTransition } from './types-49uYJN3j.js';
18
18
  import { O as OriginProjection } from './originProjection-gY0jb4Wb.js';
19
19
  import { EasingSpec, InterpolatorFactory, Target, Animator, TargetAxes } from '@weasel-js/animator';
20
- import { I as InsertAdapter, P as PasteOmission, R as RegionPickView } from './types-oFeQ5VjV.js';
21
- import { B as BoundsConstraint } from './clone-C3e0OTuF.js';
20
+ import { I as InsertAdapter, P as PasteOmission, R as RegionPickView } from './types-p2alu-R1.js';
21
+ import { B as BoundsConstraint } from './clone-qb2e1F5p.js';
22
22
  import { P as PoseDescriptor } from './poseDescriptor-DN2yfnq_.js';
23
23
 
24
24
  /** Pixel dimensions of the canvas viewport. */
@@ -201,53 +201,6 @@ declare function paintKindOf(fill: Paint): PaintKindEntry | undefined;
201
201
  /** @internal Test helper — do not call from product code. */
202
202
  declare function _resetPaintKindsForTests(): void;
203
203
 
204
- /**
205
- * What a node's `'inherit'` fill and stroke resolve to: its nearest enclosing
206
- * container's, `null` for none. A field the node does not inherit is `null`
207
- * here whatever its containers hold.
208
- */
209
- interface InheritedPaint {
210
- readonly fill: PaintedFill | null;
211
- readonly stroke: PaintedStroke | null;
212
- }
213
- /** Whether `node`'s fill or stroke is {@link INHERIT}, so painting it needs
214
- * its containers' paint. */
215
- declare function inheritsPaint(node: {
216
- data?: unknown;
217
- }): boolean;
218
- /**
219
- * {@link InheritedPaint} for `node`, given its ancestors innermost first. Only
220
- * containers are sources; one whose own field is `'inherit'` passes the
221
- * question on to the next, and running out of containers is none.
222
- *
223
- * Hands back the object it returned last time for `node` while both values
224
- * are the same references, so a memo can key on it.
225
- */
226
- declare function inheritedPaintOf(node: {
227
- data?: unknown;
228
- }, ancestors: Iterable<{
229
- kind?: string;
230
- data?: unknown;
231
- }>): InheritedPaint;
232
- /** The fields {@link withInheritedPaint} resolves. */
233
- type ResolvedPaintFields<T> = Omit<T, 'fill' | 'stroke'> & {
234
- fill?: Exclude<T extends {
235
- fill?: infer F;
236
- } ? F : never, Inherit>;
237
- stroke?: Exclude<T extends {
238
- stroke?: infer S;
239
- } ? S : never, Inherit>;
240
- };
241
- /**
242
- * `data` with an `'inherit'` fill or stroke replaced by what `inherited` says
243
- * it resolves to, for a reader that wants the paint a node shows rather than
244
- * the one it declares. Hands `data` back as is when nothing inherits.
245
- */
246
- declare function withInheritedPaint<T extends {
247
- fill?: unknown;
248
- stroke?: unknown;
249
- }>(data: T, inherited: InheritedPaint | undefined): ResolvedPaintFields<T>;
250
-
251
204
  /** The paint a path node wears, resolved: what an action that turns ink into
252
205
  * geometry reads from a node and tells a consumer to put on a result. */
253
206
  interface PathPaint {
@@ -794,7 +747,7 @@ interface LayerHit<TScratch = unknown> extends AffordanceBinding<TScratch> {
794
747
  * separated in the render order is drawn as one bracket per run, with a
795
748
  * warning.
796
749
  *
797
- * This is a render-stack bracket, not a scene `ContainerNode`.
750
+ * This is a render-stack bracket, not a scene `GroupNode`.
798
751
  */
799
752
  interface LayerGroup {
800
753
  /** Names the group in warnings; not a layer id and never drawn. */
@@ -852,11 +805,17 @@ declare function subscribeToSources(sources: readonly {
852
805
  subscribe?: RedrawSource['subscribe'];
853
806
  }[])): RedrawSource['subscribe'];
854
807
 
855
- /** Canvas size in CSS pixels, so layers don't have to know about DPR. */
808
+ /** Canvas size in CSS pixels. */
856
809
  interface Dims {
857
810
  width: number;
858
811
  height: number;
859
812
  }
813
+ /** What a layer draws into: its size, and the device pixels to the CSS pixel
814
+ * the frame is painted at, which is what an effect's reach is measured in. */
815
+ interface LayerDims extends Dims {
816
+ dpr: number;
817
+ }
818
+
860
819
  /**
861
820
  * What a layer's `draw` threw, and which layer threw it. A throw that escaped
862
821
  * the frame loop would take the whole frame with it, every other layer
@@ -893,7 +852,7 @@ interface RenderLayer<TData> {
893
852
  * Emit a DrawCommand tree, in the coordinates `space` names. A world-space
894
853
  * layer must NOT apply the view transform itself.
895
854
  */
896
- draw: (data: TData, view: View, dims: Dims) => DrawCommand[];
855
+ draw: (data: TData, view: View, dims: LayerDims) => DrawCommand[];
897
856
  /**
898
857
  * Optional cache key. When present and a `LayerCommandCache` is supplied to
899
858
  * `drawLayers`, the layer's previous `DrawCommand[]` is reused as long as
@@ -908,7 +867,7 @@ interface RenderLayer<TData> {
908
867
  * the way world-space layers are** (see `space` below) — include them in
909
868
  * `deps` if `draw` reads them.
910
869
  */
911
- deps?: (data: TData, view: View, dims: Dims) => readonly unknown[];
870
+ deps?: (data: TData, view: View, dims: LayerDims) => readonly unknown[];
912
871
  /** Whether the layer is shown when no explicit visibility entry exists.
913
872
  * Defaults to `true` when absent. */
914
873
  defaultVisible?: boolean;
@@ -995,7 +954,7 @@ interface LayerDrawSpan {
995
954
  * layer drawn. `retain` decides which unchanged layers the renderer keeps
996
955
  * as pixels between frames; `false` keeps none.
997
956
  */
998
- declare function drawLayers<TData>(layers: RenderLayer<TData>[], data: TData, visibility: Record<string, boolean>, order: string[] | undefined, view: View | undefined, dims: Dims, cache?: LayerCommandCache, onLayerError?: (failure: LayerDrawFailure) => void, groups?: readonly LayerGroup[], spans?: LayerDrawSpan[], retain?: LayerRetainPolicy | false): DrawCommand[];
957
+ declare function drawLayers<TData>(layers: RenderLayer<TData>[], data: TData, visibility: Record<string, boolean>, order: string[] | undefined, view: View | undefined, dims: LayerDims, cache?: LayerCommandCache, onLayerError?: (failure: LayerDrawFailure) => void, groups?: readonly LayerGroup[], spans?: LayerDrawSpan[], retain?: LayerRetainPolicy | false): DrawCommand[];
999
958
  /**
1000
959
  * Resolve one layer's visibility: `alwaysOn` wins, then an explicit entry in
1001
960
  * `visibility`, then `defaultVisible`, defaulting to shown.
@@ -1027,7 +986,7 @@ declare function paintedLayers<TData>(layers: readonly RenderLayer<TData>[], vis
1027
986
  * layer it was. The layer's cache entry goes with it, so the next frame is a
1028
987
  * real re-attempt.
1029
988
  */
1030
- declare function drawOneLayer<TData>(layer: RenderLayer<TData>, data: TData, view: View, dims: Dims, cache?: LayerCommandCache, onLayerError?: (failure: LayerDrawFailure) => void): DrawCommand[];
989
+ declare function drawOneLayer<TData>(layer: RenderLayer<TData>, data: TData, view: View, dims: LayerDims, cache?: LayerCommandCache, onLayerError?: (failure: LayerDrawFailure) => void): DrawCommand[];
1031
990
  /** Whether `b` matches `a` entry for entry under `Object.is` — how
1032
991
  * `RenderLayer.deps` is compared, and any cache keyed the same way. */
1033
992
  declare function depsUnchanged(a: readonly unknown[], b: readonly unknown[]): boolean;
@@ -2160,6 +2119,22 @@ interface EditAnchorsDep {
2160
2119
  setSelectedMark?(mark: PathEditMark | null): void;
2161
2120
  }
2162
2121
 
2122
+ /** The group a press resolves inside of, held beside the selection; `<SceneCanvas enteredGroup>` takes one. */
2123
+ interface EnteredGroupApi {
2124
+ get(): NodeId | null;
2125
+ enter(id: NodeId): void;
2126
+ /** Moves to the nearest group above the current one, or to none. */
2127
+ leave(): void;
2128
+ /** Leaves every entered group at once. */
2129
+ leaveAll(): void;
2130
+ subscribe(fn: () => void): () => void;
2131
+ }
2132
+ type SceneReads = Pick<Scene<unknown, string, unknown>, 'get' | 'ancestorsOf' | 'subscribe'>;
2133
+ /** An entered-group store over `scene`, with nothing entered. */
2134
+ declare function createEnteredGroup(scene: SceneReads): EnteredGroupApi;
2135
+ /** One entered-group store for the life of the scene. */
2136
+ declare function useEnteredGroup(scene: SceneReads): EnteredGroupApi;
2137
+
2163
2138
  /** Context handed to every content handler for one ingest event. */
2164
2139
  interface IngestCtx {
2165
2140
  /** World-space arrival point (drop / pointed imperative ingest); `null`
@@ -2672,6 +2647,10 @@ declare module '@weasel-js/routing' {
2672
2647
  interface DepSchema {
2673
2648
  /** Kit selection state — ids of currently selected nodes. */
2674
2649
  selection: SelectionApi;
2650
+ /** The group the user has entered, or null — session state beside the
2651
+ * selection, not scene data and not undoable. Optional: absent means
2652
+ * inside no group. */
2653
+ enteredGroup?: EnteredGroupApi;
2675
2654
  /** Current viewport — camera position + scale. */
2676
2655
  view: ViewApi;
2677
2656
  /**
@@ -2848,4 +2827,4 @@ declare module '@weasel-js/routing' {
2848
2827
  }
2849
2828
  }
2850
2829
 
2851
- export { type SnapProbeAt as $, type AnchorMarkerShape as A, type PaintKindEntry as B, type ContentHandlerEntry as C, type Dims as D, type EditAnchorsDep as E, type FitViewToBoundsOptions as F, type GestureLifecycleOptions as G, type HandleLocks as H, type InheritedPaint as I, type PathEditTarget as J, type FinishedEditFrame as K, type LayerHit as L, type PathSymmetryDep as M, type BooleanOpOptions as N, type BooleansAdapter as O, type PathEditMark as P, type BooleanOp as Q, type RenderLayer as R, type SnapSource as S, type TransformFollower as T, type PathPaint as U, type ViewAnimationOptions as V, type BooleanOpResult as W, type BooleanStrokes as X, type SnapMods as Y, type SourceCandidate as Z, _resetPaintKindsForTests as _, type PathEditExtension as a, type SnapEdge as a$, type SnapCollect as a0, type SnapMark as a1, type SnapDep as a2, type SnapOutcome as a3, type SnapReadoutStyle as a4, type AngleCandidate as a5, type AngleCollect as a6, type AnimateToBoundsOptions as a7, type AreaSelectDep as a8, type BooleanOpPreview as a9, type MinSize as aA, type NodeAtPointDep as aB, type OutlinePathSpec as aC, PATH_OVERLAY_ROLES as aD, type PaintKind as aE, type PaintKindEditorProps as aF, type PaintKindFamily as aG, type PaintKindLoader as aH, type PanBounds as aI, type PathEditDrag as aJ, type PathEditDragStart as aK, type PathEditFinishCtx as aL, type PathEditHandleMove as aM, type PathEditMarksCtx as aN, type PathEditOther as aO, type PathEditPart as aP, type PathEditStore as aQ, type PathForm as aR, type PathOverlay as aS, type PathOverlayOptions as aT, type PathOverlayStyle as aU, type PathRepresentation as aV, type PathTransformFinishCtx as aW, type PreviewPathsDep as aX, type ResizePolicy as aY, STICKY_DEFAULTS as aZ, type SnapBegin as a_, type BooleanPreviewPiece as aa, type CanDropIntoDep as ab, type ClipboardDep as ac, type ClipboardIngestCtx as ad, type CreateOutlinesResult as ae, type CustomPaintContext as af, DEFAULT_LAYER_RETAIN as ag, type DecayLoopConfig as ah, type FinishedPathEdit as ai, GEOMETRY_BOUND_DATA as aj, type HandleLock as ak, type HitTestView as al, type IngestCtx as am, type IngestionDep as an, type InsertDep as ao, type InsertEnds as ap, type InsertExtras as aq, type InsertIntoDep as ar, type InsertLive as as, type LassoSelectDep as at, type LayerCommandCache as au, type LayerDrawFailure as av, type LayerDrawSpan as aw, type LayerRetainPolicy as ax, type LayoutDep as ay, type Locus as az, type SnapGesture as b, type SnapEdges as b0, type SnapMarks as b1, type SnapReadoutDep as b2, type SvgUnpacker as b3, type TextEditDep as b4, type TextNodeData as b5, type TransformPreviewNode as b6, type TransientPath as b7, VIEW_ANIMATION_KEY as b8, type ViewAnimationApi as b9, previewBooleanOp as bA, registerContentHandler as bB, registerPaintKind as bC, registerPaintKindLoader as bD, seedTile as bE, stickyData as bF, stickyNodeFactory as bG, subscribeToSources as bH, switchGradientKind as bI, textBoxOf as bJ, useDecayLoop as bK, useViewAnimation as bL, useViewAnimationOn as bM, viewAxes as bN, withInheritedPaint as bO, withoutGeometryBoundData as bP, type ViewApi as ba, type ViewChannel as bb, applyBooleanOp as bc, applyCreateOutlines as bd, asPaint as be, createPathOverlay as bf, createSnapOutcomeStore as bg, depsUnchanged as bh, drawLayers as bi, drawOneLayer as bj, fitViewToBounds as bk, getPaintKind as bl, inheritedPaintOf as bm, inheritsPaint as bn, isLayerPainted as bo, isLayerVisible as bp, listGradientKinds as bq, listPaintKinds as br, minSizeOf as bs, paintKindOf as bt, paintKindRegistry as bu, paintedLayers as bv, paintsLayer as bw, paletteInsertPaint as bx, pathPaintOf as by, patternFamily as bz, type SnapConstrain as c, type SnapResizeBox as d, type SnapGeometry as e, type SnapOutcomeStore as f, type SnapSession as g, type ViewportDims as h, type RedrawSource as i, type EditAnchorsParams as j, type InertiaConfig as k, type SliceDep as l, type StickyStyle as m, type ChromeState as n, type LayerGroup as o, type GeometryProjection as p, type InsertNodeFactory as q, type InsertPaint as r, type SvgIngestOptions as s, type SceneCanvasSnap as t, type Affordance as u, type AffordanceRegion as v, type AffordanceBinding as w, type CommonAffordanceScratch as x, type TextOverflow as y, type CreateOutlinesAdapter as z };
2830
+ export { type SourceCandidate as $, type AnchorMarkerShape as A, type CreateOutlinesAdapter as B, type ContentHandlerEntry as C, type Dims as D, type EditAnchorsDep as E, type FitViewToBoundsOptions as F, type GestureLifecycleOptions as G, type HandleLocks as H, type InertiaConfig as I, type PaintKindEntry as J, type PathEditTarget as K, type LayerHit as L, type FinishedEditFrame as M, type PathSymmetryDep as N, type BooleanOpOptions as O, type PathEditMark as P, type BooleansAdapter as Q, type RenderLayer as R, type SnapSource as S, type TransformFollower as T, type BooleanOp as U, type ViewAnimationOptions as V, type PathPaint as W, type BooleanOpResult as X, type BooleanStrokes as Y, type SnapMods as Z, _resetPaintKindsForTests as _, type PathEditExtension as a, type SnapBegin as a$, type SnapProbeAt as a0, type SnapCollect as a1, type SnapMark as a2, type SnapDep as a3, type SnapOutcome as a4, type SnapReadoutStyle as a5, type AngleCandidate as a6, type AngleCollect as a7, type AnimateToBoundsOptions as a8, type AreaSelectDep as a9, type Locus as aA, type MinSize as aB, type NodeAtPointDep as aC, type OutlinePathSpec as aD, PATH_OVERLAY_ROLES as aE, type PaintKind as aF, type PaintKindEditorProps as aG, type PaintKindFamily as aH, type PaintKindLoader as aI, type PanBounds as aJ, type PathEditDrag as aK, type PathEditDragStart as aL, type PathEditFinishCtx as aM, type PathEditHandleMove as aN, type PathEditMarksCtx as aO, type PathEditOther as aP, type PathEditPart as aQ, type PathEditStore as aR, type PathForm as aS, type PathOverlay as aT, type PathOverlayOptions as aU, type PathOverlayStyle as aV, type PathRepresentation as aW, type PathTransformFinishCtx as aX, type PreviewPathsDep as aY, type ResizePolicy as aZ, STICKY_DEFAULTS as a_, type BooleanOpPreview as aa, type BooleanPreviewPiece as ab, type CanDropIntoDep as ac, type ClipboardDep as ad, type ClipboardIngestCtx as ae, type CreateOutlinesResult as af, type CustomPaintContext as ag, DEFAULT_LAYER_RETAIN as ah, type DecayLoopConfig as ai, type FinishedPathEdit as aj, GEOMETRY_BOUND_DATA as ak, type HandleLock as al, type HitTestView as am, type IngestCtx as an, type IngestionDep as ao, type InsertDep as ap, type InsertEnds as aq, type InsertExtras as ar, type InsertIntoDep as as, type InsertLive as at, type LassoSelectDep as au, type LayerCommandCache as av, type LayerDrawFailure as aw, type LayerDrawSpan as ax, type LayerRetainPolicy as ay, type LayoutDep as az, type SnapGesture as b, type SnapEdge as b0, type SnapEdges as b1, type SnapMarks as b2, type SnapReadoutDep as b3, type SvgUnpacker as b4, type TextEditDep as b5, type TextNodeData as b6, type TransformPreviewNode as b7, type TransientPath as b8, VIEW_ANIMATION_KEY as b9, previewBooleanOp as bA, registerContentHandler as bB, registerPaintKind as bC, registerPaintKindLoader as bD, seedTile as bE, stickyData as bF, stickyNodeFactory as bG, subscribeToSources as bH, switchGradientKind as bI, textBoxOf as bJ, useDecayLoop as bK, useEnteredGroup as bL, useViewAnimation as bM, useViewAnimationOn as bN, viewAxes as bO, withoutGeometryBoundData as bP, type ViewAnimationApi as ba, type ViewApi as bb, type ViewChannel as bc, applyBooleanOp as bd, applyCreateOutlines as be, asPaint as bf, createEnteredGroup as bg, createPathOverlay as bh, createSnapOutcomeStore as bi, depsUnchanged as bj, drawLayers as bk, drawOneLayer as bl, fitViewToBounds as bm, getPaintKind as bn, isLayerPainted as bo, isLayerVisible as bp, listGradientKinds as bq, listPaintKinds as br, minSizeOf as bs, paintKindOf as bt, paintKindRegistry as bu, paintedLayers as bv, paintsLayer as bw, paletteInsertPaint as bx, pathPaintOf as by, patternFamily as bz, type SnapConstrain as c, type SnapResizeBox as d, type SnapGeometry as e, type SnapOutcomeStore as f, type SnapSession as g, type LayerDims as h, type ViewportDims as i, type RedrawSource as j, type EditAnchorsParams as k, type SliceDep as l, type StickyStyle as m, type EnteredGroupApi as n, type ChromeState as o, type LayerGroup as p, type GeometryProjection as q, type InsertNodeFactory as r, type InsertPaint as s, type SvgIngestOptions as t, type SceneCanvasSnap as u, type Affordance as v, type AffordanceRegion as w, type AffordanceBinding as x, type CommonAffordanceScratch as y, type TextOverflow as z };
@@ -12,7 +12,7 @@ function summaryOf(node) {
12
12
  id: node.id,
13
13
  kind: node.kind,
14
14
  layer: node.layer,
15
- childCount: node.kind === "container" ? node.children.length : 0,
15
+ childCount: node.children?.length ?? 0,
16
16
  hidden: node.hidden === true,
17
17
  locked: node.locked === true,
18
18
  pickable: node.pickable !== false
@@ -1 +1 @@
1
- {"version":3,"file":"handlers.js","names":[],"sources":["../../src/devtools/handlers.ts"],"sourcesContent":["/** The answer to each devtools request, for one root. The session owns the override a steering request changes. */\nimport type { Node, NodeId, Scene } from 'core/scene/types';\nimport type { DebugConfig } from '../debug/types';\nimport type { DevtoolsRoot } from './root';\nimport type {\n DevtoolsNodeDetail, DevtoolsNodeSummary, DevtoolsOverlays, DevtoolsRequest, DevtoolsResult,\n DevtoolsRootState, DevtoolsTraceSlice,\n} from './protocol';\nimport { serialize } from './serialize';\nimport { historySlice } from './history';\n\ntype AnyScene = Scene<unknown, string, unknown>;\ntype AnyNode = Node<unknown, string, unknown>;\n\nfunction sceneOf(root: DevtoolsRoot): AnyScene {\n const scene = root.source.scene();\n if (!scene) throw new Error('scene not mounted yet');\n return scene;\n}\n\nfunction summaryOf(node: AnyNode): DevtoolsNodeSummary {\n return {\n id: node.id,\n kind: node.kind,\n layer: node.layer,\n childCount: node.kind === 'container' ? node.children.length : 0,\n hidden: node.hidden === true,\n locked: node.locked === true,\n pickable: node.pickable !== false,\n };\n}\n\nfunction nodeOf(scene: AnyScene, id: string): AnyNode {\n const node = scene.get(id as NodeId);\n if (!node) throw new Error(`no node \"${id}\"`);\n return node;\n}\n\nexport function childrenOf(root: DevtoolsRoot, nodeId: string | undefined): DevtoolsNodeSummary[] {\n const scene = sceneOf(root);\n const ids = nodeId === undefined ? scene.roots : (nodeOf(scene, nodeId), scene.childrenOf(nodeId as NodeId));\n return ids.map((id) => summaryOf(nodeOf(scene, id)));\n}\n\nexport function nodeDetail(root: DevtoolsRoot, nodeId: string): DevtoolsNodeDetail {\n const node = nodeOf(sceneOf(root), nodeId);\n return {\n ...summaryOf(node),\n parent: node.parent ?? null,\n pose: serialize(node.pose),\n data: serialize(node.data),\n };\n}\n\n/** The on/off features of a debug config, leaving out its theme and strokes. */\nexport function overlayFlags(config: DebugConfig | null): DevtoolsOverlays {\n const flags: DevtoolsOverlays = {};\n if (!config) return flags;\n for (const [key, value] of Object.entries(config)) {\n if (typeof value === 'boolean') (flags as Record<string, boolean>)[key] = value;\n }\n return flags;\n}\n\nexport function rootState(root: DevtoolsRoot): DevtoolsRootState {\n const { source } = root;\n const probes: DevtoolsRootState['probes'] = {};\n for (const [name, read] of root.probes) {\n try {\n probes[name] = serialize(read());\n } catch (err) {\n probes[name] = `[threw: ${(err as Error).message}]`;\n }\n }\n return {\n selection: [...source.selection()],\n view: serialize(source.view()),\n activeTool: source.activeTool(),\n tools: [...source.tools()],\n overlays: overlayFlags(source.overlays()),\n probes,\n };\n}\n\n/** Trace records are plain data by contract, and nest deeper than a node's data is shown. */\nconst RECORD = { depth: 12 };\n\nexport function traceSlice(root: DevtoolsRoot, cursor: number, coordCursor: number | undefined): DevtoolsTraceSlice {\n const { trace, coords } = root.source;\n const slice = trace.since(cursor);\n const out: DevtoolsTraceSlice = {\n entries: slice.entries.map((e) => serialize(e, RECORD)),\n cursor: slice.cursor,\n live: serialize(trace.live(), RECORD),\n };\n if (coordCursor !== undefined) {\n const c = coords.since(coordCursor);\n out.coords = { entries: c.entries.map((e) => serialize(e, RECORD)), cursor: c.cursor };\n }\n return out;\n}\n\n/** Answer `req` about `root`. `steer` lays a change over the panel's override. Throws on a bad request. */\nexport function answer(\n root: DevtoolsRoot,\n req: DevtoolsRequest,\n steer: (change: { overlays?: DevtoolsOverlays; highlight?: readonly string[] }) => void,\n): DevtoolsResult {\n switch (req.type) {\n case 'get-children': return childrenOf(root, req.nodeId);\n case 'get-node': return nodeDetail(root, req.nodeId);\n case 'get-state': return rootState(root);\n case 'get-trace': return traceSlice(root, req.cursor, req.coordCursor);\n case 'get-frame': return serialize(root.source.frame());\n case 'get-registry':\n if (!root.source.registry) throw new Error('this root serves no registry');\n return root.source.registry();\n case 'get-history': return historySlice(root, req.cursor, req.checkpoints);\n case 'select':\n root.source.select(req.ids);\n return null;\n case 'set-tool':\n if (req.toolId !== null && !root.source.tools().includes(req.toolId)) throw new Error(`no tool \"${req.toolId}\"`);\n root.source.setTool(req.toolId);\n return null;\n case 'highlight':\n steer({ highlight: [...req.ids] });\n return null;\n case 'set-overlays':\n steer({ overlays: req.overlays });\n return null;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"handlers.js","names":[],"sources":["../../src/devtools/handlers.ts"],"sourcesContent":["/** The answer to each devtools request, for one root. The session owns the override a steering request changes. */\nimport type { Node, NodeId, Scene } from 'core/scene/types';\nimport type { DebugConfig } from '../debug/types';\nimport type { DevtoolsRoot } from './root';\nimport type {\n DevtoolsNodeDetail, DevtoolsNodeSummary, DevtoolsOverlays, DevtoolsRequest, DevtoolsResult,\n DevtoolsRootState, DevtoolsTraceSlice,\n} from './protocol';\nimport { serialize } from './serialize';\nimport { historySlice } from './history';\n\ntype AnyScene = Scene<unknown, string, unknown>;\ntype AnyNode = Node<unknown, string, unknown>;\n\nfunction sceneOf(root: DevtoolsRoot): AnyScene {\n const scene = root.source.scene();\n if (!scene) throw new Error('scene not mounted yet');\n return scene;\n}\n\nfunction summaryOf(node: AnyNode): DevtoolsNodeSummary {\n return {\n id: node.id,\n kind: node.kind,\n layer: node.layer,\n childCount: node.children?.length ?? 0,\n hidden: node.hidden === true,\n locked: node.locked === true,\n pickable: node.pickable !== false,\n };\n}\n\nfunction nodeOf(scene: AnyScene, id: string): AnyNode {\n const node = scene.get(id as NodeId);\n if (!node) throw new Error(`no node \"${id}\"`);\n return node;\n}\n\nexport function childrenOf(root: DevtoolsRoot, nodeId: string | undefined): DevtoolsNodeSummary[] {\n const scene = sceneOf(root);\n const ids = nodeId === undefined ? scene.roots : (nodeOf(scene, nodeId), scene.childrenOf(nodeId as NodeId));\n return ids.map((id) => summaryOf(nodeOf(scene, id)));\n}\n\nexport function nodeDetail(root: DevtoolsRoot, nodeId: string): DevtoolsNodeDetail {\n const node = nodeOf(sceneOf(root), nodeId);\n return {\n ...summaryOf(node),\n parent: node.parent ?? null,\n pose: serialize(node.pose),\n data: serialize(node.data),\n };\n}\n\n/** The on/off features of a debug config, leaving out its theme and strokes. */\nexport function overlayFlags(config: DebugConfig | null): DevtoolsOverlays {\n const flags: DevtoolsOverlays = {};\n if (!config) return flags;\n for (const [key, value] of Object.entries(config)) {\n if (typeof value === 'boolean') (flags as Record<string, boolean>)[key] = value;\n }\n return flags;\n}\n\nexport function rootState(root: DevtoolsRoot): DevtoolsRootState {\n const { source } = root;\n const probes: DevtoolsRootState['probes'] = {};\n for (const [name, read] of root.probes) {\n try {\n probes[name] = serialize(read());\n } catch (err) {\n probes[name] = `[threw: ${(err as Error).message}]`;\n }\n }\n return {\n selection: [...source.selection()],\n view: serialize(source.view()),\n activeTool: source.activeTool(),\n tools: [...source.tools()],\n overlays: overlayFlags(source.overlays()),\n probes,\n };\n}\n\n/** Trace records are plain data by contract, and nest deeper than a node's data is shown. */\nconst RECORD = { depth: 12 };\n\nexport function traceSlice(root: DevtoolsRoot, cursor: number, coordCursor: number | undefined): DevtoolsTraceSlice {\n const { trace, coords } = root.source;\n const slice = trace.since(cursor);\n const out: DevtoolsTraceSlice = {\n entries: slice.entries.map((e) => serialize(e, RECORD)),\n cursor: slice.cursor,\n live: serialize(trace.live(), RECORD),\n };\n if (coordCursor !== undefined) {\n const c = coords.since(coordCursor);\n out.coords = { entries: c.entries.map((e) => serialize(e, RECORD)), cursor: c.cursor };\n }\n return out;\n}\n\n/** Answer `req` about `root`. `steer` lays a change over the panel's override. Throws on a bad request. */\nexport function answer(\n root: DevtoolsRoot,\n req: DevtoolsRequest,\n steer: (change: { overlays?: DevtoolsOverlays; highlight?: readonly string[] }) => void,\n): DevtoolsResult {\n switch (req.type) {\n case 'get-children': return childrenOf(root, req.nodeId);\n case 'get-node': return nodeDetail(root, req.nodeId);\n case 'get-state': return rootState(root);\n case 'get-trace': return traceSlice(root, req.cursor, req.coordCursor);\n case 'get-frame': return serialize(root.source.frame());\n case 'get-registry':\n if (!root.source.registry) throw new Error('this root serves no registry');\n return root.source.registry();\n case 'get-history': return historySlice(root, req.cursor, req.checkpoints);\n case 'select':\n root.source.select(req.ids);\n return null;\n case 'set-tool':\n if (req.toolId !== null && !root.source.tools().includes(req.toolId)) throw new Error(`no tool \"${req.toolId}\"`);\n root.source.setTool(req.toolId);\n return null;\n case 'highlight':\n steer({ highlight: [...req.ids] });\n return null;\n case 'set-overlays':\n steer({ overlays: req.overlays });\n return null;\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.js","names":[],"sources":["../../src/devtools/protocol.ts"],"sourcesContent":["/**\n * The messages a devtools panel and a page running weasel exchange. Plain\n * data only, so the same messages cross a direct call or `window.postMessage`.\n */\nimport type { DebugFeature } from '../debug/types';\nimport type { SerializedValue } from './serialize';\n\n/** Bumped on any change a panel built against the previous number would misread. */\nexport const DEVTOOLS_PROTOCOL = 1;\n\n/** The areas of a root a `changed` notice reports. */\nexport type DevtoolsArea = 'scene' | 'selection' | 'view' | 'tool' | 'trace' | 'registry' | 'history';\n\n/** One mounted `<SceneCanvas>`. */\nexport interface DevtoolsRootInfo {\n id: string;\n /** The canvas's `aria-label`, else its DOM id, else its root id. */\n label: string;\n dimension: '2d' | '3d';\n}\n\n/** Debug overlay features a panel turns on or off over the canvas's own `debug` prop. */\nexport type DevtoolsOverlays = Partial<Record<DebugFeature, boolean>>;\n\n/** A request a panel sends about one root. */\nexport type DevtoolsRequest = { protocol: number; requestId: number; rootId: string } & (\n | { type: 'get-children'; nodeId?: string }\n | { type: 'get-node'; nodeId: string }\n | { type: 'get-state' }\n /** Coord records come back only when `coordCursor` is given. */\n | { type: 'get-trace'; cursor: number; coordCursor?: number }\n | { type: 'get-frame' }\n | { type: 'get-registry' }\n /** The stack's entry labels come back only when `checkpoints` is true. */\n | { type: 'get-history'; cursor: number; checkpoints?: boolean }\n | { type: 'select'; ids: string[] }\n | { type: 'highlight'; ids: string[] }\n | { type: 'set-tool'; toolId: string | null }\n | { type: 'set-overlays'; overlays: DevtoolsOverlays }\n);\n\n/** Every message a panel sends the page. */\nexport type PanelMessage =\n | { type: 'yo'; protocol: number }\n /** The panel is closing: drop its overlays and highlight, and stop tracing for it. */\n | { type: 'bye'; protocol: number }\n | DevtoolsRequest;\n\n/** Every message the page sends a panel. */\nexport type PageMessage =\n | { type: 'sup'; protocol: number; kitVersion: string; roots: DevtoolsRootInfo[] }\n | { type: 'root-added'; protocol: number; root: DevtoolsRootInfo }\n | { type: 'root-removed'; protocol: number; rootId: string }\n /** Each changed area with its new version. At most one per root per frame. */\n | { type: 'changed'; protocol: number; rootId: string; areas: Partial<Record<DevtoolsArea, number>> }\n | { type: 'result'; protocol: number; requestId: number; result: DevtoolsResult }\n /** `requestId` is null for a message that was not a request. */\n | { type: 'error'; protocol: number; requestId: number | null; message: string };\n\n/** One child row of the scene tree. `nodeId` absent asks for the top level. */\nexport interface DevtoolsNodeSummary {\n id: string;\n kind: 'leaf' | 'container';\n layer: string;\n childCount: number;\n hidden: boolean;\n locked: boolean;\n pickable: boolean;\n}\n\n/** A node as `get-node` returns it: its row in the tree, plus its parent, pose and data. */\nexport interface DevtoolsNodeDetail extends DevtoolsNodeSummary {\n parent: string | null;\n pose: SerializedValue;\n data: SerializedValue;\n}\n\n/** What `get-state` returns about a root. */\nexport interface DevtoolsRootState {\n selection: string[];\n view: SerializedValue;\n activeTool: string | null;\n tools: string[];\n /** The debug overlay features in effect: the canvas's own, with the panel's over them. */\n overlays: DevtoolsOverlays;\n /** Probes registered through the test hook, by name. */\n probes: Record<string, SerializedValue>;\n}\n\n/** What `get-trace` returns: the records after the cursor, and the cursor to send next. */\nexport interface DevtoolsTraceSlice {\n entries: SerializedValue[];\n cursor: number;\n /** What a press at the pointer would do, or null when the pointer is off the canvas. */\n live: SerializedValue;\n coords?: { entries: SerializedValue[]; cursor: number };\n}\n\n/** One gesture-to-action binding, as a tool, an always-on entry or an action declares it. */\nexport interface DevtoolsBinding {\n actionId: string;\n /** The `GestureSpec` as written. */\n spec: SerializedValue;\n /** The spec in the route grammar, one string per route it expands to. */\n routes: string[];\n /** The binding's `BindingOpts`: params, roles, views. */\n opts: SerializedValue;\n}\n\n/** A tool or other contribution the canvas's dispatcher assembles bindings from. */\nexport interface DevtoolsToolEntry {\n id: string;\n /** A switchable tool, an always-on entry, or one a `features` preset brought. */\n slot: 'tool' | 'ambient' | 'contribution';\n label: string;\n /** The icon's qualified name. */\n icon: string | null;\n group: string | null;\n shortcut: string | null;\n /** The key that holds it engaged while pressed. */\n hotkey: string | null;\n hidden: boolean;\n cursor: SerializedValue;\n eligibility: SerializedValue;\n bindings: DevtoolsBinding[];\n /** Ids of the actions it brings with it. */\n actions: string[];\n /** Names of the deps it registers. */\n deps: string[];\n hasOverlay: boolean;\n}\n\n/** A registered action. `enabled` and `active` are asked when the registry is read. */\nexport interface DevtoolsActionEntry {\n id: string;\n label: string;\n icon: string | null;\n group: string | null;\n tags: string[];\n shortcut: string | null;\n /** Which binding scope its own bindings ride. */\n scope: 'hotkey' | 'ambient';\n invoker: 'immediate' | 'ongoing' | null;\n requires: string[];\n variants: { key: string; label: string; icon: string | null; params: SerializedValue }[];\n /** `true`, or the reason it is disabled. */\n enabled: true | string;\n /** Null for an action with no pressed state. */\n active: boolean | null;\n hasPreview: boolean;\n /** The eligibility rule, or null for always eligible. */\n eligible: SerializedValue;\n bindings: DevtoolsBinding[];\n}\n\n/** A dep name the canvas's registry answers. */\nexport interface DevtoolsDepEntry {\n name: string;\n /** How many scopes above the canvas's own its live source sits: 0 for the canvas's. */\n depth: number;\n /** How many sources are stacked under the name there, the live one included. */\n stacked: number;\n}\n\n/** What `get-registry` returns: what one canvas has registered. */\nexport interface DevtoolsRegistry {\n tools: DevtoolsToolEntry[];\n actions: DevtoolsActionEntry[];\n deps: DevtoolsDepEntry[];\n /** The node routing kinds a binding's `kind:` target can name, in classify order. */\n routingKinds: string[];\n}\n\n/** One op of a recorded history step. */\nexport interface DevtoolsHistoryOp {\n /** The op's registered name, `'kit:setPose'` and the like; `'?'` for an op with none. */\n kind: string;\n /** The node ids its args address. */\n targets: string[];\n summary: SerializedValue;\n}\n\n/** One change to a scene's history: an edit recorded, merged, undone, redone or jumped across. */\nexport interface DevtoolsHistoryRecord {\n ts: number;\n step: 'push' | 'coalesce' | 'undo' | 'redo' | 'goto' | 'branch' | 'prune' | 'clear' | 'restore';\n /** The entries the step pushed, merged into or walked across, in the order it touched them. */\n entries: { id: number; label: string; opCount: number; ops: DevtoolsHistoryOp[] }[];\n /** Where the history stood after the step: entries applied, and entries in all. */\n position: { index: number; total: number };\n}\n\n/** What `get-history` returns: the steps after the cursor, the cursor to send next, and where the history stands. */\nexport interface DevtoolsHistorySlice {\n entries: DevtoolsHistoryRecord[];\n cursor: number;\n position: { index: number; total: number; canUndo: boolean; canRedo: boolean };\n /** Every entry on the stacks, oldest applied first, then the redoable ones. */\n checkpoints?: { id: string; label: string }[];\n}\n\n/** The body of a `result` reply; which one depends on the request it answers. */\nexport type DevtoolsResult =\n | DevtoolsNodeSummary[]\n | DevtoolsNodeDetail\n | DevtoolsRootState\n | DevtoolsTraceSlice\n | DevtoolsRegistry\n | DevtoolsHistorySlice\n | SerializedValue\n | null;\n"],"mappings":";;AAQA,IAAa,oBAAoB"}
1
+ {"version":3,"file":"protocol.js","names":[],"sources":["../../src/devtools/protocol.ts"],"sourcesContent":["/**\n * The messages a devtools panel and a page running weasel exchange. Plain\n * data only, so the same messages cross a direct call or `window.postMessage`.\n */\nimport type { DebugFeature } from '../debug/types';\nimport type { SerializedValue } from './serialize';\n\n/** Bumped on any change a panel built against the previous number would misread. */\nexport const DEVTOOLS_PROTOCOL = 1;\n\n/** The areas of a root a `changed` notice reports. */\nexport type DevtoolsArea = 'scene' | 'selection' | 'view' | 'tool' | 'trace' | 'registry' | 'history';\n\n/** One mounted `<SceneCanvas>`. */\nexport interface DevtoolsRootInfo {\n id: string;\n /** The canvas's `aria-label`, else its DOM id, else its root id. */\n label: string;\n dimension: '2d' | '3d';\n}\n\n/** Debug overlay features a panel turns on or off over the canvas's own `debug` prop. */\nexport type DevtoolsOverlays = Partial<Record<DebugFeature, boolean>>;\n\n/** A request a panel sends about one root. */\nexport type DevtoolsRequest = { protocol: number; requestId: number; rootId: string } & (\n | { type: 'get-children'; nodeId?: string }\n | { type: 'get-node'; nodeId: string }\n | { type: 'get-state' }\n /** Coord records come back only when `coordCursor` is given. */\n | { type: 'get-trace'; cursor: number; coordCursor?: number }\n | { type: 'get-frame' }\n | { type: 'get-registry' }\n /** The stack's entry labels come back only when `checkpoints` is true. */\n | { type: 'get-history'; cursor: number; checkpoints?: boolean }\n | { type: 'select'; ids: string[] }\n | { type: 'highlight'; ids: string[] }\n | { type: 'set-tool'; toolId: string | null }\n | { type: 'set-overlays'; overlays: DevtoolsOverlays }\n);\n\n/** Every message a panel sends the page. */\nexport type PanelMessage =\n | { type: 'yo'; protocol: number }\n /** The panel is closing: drop its overlays and highlight, and stop tracing for it. */\n | { type: 'bye'; protocol: number }\n | DevtoolsRequest;\n\n/** Every message the page sends a panel. */\nexport type PageMessage =\n | { type: 'sup'; protocol: number; kitVersion: string; roots: DevtoolsRootInfo[] }\n | { type: 'root-added'; protocol: number; root: DevtoolsRootInfo }\n | { type: 'root-removed'; protocol: number; rootId: string }\n /** Each changed area with its new version. At most one per root per frame. */\n | { type: 'changed'; protocol: number; rootId: string; areas: Partial<Record<DevtoolsArea, number>> }\n | { type: 'result'; protocol: number; requestId: number; result: DevtoolsResult }\n /** `requestId` is null for a message that was not a request. */\n | { type: 'error'; protocol: number; requestId: number | null; message: string };\n\n/** One child row of the scene tree. `nodeId` absent asks for the top level. */\nexport interface DevtoolsNodeSummary {\n id: string;\n kind: 'mark' | 'group';\n layer: string;\n childCount: number;\n hidden: boolean;\n locked: boolean;\n pickable: boolean;\n}\n\n/** A node as `get-node` returns it: its row in the tree, plus its parent, pose and data. */\nexport interface DevtoolsNodeDetail extends DevtoolsNodeSummary {\n parent: string | null;\n pose: SerializedValue;\n data: SerializedValue;\n}\n\n/** What `get-state` returns about a root. */\nexport interface DevtoolsRootState {\n selection: string[];\n view: SerializedValue;\n activeTool: string | null;\n tools: string[];\n /** The debug overlay features in effect: the canvas's own, with the panel's over them. */\n overlays: DevtoolsOverlays;\n /** Probes registered through the test hook, by name. */\n probes: Record<string, SerializedValue>;\n}\n\n/** What `get-trace` returns: the records after the cursor, and the cursor to send next. */\nexport interface DevtoolsTraceSlice {\n entries: SerializedValue[];\n cursor: number;\n /** What a press at the pointer would do, or null when the pointer is off the canvas. */\n live: SerializedValue;\n coords?: { entries: SerializedValue[]; cursor: number };\n}\n\n/** One gesture-to-action binding, as a tool, an always-on entry or an action declares it. */\nexport interface DevtoolsBinding {\n actionId: string;\n /** The `GestureSpec` as written. */\n spec: SerializedValue;\n /** The spec in the route grammar, one string per route it expands to. */\n routes: string[];\n /** The binding's `BindingOpts`: params, roles, views. */\n opts: SerializedValue;\n}\n\n/** A tool or other contribution the canvas's dispatcher assembles bindings from. */\nexport interface DevtoolsToolEntry {\n id: string;\n /** A switchable tool, an always-on entry, or one a `features` preset brought. */\n slot: 'tool' | 'ambient' | 'contribution';\n label: string;\n /** The icon's qualified name. */\n icon: string | null;\n group: string | null;\n shortcut: string | null;\n /** The key that holds it engaged while pressed. */\n hotkey: string | null;\n hidden: boolean;\n cursor: SerializedValue;\n eligibility: SerializedValue;\n bindings: DevtoolsBinding[];\n /** Ids of the actions it brings with it. */\n actions: string[];\n /** Names of the deps it registers. */\n deps: string[];\n hasOverlay: boolean;\n}\n\n/** A registered action. `enabled` and `active` are asked when the registry is read. */\nexport interface DevtoolsActionEntry {\n id: string;\n label: string;\n icon: string | null;\n group: string | null;\n tags: string[];\n shortcut: string | null;\n /** Which binding scope its own bindings ride. */\n scope: 'hotkey' | 'ambient';\n invoker: 'immediate' | 'ongoing' | null;\n requires: string[];\n variants: { key: string; label: string; icon: string | null; params: SerializedValue }[];\n /** `true`, or the reason it is disabled. */\n enabled: true | string;\n /** Null for an action with no pressed state. */\n active: boolean | null;\n hasPreview: boolean;\n /** The eligibility rule, or null for always eligible. */\n eligible: SerializedValue;\n bindings: DevtoolsBinding[];\n}\n\n/** A dep name the canvas's registry answers. */\nexport interface DevtoolsDepEntry {\n name: string;\n /** How many scopes above the canvas's own its live source sits: 0 for the canvas's. */\n depth: number;\n /** How many sources are stacked under the name there, the live one included. */\n stacked: number;\n}\n\n/** What `get-registry` returns: what one canvas has registered. */\nexport interface DevtoolsRegistry {\n tools: DevtoolsToolEntry[];\n actions: DevtoolsActionEntry[];\n deps: DevtoolsDepEntry[];\n /** The node routing kinds a binding's `kind:` target can name, in classify order. */\n routingKinds: string[];\n}\n\n/** One op of a recorded history step. */\nexport interface DevtoolsHistoryOp {\n /** The op's registered name, `'kit:setPose'` and the like; `'?'` for an op with none. */\n kind: string;\n /** The node ids its args address. */\n targets: string[];\n summary: SerializedValue;\n}\n\n/** One change to a scene's history: an edit recorded, merged, undone, redone or jumped across. */\nexport interface DevtoolsHistoryRecord {\n ts: number;\n step: 'push' | 'coalesce' | 'undo' | 'redo' | 'goto' | 'branch' | 'prune' | 'clear' | 'restore';\n /** The entries the step pushed, merged into or walked across, in the order it touched them. */\n entries: { id: number; label: string; opCount: number; ops: DevtoolsHistoryOp[] }[];\n /** Where the history stood after the step: entries applied, and entries in all. */\n position: { index: number; total: number };\n}\n\n/** What `get-history` returns: the steps after the cursor, the cursor to send next, and where the history stands. */\nexport interface DevtoolsHistorySlice {\n entries: DevtoolsHistoryRecord[];\n cursor: number;\n position: { index: number; total: number; canUndo: boolean; canRedo: boolean };\n /** Every entry on the stacks, oldest applied first, then the redoable ones. */\n checkpoints?: { id: string; label: string }[];\n}\n\n/** The body of a `result` reply; which one depends on the request it answers. */\nexport type DevtoolsResult =\n | DevtoolsNodeSummary[]\n | DevtoolsNodeDetail\n | DevtoolsRootState\n | DevtoolsTraceSlice\n | DevtoolsRegistry\n | DevtoolsHistorySlice\n | SerializedValue\n | null;\n"],"mappings":";;AAQA,IAAa,oBAAoB"}
@@ -1,10 +1,11 @@
1
1
  import { e as DebugFeature, a as DebugConfig } from './types-BN9hbdMI.js';
2
2
  import { DispatchTrace, TraceRing } from '@weasel-js/routing';
3
- import { S as Scene } from './types-CniVctmk.js';
3
+ import { S as Scene } from './types-BEdRIhNc.js';
4
4
  import '@weasel-js/history';
5
- import './types-7puAuVD2.js';
5
+ import './types-49uYJN3j.js';
6
6
  import '@weasel-js/geom';
7
7
  import '@weasel-js/quantity';
8
+ import '@weasel-js/paint';
8
9
 
9
10
  /**
10
11
  * Turn any value into plain data a panel can display. A node's `data` is the
@@ -134,7 +135,7 @@ type PageMessage = {
134
135
  /** One child row of the scene tree. `nodeId` absent asks for the top level. */
135
136
  interface DevtoolsNodeSummary {
136
137
  id: string;
137
- kind: 'leaf' | 'container';
138
+ kind: 'mark' | 'group';
138
139
  layer: string;
139
140
  childCount: number;
140
141
  hidden: boolean;
@@ -1,8 +1,9 @@
1
1
  export { DEVTOOLS_ENVELOPE, DEVTOOLS_PROTOCOL, DevtoolsActionEntry, DevtoolsArea, DevtoolsBinding, DevtoolsDepEntry, DevtoolsEnvelope, DevtoolsHistoryOp, DevtoolsHistoryRecord, DevtoolsHistorySlice, DevtoolsNodeDetail, DevtoolsNodeSummary, DevtoolsOverlays, DevtoolsRegistry, DevtoolsRequest, DevtoolsResult, DevtoolsRoot, DevtoolsRootInfo, DevtoolsRootSource, DevtoolsRootState, DevtoolsSchedule, DevtoolsToolEntry, DevtoolsTraceSlice, DevtoolsTransport, PageMessage, PageTransport, PanelMessage, PanelTransport, SerializeLimits, SerializedValue, WeaselDevtoolsHook, createDevtoolsHook, createDevtoolsRoot, createInPageTransports, createWindowPageTransport, createWindowPanelTransport, installDevtoolsHook, isDevtoolsEnvelope, serialize } from './devtools-protocol.js';
2
2
  import './types-BN9hbdMI.js';
3
3
  import '@weasel-js/routing';
4
- import './types-CniVctmk.js';
4
+ import './types-BEdRIhNc.js';
5
5
  import '@weasel-js/history';
6
- import './types-7puAuVD2.js';
6
+ import './types-49uYJN3j.js';
7
7
  import '@weasel-js/geom';
8
8
  import '@weasel-js/quantity';
9
+ import '@weasel-js/paint';
@@ -1,3 +1,4 @@
1
+ import { nodesAsShown, shownLookup } from "../../core/scene/nodeAsShown.js";
1
2
  import { measureEndsOf } from "./measurement.js";
2
3
  import { nodeGates } from "../../core/scene/nodeGates.js";
3
4
  import { measureIds } from "./measurements.js";
@@ -17,9 +18,10 @@ function distanceToSegment(p, a, b) {
17
18
  */
18
19
  function measureAt(scene, ms, p, tolerance) {
19
20
  const gates = nodeGates(scene);
21
+ const shown = shownLookup(scene);
20
22
  const ids = measureIds(scene, ms).reverse().filter((id) => !gates.hidden(id) && !gates.locked(id));
21
23
  for (const id of ids) {
22
- const ends = measureEndsOf(scene.get(id));
24
+ const ends = measureEndsOf(shown.get(id));
23
25
  if (!ends) continue;
24
26
  for (const part of ["end", "start"]) if (Math.hypot(p.x - ends[part].x, p.y - ends[part].y) <= tolerance) return {
25
27
  id,
@@ -27,7 +29,7 @@ function measureAt(scene, ms, p, tolerance) {
27
29
  };
28
30
  }
29
31
  for (const id of ids) {
30
- const ends = measureEndsOf(scene.get(id));
32
+ const ends = measureEndsOf(shown.get(id));
31
33
  if (ends && distanceToSegment(p, ends.start, ends.end) <= tolerance) return {
32
34
  id,
33
35
  part: "line"
@@ -46,7 +48,7 @@ function measureEndSource(opts = {}) {
46
48
  const gates = nodeGates(q.scene);
47
49
  const skip = new Set(q.exclude);
48
50
  const reach = q.reachWorld(q.reachPx);
49
- for (const node of q.scene.renderOrderNodes()) {
51
+ for (const node of nodesAsShown(q.scene.overrides, q.scene.renderOrderNodes())) {
50
52
  const ends = measureEndsOf(node);
51
53
  if (!ends || skip.has(node.id) || gates.hidden(node.id)) continue;
52
54
  for (const end of [ends.start, ends.end]) {
@@ -1 +1 @@
1
- {"version":3,"file":"measureHit.js","names":[],"sources":["../../../src/features/measure/measureHit.ts"],"sourcesContent":["import type { NodeId, Scene } from 'core/scene/types';\nimport { measureEndsOf, type MeasureEnd, type MeasurePoint } from './measurement';\nimport { measureIds, type Measurements } from './measurements';\nimport { nodeGates } from 'core/scene/nodeGates';\nimport type { SnapSource } from 'features/snap/types';\n\n/** Which part of which measurement lies under a point. */\nexport interface MeasureHit {\n id: NodeId;\n part: MeasureEnd | 'line';\n}\n\n\nfunction distanceToSegment(p: MeasurePoint, a: MeasurePoint, b: MeasurePoint): number {\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n const len2 = dx * dx + dy * dy;\n const t = len2 === 0 ? 0 : Math.max(0, Math.min(1, ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2));\n return Math.hypot(p.x - (a.x + t * dx), p.y - (a.y + t * dy));\n}\n\n/**\n * The measurement under `p`, newest first: an end within `tolerance` world\n * units of it, else a line within the same distance. A hidden or locked\n * measurement (`nodeGates`) is passed over.\n */\nexport function measureAt(\n scene: Pick<Scene<unknown, string, unknown>, 'renderOrderNodes' | 'get' | 'layers'>,\n ms: Pick<Measurements, 'layer'>,\n p: MeasurePoint,\n tolerance: number,\n): MeasureHit | null {\n const gates = nodeGates(scene);\n const ids = measureIds(scene, ms).reverse().filter((id) => !gates.hidden(id) && !gates.locked(id));\n for (const id of ids) {\n const ends = measureEndsOf(scene.get(id));\n if (!ends) continue;\n for (const part of ['end', 'start'] as const) {\n if (Math.hypot(p.x - ends[part].x, p.y - ends[part].y) <= tolerance) return { id, part };\n }\n }\n for (const id of ids) {\n const ends = measureEndsOf(scene.get(id));\n if (ends && distanceToSegment(p, ends.start, ends.end) <= tolerance) return { id, part: 'line' };\n }\n return null;\n}\n\n/** Options for `measureEndSource`. */\nexport interface MeasureEndSourceOptions {\n /** Overrides the canvas's reach for measurement ends, in screen px. */\n reachPx?: number;\n}\n\n/** The ends of every shown measurement as snap targets for a pointer probe. */\nexport function measureEndSource(opts: MeasureEndSourceOptions = {}): SnapSource {\n return {\n id: 'measure-ends',\n roles: ['pointer'],\n ...(opts.reachPx !== undefined ? { reachPx: opts.reachPx } : {}),\n *collect(q) {\n if (!q.scene) return;\n const gates = nodeGates(q.scene);\n const skip = new Set(q.exclude);\n const reach = q.reachWorld(q.reachPx);\n for (const node of q.scene.renderOrderNodes()) {\n const ends = measureEndsOf(node);\n if (!ends || skip.has(node.id) || gates.hidden(node.id)) continue;\n for (const end of [ends.start, ends.end]) {\n const at = { x: end.x, y: end.y };\n for (const p of q.probes) {\n if (Math.hypot(at.x - p.at.x, at.y - p.at.y) > reach) continue;\n yield { probe: p.id, locus: { at, dirs: [] }, mark: { kind: 'point', at, glyph: 'center' } };\n }\n }\n }\n },\n };\n}\n"],"mappings":";;;;;AAaA,SAAS,kBAAkB,GAAiB,GAAiB,GAAyB;CACpF,MAAM,KAAK,EAAE,IAAI,EAAE;CACnB,MAAM,KAAK,EAAE,IAAI,EAAE;CACnB,MAAM,OAAO,KAAK,KAAK,KAAK;CAC5B,MAAM,IAAI,SAAS,IAAI,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,EAAE,IAAI,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,KAAK,MAAM,IAAI,CAAC;CAChG,OAAO,KAAK,MAAM,EAAE,KAAK,EAAE,IAAI,IAAI,KAAK,EAAE,KAAK,EAAE,IAAI,IAAI,GAAG;AAC9D;;;;;;AAOA,SAAgB,UACd,OACA,IACA,GACA,WACmB;CACnB,MAAM,QAAQ,UAAU,KAAK;CAC7B,MAAM,MAAM,WAAW,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,OAAO,CAAC,MAAM,OAAO,EAAE,KAAK,CAAC,MAAM,OAAO,EAAE,CAAC;CACjG,KAAK,MAAM,MAAM,KAAK;EACpB,MAAM,OAAO,cAAc,MAAM,IAAI,EAAE,CAAC;EACxC,IAAI,CAAC,MAAM;EACX,KAAK,MAAM,QAAQ,CAAC,OAAO,OAAO,GAChC,IAAI,KAAK,MAAM,EAAE,IAAI,KAAK,KAAK,CAAC,GAAG,EAAE,IAAI,KAAK,KAAK,CAAC,CAAC,KAAK,WAAW,OAAO;GAAE;GAAI;EAAK;CAE3F;CACA,KAAK,MAAM,MAAM,KAAK;EACpB,MAAM,OAAO,cAAc,MAAM,IAAI,EAAE,CAAC;EACxC,IAAI,QAAQ,kBAAkB,GAAG,KAAK,OAAO,KAAK,GAAG,KAAK,WAAW,OAAO;GAAE;GAAI,MAAM;EAAO;CACjG;CACA,OAAO;AACT;;AASA,SAAgB,iBAAiB,OAAgC,CAAC,GAAe;CAC/E,OAAO;EACL,IAAI;EACJ,OAAO,CAAC,SAAS;EACjB,GAAI,KAAK,YAAY,SAAY,EAAE,SAAS,KAAK,QAAQ,IAAI,CAAC;EAC9D,CAAC,QAAQ,GAAG;GACV,IAAI,CAAC,EAAE,OAAO;GACd,MAAM,QAAQ,UAAU,EAAE,KAAK;GAC/B,MAAM,OAAO,IAAI,IAAI,EAAE,OAAO;GAC9B,MAAM,QAAQ,EAAE,WAAW,EAAE,OAAO;GACpC,KAAK,MAAM,QAAQ,EAAE,MAAM,iBAAiB,GAAG;IAC7C,MAAM,OAAO,cAAc,IAAI;IAC/B,IAAI,CAAC,QAAQ,KAAK,IAAI,KAAK,EAAE,KAAK,MAAM,OAAO,KAAK,EAAE,GAAG;IACzD,KAAK,MAAM,OAAO,CAAC,KAAK,OAAO,KAAK,GAAG,GAAG;KACxC,MAAM,KAAK;MAAE,GAAG,IAAI;MAAG,GAAG,IAAI;KAAE;KAChC,KAAK,MAAM,KAAK,EAAE,QAAQ;MACxB,IAAI,KAAK,MAAM,GAAG,IAAI,EAAE,GAAG,GAAG,GAAG,IAAI,EAAE,GAAG,CAAC,IAAI,OAAO;MACtD,MAAM;OAAE,OAAO,EAAE;OAAI,OAAO;QAAE;QAAI,MAAM,CAAC;OAAE;OAAG,MAAM;QAAE,MAAM;QAAS;QAAI,OAAO;OAAS;MAAE;KAC7F;IACF;GACF;EACF;CACF;AACF"}
1
+ {"version":3,"file":"measureHit.js","names":[],"sources":["../../../src/features/measure/measureHit.ts"],"sourcesContent":["import type { NodeId, Scene } from 'core/scene/types';\nimport { measureEndsOf, type MeasureEnd, type MeasurePoint } from './measurement';\nimport { measureIds, type Measurements } from './measurements';\nimport { nodesAsShown, shownLookup } from 'core/scene/nodeAsShown';\nimport { nodeGates } from 'core/scene/nodeGates';\nimport type { SnapSource } from 'features/snap/types';\n\n/** Which part of which measurement lies under a point. */\nexport interface MeasureHit {\n id: NodeId;\n part: MeasureEnd | 'line';\n}\n\n\nfunction distanceToSegment(p: MeasurePoint, a: MeasurePoint, b: MeasurePoint): number {\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n const len2 = dx * dx + dy * dy;\n const t = len2 === 0 ? 0 : Math.max(0, Math.min(1, ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2));\n return Math.hypot(p.x - (a.x + t * dx), p.y - (a.y + t * dy));\n}\n\n/**\n * The measurement under `p`, newest first: an end within `tolerance` world\n * units of it, else a line within the same distance. A hidden or locked\n * measurement (`nodeGates`) is passed over.\n */\nexport function measureAt(\n scene: Pick<Scene<unknown, string, unknown>, 'renderOrderNodes' | 'get' | 'layers' | 'overrides'>,\n ms: Pick<Measurements, 'layer'>,\n p: MeasurePoint,\n tolerance: number,\n): MeasureHit | null {\n const gates = nodeGates(scene);\n // Hit where the ends are shown: a gesture moving one publishes it as a data override.\n const shown = shownLookup(scene);\n const ids = measureIds(scene, ms).reverse().filter((id) => !gates.hidden(id) && !gates.locked(id));\n for (const id of ids) {\n const ends = measureEndsOf(shown.get(id));\n if (!ends) continue;\n for (const part of ['end', 'start'] as const) {\n if (Math.hypot(p.x - ends[part].x, p.y - ends[part].y) <= tolerance) return { id, part };\n }\n }\n for (const id of ids) {\n const ends = measureEndsOf(shown.get(id));\n if (ends && distanceToSegment(p, ends.start, ends.end) <= tolerance) return { id, part: 'line' };\n }\n return null;\n}\n\n/** Options for `measureEndSource`. */\nexport interface MeasureEndSourceOptions {\n /** Overrides the canvas's reach for measurement ends, in screen px. */\n reachPx?: number;\n}\n\n/** The ends of every shown measurement as snap targets for a pointer probe. */\nexport function measureEndSource(opts: MeasureEndSourceOptions = {}): SnapSource {\n return {\n id: 'measure-ends',\n roles: ['pointer'],\n ...(opts.reachPx !== undefined ? { reachPx: opts.reachPx } : {}),\n *collect(q) {\n if (!q.scene) return;\n const gates = nodeGates(q.scene);\n const skip = new Set(q.exclude);\n const reach = q.reachWorld(q.reachPx);\n for (const node of nodesAsShown(q.scene.overrides, q.scene.renderOrderNodes())) {\n const ends = measureEndsOf(node);\n if (!ends || skip.has(node.id) || gates.hidden(node.id)) continue;\n for (const end of [ends.start, ends.end]) {\n const at = { x: end.x, y: end.y };\n for (const p of q.probes) {\n if (Math.hypot(at.x - p.at.x, at.y - p.at.y) > reach) continue;\n yield { probe: p.id, locus: { at, dirs: [] }, mark: { kind: 'point', at, glyph: 'center' } };\n }\n }\n }\n },\n };\n}\n"],"mappings":";;;;;;AAcA,SAAS,kBAAkB,GAAiB,GAAiB,GAAyB;CACpF,MAAM,KAAK,EAAE,IAAI,EAAE;CACnB,MAAM,KAAK,EAAE,IAAI,EAAE;CACnB,MAAM,OAAO,KAAK,KAAK,KAAK;CAC5B,MAAM,IAAI,SAAS,IAAI,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,EAAE,IAAI,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,KAAK,MAAM,IAAI,CAAC;CAChG,OAAO,KAAK,MAAM,EAAE,KAAK,EAAE,IAAI,IAAI,KAAK,EAAE,KAAK,EAAE,IAAI,IAAI,GAAG;AAC9D;;;;;;AAOA,SAAgB,UACd,OACA,IACA,GACA,WACmB;CACnB,MAAM,QAAQ,UAAU,KAAK;CAE7B,MAAM,QAAQ,YAAY,KAAK;CAC/B,MAAM,MAAM,WAAW,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,OAAO,CAAC,MAAM,OAAO,EAAE,KAAK,CAAC,MAAM,OAAO,EAAE,CAAC;CACjG,KAAK,MAAM,MAAM,KAAK;EACpB,MAAM,OAAO,cAAc,MAAM,IAAI,EAAE,CAAC;EACxC,IAAI,CAAC,MAAM;EACX,KAAK,MAAM,QAAQ,CAAC,OAAO,OAAO,GAChC,IAAI,KAAK,MAAM,EAAE,IAAI,KAAK,KAAK,CAAC,GAAG,EAAE,IAAI,KAAK,KAAK,CAAC,CAAC,KAAK,WAAW,OAAO;GAAE;GAAI;EAAK;CAE3F;CACA,KAAK,MAAM,MAAM,KAAK;EACpB,MAAM,OAAO,cAAc,MAAM,IAAI,EAAE,CAAC;EACxC,IAAI,QAAQ,kBAAkB,GAAG,KAAK,OAAO,KAAK,GAAG,KAAK,WAAW,OAAO;GAAE;GAAI,MAAM;EAAO;CACjG;CACA,OAAO;AACT;;AASA,SAAgB,iBAAiB,OAAgC,CAAC,GAAe;CAC/E,OAAO;EACL,IAAI;EACJ,OAAO,CAAC,SAAS;EACjB,GAAI,KAAK,YAAY,SAAY,EAAE,SAAS,KAAK,QAAQ,IAAI,CAAC;EAC9D,CAAC,QAAQ,GAAG;GACV,IAAI,CAAC,EAAE,OAAO;GACd,MAAM,QAAQ,UAAU,EAAE,KAAK;GAC/B,MAAM,OAAO,IAAI,IAAI,EAAE,OAAO;GAC9B,MAAM,QAAQ,EAAE,WAAW,EAAE,OAAO;GACpC,KAAK,MAAM,QAAQ,aAAa,EAAE,MAAM,WAAW,EAAE,MAAM,iBAAiB,CAAC,GAAG;IAC9E,MAAM,OAAO,cAAc,IAAI;IAC/B,IAAI,CAAC,QAAQ,KAAK,IAAI,KAAK,EAAE,KAAK,MAAM,OAAO,KAAK,EAAE,GAAG;IACzD,KAAK,MAAM,OAAO,CAAC,KAAK,OAAO,KAAK,GAAG,GAAG;KACxC,MAAM,KAAK;MAAE,GAAG,IAAI;MAAG,GAAG,IAAI;KAAE;KAChC,KAAK,MAAM,KAAK,EAAE,QAAQ;MACxB,IAAI,KAAK,MAAM,GAAG,IAAI,EAAE,GAAG,GAAG,GAAG,IAAI,EAAE,GAAG,CAAC,IAAI,OAAO;MACtD,MAAM;OAAE,OAAO,EAAE;OAAI,OAAO;QAAE;QAAI,MAAM,CAAC;OAAE;OAAG,MAAM;QAAE,MAAM;QAAS;QAAI,OAAO;OAAS;MAAE;KAC7F;IACF;GACF;EACF;CACF;AACF"}
@@ -47,7 +47,7 @@ function measureNode(id, layer, start, end) {
47
47
  } };
48
48
  return {
49
49
  id,
50
- kind: "leaf",
50
+ kind: "mark",
51
51
  layer,
52
52
  parent: null,
53
53
  pose: measurePose(start, end),
@@ -1 +1 @@
1
- {"version":3,"file":"measurement.js","names":[],"sources":["../../../src/features/measure/measurement.ts"],"sourcesContent":["import type { Node, NodeId, RectPose } from 'core/scene/types';\n\n/** A world-space point. */\nexport interface MeasurePoint { x: number; y: number }\n\n/**\n * The offset from `start` to `end`, in world units. `dx` and `dy` are on the\n * world's own axes, where y grows downward; `angle` reads like a protractor\n * instead, counterclockwise-positive on screen, so a line rising up and to\n * the right at 30° reads +30°. The canvas's own rotations are\n * clockwise-positive: a rotate that follows the line turns by `-angle`.\n */\nexport interface Measurement {\n readonly start: MeasurePoint;\n readonly end: MeasurePoint;\n readonly dx: number;\n readonly dy: number;\n readonly distance: number;\n /** `atan2(-dy, dx)`, in `(-π, π]`. Zero for a zero-length measurement. */\n readonly angle: number;\n}\n\n/** Measure from `start` to `end`. */\nexport function measureBetween(start: MeasurePoint, end: MeasurePoint): Measurement {\n const dx = end.x - start.x;\n const dy = end.y - start.y;\n return {\n start: { x: start.x, y: start.y },\n end: { x: end.x, y: end.y },\n dx,\n dy,\n distance: Math.hypot(dx, dy),\n // `|| 0` drops the sign of a negated zero, which would read a leftward line as -π.\n angle: dx === 0 && dy === 0 ? 0 : Math.atan2(-dy || 0, dx),\n };\n}\n\n/** What a measurement node keeps in `data`: its two ends, in world units. A\n * `label` beside it names the node in a layer list. */\nexport interface MeasureData {\n measure: { start: MeasurePoint; end: MeasurePoint };\n label?: string;\n}\n\n/** Which end of a measurement. */\nexport type MeasureEnd = 'start' | 'end';\n\n\n/** The ends `node` measures between, or null when it is not a measurement. */\nexport function measureEndsOf(node: Pick<Node<unknown, string, unknown>, 'data'> | undefined): MeasureData['measure'] | null {\n const m = (node?.data as Partial<MeasureData> | null | undefined)?.measure;\n if (!m || typeof m !== 'object' || !m.start || !m.end) return null;\n return m;\n}\n\n/** The measurement `node` holds, or null when it is not one. */\nexport function measurementOf(node: Pick<Node<unknown, string, unknown>, 'data'> | undefined): Measurement | null {\n const ends = measureEndsOf(node);\n return ends ? measureBetween(ends.start, ends.end) : null;\n}\n\n/** The box a measurement's pose holds: its two ends' bounds, which is what\n * culling, area queries, and fitting the view read. */\nexport function measurePose(start: MeasurePoint, end: MeasurePoint): RectPose {\n return {\n x: Math.min(start.x, end.x),\n y: Math.min(start.y, end.y),\n width: Math.abs(end.x - start.x),\n height: Math.abs(end.y - start.y),\n };\n}\n\n/** A measurement node on `layer`, ready to insert. */\nexport function measureNode(id: NodeId, layer: string, start: MeasurePoint, end: MeasurePoint): Node<unknown, string, unknown> {\n const data: MeasureData = { measure: { start: { ...start }, end: { ...end } } };\n return { id, kind: 'leaf', layer, parent: null, pose: measurePose(start, end), data };\n}\n\n/** `node`'s data and pose with its `which` end moved to `to`. */\nexport function withMeasureEnd(\n node: Node<unknown, string, unknown>,\n which: MeasureEnd,\n to: MeasurePoint,\n): { data: MeasureData; pose: RectPose } | null {\n const ends = measureEndsOf(node);\n if (!ends) return null;\n const next = { ...ends, [which]: { x: to.x, y: to.y } };\n const data: MeasureData = { ...(node.data as MeasureData), measure: next };\n return { data, pose: measurePose(next.start, next.end) };\n}\n"],"mappings":";;AAuBA,SAAgB,eAAe,OAAqB,KAAgC;CAClF,MAAM,KAAK,IAAI,IAAI,MAAM;CACzB,MAAM,KAAK,IAAI,IAAI,MAAM;CACzB,OAAO;EACL,OAAO;GAAE,GAAG,MAAM;GAAG,GAAG,MAAM;EAAE;EAChC,KAAK;GAAE,GAAG,IAAI;GAAG,GAAG,IAAI;EAAE;EAC1B;EACA;EACA,UAAU,KAAK,MAAM,IAAI,EAAE;EAE3B,OAAO,OAAO,KAAK,OAAO,IAAI,IAAI,KAAK,MAAM,CAAC,MAAM,GAAG,EAAE;CAC3D;AACF;;AAcA,SAAgB,cAAc,MAA+F;CAC3H,MAAM,KAAK,MAAM,KAAA,EAAkD;CACnE,IAAI,CAAC,KAAK,OAAO,MAAM,YAAY,CAAC,EAAE,SAAS,CAAC,EAAE,KAAK,OAAO;CAC9D,OAAO;AACT;;AAGA,SAAgB,cAAc,MAAoF;CAChH,MAAM,OAAO,cAAc,IAAI;CAC/B,OAAO,OAAO,eAAe,KAAK,OAAO,KAAK,GAAG,IAAI;AACvD;;;AAIA,SAAgB,YAAY,OAAqB,KAA6B;CAC5E,OAAO;EACL,GAAG,KAAK,IAAI,MAAM,GAAG,IAAI,CAAC;EAC1B,GAAG,KAAK,IAAI,MAAM,GAAG,IAAI,CAAC;EAC1B,OAAO,KAAK,IAAI,IAAI,IAAI,MAAM,CAAC;EAC/B,QAAQ,KAAK,IAAI,IAAI,IAAI,MAAM,CAAC;CAClC;AACF;;AAGA,SAAgB,YAAY,IAAY,OAAe,OAAqB,KAAmD;CAC7H,MAAM,OAAoB,EAAE,SAAS;EAAE,OAAO,EAAE,GAAG,MAAM;EAAG,KAAK,EAAE,GAAG,IAAI;CAAE,EAAE;CAC9E,OAAO;EAAE;EAAI,MAAM;EAAQ;EAAO,QAAQ;EAAM,MAAM,YAAY,OAAO,GAAG;EAAG;CAAK;AACtF;;AAGA,SAAgB,eACd,MACA,OACA,IAC8C;CAC9C,MAAM,OAAO,cAAc,IAAI;CAC/B,IAAI,CAAC,MAAM,OAAO;CAClB,MAAM,OAAO;EAAE,GAAG;GAAO,QAAQ;GAAE,GAAG,GAAG;GAAG,GAAG,GAAG;EAAE;CAAE;CAEtD,OAAO;EAAE;GADmB,GAAI,KAAK;GAAsB,SAAS;EAC3D;EAAM,MAAM,YAAY,KAAK,OAAO,KAAK,GAAG;CAAE;AACzD"}
1
+ {"version":3,"file":"measurement.js","names":[],"sources":["../../../src/features/measure/measurement.ts"],"sourcesContent":["import type { Node, NodeId, RectPose } from 'core/scene/types';\n\n/** A world-space point. */\nexport interface MeasurePoint { x: number; y: number }\n\n/**\n * The offset from `start` to `end`, in world units. `dx` and `dy` are on the\n * world's own axes, where y grows downward; `angle` reads like a protractor\n * instead, counterclockwise-positive on screen, so a line rising up and to\n * the right at 30° reads +30°. The canvas's own rotations are\n * clockwise-positive: a rotate that follows the line turns by `-angle`.\n */\nexport interface Measurement {\n readonly start: MeasurePoint;\n readonly end: MeasurePoint;\n readonly dx: number;\n readonly dy: number;\n readonly distance: number;\n /** `atan2(-dy, dx)`, in `(-π, π]`. Zero for a zero-length measurement. */\n readonly angle: number;\n}\n\n/** Measure from `start` to `end`. */\nexport function measureBetween(start: MeasurePoint, end: MeasurePoint): Measurement {\n const dx = end.x - start.x;\n const dy = end.y - start.y;\n return {\n start: { x: start.x, y: start.y },\n end: { x: end.x, y: end.y },\n dx,\n dy,\n distance: Math.hypot(dx, dy),\n // `|| 0` drops the sign of a negated zero, which would read a leftward line as -π.\n angle: dx === 0 && dy === 0 ? 0 : Math.atan2(-dy || 0, dx),\n };\n}\n\n/** What a measurement node keeps in `data`: its two ends, in world units. A\n * `label` beside it names the node in a layer list. */\nexport interface MeasureData {\n measure: { start: MeasurePoint; end: MeasurePoint };\n label?: string;\n}\n\n/** Which end of a measurement. */\nexport type MeasureEnd = 'start' | 'end';\n\n\n/** The ends `node` measures between, or null when it is not a measurement. */\nexport function measureEndsOf(node: Pick<Node<unknown, string, unknown>, 'data'> | undefined): MeasureData['measure'] | null {\n const m = (node?.data as Partial<MeasureData> | null | undefined)?.measure;\n if (!m || typeof m !== 'object' || !m.start || !m.end) return null;\n return m;\n}\n\n/** The measurement `node` holds, or null when it is not one. */\nexport function measurementOf(node: Pick<Node<unknown, string, unknown>, 'data'> | undefined): Measurement | null {\n const ends = measureEndsOf(node);\n return ends ? measureBetween(ends.start, ends.end) : null;\n}\n\n/** The box a measurement's pose holds: its two ends' bounds, which is what\n * culling, area queries, and fitting the view read. */\nexport function measurePose(start: MeasurePoint, end: MeasurePoint): RectPose {\n return {\n x: Math.min(start.x, end.x),\n y: Math.min(start.y, end.y),\n width: Math.abs(end.x - start.x),\n height: Math.abs(end.y - start.y),\n };\n}\n\n/** A measurement node on `layer`, ready to insert. */\nexport function measureNode(id: NodeId, layer: string, start: MeasurePoint, end: MeasurePoint): Node<unknown, string, unknown> {\n const data: MeasureData = { measure: { start: { ...start }, end: { ...end } } };\n return { id, kind: 'mark', layer, parent: null, pose: measurePose(start, end), data };\n}\n\n/** `node`'s data and pose with its `which` end moved to `to`. */\nexport function withMeasureEnd(\n node: Node<unknown, string, unknown>,\n which: MeasureEnd,\n to: MeasurePoint,\n): { data: MeasureData; pose: RectPose } | null {\n const ends = measureEndsOf(node);\n if (!ends) return null;\n const next = { ...ends, [which]: { x: to.x, y: to.y } };\n const data: MeasureData = { ...(node.data as MeasureData), measure: next };\n return { data, pose: measurePose(next.start, next.end) };\n}\n"],"mappings":";;AAuBA,SAAgB,eAAe,OAAqB,KAAgC;CAClF,MAAM,KAAK,IAAI,IAAI,MAAM;CACzB,MAAM,KAAK,IAAI,IAAI,MAAM;CACzB,OAAO;EACL,OAAO;GAAE,GAAG,MAAM;GAAG,GAAG,MAAM;EAAE;EAChC,KAAK;GAAE,GAAG,IAAI;GAAG,GAAG,IAAI;EAAE;EAC1B;EACA;EACA,UAAU,KAAK,MAAM,IAAI,EAAE;EAE3B,OAAO,OAAO,KAAK,OAAO,IAAI,IAAI,KAAK,MAAM,CAAC,MAAM,GAAG,EAAE;CAC3D;AACF;;AAcA,SAAgB,cAAc,MAA+F;CAC3H,MAAM,KAAK,MAAM,KAAA,EAAkD;CACnE,IAAI,CAAC,KAAK,OAAO,MAAM,YAAY,CAAC,EAAE,SAAS,CAAC,EAAE,KAAK,OAAO;CAC9D,OAAO;AACT;;AAGA,SAAgB,cAAc,MAAoF;CAChH,MAAM,OAAO,cAAc,IAAI;CAC/B,OAAO,OAAO,eAAe,KAAK,OAAO,KAAK,GAAG,IAAI;AACvD;;;AAIA,SAAgB,YAAY,OAAqB,KAA6B;CAC5E,OAAO;EACL,GAAG,KAAK,IAAI,MAAM,GAAG,IAAI,CAAC;EAC1B,GAAG,KAAK,IAAI,MAAM,GAAG,IAAI,CAAC;EAC1B,OAAO,KAAK,IAAI,IAAI,IAAI,MAAM,CAAC;EAC/B,QAAQ,KAAK,IAAI,IAAI,IAAI,MAAM,CAAC;CAClC;AACF;;AAGA,SAAgB,YAAY,IAAY,OAAe,OAAqB,KAAmD;CAC7H,MAAM,OAAoB,EAAE,SAAS;EAAE,OAAO,EAAE,GAAG,MAAM;EAAG,KAAK,EAAE,GAAG,IAAI;CAAE,EAAE;CAC9E,OAAO;EAAE;EAAI,MAAM;EAAQ;EAAO,QAAQ;EAAM,MAAM,YAAY,OAAO,GAAG;EAAG;CAAK;AACtF;;AAGA,SAAgB,eACd,MACA,OACA,IAC8C;CAC9C,MAAM,OAAO,cAAc,IAAI;CAC/B,IAAI,CAAC,MAAM,OAAO;CAClB,MAAM,OAAO;EAAE,GAAG;GAAO,QAAQ;GAAE,GAAG,GAAG;GAAG,GAAG,GAAG;EAAE;CAAE;CAEtD,OAAO;EAAE;GADmB,GAAI,KAAK;GAAsB,SAAS;EAC3D;EAAM,MAAM,YAAY,KAAK,OAAO,KAAK,GAAG;CAAE;AACzD"}
@@ -4,10 +4,9 @@ import { worldToScreen } from "../../core/viewport/viewTransform.js";
4
4
  //#region src/features/modes/modeLayers.ts
5
5
  var DEFAULT_INTENSITY = .12;
6
6
  /**
7
- * A screen-space layer painting the active mode's `WorkspaceVisual`: nothing in
8
- * a mode without a tint, otherwise a wash at the mode's intensity, bottom-up or
9
- * top-down when the mode names a gradient. It subscribes to the registry, so a
10
- * mode switch repaints it.
7
+ * A screen-space layer painting the active mode's `WorkspaceVisual`, or the `fallback`'s in a
8
+ * mode without one: a wash at `intensity`, else that visual's own, bottom-up or top-down when it
9
+ * names a gradient.
11
10
  */
12
11
  function workspaceTintLayer(opts) {
13
12
  const { registry } = opts;
@@ -15,11 +14,19 @@ function workspaceTintLayer(opts) {
15
14
  id: opts.id ?? "mode-tint",
16
15
  label: opts.label ?? "Mode tint",
17
16
  space: "screen",
18
- subscribe: registry.subscribe,
17
+ subscribe: (repaint) => {
18
+ const offMode = registry.subscribe(repaint);
19
+ const offFallback = opts.fallback?.subscribe(repaint);
20
+ return () => {
21
+ offMode();
22
+ offFallback?.();
23
+ };
24
+ },
19
25
  draw: (_data, view, dims) => {
20
- const ws = registry.current().workspace;
26
+ const own = registry.current().workspace;
27
+ const ws = own?.tint && own.tint !== "transparent" ? own : opts.fallback?.visual() ?? void 0;
21
28
  const tint = ws?.tint;
22
- if (!tint || tint === "transparent") return [];
29
+ if (!ws || !tint || tint === "transparent") return [];
23
30
  const opacity = opts.intensity ?? ws.intensity ?? DEFAULT_INTENSITY;
24
31
  const { width: W, height: H } = dims;
25
32
  const strip = (x, y, width, height, from, to) => ({
@@ -1 +1 @@
1
- {"version":3,"file":"modeLayers.js","names":[],"sources":["../../../src/features/modes/modeLayers.ts"],"sourcesContent":["import type { Rect } from '@weasel-js/geom';\nimport type { ModeDecorations, ModeRegistry } from '@weasel-js/modes';\nimport type { DrawCommand } from '@weasel-js/renderer';\nimport type { RenderLayer } from 'core/layers/render';\nimport { viewToTransform } from 'core/viewport/view';\nimport { worldToScreen } from 'core/viewport/viewTransform';\n\n/** Options for {@link workspaceTintLayer}. */\nexport interface WorkspaceTintLayerOptions {\n registry: ModeRegistry;\n /** The page, in world coordinates. When given, the tint covers only the\n * workspace around it, each strip fading toward the page edge if the mode\n * names a gradient. Without it the tint covers the whole canvas. */\n page?: () => Rect | null;\n /** Opacity for every mode's tint, in place of its `workspace.intensity`. */\n intensity?: number;\n id?: string;\n label?: string;\n}\n\nconst DEFAULT_INTENSITY = 0.12;\n\n/**\n * A screen-space layer painting the active mode's `WorkspaceVisual`: nothing in\n * a mode without a tint, otherwise a wash at the mode's intensity, bottom-up or\n * top-down when the mode names a gradient. It subscribes to the registry, so a\n * mode switch repaints it.\n */\nexport function workspaceTintLayer(opts: WorkspaceTintLayerOptions): RenderLayer<unknown> {\n const { registry } = opts;\n return {\n id: opts.id ?? 'mode-tint',\n label: opts.label ?? 'Mode tint',\n space: 'screen',\n subscribe: registry.subscribe,\n draw: (_data, view, dims) => {\n const ws = registry.current().workspace;\n const tint = ws?.tint;\n if (!tint || tint === 'transparent') return [];\n const opacity = opts.intensity ?? ws.intensity ?? DEFAULT_INTENSITY;\n const { width: W, height: H } = dims;\n\n const strip = (\n x: number, y: number, width: number, height: number,\n from: { x: number; y: number }, to: { x: number; y: number },\n ): DrawCommand => ({\n kind: 'path',\n path: { kind: 'rect', x, y, width, height },\n fill: { paint: ws.gradient\n ? { kind: 'linear-gradient', from, to, stops: [{ offset: 0, color: tint }, { offset: 1, color: 'transparent' }], opacity }\n : { kind: 'solid', color: tint, opacity } },\n });\n\n const whole = (): DrawCommand[] => [\n ws.gradient === 'top-down'\n ? strip(0, 0, W, H, { x: 0, y: 0 }, { x: 0, y: H })\n : strip(0, 0, W, H, { x: 0, y: H }, { x: 0, y: 0 }),\n ];\n\n const page = opts.page?.();\n if (!page) return whole();\n\n const t = viewToTransform(view);\n const [sx0, sy0] = worldToScreen(page.x, page.y, t);\n const [sx1, sy1] = worldToScreen(page.x + page.width, page.y + page.height, t);\n const pl = Math.max(0, Math.min(sx0, sx1));\n const pt = Math.max(0, Math.min(sy0, sy1));\n const pr = Math.min(W, Math.max(sx0, sx1));\n const pb = Math.min(H, Math.max(sy0, sy1));\n if (pr <= pl || pb <= pt) {\n return [{ kind: 'path', path: { kind: 'rect', x: 0, y: 0, width: W, height: H }, fill: { paint: { kind: 'solid', color: tint, opacity } } }];\n }\n\n const cmds: DrawCommand[] = [];\n const push = (\n x: number, y: number, w: number, h: number,\n from: { x: number; y: number }, to: { x: number; y: number },\n ): void => { if (w > 0 && h > 0) cmds.push(strip(x, y, w, h, from, to)); };\n push(0, 0, W, pt, { x: 0, y: 0 }, { x: 0, y: pt });\n push(0, pb, W, H - pb, { x: 0, y: H }, { x: 0, y: pb });\n push(0, pt, pl, pb - pt, { x: 0, y: 0 }, { x: pl, y: 0 });\n push(pr, pt, W - pr, pb - pt, { x: W, y: 0 }, { x: pr, y: 0 });\n return cmds;\n },\n };\n}\n\n/** A world-space layer drawing a `ModeDecorations`' active painter. It\n * subscribes to the decorations, so a mode switch or a new painter repaints\n * it. */\nexport function modeDecorationLayer(\n decorations: ModeDecorations,\n opts: { id?: string; label?: string } = {},\n): RenderLayer<unknown> {\n return {\n id: opts.id ?? 'mode-decorations',\n label: opts.label ?? 'Mode decorations',\n subscribe: decorations.subscribe,\n draw: () => decorations.paint() as DrawCommand[],\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"modeLayers.js","names":[],"sources":["../../../src/features/modes/modeLayers.ts"],"sourcesContent":["import type { Rect } from '@weasel-js/geom';\nimport type { ModeDecorations, ModeRegistry, WorkspaceVisual } from '@weasel-js/modes';\nimport type { DrawCommand } from '@weasel-js/renderer';\nimport type { RenderLayer } from 'core/layers/render';\nimport { viewToTransform } from 'core/viewport/view';\nimport { worldToScreen } from 'core/viewport/viewTransform';\n\n/** Options for {@link workspaceTintLayer}. */\nexport interface WorkspaceTintLayerOptions {\n registry: ModeRegistry;\n /** The page, in world coordinates. When given, the tint covers only the\n * workspace around it, each strip fading toward the page edge if the mode\n * names a gradient. Without it the tint covers the whole canvas. */\n page?: () => Rect | null;\n /** Opacity for every mode's tint, in place of its `workspace.intensity`. */\n intensity?: number;\n /** The tint shown in a mode with none of its own, and what announces a change to it. */\n fallback?: { visual: () => WorkspaceVisual | null; subscribe: (fn: () => void) => () => void };\n id?: string;\n label?: string;\n}\n\nconst DEFAULT_INTENSITY = 0.12;\n\n/**\n * A screen-space layer painting the active mode's `WorkspaceVisual`, or the `fallback`'s in a\n * mode without one: a wash at `intensity`, else that visual's own, bottom-up or top-down when it\n * names a gradient.\n */\nexport function workspaceTintLayer(opts: WorkspaceTintLayerOptions): RenderLayer<unknown> {\n const { registry } = opts;\n return {\n id: opts.id ?? 'mode-tint',\n label: opts.label ?? 'Mode tint',\n space: 'screen',\n subscribe: (repaint) => {\n const offMode = registry.subscribe(repaint);\n const offFallback = opts.fallback?.subscribe(repaint);\n return () => { offMode(); offFallback?.(); };\n },\n draw: (_data, view, dims) => {\n const own = registry.current().workspace;\n const ws = own?.tint && own.tint !== 'transparent' ? own : opts.fallback?.visual() ?? undefined;\n const tint = ws?.tint;\n if (!ws || !tint || tint === 'transparent') return [];\n const opacity = opts.intensity ?? ws.intensity ?? DEFAULT_INTENSITY;\n const { width: W, height: H } = dims;\n\n const strip = (\n x: number, y: number, width: number, height: number,\n from: { x: number; y: number }, to: { x: number; y: number },\n ): DrawCommand => ({\n kind: 'path',\n path: { kind: 'rect', x, y, width, height },\n fill: { paint: ws.gradient\n ? { kind: 'linear-gradient', from, to, stops: [{ offset: 0, color: tint }, { offset: 1, color: 'transparent' }], opacity }\n : { kind: 'solid', color: tint, opacity } },\n });\n\n const whole = (): DrawCommand[] => [\n ws.gradient === 'top-down'\n ? strip(0, 0, W, H, { x: 0, y: 0 }, { x: 0, y: H })\n : strip(0, 0, W, H, { x: 0, y: H }, { x: 0, y: 0 }),\n ];\n\n const page = opts.page?.();\n if (!page) return whole();\n\n const t = viewToTransform(view);\n const [sx0, sy0] = worldToScreen(page.x, page.y, t);\n const [sx1, sy1] = worldToScreen(page.x + page.width, page.y + page.height, t);\n const pl = Math.max(0, Math.min(sx0, sx1));\n const pt = Math.max(0, Math.min(sy0, sy1));\n const pr = Math.min(W, Math.max(sx0, sx1));\n const pb = Math.min(H, Math.max(sy0, sy1));\n if (pr <= pl || pb <= pt) {\n return [{ kind: 'path', path: { kind: 'rect', x: 0, y: 0, width: W, height: H }, fill: { paint: { kind: 'solid', color: tint, opacity } } }];\n }\n\n const cmds: DrawCommand[] = [];\n const push = (\n x: number, y: number, w: number, h: number,\n from: { x: number; y: number }, to: { x: number; y: number },\n ): void => { if (w > 0 && h > 0) cmds.push(strip(x, y, w, h, from, to)); };\n push(0, 0, W, pt, { x: 0, y: 0 }, { x: 0, y: pt });\n push(0, pb, W, H - pb, { x: 0, y: H }, { x: 0, y: pb });\n push(0, pt, pl, pb - pt, { x: 0, y: 0 }, { x: pl, y: 0 });\n push(pr, pt, W - pr, pb - pt, { x: W, y: 0 }, { x: pr, y: 0 });\n return cmds;\n },\n };\n}\n\n/** A world-space layer drawing a `ModeDecorations`' active painter. It\n * subscribes to the decorations, so a mode switch or a new painter repaints\n * it. */\nexport function modeDecorationLayer(\n decorations: ModeDecorations,\n opts: { id?: string; label?: string } = {},\n): RenderLayer<unknown> {\n return {\n id: opts.id ?? 'mode-decorations',\n label: opts.label ?? 'Mode decorations',\n subscribe: decorations.subscribe,\n draw: () => decorations.paint() as DrawCommand[],\n };\n}\n"],"mappings":";;;;AAsBA,IAAM,oBAAoB;;;;;;AAO1B,SAAgB,mBAAmB,MAAuD;CACxF,MAAM,EAAE,aAAa;CACrB,OAAO;EACL,IAAI,KAAK,MAAM;EACf,OAAO,KAAK,SAAS;EACrB,OAAO;EACP,YAAY,YAAY;GACtB,MAAM,UAAU,SAAS,UAAU,OAAO;GAC1C,MAAM,cAAc,KAAK,UAAU,UAAU,OAAO;GACpD,aAAa;IAAE,QAAQ;IAAG,cAAc;GAAG;EAC7C;EACA,OAAO,OAAO,MAAM,SAAS;GAC3B,MAAM,MAAM,SAAS,QAAQ,CAAC,CAAC;GAC/B,MAAM,KAAK,KAAK,QAAQ,IAAI,SAAS,gBAAgB,MAAM,KAAK,UAAU,OAAO,KAAK;GACtF,MAAM,OAAO,IAAI;GACjB,IAAI,CAAC,MAAM,CAAC,QAAQ,SAAS,eAAe,OAAO,CAAC;GACpD,MAAM,UAAU,KAAK,aAAa,GAAG,aAAa;GAClD,MAAM,EAAE,OAAO,GAAG,QAAQ,MAAM;GAEhC,MAAM,SACJ,GAAW,GAAW,OAAe,QACrC,MAAgC,QACf;IACjB,MAAM;IACN,MAAM;KAAE,MAAM;KAAQ;KAAG;KAAG;KAAO;IAAO;IAC1C,MAAM,EAAE,OAAO,GAAG,WACd;KAAE,MAAM;KAAmB;KAAM;KAAI,OAAO,CAAC;MAAE,QAAQ;MAAG,OAAO;KAAK,GAAG;MAAE,QAAQ;MAAG,OAAO;KAAc,CAAC;KAAG;IAAQ,IACvH;KAAE,MAAM;KAAS,OAAO;KAAM;IAAQ,EAAE;GAC9C;GAEA,MAAM,cAA6B,CACjC,GAAG,aAAa,aACZ,MAAM,GAAG,GAAG,GAAG,GAAG;IAAE,GAAG;IAAG,GAAG;GAAE,GAAG;IAAE,GAAG;IAAG,GAAG;GAAE,CAAC,IAChD,MAAM,GAAG,GAAG,GAAG,GAAG;IAAE,GAAG;IAAG,GAAG;GAAE,GAAG;IAAE,GAAG;IAAG,GAAG;GAAE,CAAC,CACtD;GAEA,MAAM,OAAO,KAAK,OAAO;GACzB,IAAI,CAAC,MAAM,OAAO,MAAM;GAExB,MAAM,IAAI,gBAAgB,IAAI;GAC9B,MAAM,CAAC,KAAK,OAAO,cAAc,KAAK,GAAG,KAAK,GAAG,CAAC;GAClD,MAAM,CAAC,KAAK,OAAO,cAAc,KAAK,IAAI,KAAK,OAAO,KAAK,IAAI,KAAK,QAAQ,CAAC;GAC7E,MAAM,KAAK,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,GAAG,CAAC;GACzC,MAAM,KAAK,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,GAAG,CAAC;GACzC,MAAM,KAAK,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,GAAG,CAAC;GACzC,MAAM,KAAK,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,GAAG,CAAC;GACzC,IAAI,MAAM,MAAM,MAAM,IACpB,OAAO,CAAC;IAAE,MAAM;IAAQ,MAAM;KAAE,MAAM;KAAQ,GAAG;KAAG,GAAG;KAAG,OAAO;KAAG,QAAQ;IAAE;IAAG,MAAM,EAAE,OAAO;KAAE,MAAM;KAAS,OAAO;KAAM;IAAQ,EAAE;GAAE,CAAC;GAG7I,MAAM,OAAsB,CAAC;GAC7B,MAAM,QACJ,GAAW,GAAW,GAAW,GACjC,MAAgC,OACvB;IAAE,IAAI,IAAI,KAAK,IAAI,GAAG,KAAK,KAAK,MAAM,GAAG,GAAG,GAAG,GAAG,MAAM,EAAE,CAAC;GAAG;GACzE,KAAK,GAAG,GAAG,GAAG,IAAI;IAAE,GAAG;IAAG,GAAG;GAAE,GAAG;IAAE,GAAG;IAAG,GAAG;GAAG,CAAC;GACjD,KAAK,GAAG,IAAI,GAAG,IAAI,IAAI;IAAE,GAAG;IAAG,GAAG;GAAE,GAAG;IAAE,GAAG;IAAG,GAAG;GAAG,CAAC;GACtD,KAAK,GAAG,IAAI,IAAI,KAAK,IAAI;IAAE,GAAG;IAAG,GAAG;GAAE,GAAG;IAAE,GAAG;IAAI,GAAG;GAAE,CAAC;GACxD,KAAK,IAAI,IAAI,IAAI,IAAI,KAAK,IAAI;IAAE,GAAG;IAAG,GAAG;GAAE,GAAG;IAAE,GAAG;IAAI,GAAG;GAAE,CAAC;GAC7D,OAAO;EACT;CACF;AACF;;;;AAKA,SAAgB,oBACd,aACA,OAAwC,CAAC,GACnB;CACtB,OAAO;EACL,IAAI,KAAK,MAAM;EACf,OAAO,KAAK,SAAS;EACrB,WAAW,YAAY;EACvB,YAAY,YAAY,MAAM;CAChC;AACF"}
@@ -10,9 +10,9 @@ import { modeLabel } from "@weasel-js/modes";
10
10
  *
11
11
  * The target is the node the dispatcher classified the double-click on (the `hitAt` dep's
12
12
  * `doubleClick`, else its hit), the one the binding's `kind:` target matched: a double-click on
13
- * a grouped member names its outermost group inside `<SceneCanvas isolatedGroup>`. The action is live in every mode, the target mode
14
- * included, so a double-click on another node moves a mode onto it; `canEnter` and `enter`
15
- * decide what a switch from the current mode means.
13
+ * a grouped member names its outermost group inside the entered group (`<SceneCanvas enteredGroup>`).
14
+ * The action is live in every mode, the target mode included, so a double-click on another node
15
+ * moves a mode onto it; `canEnter` and `enter` decide what a switch from the current mode means.
16
16
  *
17
17
  * The bindings ride the ambient tier: a tool that claims the double-click itself, the pen
18
18
  * finishing a stroke, keeps it.
@@ -1 +1 @@
1
- {"version":3,"file":"modeTriggers.js","names":[],"sources":["../../../src/features/modes/modeTriggers.ts"],"sourcesContent":["import { modeLabel, type ModeDefinition, type ModeRegistry } from '@weasel-js/modes';\nimport { ActionDisabledReason, type Action, type ActionDeps, type Contribution } from '@weasel-js/routing';\n\n/** What a mode's double-click trigger does. */\nexport interface ModeTriggerHandlers {\n /** Enter mode `id` on the node `targetId`. Without it nothing is bound. */\n enter?: (id: string, targetId: string) => void;\n /**\n * Whether mode `id` can be entered on `targetId` now. Where it says no the double-click falls\n * through to the next binding on it, so a canvas action that already enters the mode on some\n * of those nodes keeps them: `enterPathEdit` on a path with anchors, say. Default yes.\n */\n canEnter?: (id: string, targetId: string) => boolean;\n}\n\n/**\n * The registry's double-click entries as an always-on contribution: for each mode whose\n * `entry.trigger` is `'double-click-target'`, one action bound to a double-click on each routing\n * kind its `entry.targets` names, entering the mode on the node double-clicked. Install it with\n * `<SceneCanvas ambient>`, beside `modeShortcuts`.\n *\n * The target is the node the dispatcher classified the double-click on (the `hitAt` dep's\n * `doubleClick`, else its hit), the one the binding's `kind:` target matched: a double-click on\n * a grouped member names its outermost group inside `<SceneCanvas isolatedGroup>`. The action is live in every mode, the target mode\n * included, so a double-click on another node moves a mode onto it; `canEnter` and `enter`\n * decide what a switch from the current mode means.\n *\n * The bindings ride the ambient tier: a tool that claims the double-click itself, the pen\n * finishing a stroke, keeps it.\n */\nexport function modeTriggers(registry: ModeRegistry, handlers: ModeTriggerHandlers): Contribution<never> {\n const actions: Action[] = [];\n const { enter } = handlers;\n if (enter) {\n for (const mode of registry.list()) {\n const targets = mode.entry?.trigger === 'double-click-target' ? mode.entry.targets ?? [] : [];\n if (targets.length > 0) actions.push(triggerAction(mode, targets, enter, handlers.canEnter));\n }\n }\n return { id: 'modes.triggers', eligibility: { always: true }, actions };\n}\n\nfunction triggerAction(\n mode: ModeDefinition,\n targets: readonly string[],\n enter: (id: string, targetId: string) => void,\n canEnter: ModeTriggerHandlers['canEnter'],\n): Action {\n const targetAt = (deps: ActionDeps | undefined, at: { x: number; y: number } | undefined): string | null => {\n if (!at) return null;\n const hit = deps?.hitAt?.(at);\n const id = (hit?.doubleClick ?? hit)?.id;\n if (id === undefined) return null;\n return canEnter?.(mode.id, id) === false ? null : id;\n };\n const id = `mode.${mode.id}.trigger`;\n return {\n id,\n label: `Enter ${modeLabel(mode)}`,\n defaultBinding: targets.map((kind) => ({\n spec: { kind: 'doubleClick' as const, target: `kind:${kind}` as const },\n opts: { id: `${id}.doubleClick.${kind}` },\n })),\n requires: ['hitAt'],\n enabled: (deps, at) => (targetAt(deps, at) !== null ? true : ActionDisabledReason.NotApplicable),\n invoker: {\n timing: 'immediate',\n run: (deps, params) => {\n const x = params?.worldX, y = params?.worldY;\n const id = typeof x === 'number' && typeof y === 'number' ? targetAt(deps, { x, y }) : null;\n if (id !== null) enter(mode.id, id);\n },\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA8BA,SAAgB,aAAa,UAAwB,UAAoD;CACvG,MAAM,UAAoB,CAAC;CAC3B,MAAM,EAAE,UAAU;CAClB,IAAI,OACF,KAAK,MAAM,QAAQ,SAAS,KAAK,GAAG;EAClC,MAAM,UAAU,KAAK,OAAO,YAAY,wBAAwB,KAAK,MAAM,WAAW,CAAC,IAAI,CAAC;EAC5F,IAAI,QAAQ,SAAS,GAAG,QAAQ,KAAK,cAAc,MAAM,SAAS,OAAO,SAAS,QAAQ,CAAC;CAC7F;CAEF,OAAO;EAAE,IAAI;EAAkB,aAAa,EAAE,QAAQ,KAAK;EAAG;CAAQ;AACxE;AAEA,SAAS,cACP,MACA,SACA,OACA,UACQ;CACR,MAAM,YAAY,MAA8B,OAA4D;EAC1G,IAAI,CAAC,IAAI,OAAO;EAChB,MAAM,MAAM,MAAM,QAAQ,EAAE;EAC5B,MAAM,MAAM,KAAK,eAAe,IAAA,EAAM;EACtC,IAAI,OAAO,QAAW,OAAO;EAC7B,OAAO,WAAW,KAAK,IAAI,EAAE,MAAM,QAAQ,OAAO;CACpD;CACA,MAAM,KAAK,QAAQ,KAAK,GAAG;CAC3B,OAAO;EACL;EACA,OAAO,SAAS,UAAU,IAAI;EAC9B,gBAAgB,QAAQ,KAAK,UAAU;GACrC,MAAM;IAAE,MAAM;IAAwB,QAAQ,QAAQ;GAAgB;GACtE,MAAM,EAAE,IAAI,GAAG,GAAG,eAAe,OAAO;EAC1C,EAAE;EACF,UAAU,CAAC,OAAO;EAClB,UAAU,MAAM,OAAQ,SAAS,MAAM,EAAE,MAAM,OAAO,OAAO,qBAAqB;EAClF,SAAS;GACP,QAAQ;GACR,MAAM,MAAM,WAAW;IACrB,MAAM,IAAI,QAAQ,QAAQ,IAAI,QAAQ;IACtC,MAAM,KAAK,OAAO,MAAM,YAAY,OAAO,MAAM,WAAW,SAAS,MAAM;KAAE;KAAG;IAAE,CAAC,IAAI;IACvF,IAAI,OAAO,MAAM,MAAM,KAAK,IAAI,EAAE;GACpC;EACF;CACF;AACF"}
1
+ {"version":3,"file":"modeTriggers.js","names":[],"sources":["../../../src/features/modes/modeTriggers.ts"],"sourcesContent":["import { modeLabel, type ModeDefinition, type ModeRegistry } from '@weasel-js/modes';\nimport { ActionDisabledReason, type Action, type ActionDeps, type Contribution } from '@weasel-js/routing';\n\n/** What a mode's double-click trigger does. */\nexport interface ModeTriggerHandlers {\n /** Enter mode `id` on the node `targetId`. Without it nothing is bound. */\n enter?: (id: string, targetId: string) => void;\n /**\n * Whether mode `id` can be entered on `targetId` now. Where it says no the double-click falls\n * through to the next binding on it, so a canvas action that already enters the mode on some\n * of those nodes keeps them: `enterPathEdit` on a path with anchors, say. Default yes.\n */\n canEnter?: (id: string, targetId: string) => boolean;\n}\n\n/**\n * The registry's double-click entries as an always-on contribution: for each mode whose\n * `entry.trigger` is `'double-click-target'`, one action bound to a double-click on each routing\n * kind its `entry.targets` names, entering the mode on the node double-clicked. Install it with\n * `<SceneCanvas ambient>`, beside `modeShortcuts`.\n *\n * The target is the node the dispatcher classified the double-click on (the `hitAt` dep's\n * `doubleClick`, else its hit), the one the binding's `kind:` target matched: a double-click on\n * a grouped member names its outermost group inside the entered group (`<SceneCanvas enteredGroup>`).\n * The action is live in every mode, the target mode included, so a double-click on another node\n * moves a mode onto it; `canEnter` and `enter` decide what a switch from the current mode means.\n *\n * The bindings ride the ambient tier: a tool that claims the double-click itself, the pen\n * finishing a stroke, keeps it.\n */\nexport function modeTriggers(registry: ModeRegistry, handlers: ModeTriggerHandlers): Contribution<never> {\n const actions: Action[] = [];\n const { enter } = handlers;\n if (enter) {\n for (const mode of registry.list()) {\n const targets = mode.entry?.trigger === 'double-click-target' ? mode.entry.targets ?? [] : [];\n if (targets.length > 0) actions.push(triggerAction(mode, targets, enter, handlers.canEnter));\n }\n }\n return { id: 'modes.triggers', eligibility: { always: true }, actions };\n}\n\nfunction triggerAction(\n mode: ModeDefinition,\n targets: readonly string[],\n enter: (id: string, targetId: string) => void,\n canEnter: ModeTriggerHandlers['canEnter'],\n): Action {\n const targetAt = (deps: ActionDeps | undefined, at: { x: number; y: number } | undefined): string | null => {\n if (!at) return null;\n const hit = deps?.hitAt?.(at);\n const id = (hit?.doubleClick ?? hit)?.id;\n if (id === undefined) return null;\n return canEnter?.(mode.id, id) === false ? null : id;\n };\n const id = `mode.${mode.id}.trigger`;\n return {\n id,\n label: `Enter ${modeLabel(mode)}`,\n defaultBinding: targets.map((kind) => ({\n spec: { kind: 'doubleClick' as const, target: `kind:${kind}` as const },\n opts: { id: `${id}.doubleClick.${kind}` },\n })),\n requires: ['hitAt'],\n enabled: (deps, at) => (targetAt(deps, at) !== null ? true : ActionDisabledReason.NotApplicable),\n invoker: {\n timing: 'immediate',\n run: (deps, params) => {\n const x = params?.worldX, y = params?.worldY;\n const id = typeof x === 'number' && typeof y === 'number' ? targetAt(deps, { x, y }) : null;\n if (id !== null) enter(mode.id, id);\n },\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA8BA,SAAgB,aAAa,UAAwB,UAAoD;CACvG,MAAM,UAAoB,CAAC;CAC3B,MAAM,EAAE,UAAU;CAClB,IAAI,OACF,KAAK,MAAM,QAAQ,SAAS,KAAK,GAAG;EAClC,MAAM,UAAU,KAAK,OAAO,YAAY,wBAAwB,KAAK,MAAM,WAAW,CAAC,IAAI,CAAC;EAC5F,IAAI,QAAQ,SAAS,GAAG,QAAQ,KAAK,cAAc,MAAM,SAAS,OAAO,SAAS,QAAQ,CAAC;CAC7F;CAEF,OAAO;EAAE,IAAI;EAAkB,aAAa,EAAE,QAAQ,KAAK;EAAG;CAAQ;AACxE;AAEA,SAAS,cACP,MACA,SACA,OACA,UACQ;CACR,MAAM,YAAY,MAA8B,OAA4D;EAC1G,IAAI,CAAC,IAAI,OAAO;EAChB,MAAM,MAAM,MAAM,QAAQ,EAAE;EAC5B,MAAM,MAAM,KAAK,eAAe,IAAA,EAAM;EACtC,IAAI,OAAO,QAAW,OAAO;EAC7B,OAAO,WAAW,KAAK,IAAI,EAAE,MAAM,QAAQ,OAAO;CACpD;CACA,MAAM,KAAK,QAAQ,KAAK,GAAG;CAC3B,OAAO;EACL;EACA,OAAO,SAAS,UAAU,IAAI;EAC9B,gBAAgB,QAAQ,KAAK,UAAU;GACrC,MAAM;IAAE,MAAM;IAAwB,QAAQ,QAAQ;GAAgB;GACtE,MAAM,EAAE,IAAI,GAAG,GAAG,eAAe,OAAO;EAC1C,EAAE;EACF,UAAU,CAAC,OAAO;EAClB,UAAU,MAAM,OAAQ,SAAS,MAAM,EAAE,MAAM,OAAO,OAAO,qBAAqB;EAClF,SAAS;GACP,QAAQ;GACR,MAAM,MAAM,WAAW;IACrB,MAAM,IAAI,QAAQ,QAAQ,IAAI,QAAQ;IACtC,MAAM,KAAK,OAAO,MAAM,YAAY,OAAO,MAAM,WAAW,SAAS,MAAM;KAAE;KAAG;IAAE,CAAC,IAAI;IACvF,IAAI,OAAO,MAAM,MAAM,KAAK,IAAI,EAAE;GACpC;EACF;CACF;AACF"}