@weasel-js/core 1.13.0 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (741) hide show
  1. package/CHANGELOG.md +300 -0
  2. package/README.md +8 -12
  3. package/dist/affordances/hitAffordanceRegions.js +1 -1
  4. package/dist/affordances/hitAffordanceRegions.js.map +1 -1
  5. package/dist/affordances/pathAnchors.js.map +1 -1
  6. package/dist/animation/clipTargets.js +1 -1
  7. package/dist/animation/clipTargets.js.map +1 -1
  8. package/dist/animation/poseTarget.js +5 -32
  9. package/dist/animation/poseTarget.js.map +1 -1
  10. package/dist/{autoPoseDescriptor-CZ1eXbzY.d.ts → autoPoseDescriptor-C25QVb0W.d.ts} +1 -1
  11. package/dist/barrel/canvas.js +8 -8
  12. package/dist/barrel/chrome.js +9 -9
  13. package/dist/barrel/input.js +1 -1
  14. package/dist/barrel/interactions.js +3 -3
  15. package/dist/barrel/paint.js +2 -2
  16. package/dist/barrel/paths.js +19 -17
  17. package/dist/barrel/renderer.js +11 -7
  18. package/dist/barrel/routing.js +4 -1
  19. package/dist/barrel/scene.js +16 -12
  20. package/dist/barrel/text.js +5 -5
  21. package/dist/barrel/viewport.js +8 -8
  22. package/dist/canvas/Canvas.js +2 -2
  23. package/dist/canvas/Canvas.js.map +1 -1
  24. package/dist/canvas/CanvasView.js +20 -14
  25. package/dist/canvas/CanvasView.js.map +1 -1
  26. package/dist/canvas/GestureReason.js +64 -0
  27. package/dist/canvas/GestureReason.js.map +1 -0
  28. package/dist/canvas/GestureReason.module.js +7 -0
  29. package/dist/canvas/GestureReason.module.js.map +1 -0
  30. package/dist/canvas/LeanSceneCanvas.js +3 -1
  31. package/dist/canvas/LeanSceneCanvas.js.map +1 -1
  32. package/dist/canvas/NodeShape.js +2 -0
  33. package/dist/canvas/NodeShape.js.map +1 -1
  34. package/dist/canvas/PickHud.js +7 -3
  35. package/dist/canvas/PickHud.js.map +1 -1
  36. package/dist/canvas/SceneCanvas/CanvasKitProviders.js +1 -1
  37. package/dist/canvas/SceneCanvas/CanvasKitProviders.js.map +1 -1
  38. package/dist/canvas/SceneCanvas/DevHuds.js +2 -2
  39. package/dist/canvas/SceneCanvas/DevHuds.js.map +1 -1
  40. package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js +4 -2
  41. package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js.map +1 -1
  42. package/dist/canvas/SceneCanvas/SceneCanvasInput.js +3 -1
  43. package/dist/canvas/SceneCanvas/SceneCanvasInput.js.map +1 -1
  44. package/dist/canvas/SceneCanvas/brushOverlay.js.map +1 -1
  45. package/dist/canvas/SceneCanvas/builtinToolSets.js +17 -9
  46. package/dist/canvas/SceneCanvas/builtinToolSets.js.map +1 -1
  47. package/dist/canvas/SceneCanvas/canvasPassThrough.js +2 -1
  48. package/dist/canvas/SceneCanvas/canvasPassThrough.js.map +1 -1
  49. package/dist/canvas/SceneCanvas/defaultNodeProperties.js +75 -19
  50. package/dist/canvas/SceneCanvas/defaultNodeProperties.js.map +1 -1
  51. package/dist/canvas/SceneCanvas/defaultNodeRouting.js +7 -1
  52. package/dist/canvas/SceneCanvas/defaultNodeRouting.js.map +1 -1
  53. package/dist/canvas/SceneCanvas/defaultTextEditor.js +108 -0
  54. package/dist/canvas/SceneCanvas/defaultTextEditor.js.map +1 -0
  55. package/dist/canvas/SceneCanvas/describePresets.js +47 -0
  56. package/dist/canvas/SceneCanvas/describePresets.js.map +1 -0
  57. package/dist/canvas/SceneCanvas/features.js +57 -16
  58. package/dist/canvas/SceneCanvas/features.js.map +1 -1
  59. package/dist/canvas/SceneCanvas/mergeLayers.js +10 -1
  60. package/dist/canvas/SceneCanvas/mergeLayers.js.map +1 -1
  61. package/dist/canvas/SceneCanvas/namedPresets.js +52 -24
  62. package/dist/canvas/SceneCanvas/namedPresets.js.map +1 -1
  63. package/dist/canvas/SceneCanvas/presets/arrange.js +35 -10
  64. package/dist/canvas/SceneCanvas/presets/arrange.js.map +1 -1
  65. package/dist/canvas/SceneCanvas/presets/baseline.js +9 -0
  66. package/dist/canvas/SceneCanvas/presets/baseline.js.map +1 -0
  67. package/dist/canvas/SceneCanvas/presets/chrome.js.map +1 -1
  68. package/dist/canvas/SceneCanvas/presets/composites.js +43 -0
  69. package/dist/canvas/SceneCanvas/presets/composites.js.map +1 -0
  70. package/dist/canvas/SceneCanvas/presets/edit.js +79 -26
  71. package/dist/canvas/SceneCanvas/presets/edit.js.map +1 -1
  72. package/dist/canvas/SceneCanvas/presets/ingest.js +1 -0
  73. package/dist/canvas/SceneCanvas/presets/ingest.js.map +1 -1
  74. package/dist/canvas/SceneCanvas/presets/paths.js +58 -16
  75. package/dist/canvas/SceneCanvas/presets/paths.js.map +1 -1
  76. package/dist/canvas/SceneCanvas/presets/select.js +4 -2
  77. package/dist/canvas/SceneCanvas/presets/select.js.map +1 -1
  78. package/dist/canvas/SceneCanvas/presets/tools.js +24 -0
  79. package/dist/canvas/SceneCanvas/presets/tools.js.map +1 -0
  80. package/dist/canvas/SceneCanvas/presets/transform.js +4 -0
  81. package/dist/canvas/SceneCanvas/presets/transform.js.map +1 -1
  82. package/dist/canvas/SceneCanvas/presets/view.js +3 -1
  83. package/dist/canvas/SceneCanvas/presets/view.js.map +1 -1
  84. package/dist/canvas/SceneCanvas/props.js.map +1 -1
  85. package/dist/canvas/SceneCanvas/shapeGeneratorProperties.js +1 -1
  86. package/dist/canvas/SceneCanvas/shapeGeneratorProperties.js.map +1 -1
  87. package/dist/canvas/SceneCanvas/toolRegistry.js +5 -4
  88. package/dist/canvas/SceneCanvas/toolRegistry.js.map +1 -1
  89. package/dist/canvas/SceneCanvas/useCanvasTools.js +26 -10
  90. package/dist/canvas/SceneCanvas/useCanvasTools.js.map +1 -1
  91. package/dist/canvas/SceneCanvas/useContributionRoles.js +15 -2
  92. package/dist/canvas/SceneCanvas/useContributionRoles.js.map +1 -1
  93. package/dist/canvas/SceneCanvas/useDepartureGhostLayer.js +5 -4
  94. package/dist/canvas/SceneCanvas/useDepartureGhostLayer.js.map +1 -1
  95. package/dist/canvas/SceneCanvas/useDispatcherOverlayLayer.js +1 -1
  96. package/dist/canvas/SceneCanvas/useNodeAtPoint.js.map +1 -1
  97. package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js +9 -8
  98. package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js.map +1 -1
  99. package/dist/canvas/SceneCanvas/useSceneCanvasPathEdit.js +2 -2
  100. package/dist/canvas/SceneCanvas/useSceneCanvasPathEdit.js.map +1 -1
  101. package/dist/canvas/SceneCanvas/useSceneCanvasTools.js +4 -1
  102. package/dist/canvas/SceneCanvas/useSceneCanvasTools.js.map +1 -1
  103. package/dist/canvas/SceneCanvas/useSceneChrome.js +7 -4
  104. package/dist/canvas/SceneCanvas/useSceneChrome.js.map +1 -1
  105. package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js +9 -9
  106. package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js.map +1 -1
  107. package/dist/canvas/SceneCanvas/useScenePicking.js +4 -7
  108. package/dist/canvas/SceneCanvas/useScenePicking.js.map +1 -1
  109. package/dist/canvas/SceneCanvas/useSceneRedrawSources.js +2 -1
  110. package/dist/canvas/SceneCanvas/useSceneRedrawSources.js.map +1 -1
  111. package/dist/canvas/SceneCanvas/useSceneSelectTool.js +6 -6
  112. package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
  113. package/dist/canvas/SceneCanvas/useSceneStandardActions.js +2 -2
  114. package/dist/canvas/SceneCanvas/useSceneStandardActions.js.map +1 -1
  115. package/dist/canvas/SceneCanvas/useSelectionOverlayLayer.js +1 -1
  116. package/dist/canvas/SceneCanvas/useSelectionOverlayLayer.js.map +1 -1
  117. package/dist/canvas/SceneCanvas/useSurfaceViewInputs.js.map +1 -1
  118. package/dist/canvas/SceneCanvas/useToolKeybindings.js +2 -2
  119. package/dist/canvas/SceneCanvas/useToolKeybindings.js.map +1 -1
  120. package/dist/canvas/SceneCanvas/useViewportActions.js +5 -2
  121. package/dist/canvas/SceneCanvas/useViewportActions.js.map +1 -1
  122. package/dist/canvas/SceneCanvas/worldPointAt.js.map +1 -1
  123. package/dist/canvas/SceneCanvas.js +22 -11
  124. package/dist/canvas/SceneCanvas.js.map +1 -1
  125. package/dist/canvas/SceneViewCanvas.js +1 -0
  126. package/dist/canvas/SceneViewCanvas.js.map +1 -1
  127. package/dist/canvas/ViewMarker.js.map +1 -1
  128. package/dist/canvas/accentColor.js +14 -1
  129. package/dist/canvas/accentColor.js.map +1 -1
  130. package/dist/canvas/affordanceAt.js +2 -2
  131. package/dist/canvas/affordanceAt.js.map +1 -1
  132. package/dist/canvas/buildSceneTree.js +5 -3
  133. package/dist/canvas/buildSceneTree.js.map +1 -1
  134. package/dist/canvas/deps/editAnchors.js +4 -4
  135. package/dist/canvas/deps/editAnchors.js.map +1 -1
  136. package/dist/canvas/deps/enteredGroup.js +14 -5
  137. package/dist/canvas/deps/enteredGroup.js.map +1 -1
  138. package/dist/canvas/deps/ingestion.js +5 -1
  139. package/dist/canvas/deps/ingestion.js.map +1 -1
  140. package/dist/canvas/deps/insert.js.map +1 -1
  141. package/dist/canvas/deps/insertSite.js +5 -3
  142. package/dist/canvas/deps/insertSite.js.map +1 -1
  143. package/dist/canvas/deps/nodeAtPoint.js +1 -12
  144. package/dist/canvas/deps/nodeAtPoint.js.map +1 -1
  145. package/dist/canvas/deps/pathEditCommits.js +3 -3
  146. package/dist/canvas/deps/pathEditCommits.js.map +1 -1
  147. package/dist/canvas/deps/slice.js +16 -2
  148. package/dist/canvas/deps/slice.js.map +1 -1
  149. package/dist/canvas/deps/snap.js +5 -5
  150. package/dist/canvas/deps/snap.js.map +1 -1
  151. package/dist/canvas/deps/textEdit.js +5 -6
  152. package/dist/canvas/deps/textEdit.js.map +1 -1
  153. package/dist/canvas/derivedPath.js +8 -3
  154. package/dist/canvas/derivedPath.js.map +1 -1
  155. package/dist/canvas/getNodeAtPoint.js +2 -2
  156. package/dist/canvas/getNodeAtPoint.js.map +1 -1
  157. package/dist/canvas/insertPreviewExtent.js.map +1 -1
  158. package/dist/canvas/layerSlots.js.map +1 -1
  159. package/dist/canvas/nodeShapes/measurePainter.js +1 -1
  160. package/dist/canvas/nodeShapes/measurePainter.js.map +1 -1
  161. package/dist/canvas/nodeShapes/strokedBounds.js +5 -3
  162. package/dist/canvas/nodeShapes/strokedBounds.js.map +1 -1
  163. package/dist/canvas/overlayRoleStrokes.js +6 -0
  164. package/dist/canvas/overlayRoleStrokes.js.map +1 -1
  165. package/dist/canvas/pickIndex/scenePickIndex.js +10 -12
  166. package/dist/canvas/pickIndex/scenePickIndex.js.map +1 -1
  167. package/dist/canvas/sceneAdapter.js +10 -19
  168. package/dist/canvas/sceneAdapter.js.map +1 -1
  169. package/dist/canvas/sceneClipboard.js +17 -4
  170. package/dist/canvas/sceneClipboard.js.map +1 -1
  171. package/dist/canvas/sceneViewRender.js +3 -2
  172. package/dist/canvas/sceneViewRender.js.map +1 -1
  173. package/dist/canvas/surfaceContribution.js.map +1 -1
  174. package/dist/canvas/useCanvasPaint.js +10 -4
  175. package/dist/canvas/useCanvasPaint.js.map +1 -1
  176. package/dist/canvas/useHitObserver.js +1 -1
  177. package/dist/canvas/useHitObserver.js.map +1 -1
  178. package/dist/canvas/useViewAim.js +1 -1
  179. package/dist/canvas/useViewAim.js.map +1 -1
  180. package/dist/canvas/useViewHelpers.js +8 -5
  181. package/dist/canvas/useViewHelpers.js.map +1 -1
  182. package/dist/canvas/useViewNesting.js +39 -12
  183. package/dist/canvas/useViewNesting.js.map +1 -1
  184. package/dist/canvas/viewInputs.js.map +1 -1
  185. package/dist/canvas/viewMarkers.js +2 -2
  186. package/dist/canvas/viewMarkers.js.map +1 -1
  187. package/dist/canvas/viewRegistry.js +2 -2
  188. package/dist/canvas/viewRegistry.js.map +1 -1
  189. package/dist/canvas/viewSnap.js +67 -0
  190. package/dist/canvas/viewSnap.js.map +1 -0
  191. package/dist/clipboard.d.ts +6 -12
  192. package/dist/{clone-qb2e1F5p.d.ts → clone-Bz4iqGfE.d.ts} +10 -5
  193. package/dist/clone.d.ts +5 -5
  194. package/dist/core/geometry/booleanOfPaths.js +23 -0
  195. package/dist/core/geometry/booleanOfPaths.js.map +1 -0
  196. package/dist/core/geometry/pathPoseDescriptor.js +12 -9
  197. package/dist/core/geometry/pathPoseDescriptor.js.map +1 -1
  198. package/dist/core/geometry/poseDescriptor.js +12 -3
  199. package/dist/core/geometry/poseDescriptor.js.map +1 -1
  200. package/dist/core/geometry/targetFrame.js +2 -13
  201. package/dist/core/geometry/targetFrame.js.map +1 -1
  202. package/dist/core/layers/render.js +2 -1
  203. package/dist/core/layers/render.js.map +1 -1
  204. package/dist/core/layers/retain.js +5 -3
  205. package/dist/core/layers/retain.js.map +1 -1
  206. package/dist/core/ops/setPath.js +3 -0
  207. package/dist/core/ops/setPath.js.map +1 -1
  208. package/dist/core/paint/meshKindLazy.js.map +1 -1
  209. package/dist/core/paint/paintKindEntry.js.map +1 -1
  210. package/dist/core/paint/paintKinds.js +8 -1
  211. package/dist/core/paint/paintKinds.js.map +1 -1
  212. package/dist/core/scene/NodeProperties.js.map +1 -1
  213. package/dist/core/scene/NodeRouting.js.map +1 -1
  214. package/dist/core/scene/booleanGroup.js +33 -0
  215. package/dist/core/scene/booleanGroup.js.map +1 -0
  216. package/dist/core/scene/booleanOfChildren.js +53 -0
  217. package/dist/core/scene/booleanOfChildren.js.map +1 -0
  218. package/dist/core/scene/carryDescendants.js +18 -6
  219. package/dist/core/scene/carryDescendants.js.map +1 -1
  220. package/dist/core/scene/composeFrame.js +42 -5
  221. package/dist/core/scene/composeFrame.js.map +1 -1
  222. package/dist/core/scene/composePose.js +35 -16
  223. package/dist/core/scene/composePose.js.map +1 -1
  224. package/dist/core/scene/depMemo.js +36 -18
  225. package/dist/core/scene/depMemo.js.map +1 -1
  226. package/dist/core/scene/derivedParts.js +2 -23
  227. package/dist/core/scene/derivedParts.js.map +1 -1
  228. package/dist/core/scene/effectivePose.js +10 -0
  229. package/dist/core/scene/effectivePose.js.map +1 -1
  230. package/dist/core/scene/historyForm.js +5 -4
  231. package/dist/core/scene/historyForm.js.map +1 -1
  232. package/dist/core/scene/kitOps.js +5 -71
  233. package/dist/core/scene/kitOps.js.map +1 -1
  234. package/dist/core/scene/kitRegistry.js +5 -0
  235. package/dist/core/scene/kitRegistry.js.map +1 -1
  236. package/dist/core/scene/layerOps.js +82 -0
  237. package/dist/core/scene/layerOps.js.map +1 -0
  238. package/dist/core/scene/layoutPass.js +14 -12
  239. package/dist/core/scene/layoutPass.js.map +1 -1
  240. package/dist/core/scene/lockGuard.js +9 -3
  241. package/dist/core/scene/lockGuard.js.map +1 -1
  242. package/dist/core/scene/nestedHit.js +5 -5
  243. package/dist/core/scene/nestedHit.js.map +1 -1
  244. package/dist/core/scene/nodeFlags.js +8 -2
  245. package/dist/core/scene/nodeFlags.js.map +1 -1
  246. package/dist/core/scene/nodeGates.js +31 -2
  247. package/dist/core/scene/nodeGates.js.map +1 -1
  248. package/dist/core/scene/pathEffects/effects.js +22 -3
  249. package/dist/core/scene/pathEffects/effects.js.map +1 -1
  250. package/dist/core/scene/pathEffects/textAlong.js +16 -3
  251. package/dist/core/scene/pathEffects/textAlong.js.map +1 -1
  252. package/dist/core/scene/pathEffects/textAlongBands.js +65 -0
  253. package/dist/core/scene/pathEffects/textAlongBands.js.map +1 -0
  254. package/dist/core/scene/reconcileSpecs.js +1 -1
  255. package/dist/core/scene/reconcileSpecs.js.map +1 -1
  256. package/dist/core/scene/scene.js +11 -5
  257. package/dist/core/scene/scene.js.map +1 -1
  258. package/dist/core/scene/sceneEdits.js +5 -2
  259. package/dist/core/scene/sceneEdits.js.map +1 -1
  260. package/dist/core/scene/scenePoseAdapter.js +10 -1
  261. package/dist/core/scene/scenePoseAdapter.js.map +1 -1
  262. package/dist/core/scene/scrollOffsets.js +93 -0
  263. package/dist/core/scene/scrollOffsets.js.map +1 -0
  264. package/dist/core/scene/serialization.js +15 -5
  265. package/dist/core/scene/serialization.js.map +1 -1
  266. package/dist/core/scene/statedFields.js +2 -1
  267. package/dist/core/scene/statedFields.js.map +1 -1
  268. package/dist/core/scene/strategyView.js +35 -0
  269. package/dist/core/scene/strategyView.js.map +1 -0
  270. package/dist/core/scene/types.js.map +1 -1
  271. package/dist/core/scene/useScene.js +2 -0
  272. package/dist/core/scene/useScene.js.map +1 -1
  273. package/dist/core/scene/worldSave.js +87 -0
  274. package/dist/core/scene/worldSave.js.map +1 -0
  275. package/dist/core/selection/selectionTarget.js +1 -1
  276. package/dist/core/selection/selectionTarget.js.map +1 -1
  277. package/dist/{cornerHandles-CNCb7aTY.d.ts → cornerHandles-DpmQM_z5.d.ts} +1 -1
  278. package/dist/{depSchema-CpIQzKWQ.d.ts → depSchema-C3BwxitB.d.ts} +119 -99
  279. package/dist/devtools-protocol.d.ts +2 -2
  280. package/dist/devtools.d.ts +2 -2
  281. package/dist/{features → domains}/chrome-caps/buildChromeCtx.js +1 -1
  282. package/dist/domains/chrome-caps/buildChromeCtx.js.map +1 -0
  283. package/dist/{features → domains}/chrome-caps/conditions.js +1 -1
  284. package/dist/domains/chrome-caps/conditions.js.map +1 -0
  285. package/dist/{features → domains}/chrome-caps/defaults.js +1 -1
  286. package/dist/domains/chrome-caps/defaults.js.map +1 -0
  287. package/dist/{features → domains}/chrome-caps/resolve.js +1 -1
  288. package/dist/domains/chrome-caps/resolve.js.map +1 -0
  289. package/dist/{features → domains}/chrome-caps/useChromeCaps.js +1 -1
  290. package/dist/domains/chrome-caps/useChromeCaps.js.map +1 -0
  291. package/dist/{features → domains}/chrome-caps/useHoverTracking.js +1 -1
  292. package/dist/domains/chrome-caps/useHoverTracking.js.map +1 -0
  293. package/dist/{features → domains}/ingestion/contentHandlers.js +20 -3
  294. package/dist/domains/ingestion/contentHandlers.js.map +1 -0
  295. package/dist/domains/ingestion/ghost.js +56 -0
  296. package/dist/domains/ingestion/ghost.js.map +1 -0
  297. package/dist/{features → domains}/ingestion/imageHandler.js +1 -1
  298. package/dist/domains/ingestion/imageHandler.js.map +1 -0
  299. package/dist/domains/ingestion/kitHandlers.js +22 -0
  300. package/dist/domains/ingestion/kitHandlers.js.map +1 -0
  301. package/dist/{features → domains}/ingestion/openFilePicker.js +1 -1
  302. package/dist/domains/ingestion/openFilePicker.js.map +1 -0
  303. package/dist/{features → domains}/ingestion/placement.js +1 -1
  304. package/dist/domains/ingestion/placement.js.map +1 -0
  305. package/dist/{features → domains}/ingestion/svgHandler.js +1 -1
  306. package/dist/domains/ingestion/svgHandler.js.map +1 -0
  307. package/dist/{features → domains}/ingestion/textHandler.js +9 -7
  308. package/dist/domains/ingestion/textHandler.js.map +1 -0
  309. package/dist/{features → domains}/ingestion/useCanvasIngestion.js +13 -15
  310. package/dist/domains/ingestion/useCanvasIngestion.js.map +1 -0
  311. package/dist/{features → domains}/ingestion/weaselJsonHandler.js +1 -1
  312. package/dist/domains/ingestion/weaselJsonHandler.js.map +1 -0
  313. package/dist/{features → domains}/longPress/longPressFeedback.js +1 -1
  314. package/dist/domains/longPress/longPressFeedback.js.map +1 -0
  315. package/dist/{features → domains}/measure/actionIds.js +1 -1
  316. package/dist/domains/measure/actionIds.js.map +1 -0
  317. package/dist/{features → domains}/measure/measureCommands.js +1 -1
  318. package/dist/domains/measure/measureCommands.js.map +1 -0
  319. package/dist/{features → domains}/measure/measureContribution.js +1 -1
  320. package/dist/domains/measure/measureContribution.js.map +1 -0
  321. package/dist/{features → domains}/measure/measureEdits.js +1 -1
  322. package/dist/domains/measure/measureEdits.js.map +1 -0
  323. package/dist/{features → domains}/measure/measureGestures.js +4 -7
  324. package/dist/domains/measure/measureGestures.js.map +1 -0
  325. package/dist/{features → domains}/measure/measureHit.js +1 -1
  326. package/dist/domains/measure/measureHit.js.map +1 -0
  327. package/dist/{features → domains}/measure/measureLayer.js +1 -1
  328. package/dist/domains/measure/measureLayer.js.map +1 -0
  329. package/dist/{features → domains}/measure/measurement.js +1 -1
  330. package/dist/domains/measure/measurement.js.map +1 -0
  331. package/dist/{features → domains}/measure/measurements.js +2 -2
  332. package/dist/domains/measure/measurements.js.map +1 -0
  333. package/dist/{features → domains}/modes/modeLayers.js +1 -1
  334. package/dist/domains/modes/modeLayers.js.map +1 -0
  335. package/dist/{features → domains}/modes/modeTriggers.js +3 -6
  336. package/dist/domains/modes/modeTriggers.js.map +1 -0
  337. package/dist/{features → domains}/pathEffects/effectSources.js +1 -1
  338. package/dist/domains/pathEffects/effectSources.js.map +1 -0
  339. package/dist/{features → domains}/pathEffects/index.js +1 -1
  340. package/dist/domains/pathEffects/index.js.map +1 -0
  341. package/dist/{features → domains}/pathEffects/readSource.js +1 -1
  342. package/dist/domains/pathEffects/readSource.js.map +1 -0
  343. package/dist/{features → domains}/pathEffects/textItems.js +11 -2
  344. package/dist/domains/pathEffects/textItems.js.map +1 -0
  345. package/dist/{features → domains}/pathEffects/useEffectSourcesLayer.js +1 -1
  346. package/dist/domains/pathEffects/useEffectSourcesLayer.js.map +1 -0
  347. package/dist/{features → domains}/paths/anchorData.js +1 -1
  348. package/dist/domains/paths/anchorData.js.map +1 -0
  349. package/dist/{features → domains}/paths/anchorMarkers.js +9 -7
  350. package/dist/domains/paths/anchorMarkers.js.map +1 -0
  351. package/dist/{features → domains}/paths/editablePath.js +1 -1
  352. package/dist/domains/paths/editablePath.js.map +1 -0
  353. package/dist/{features → domains}/paths/index.js +3 -3
  354. package/dist/{features → domains}/paths/originProjection.js +1 -1
  355. package/dist/domains/paths/originProjection.js.map +1 -0
  356. package/dist/{features → domains}/paths/pathEditExtension.js +1 -1
  357. package/dist/domains/paths/pathEditExtension.js.map +1 -0
  358. package/dist/{features → domains}/paths/pathEditState.js +1 -1
  359. package/dist/domains/paths/pathEditState.js.map +1 -0
  360. package/dist/{features → domains}/paths/pathEditingOverlayLayer.js +1 -1
  361. package/dist/domains/paths/pathEditingOverlayLayer.js.map +1 -0
  362. package/dist/{features → domains}/paths/pathLayer.js +1 -1
  363. package/dist/domains/paths/pathLayer.js.map +1 -0
  364. package/dist/{features → domains}/paths/pathRepresentation.js +1 -1
  365. package/dist/domains/paths/pathRepresentation.js.map +1 -0
  366. package/dist/{features → domains}/paths/pathSnap/glyphs.js +1 -1
  367. package/dist/domains/paths/pathSnap/glyphs.js.map +1 -0
  368. package/dist/{features → domains}/paths/pathSnap/pathSnapSource.js +1 -1
  369. package/dist/domains/paths/pathSnap/pathSnapSource.js.map +1 -0
  370. package/dist/{features → domains}/paths/pathSnap/sceneIndex.js +1 -1
  371. package/dist/domains/paths/pathSnap/sceneIndex.js.map +1 -0
  372. package/dist/{features → domains}/paths/pathSnap/snapGeometry.js +3 -2
  373. package/dist/domains/paths/pathSnap/snapGeometry.js.map +1 -0
  374. package/dist/{features → domains}/paths/penPreviewLayer.js +1 -1
  375. package/dist/domains/paths/penPreviewLayer.js.map +1 -0
  376. package/dist/{features → domains}/paths/shapeGenerator.js +6 -4
  377. package/dist/domains/paths/shapeGenerator.js.map +1 -0
  378. package/dist/{features → domains}/paths/strokeOutline.js +1 -1
  379. package/dist/domains/paths/strokeOutline.js.map +1 -0
  380. package/dist/{features → domains}/paths/useAnchorFields.js +1 -1
  381. package/dist/domains/paths/useAnchorFields.js.map +1 -0
  382. package/dist/{features → domains}/paths/usePathEdit.js +1 -1
  383. package/dist/domains/paths/usePathEdit.js.map +1 -0
  384. package/dist/{features → domains}/poseRun/usePoseRun.js +1 -1
  385. package/dist/domains/poseRun/usePoseRun.js.map +1 -0
  386. package/dist/{features → domains}/selection/enteredGroupFrame.js +49 -10
  387. package/dist/domains/selection/enteredGroupFrame.js.map +1 -0
  388. package/dist/{features → domains}/selection/enteredGroupScope.js +1 -1
  389. package/dist/domains/selection/enteredGroupScope.js.map +1 -0
  390. package/dist/domains/selection/enteredOperands.js +29 -0
  391. package/dist/domains/selection/enteredOperands.js.map +1 -0
  392. package/dist/{features → domains}/selection/handleCommands.js +1 -1
  393. package/dist/domains/selection/handleCommands.js.map +1 -0
  394. package/dist/{features → domains}/selection/overlay.js +1 -1
  395. package/dist/domains/selection/overlay.js.map +1 -0
  396. package/dist/{features → domains}/selection/selectionPose.js +1 -1
  397. package/dist/domains/selection/selectionPose.js.map +1 -0
  398. package/dist/{features → domains}/snap/angle2d.js +1 -1
  399. package/dist/domains/snap/angle2d.js.map +1 -0
  400. package/dist/{features → domains}/snap/debugCandidates.js +1 -1
  401. package/dist/domains/snap/debugCandidates.js.map +1 -0
  402. package/dist/{features → domains}/snap/gather.js +1 -1
  403. package/dist/domains/snap/gather.js.map +1 -0
  404. package/dist/{features → domains}/snap/inputSubspace.js +1 -1
  405. package/dist/domains/snap/inputSubspace.js.map +1 -0
  406. package/dist/{features → domains}/snap/lazySession.js +1 -1
  407. package/dist/domains/snap/lazySession.js.map +1 -0
  408. package/dist/{features → domains}/snap/marks.js +1 -1
  409. package/dist/domains/snap/marks.js.map +1 -0
  410. package/dist/{features → domains}/snap/outcomeStore.js +1 -1
  411. package/dist/domains/snap/outcomeStore.js.map +1 -0
  412. package/dist/{features → domains}/snap/pointSnap.js +1 -1
  413. package/dist/domains/snap/pointSnap.js.map +1 -0
  414. package/dist/{features → domains}/snap/probes2d.js +1 -1
  415. package/dist/domains/snap/probes2d.js.map +1 -0
  416. package/dist/{features → domains}/snap/readoutDraw.js +1 -1
  417. package/dist/domains/snap/readoutDraw.js.map +1 -0
  418. package/dist/{features → domains}/snap/readoutLayer.js +1 -1
  419. package/dist/domains/snap/readoutLayer.js.map +1 -0
  420. package/dist/{features → domains}/snap/registry.js +1 -1
  421. package/dist/domains/snap/registry.js.map +1 -0
  422. package/dist/{features → domains}/snap/resizeBox.js +1 -1
  423. package/dist/domains/snap/resizeBox.js.map +1 -0
  424. package/dist/{features → domains}/snap/roles.js +1 -1
  425. package/dist/domains/snap/roles.js.map +1 -0
  426. package/dist/{features → domains}/snap/rotationSource.js +1 -1
  427. package/dist/domains/snap/rotationSource.js.map +1 -0
  428. package/dist/{features → domains}/snap/sceneReads.js +24 -3
  429. package/dist/domains/snap/sceneReads.js.map +1 -0
  430. package/dist/{features → domains}/snap/sceneSession2d.js +3 -2
  431. package/dist/domains/snap/sceneSession2d.js.map +1 -0
  432. package/dist/{features → domains}/snap/screen.js +1 -1
  433. package/dist/domains/snap/screen.js.map +1 -0
  434. package/dist/{features → domains}/snap/session2d.js +3 -2
  435. package/dist/domains/snap/session2d.js.map +1 -0
  436. package/dist/{features → domains}/snap/spanSnap.js +1 -1
  437. package/dist/domains/snap/spanSnap.js.map +1 -0
  438. package/dist/{features → domains}/snap/stepAngles.js +1 -1
  439. package/dist/domains/snap/stepAngles.js.map +1 -0
  440. package/dist/{features → domains}/text/editingDrawOne.js +4 -4
  441. package/dist/domains/text/editingDrawOne.js.map +1 -0
  442. package/dist/domains/text/hostTextEditors.js +24 -0
  443. package/dist/domains/text/hostTextEditors.js.map +1 -0
  444. package/dist/{features → domains}/text/index.js +1 -1
  445. package/dist/{features → domains}/text/sceneTextDrafts.js +1 -1
  446. package/dist/domains/text/sceneTextDrafts.js.map +1 -0
  447. package/dist/{features → domains}/text/textHitAt.js +18 -2
  448. package/dist/domains/text/textHitAt.js.map +1 -0
  449. package/dist/{features → domains}/text/textLayer.js +1 -1
  450. package/dist/domains/text/textLayer.js.map +1 -0
  451. package/dist/{features → domains}/text/useSceneTextEdit.js +11 -3
  452. package/dist/domains/text/useSceneTextEdit.js.map +1 -0
  453. package/dist/{features → domains}/viewports/aim.js +1 -1
  454. package/dist/domains/viewports/aim.js.map +1 -0
  455. package/dist/{features → domains}/viewports/viewMarker.js +23 -5
  456. package/dist/domains/viewports/viewMarker.js.map +1 -0
  457. package/dist/{features → domains}/viewports/viewMarkerDrag.js +3 -3
  458. package/dist/domains/viewports/viewMarkerDrag.js.map +1 -0
  459. package/dist/{features → domains}/viewports/viewResolver.js +1 -1
  460. package/dist/domains/viewports/viewResolver.js.map +1 -0
  461. package/dist/{features → domains}/viewports/viewportLayer.js +1 -1
  462. package/dist/domains/viewports/viewportLayer.js.map +1 -0
  463. package/dist/icons.table.js +1 -1
  464. package/dist/icons.table.js.map +1 -1
  465. package/dist/icons.table.json +106 -6
  466. package/dist/index.d.ts +1441 -1050
  467. package/dist/index.js +90 -77
  468. package/dist/interactions/actions/admitsChild.js +6 -2
  469. package/dist/interactions/actions/admitsChild.js.map +1 -1
  470. package/dist/interactions/actions/align/align.js +1 -4
  471. package/dist/interactions/actions/align/align.js.map +1 -1
  472. package/dist/interactions/actions/booleans/booleanGroupAction.js +66 -0
  473. package/dist/interactions/actions/booleans/booleanGroupAction.js.map +1 -0
  474. package/dist/interactions/actions/booleans/keymap.js +52 -0
  475. package/dist/interactions/actions/booleans/keymap.js.map +1 -0
  476. package/dist/interactions/actions/booleans/operands.js +1 -1
  477. package/dist/interactions/actions/booleans/pieces.js +4 -11
  478. package/dist/interactions/actions/booleans/pieces.js.map +1 -1
  479. package/dist/interactions/actions/clipboard/action.js +3 -7
  480. package/dist/interactions/actions/clipboard/action.js.map +1 -1
  481. package/dist/interactions/actions/clipboard/clipboardOps.js +1 -1
  482. package/dist/interactions/actions/clipboard/clipboardOps.js.map +1 -1
  483. package/dist/interactions/actions/clipboard/materializePaste.js.map +1 -1
  484. package/dist/interactions/actions/defaults/anchorEditing.js +6 -6
  485. package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
  486. package/dist/interactions/actions/defaults/bendSegment.js +1 -1
  487. package/dist/interactions/actions/defaults/bendSegment.js.map +1 -1
  488. package/dist/interactions/actions/defaults/containerScroll.js +170 -0
  489. package/dist/interactions/actions/defaults/containerScroll.js.map +1 -0
  490. package/dist/interactions/actions/defaults/contextMenu.js +2 -5
  491. package/dist/interactions/actions/defaults/contextMenu.js.map +1 -1
  492. package/dist/interactions/actions/defaults/delete.js +1 -1
  493. package/dist/interactions/actions/defaults/delete.js.map +1 -1
  494. package/dist/interactions/actions/defaults/duplicate.js +1 -3
  495. package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
  496. package/dist/interactions/actions/defaults/enterGroup.js +7 -12
  497. package/dist/interactions/actions/defaults/enterGroup.js.map +1 -1
  498. package/dist/interactions/actions/defaults/enterPathEdit.js +4 -8
  499. package/dist/interactions/actions/defaults/enterPathEdit.js.map +1 -1
  500. package/dist/interactions/actions/defaults/group.js +12 -24
  501. package/dist/interactions/actions/defaults/group.js.map +1 -1
  502. package/dist/interactions/actions/defaults/ingest.js +18 -10
  503. package/dist/interactions/actions/defaults/ingest.js.map +1 -1
  504. package/dist/interactions/actions/defaults/insertPathAnchor.js +3 -3
  505. package/dist/interactions/actions/defaults/insertPathAnchor.js.map +1 -1
  506. package/dist/interactions/actions/defaults/nudgeSteps.js +3 -21
  507. package/dist/interactions/actions/defaults/nudgeSteps.js.map +1 -1
  508. package/dist/interactions/actions/defaults/pathEditMarks.js.map +1 -1
  509. package/dist/interactions/actions/defaults/selectDeep.js +2 -2
  510. package/dist/interactions/actions/defaults/selectDeep.js.map +1 -1
  511. package/dist/interactions/actions/distribute/distribute.js.map +1 -1
  512. package/dist/interactions/actions/dropVerdict.js +9 -0
  513. package/dist/interactions/actions/dropVerdict.js.map +1 -0
  514. package/dist/interactions/actions/edit-anchors/action.js +2 -2
  515. package/dist/interactions/actions/edit-anchors/action.js.map +1 -1
  516. package/dist/interactions/actions/edit-anchors/extensionDrags.js.map +1 -1
  517. package/dist/interactions/actions/edit-anchors/geometry.js +8 -6
  518. package/dist/interactions/actions/edit-anchors/geometry.js.map +1 -1
  519. package/dist/interactions/actions/edit-anchors/handleDrag.js.map +1 -1
  520. package/dist/interactions/actions/edit-anchors/preview.js +1 -1
  521. package/dist/interactions/actions/edit-anchors/preview.js.map +1 -1
  522. package/dist/interactions/actions/flip/action.js +3 -2
  523. package/dist/interactions/actions/flip/action.js.map +1 -1
  524. package/dist/interactions/actions/geometryProjection.js +8 -3
  525. package/dist/interactions/actions/geometryProjection.js.map +1 -1
  526. package/dist/interactions/actions/gestureFrames.js +1 -0
  527. package/dist/interactions/actions/gestureFrames.js.map +1 -1
  528. package/dist/interactions/actions/insert/action.js +6 -2
  529. package/dist/interactions/actions/insert/action.js.map +1 -1
  530. package/dist/interactions/actions/insert/frame.js +1 -1
  531. package/dist/interactions/actions/insert/frame.js.map +1 -1
  532. package/dist/interactions/actions/insert/placeAction.js +6 -8
  533. package/dist/interactions/actions/insert/placeAction.js.map +1 -1
  534. package/dist/interactions/actions/insert/session.js +2 -2
  535. package/dist/interactions/actions/insert/session.js.map +1 -1
  536. package/dist/interactions/actions/move/action.js +6 -15
  537. package/dist/interactions/actions/move/action.js.map +1 -1
  538. package/dist/interactions/actions/move/behaviors/layoutDrop.js +12 -2
  539. package/dist/interactions/actions/move/behaviors/layoutDrop.js.map +1 -1
  540. package/dist/interactions/actions/move/behaviors/reparentOnDrop.js +29 -10
  541. package/dist/interactions/actions/move/behaviors/reparentOnDrop.js.map +1 -1
  542. package/dist/interactions/actions/move/behaviors/snapToContainer.js +42 -6
  543. package/dist/interactions/actions/move/behaviors/snapToContainer.js.map +1 -1
  544. package/dist/interactions/actions/move/capture.js +4 -8
  545. package/dist/interactions/actions/move/capture.js.map +1 -1
  546. package/dist/interactions/actions/move/commit/index.js.map +1 -1
  547. package/dist/interactions/actions/move/commit/layoutDrop.js +4 -3
  548. package/dist/interactions/actions/move/commit/layoutDrop.js.map +1 -1
  549. package/dist/interactions/actions/move/commit/layoutRelease.js +15 -9
  550. package/dist/interactions/actions/move/commit/layoutRelease.js.map +1 -1
  551. package/dist/interactions/actions/move/commit/reparent.js +19 -13
  552. package/dist/interactions/actions/move/commit/reparent.js.map +1 -1
  553. package/dist/interactions/actions/move/delta.js +51 -5
  554. package/dist/interactions/actions/move/delta.js.map +1 -1
  555. package/dist/interactions/actions/move/frame.js +8 -4
  556. package/dist/interactions/actions/move/frame.js.map +1 -1
  557. package/dist/interactions/actions/move/layout/container.js +19 -9
  558. package/dist/interactions/actions/move/layout/container.js.map +1 -1
  559. package/dist/interactions/actions/move/layout/dragged.js +2 -2
  560. package/dist/interactions/actions/move/layout/dragged.js.map +1 -1
  561. package/dist/interactions/actions/move/layout/dropPass.js +36 -18
  562. package/dist/interactions/actions/move/layout/dropPass.js.map +1 -1
  563. package/dist/interactions/actions/move/layout/exits.js +2 -2
  564. package/dist/interactions/actions/move/layout/exits.js.map +1 -1
  565. package/dist/interactions/actions/move/layout/glide.js +5 -5
  566. package/dist/interactions/actions/move/layout/glide.js.map +1 -1
  567. package/dist/interactions/actions/move/layout/view.js +55 -0
  568. package/dist/interactions/actions/move/layout/view.js.map +1 -0
  569. package/dist/interactions/actions/move/refusal.js +52 -0
  570. package/dist/interactions/actions/move/refusal.js.map +1 -0
  571. package/dist/interactions/actions/move/session.js +9 -8
  572. package/dist/interactions/actions/move/session.js.map +1 -1
  573. package/dist/interactions/actions/move/translate.js +10 -4
  574. package/dist/interactions/actions/move/translate.js.map +1 -1
  575. package/dist/interactions/actions/moveNodes.js +21 -5
  576. package/dist/interactions/actions/moveNodes.js.map +1 -1
  577. package/dist/interactions/actions/pathCommands/pathNodes.js +3 -3
  578. package/dist/interactions/actions/pathCommands/pathNodes.js.map +1 -1
  579. package/dist/interactions/actions/planeInput.js +14 -13
  580. package/dist/interactions/actions/planeInput.js.map +1 -1
  581. package/dist/interactions/actions/poseFrame.js +21 -8
  582. package/dist/interactions/actions/poseFrame.js.map +1 -1
  583. package/dist/interactions/actions/resize/action.js +1 -1
  584. package/dist/interactions/actions/resize/action.js.map +1 -1
  585. package/dist/interactions/actions/resize/aspect.js +1 -1
  586. package/dist/interactions/actions/resize/aspect.js.map +1 -1
  587. package/dist/interactions/actions/resize/commit.js +10 -1
  588. package/dist/interactions/actions/resize/commit.js.map +1 -1
  589. package/dist/interactions/actions/resize/frame.js +3 -2
  590. package/dist/interactions/actions/resize/frame.js.map +1 -1
  591. package/dist/interactions/actions/resize/held.js +44 -0
  592. package/dist/interactions/actions/resize/held.js.map +1 -0
  593. package/dist/interactions/actions/resize/session.js +3 -1
  594. package/dist/interactions/actions/resize/session.js.map +1 -1
  595. package/dist/interactions/actions/resolveOverlays.js +9 -0
  596. package/dist/interactions/actions/resolveOverlays.js.map +1 -1
  597. package/dist/interactions/actions/resolvePreviews.js +23 -7
  598. package/dist/interactions/actions/resolvePreviews.js.map +1 -1
  599. package/dist/interactions/actions/rotate/action.js +1 -1
  600. package/dist/interactions/actions/rotate/action.js.map +1 -1
  601. package/dist/interactions/actions/rotate/frame.js +1 -1
  602. package/dist/interactions/actions/rotate/frame.js.map +1 -1
  603. package/dist/interactions/actions/rotate/session.js +2 -2
  604. package/dist/interactions/actions/rotate/session.js.map +1 -1
  605. package/dist/interactions/actions/selectionSubtree.js +2 -4
  606. package/dist/interactions/actions/selectionSubtree.js.map +1 -1
  607. package/dist/interactions/actions/spatialReference.js +4 -9
  608. package/dist/interactions/actions/spatialReference.js.map +1 -1
  609. package/dist/interactions/gestures/dragRadial/dragRadial.js.map +1 -1
  610. package/dist/interactions/gestures/dragRect/dragRect.js.map +1 -1
  611. package/dist/kitSettingsSchema.js +2 -2
  612. package/dist/kitSettingsSchema.js.map +1 -1
  613. package/dist/{math-C_elShP_.d.ts → math-B_unOnF4.d.ts} +2 -2
  614. package/dist/math.d.ts +3 -3
  615. package/dist/move.d.ts +6 -6
  616. package/dist/{options-ClEm3SEl.d.ts → options-DAjkhbcs.d.ts} +5 -5
  617. package/dist/{poseDescriptor-DN2yfnq_.d.ts → poseDescriptor-CvSxVhCd.d.ts} +17 -4
  618. package/dist/resize.d.ts +6 -6
  619. package/dist/style.css +14 -0
  620. package/dist/test-seams.d.ts +11 -15
  621. package/dist/test-seams.js +1 -1
  622. package/dist/tools/builtin/eyedropper/useEyedropperTool.js +3 -6
  623. package/dist/tools/builtin/eyedropper/useEyedropperTool.js.map +1 -1
  624. package/dist/tools/builtin/line/useLineTool.js.map +1 -1
  625. package/dist/tools/builtin/measure/useMeasureTool.js +2 -2
  626. package/dist/tools/builtin/measure/useMeasureTool.js.map +1 -1
  627. package/dist/tools/builtin/pen/penHandle.js +2 -2
  628. package/dist/tools/builtin/pen/penHandle.js.map +1 -1
  629. package/dist/tools/builtin/pen/usePenTool.js +13 -19
  630. package/dist/tools/builtin/pen/usePenTool.js.map +1 -1
  631. package/dist/tools/builtin/polygon/usePolygonTool.js +1 -1
  632. package/dist/tools/builtin/polygon/usePolygonTool.js.map +1 -1
  633. package/dist/tools/builtin/select/groupEntry.js +1 -1
  634. package/dist/tools/builtin/select/groupEntry.js.map +1 -1
  635. package/dist/tools/builtin/select/selectionContributions.js +1 -1
  636. package/dist/tools/builtin/select/selectionContributions.js.map +1 -1
  637. package/dist/tools/builtin/select/useSelectTool.js +7 -7
  638. package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
  639. package/dist/tools/builtin/slice/useSliceTool.js +5 -8
  640. package/dist/tools/builtin/slice/useSliceTool.js.map +1 -1
  641. package/dist/tools/builtin/star/useStarTool.js +1 -1
  642. package/dist/tools/builtin/star/useStarTool.js.map +1 -1
  643. package/dist/tools/index.js +2 -1
  644. package/dist/{types-49uYJN3j.d.ts → types-A_sNJYRd.d.ts} +8 -0
  645. package/dist/{types-BEdRIhNc.d.ts → types-DW14eu9B.d.ts} +131 -36
  646. package/dist/{types-p2alu-R1.d.ts → types-VCtfchp-.d.ts} +4 -6
  647. package/dist/util/color.js +12 -14
  648. package/dist/util/color.js.map +1 -1
  649. package/dist/version.js +1 -1
  650. package/package.json +25 -24
  651. package/dist/canvas/poseCascade.js +0 -65
  652. package/dist/canvas/poseCascade.js.map +0 -1
  653. package/dist/features/chrome-caps/buildChromeCtx.js.map +0 -1
  654. package/dist/features/chrome-caps/conditions.js.map +0 -1
  655. package/dist/features/chrome-caps/defaults.js.map +0 -1
  656. package/dist/features/chrome-caps/resolve.js.map +0 -1
  657. package/dist/features/chrome-caps/useChromeCaps.js.map +0 -1
  658. package/dist/features/chrome-caps/useHoverTracking.js.map +0 -1
  659. package/dist/features/ingestion/contentHandlers.js.map +0 -1
  660. package/dist/features/ingestion/imageHandler.js.map +0 -1
  661. package/dist/features/ingestion/openFilePicker.js.map +0 -1
  662. package/dist/features/ingestion/placement.js.map +0 -1
  663. package/dist/features/ingestion/registerKitHandlers.js +0 -43
  664. package/dist/features/ingestion/registerKitHandlers.js.map +0 -1
  665. package/dist/features/ingestion/svgHandler.js.map +0 -1
  666. package/dist/features/ingestion/textHandler.js.map +0 -1
  667. package/dist/features/ingestion/useCanvasIngestion.js.map +0 -1
  668. package/dist/features/ingestion/weaselJsonHandler.js.map +0 -1
  669. package/dist/features/longPress/longPressFeedback.js.map +0 -1
  670. package/dist/features/measure/actionIds.js.map +0 -1
  671. package/dist/features/measure/measureCommands.js.map +0 -1
  672. package/dist/features/measure/measureContribution.js.map +0 -1
  673. package/dist/features/measure/measureEdits.js.map +0 -1
  674. package/dist/features/measure/measureGestures.js.map +0 -1
  675. package/dist/features/measure/measureHit.js.map +0 -1
  676. package/dist/features/measure/measureLayer.js.map +0 -1
  677. package/dist/features/measure/measurement.js.map +0 -1
  678. package/dist/features/measure/measurements.js.map +0 -1
  679. package/dist/features/modes/modeLayers.js.map +0 -1
  680. package/dist/features/modes/modeTriggers.js.map +0 -1
  681. package/dist/features/pathEffects/effectSources.js.map +0 -1
  682. package/dist/features/pathEffects/index.js.map +0 -1
  683. package/dist/features/pathEffects/readSource.js.map +0 -1
  684. package/dist/features/pathEffects/textItems.js.map +0 -1
  685. package/dist/features/pathEffects/useEffectSourcesLayer.js.map +0 -1
  686. package/dist/features/paths/anchorData.js.map +0 -1
  687. package/dist/features/paths/anchorMarkers.js.map +0 -1
  688. package/dist/features/paths/editablePath.js.map +0 -1
  689. package/dist/features/paths/originProjection.js.map +0 -1
  690. package/dist/features/paths/pathEditExtension.js.map +0 -1
  691. package/dist/features/paths/pathEditState.js.map +0 -1
  692. package/dist/features/paths/pathEditingOverlayLayer.js.map +0 -1
  693. package/dist/features/paths/pathLayer.js.map +0 -1
  694. package/dist/features/paths/pathRepresentation.js.map +0 -1
  695. package/dist/features/paths/pathSnap/glyphs.js.map +0 -1
  696. package/dist/features/paths/pathSnap/pathSnapSource.js.map +0 -1
  697. package/dist/features/paths/pathSnap/sceneIndex.js.map +0 -1
  698. package/dist/features/paths/pathSnap/snapGeometry.js.map +0 -1
  699. package/dist/features/paths/penPreviewLayer.js.map +0 -1
  700. package/dist/features/paths/shapeGenerator.js.map +0 -1
  701. package/dist/features/paths/strokeOutline.js.map +0 -1
  702. package/dist/features/paths/useAnchorFields.js.map +0 -1
  703. package/dist/features/paths/usePathEdit.js.map +0 -1
  704. package/dist/features/poseRun/usePoseRun.js.map +0 -1
  705. package/dist/features/selection/enteredGroupFrame.js.map +0 -1
  706. package/dist/features/selection/enteredGroupScope.js.map +0 -1
  707. package/dist/features/selection/handleCommands.js.map +0 -1
  708. package/dist/features/selection/overlay.js.map +0 -1
  709. package/dist/features/selection/selectionPose.js.map +0 -1
  710. package/dist/features/snap/angle2d.js.map +0 -1
  711. package/dist/features/snap/debugCandidates.js.map +0 -1
  712. package/dist/features/snap/gather.js.map +0 -1
  713. package/dist/features/snap/inputSubspace.js.map +0 -1
  714. package/dist/features/snap/lazySession.js.map +0 -1
  715. package/dist/features/snap/marks.js.map +0 -1
  716. package/dist/features/snap/outcomeStore.js.map +0 -1
  717. package/dist/features/snap/pointSnap.js.map +0 -1
  718. package/dist/features/snap/probes2d.js.map +0 -1
  719. package/dist/features/snap/readoutDraw.js.map +0 -1
  720. package/dist/features/snap/readoutLayer.js.map +0 -1
  721. package/dist/features/snap/registry.js.map +0 -1
  722. package/dist/features/snap/resizeBox.js.map +0 -1
  723. package/dist/features/snap/roles.js.map +0 -1
  724. package/dist/features/snap/rotationSource.js.map +0 -1
  725. package/dist/features/snap/sceneReads.js.map +0 -1
  726. package/dist/features/snap/sceneSession2d.js.map +0 -1
  727. package/dist/features/snap/screen.js.map +0 -1
  728. package/dist/features/snap/session2d.js.map +0 -1
  729. package/dist/features/snap/spanSnap.js.map +0 -1
  730. package/dist/features/snap/stepAngles.js.map +0 -1
  731. package/dist/features/text/editingDrawOne.js.map +0 -1
  732. package/dist/features/text/sceneTextDrafts.js.map +0 -1
  733. package/dist/features/text/textHitAt.js.map +0 -1
  734. package/dist/features/text/textLayer.js.map +0 -1
  735. package/dist/features/text/useSceneTextEdit.js.map +0 -1
  736. package/dist/features/viewports/aim.js.map +0 -1
  737. package/dist/features/viewports/viewMarker.js.map +0 -1
  738. package/dist/features/viewports/viewMarkerDrag.js.map +0 -1
  739. package/dist/features/viewports/viewResolver.js.map +0 -1
  740. package/dist/features/viewports/viewportLayer.js.map +0 -1
  741. /package/dist/{features → domains}/chrome-caps/index.js +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session2d.js","names":[],"sources":["../../../src/domains/snap/session2d.ts"],"sourcesContent":["import { closestOnFlat, resolveSnap, type SnapCandidate } from '@weasel-js/geom/nd';\nimport type { View, Point } from '@weasel-js/geom';\nimport type { DebugSink } from '@weasel-js/routing';\nimport type { Scene } from 'core/scene/types';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport type { PlaneMap } from 'core/viewport/parallax';\nimport type { SnapBegin, SnapOutcome, SnapSession } from 'interactions/actions/snapDep';\nimport { angleAt } from './angle2d';\nimport { inputFlat } from './inputSubspace';\nimport { gatherSnapCandidates, resolvedSnapMarks, snapSourcesFor, type SnapCandidateMark } from './gather';\nimport { recordSnapCandidates } from './debugCandidates';\nimport { markFromPlane } from './marks';\nimport { createSnapOutcomeStore, type SnapOutcomeStore } from './outcomeStore';\nimport { resizedBox, resizeEdges, resizeSubject, type ResizeSubject } from './resizeBox';\nimport { screenOf } from './screen';\nimport type { SnapCollect, SnapFrame, SnapMark, SnapProbeAt, SnapSource } from './types';\n\n/** The canvas's reach when neither a source nor the kit settings name one. */\nexport const DEFAULT_SNAP_PX = 8;\n\n/** What a 2D session runs on, captured at press. */\nexport interface Session2dInputs {\n req: SnapBegin;\n /** In source order: earlier wins a full tie. */\n sources: readonly SnapSource[];\n /** At press. */\n probes: readonly SnapProbeAt[];\n /** A move's subject: its drawn box at press. */\n box?: Bounds | null;\n /** A resize's box and frame. */\n resize?: ResizeSubject | null;\n /** `id`'s drawn box now, for sources measuring the scene the way the\n * subject's box was. */\n drawnBox?: (id: string) => Bounds | undefined;\n view(): View | null;\n /** The reach of a source that sets none, in screen px. */\n reachPx: number;\n scene?: Scene<unknown, string, unknown>;\n /** `id`'s world rotation, for angle targets; undefined for a node that\n * cannot turn. */\n rotationOf?: (id: string) => number | undefined;\n /** `id`'s box frame, for sources that work in a container's axes. */\n frameOf?: (id: string) => SnapFrame | undefined;\n store: SnapOutcomeStore;\n /** Nodes that offer no targets. Default `req.ids`. */\n exclude?: readonly string[];\n /** Where each frame's candidates are recorded, cleared at `end`. */\n debug?: DebugSink;\n}\n\n/** A snap session over a 2D canvas: ctx points are world points. */\nexport function createSession2d(inputs: Session2dInputs): SnapSession {\n const { req, store } = inputs;\n const sources = snapSourcesFor(inputs.sources, req.gesture);\n const fixed = collectAtPress(inputs);\n return {\n at(p, mods) {\n const delta0 = { x: p.x - req.press.x, y: p.y - req.press.y };\n const input = inputFlat(req, mods, delta0, inputs.resize);\n if (mods.suspend) {\n store.set([]);\n inputs.debug?.clearSnap();\n return outcome(req.press, closestOnFlat(input, [delta0.x, delta0.y]), false, []);\n }\n const view = inputs.view();\n const plane = req.plane?.() ?? null;\n const subject = subjectAt(inputs, delta0);\n const candidates = collectAll(sources, inputs.probes, { ...fixed, ...subject }, { delta0, view, plane }, inputs.reachPx);\n const res = resolveSnap({\n probes: inputs.probes.map((q) => ({ id: q.id, at: [q.at.x, q.at.y], role: q.role })),\n input,\n delta0: [delta0.x, delta0.y],\n candidates,\n toScreen: screenOf(view, plane),\n });\n if (inputs.debug) recordSnapCandidates(inputs.debug, candidates, res.satisfied, inputs.probes, delta0, plane);\n const delta = { x: res.delta[0], y: res.delta[1] };\n const marks = resolvedSnapMarks(res.satisfied, delta);\n store.set(plane ? marks.map((m) => markFromPlane(m, plane)) : marks);\n return outcome(req.press, res.delta, res.members.length > 0, marks);\n },\n angle: (theta, mods) => angleAt(inputs, sources, theta, mods),\n end() {\n store.set([]);\n inputs.debug?.clearSnap();\n },\n };\n}\n\nfunction outcome(press: Point, delta: readonly number[], held: boolean, marks: readonly SnapMark[]): SnapOutcome {\n return { point: { x: press.x + delta[0], y: press.y + delta[1] }, held, marks };\n}\n\n/** What a source is told that holds from press to release. */\ntype FixedCollect = Pick<SnapCollect, 'gesture' | 'ids' | 'parent' | 'exclude' | 'drawnBox' | 'origin' | 'also' | 'scene' | 'frameOf'>;\n\n/** What changes from one frame to the next. */\ninterface Frame {\n delta0: Point;\n view: View | null;\n plane: PlaneMap | null;\n}\n\nfunction collectAtPress(inputs: Session2dInputs): FixedCollect {\n const { req } = inputs;\n return {\n gesture: req.gesture,\n ...(req.ids ? { ids: req.ids } : {}),\n ...(req.parent !== undefined ? { parent: req.parent } : {}),\n exclude: inputs.exclude ?? req.ids ?? [],\n ...(inputs.drawnBox ? { drawnBox: inputs.drawnBox } : {}),\n ...(req.origin ? { origin: req.origin } : {}),\n ...(req.also ? { also: req.also } : {}),\n ...(inputs.scene ? { scene: inputs.scene } : {}),\n ...(inputs.frameOf ? { frameOf: inputs.frameOf } : {}),\n };\n}\n\nfunction collectAll(\n sources: readonly SnapSource[], probesAtPress: readonly SnapProbeAt[],\n fixed: FixedCollect & Pick<SnapCollect, 'box' | 'edges'>, { delta0, view, plane }: Frame, reachPx: number,\n): SnapCandidate<SnapCandidateMark<Point>>[] {\n const camera = view ? Math.min(Math.abs(view.scale.x), Math.abs(view.scale.y)) : 1;\n // A camera unit is `plane.scale` plane units.\n const scale = plane ? camera / Math.max(Math.abs(plane.scale.x), Math.abs(plane.scale.y)) : camera;\n const probes = probesAtPress.map((q) => ({ ...q, at: { x: q.at.x + delta0.x, y: q.at.y + delta0.y } }));\n return gatherSnapCandidates(sources, probes, (_source, offered, sourceReach) => ({\n ...fixed, probes: offered, delta0,\n ...(plane ? { plane } : {}),\n reachWorld: (px) => px / scale,\n reachPx: sourceReach,\n }), { toVec: (p) => [p.x, p.y], reachPx, ...(fixed.origin ? { origin: fixed.origin } : {}) });\n}\n\n/** The subject's box where `delta0` puts it, and the sides that moved. */\nfunction subjectAt(\n { box, resize }: Pick<Session2dInputs, 'box' | 'resize'>, delta0: Point,\n): Pick<SnapCollect, 'box' | 'edges'> {\n if (resize) return { box: resizedBox(resize, delta0), edges: resizeEdges(resize, delta0) };\n return box ? { box: { ...box, x: box.x + delta0.x, y: box.y + delta0.y } } : {};\n}\n\n/** A session no source answers: it only holds the point to the gesture's\n * input subspace, as when a host provides no `snap` dep. */\nexport function inputSession(req: SnapBegin): SnapSession {\n return createSession2d({\n req,\n sources: [],\n probes: [],\n resize: req.gesture === 'resize' && req.handle && req.box ? resizeSubject(req.box, req.handle) : null,\n view: () => null,\n reachPx: 0,\n store: createSnapOutcomeStore(),\n });\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { inputSession } from "./session2d.js";
2
2
 
3
- //#region src/features/snap/spanSnap.ts
3
+ //#region src/domains/snap/spanSnap.ts
4
4
  /**
5
5
  * A `point` session for each end of a span. The end's session has the
6
6
  * snapped start as its `origin`, so a `constrain` step turns about the start
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spanSnap.js","names":[],"sources":["../../../src/domains/snap/spanSnap.ts"],"sourcesContent":["import type { SnapBegin, SnapConstrain, SnapDep, SnapMods, SnapOutcome, SnapSession } from 'interactions/actions/snapDep';\nimport { inputSession } from './session2d';\nimport type { Point } from '@weasel-js/geom';\n\n/** A two-point gesture's snapping: a drawn line, a measurement, a box. */\nexport interface SpanSnap {\n /** Snap the start, pressed at `p`. The end then snaps about where it lands. */\n start(p: Point, mods: SnapMods): SnapOutcome;\n /** Move the start by `(dx, dy)` from where it was pressed or last carried\n * to, unsnapped, and snap it there. */\n carry(dx: number, dy: number, mods: SnapMods): SnapOutcome;\n /** Snap the end. */\n end(p: Point, mods: SnapMods): SnapOutcome;\n /** Turn the end about `p`, a start placed by something other than this\n * snap. */\n anchor(p: Point): void;\n /** Whether a target holds the start. */\n startHeld(): boolean;\n /** Clears the gesture's marks. */\n close(): void;\n}\n\n/**\n * A `point` session for each end of a span. The end's session has the\n * snapped start as its `origin`, so a `constrain` step turns about the start\n * as drawn. `constrain` is read whenever the end snaps, and a new value\n * reopens the end's session. With no `snap` dep, or one that runs no\n * sessions, only `constrain` applies. `common` goes into every session's\n * request.\n */\nexport function spanSnap(\n dep: SnapDep | undefined,\n constrain: () => SnapConstrain | undefined = () => undefined,\n common: Pick<SnapBegin, 'parent'> = {},\n): SpanSnap {\n const begin = (req: SnapBegin): SnapSession => dep?.begin(req) ?? inputSession(req);\n let raw: Point = { x: 0, y: 0 };\n let start: Point = raw;\n let held = false;\n let live: { session: SnapSession; key: string } | null = null;\n\n const open = (): SnapSession => {\n const c = constrain();\n const key = JSON.stringify(c ?? null);\n if (live?.key === key) return live.session;\n live?.session.end();\n live = { session: begin({ ...common, gesture: 'point', press: start, origin: start, ...(c ? { constrain: c } : {}) }), key };\n return live.session;\n };\n const place = (p: Point, mods: SnapMods): SnapOutcome => {\n raw = p;\n live?.session.end();\n live = null;\n const out = begin({ ...common, gesture: 'point', press: p });\n const landed = out.at(p, mods);\n out.end();\n start = landed.point;\n held = landed.held;\n // Draws the start's marks again, from the session the end runs in.\n open().at(start, { ...mods, constrain: false });\n return landed;\n };\n\n return {\n start: place,\n carry: (dx, dy, mods) => place({ x: raw.x + dx, y: raw.y + dy }, mods),\n end: (p, mods) => open().at(p, mods),\n anchor(p) {\n if (p.x === start.x && p.y === start.y) return;\n start = p;\n live?.session.end();\n live = null;\n },\n startHeld: () => held,\n close() {\n live?.session.end();\n live = null;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;AA8BA,SAAgB,SACd,KACA,kBAAmD,QACnD,SAAoC,CAAC,GAC3B;CACV,MAAM,SAAS,QAAgC,KAAK,MAAM,GAAG,KAAK,aAAa,GAAG;CAClF,IAAI,MAAa;EAAE,GAAG;EAAG,GAAG;CAAE;CAC9B,IAAI,QAAe;CACnB,IAAI,OAAO;CACX,IAAI,OAAqD;CAEzD,MAAM,aAA0B;EAC9B,MAAM,IAAI,UAAU;EACpB,MAAM,MAAM,KAAK,UAAU,KAAK,IAAI;EACpC,IAAI,MAAM,QAAQ,KAAK,OAAO,KAAK;EACnC,MAAM,QAAQ,IAAI;EAClB,OAAO;GAAE,SAAS,MAAM;IAAE,GAAG;IAAQ,SAAS;IAAS,OAAO;IAAO,QAAQ;IAAO,GAAI,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC;GAAG,CAAC;GAAG;EAAI;EAC3H,OAAO,KAAK;CACd;CACA,MAAM,SAAS,GAAU,SAAgC;EACvD,MAAM;EACN,MAAM,QAAQ,IAAI;EAClB,OAAO;EACP,MAAM,MAAM,MAAM;GAAE,GAAG;GAAQ,SAAS;GAAS,OAAO;EAAE,CAAC;EAC3D,MAAM,SAAS,IAAI,GAAG,GAAG,IAAI;EAC7B,IAAI,IAAI;EACR,QAAQ,OAAO;EACf,OAAO,OAAO;EAEd,KAAK,CAAC,CAAC,GAAG,OAAO;GAAE,GAAG;GAAM,WAAW;EAAM,CAAC;EAC9C,OAAO;CACT;CAEA,OAAO;EACL,OAAO;EACP,QAAQ,IAAI,IAAI,SAAS,MAAM;GAAE,GAAG,IAAI,IAAI;GAAI,GAAG,IAAI,IAAI;EAAG,GAAG,IAAI;EACrE,MAAM,GAAG,SAAS,KAAK,CAAC,CAAC,GAAG,GAAG,IAAI;EACnC,OAAO,GAAG;GACR,IAAI,EAAE,MAAM,MAAM,KAAK,EAAE,MAAM,MAAM,GAAG;GACxC,QAAQ;GACR,MAAM,QAAQ,IAAI;GAClB,OAAO;EACT;EACA,iBAAiB;EACjB,QAAQ;GACN,MAAM,QAAQ,IAAI;GAClB,OAAO;EACT;CACF;AACF"}
@@ -1,4 +1,4 @@
1
- //#region src/features/snap/stepAngles.ts
1
+ //#region src/domains/snap/stepAngles.ts
2
2
  /** The multiples of `step` either side of `theta`, and the nearest: the
3
3
  * angles an angle step offers a direction at `theta`. Radians. */
4
4
  function stepAngles(theta, step) {
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stepAngles.js","names":[],"sources":["../../../src/domains/snap/stepAngles.ts"],"sourcesContent":["/** The multiples of `step` either side of `theta`, and the nearest: the\n * angles an angle step offers a direction at `theta`. Radians. */\nexport function stepAngles(theta: number, step: number): number[] {\n const k = Math.round(theta / step);\n return [k - 1, k, k + 1].map((i) => i * step);\n}\n"],"mappings":";;;AAEA,SAAgB,WAAW,OAAe,MAAwB;CAChE,MAAM,IAAI,KAAK,MAAM,QAAQ,IAAI;CACjC,OAAO;EAAC,IAAI;EAAG;EAAG,IAAI;CAAC,CAAC,CAAC,KAAK,MAAM,IAAI,IAAI;AAC9C"}
@@ -1,10 +1,10 @@
1
1
  import { defaultDrawOne } from "../../canvas/defaultDrawOne.js";
2
2
 
3
- //#region src/features/text/editingDrawOne.ts
4
- /** `defaultDrawOne`, less the text commands of the node `editingId` names. */
5
- function drawOneEditing(editingId) {
3
+ //#region src/domains/text/editingDrawOne.ts
4
+ /** `base`, `defaultDrawOne` by default, less the text commands of the node `editingId` names. */
5
+ function drawOneEditing(editingId, base = defaultDrawOne) {
6
6
  return (node, pose, view, ctx) => {
7
- const out = defaultDrawOne(node, pose, view, ctx);
7
+ const out = base(node, pose, view, ctx);
8
8
  return node.id === editingId ? withoutText(out) : out;
9
9
  };
10
10
  }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editingDrawOne.js","names":[],"sources":["../../../src/domains/text/editingDrawOne.ts"],"sourcesContent":["import type { Node } from '../../core/scene/types';\nimport type { View } from '../../core/viewport/view';\nimport type { DrawCommand } from '@weasel-js/renderer';\nimport { defaultDrawOne } from '../../canvas/defaultDrawOne';\nimport type { NodePaintCtx } from '../../canvas/NodeShape';\n\n/** A scene `drawOne` that leaves out one node's glyphs. */\nexport type EditingDrawOne = <TNode extends Node<unknown, string, unknown>>(\n node: TNode, pose: unknown, view?: View, ctx?: NodePaintCtx,\n) => DrawCommand[];\n\n/** `base`, `defaultDrawOne` by default, less the text commands of the node `editingId` names. */\nexport function drawOneEditing(editingId: string | null, base: EditingDrawOne = defaultDrawOne): EditingDrawOne {\n return (node, pose, view, ctx) => {\n const out = base(node, pose, view, ctx);\n return node.id === editingId ? withoutText(out) : out;\n };\n}\n\n/** `cmds` with every text command gone, at any depth, and any group left\n * empty by it — a clipped node's glyphs ride in one. */\nfunction withoutText(cmds: readonly DrawCommand[]): DrawCommand[] {\n const out: DrawCommand[] = [];\n for (const c of cmds) {\n if (c.kind === 'text') continue;\n if (c.kind !== 'group') {\n out.push(c);\n continue;\n }\n const children = withoutText(c.children);\n if (children.length > 0) out.push({ ...c, children });\n }\n return out;\n}\n"],"mappings":";;;;AAYA,SAAgB,eAAe,WAA0B,OAAuB,gBAAgC;CAC9G,QAAQ,MAAM,MAAM,MAAM,QAAQ;EAChC,MAAM,MAAM,KAAK,MAAM,MAAM,MAAM,GAAG;EACtC,OAAO,KAAK,OAAO,YAAY,YAAY,GAAG,IAAI;CACpD;AACF;;;AAIA,SAAS,YAAY,MAA6C;CAChE,MAAM,MAAqB,CAAC;CAC5B,KAAK,MAAM,KAAK,MAAM;EACpB,IAAI,EAAE,SAAS,QAAQ;EACvB,IAAI,EAAE,SAAS,SAAS;GACtB,IAAI,KAAK,CAAC;GACV;EACF;EACA,MAAM,WAAW,YAAY,EAAE,QAAQ;EACvC,IAAI,SAAS,SAAS,GAAG,IAAI,KAAK;GAAE,GAAG;GAAG;EAAS,CAAC;CACtD;CACA,OAAO;AACT"}
@@ -0,0 +1,24 @@
1
+ //#region src/domains/text/hostTextEditors.ts
2
+ /**
3
+ * The containers of every text editor a host mounted with `useSceneTextEdit`.
4
+ * `<SceneCanvas>`'s own editor stands down for a canvas inside one, so a host
5
+ * that wires its own never gets a second overlay on the same double-click.
6
+ */
7
+ var containers = /* @__PURE__ */ new Map();
8
+ /** Record `container` as holding a host's editor until the disposer runs. */
9
+ function registerHostTextEditor(container) {
10
+ const key = Symbol("textEditor");
11
+ containers.set(key, container);
12
+ return () => {
13
+ containers.delete(key);
14
+ };
15
+ }
16
+ /** Whether a host's editor holds `el`. */
17
+ function hostTextEditorHolds(el) {
18
+ for (const c of containers.values()) if (c.contains(el)) return true;
19
+ return false;
20
+ }
21
+
22
+ //#endregion
23
+ export { hostTextEditorHolds, registerHostTextEditor };
24
+ //# sourceMappingURL=hostTextEditors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hostTextEditors.js","names":[],"sources":["../../../src/domains/text/hostTextEditors.ts"],"sourcesContent":["/**\n * The containers of every text editor a host mounted with `useSceneTextEdit`.\n * `<SceneCanvas>`'s own editor stands down for a canvas inside one, so a host\n * that wires its own never gets a second overlay on the same double-click.\n */\nconst containers = new Map<symbol, HTMLElement>();\n\n/** Record `container` as holding a host's editor until the disposer runs. */\nexport function registerHostTextEditor(container: HTMLElement): () => void {\n const key = Symbol('textEditor');\n containers.set(key, container);\n return () => {\n containers.delete(key);\n };\n}\n\n/** Whether a host's editor holds `el`. */\nexport function hostTextEditorHolds(el: Element): boolean {\n for (const c of containers.values()) if (c.contains(el)) return true;\n return false;\n}\n"],"mappings":";;;;;;AAKA,IAAM,6BAAa,IAAI,IAAyB;;AAGhD,SAAgB,uBAAuB,WAAoC;CACzE,MAAM,MAAM,OAAO,YAAY;CAC/B,WAAW,IAAI,KAAK,SAAS;CAC7B,aAAa;EACX,WAAW,OAAO,GAAG;CACvB;AACF;;AAGA,SAAgB,oBAAoB,IAAsB;CACxD,KAAK,MAAM,KAAK,WAAW,OAAO,GAAG,IAAI,EAAE,SAAS,EAAE,GAAG,OAAO;CAChE,OAAO;AACT"}
@@ -1,5 +1,5 @@
1
- import { createTextLayer } from "./textLayer.js";
2
1
  import { useSceneTextEdit } from "./useSceneTextEdit.js";
2
+ import { createTextLayer } from "./textLayer.js";
3
3
  import { textCommand as textCommand$1, textCommandFromPose as textCommandFromPose$1, textCommandFromRuns as textCommandFromRuns$1 } from "@weasel-js/renderer";
4
4
  import { TextOutlinesError as TextOutlinesError$1, loadTextOutlines as loadTextOutlines$1, textToPath as textToPath$1, textToPathsByPaint as textToPathsByPaint$1 } from "@weasel-js/text";
5
5
  import { TEXT_EDIT_FONT_HOLD, charOffsetToDomPosition, domPositionToCharOffset, domToRuns, runsToDom, useTextEdit as useTextEdit$1 } from "@weasel-js/text-dom";
@@ -1,6 +1,6 @@
1
1
  import { asNodeId } from "../../core/scene/types.js";
2
2
 
3
- //#region src/features/text/sceneTextDrafts.ts
3
+ //#region src/domains/text/sceneTextDrafts.ts
4
4
  /**
5
5
  * A text edit shown to the scene keystroke by keystroke, for the readers that
6
6
  * see a node somewhere besides the overlay: text something derives from, such
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sceneTextDrafts.js","names":[],"sources":["../../../src/domains/text/sceneTextDrafts.ts"],"sourcesContent":["/**\n * A text edit shown to the scene keystroke by keystroke, for the readers that\n * see a node somewhere besides the overlay: text something derives from, such\n * as the hidden text a path effect lays along a path, or a second view.\n */\nimport { asNodeId, type PoseHold, type Scene } from '../../core/scene/types';\nimport type { StyledRun, TextStyle } from '@weasel-js/text';\nimport type { TextEditDraft } from '@weasel-js/text-dom';\nimport type { DefaultTextData, UseSceneTextEditOptions } from './useSceneTextEdit';\n\n/** `data` with an edit's text, runs and style folded in through the options' setters, for one `scene.update`. */\nexport function editedData<TData extends DefaultTextData>(\n data: TData,\n edit: { text: string; runs?: readonly StyledRun[]; style?: TextStyle },\n { setText, setRuns, setStyle }: UseSceneTextEditOptions<TData>,\n): TData {\n let next = setText ? setText(data, edit.text) : { ...data, text: edit.text };\n if (edit.runs) next = setRuns ? setRuns(next, [...edit.runs]) : { ...next, runs: edit.runs };\n if (edit.style) next = setStyle ? setStyle(next, edit.style) : { ...next, style: edit.style };\n return next;\n}\n\n/** Where an edit's writes go. Each `draft` is shown as a data override folding the draft into whatever the node stores,\n * so a recorded write made meanwhile shows through it and the document holds no draft; the `commit` records the\n * result as one undo entry and then lets the override go; `end` lets it go, so a canceled edit leaves no entry and\n * the redo stack as it was. */\nexport function sceneTextDrafts<TData extends DefaultTextData, TLayer extends string, TPose>(\n sceneRef: { readonly current: Scene<TData, TLayer, TPose> },\n optsRef: { readonly current: UseSceneTextEditOptions<TData> },\n): {\n draft: (id: string, draft: TextEditDraft) => void;\n commit: (id: string, data: TData) => void;\n end: (id: string) => void;\n /** Let any open edit's draft go, as an unmount must: the overlay goes without an `end`. */\n close: () => void;\n} {\n /** The open edit's claim on the scene's overrides, with the scene it was made on. */\n let held: { id: string; scene: Scene<TData, TLayer, TPose>; hold: PoseHold<TPose, TData> } | null = null;\n const release = (id: string): void => {\n if (held?.id !== id) return;\n held.hold.drop();\n held = null;\n };\n return {\n draft: (id, draft) => {\n const scene = sceneRef.current;\n if (scene.get(asNodeId(id)) === undefined) return;\n if (held !== null && (held.id !== id || held.scene !== scene)) release(held.id);\n held ??= { id, scene, hold: scene.overrides.hold() };\n const opts = optsRef.current;\n held.hold.show([[asNodeId(id), { data: (stored) => editedData(stored, draft, opts) }]]);\n },\n commit: (id, data) => {\n const scene = sceneRef.current;\n scene.batch('Edit text', () => scene.update(asNodeId(id), { data }));\n // After the write, so no frame shows the text from before the edit in between.\n release(id);\n },\n end: release,\n close: () => {\n if (held !== null) release(held.id);\n },\n };\n}\n"],"mappings":";;;;;;;;;AAWA,SAAgB,WACd,MACA,MACA,EAAE,SAAS,SAAS,YACb;CACP,IAAI,OAAO,UAAU,QAAQ,MAAM,KAAK,IAAI,IAAI;EAAE,GAAG;EAAM,MAAM,KAAK;CAAK;CAC3E,IAAI,KAAK,MAAM,OAAO,UAAU,QAAQ,MAAM,CAAC,GAAG,KAAK,IAAI,CAAC,IAAI;EAAE,GAAG;EAAM,MAAM,KAAK;CAAK;CAC3F,IAAI,KAAK,OAAO,OAAO,WAAW,SAAS,MAAM,KAAK,KAAK,IAAI;EAAE,GAAG;EAAM,OAAO,KAAK;CAAM;CAC5F,OAAO;AACT;;;;;AAMA,SAAgB,gBACd,UACA,SAOA;;CAEA,IAAI,OAAgG;CACpG,MAAM,WAAW,OAAqB;EACpC,IAAI,MAAM,OAAO,IAAI;EACrB,KAAK,KAAK,KAAK;EACf,OAAO;CACT;CACA,OAAO;EACL,QAAQ,IAAI,UAAU;GACpB,MAAM,QAAQ,SAAS;GACvB,IAAI,MAAM,IAAI,SAAS,EAAE,CAAC,MAAM,QAAW;GAC3C,IAAI,SAAS,SAAS,KAAK,OAAO,MAAM,KAAK,UAAU,QAAQ,QAAQ,KAAK,EAAE;GAC9E,SAAS;IAAE;IAAI;IAAO,MAAM,MAAM,UAAU,KAAK;GAAE;GACnD,MAAM,OAAO,QAAQ;GACrB,KAAK,KAAK,KAAK,CAAC,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,WAAW,WAAW,QAAQ,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC;EACxF;EACA,SAAS,IAAI,SAAS;GACpB,MAAM,QAAQ,SAAS;GACvB,MAAM,MAAM,mBAAmB,MAAM,OAAO,SAAS,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;GAEnE,QAAQ,EAAE;EACZ;EACA,KAAK;EACL,aAAa;GACX,IAAI,SAAS,MAAM,QAAQ,KAAK,EAAE;EACpC;CACF;AACF"}
@@ -1,13 +1,15 @@
1
1
  import { derivedPartsOf } from "../../core/scene/derivedParts.js";
2
2
  import { nodesAsShown, shownLookup } from "../../core/scene/nodeAsShown.js";
3
+ import { AUTO_POSE_DESCRIPTOR } from "../../core/geometry/autoPoseDescriptor.js";
3
4
  import { targetTransformOf, worldToLocal } from "../../core/geometry/targetFrame.js";
4
5
  import { textBoxOf } from "../../canvas/nodeShapes/textPainter.js";
5
6
  import { sceneDerivedPathOf, scenePaintedPoseOf } from "../../canvas/derivedPath.js";
7
+ import { indexedCandidates } from "../../canvas/pickIndex/scenePickIndex.js";
6
8
  import { effectSourceFor } from "../pathEffects/effectSources.js";
7
9
  import { boundsOfPath, pathContainsPoint } from "@weasel-js/geom";
8
10
  import { caretIndexAt, pointInTextPose } from "@weasel-js/text";
9
11
 
10
- //#region src/features/text/textHitAt.ts
12
+ //#region src/domains/text/textHitAt.ts
11
13
  /**
12
14
  * The top-most unlocked text node at world `(cx, cy)`, and the caret there.
13
15
  *
@@ -19,9 +21,13 @@ import { caretIndexAt, pointInTextPose } from "@weasel-js/text";
19
21
  * glyph it lays, counters included, with the caret at the end of the text.
20
22
  * A curved glyph takes a double-click anywhere in its axis-aligned box, wider
21
23
  * than the ink selection picks.
24
+ *
25
+ * The scene's pick index, read through `AUTO_POSE_DESCRIPTOR` because the
26
+ * text box is read as a rect, narrows the walk to the nodes boxed over the
27
+ * point when it is current; the answer is the full walk's either way.
22
28
  */
23
29
  function textHitAt(scene, read, cx, cy) {
24
- const order = nodesAsShown(scene.overrides, scene.renderOrderNodes());
30
+ const order = indexedOrderAt(scene, cx, cy) ?? nodesAsShown(scene.overrides, scene.renderOrderNodes());
25
31
  const shown = shownLookup(scene);
26
32
  const paintedPoseOf = scenePaintedPoseOf(scene);
27
33
  const derivedPathOf = sceneDerivedPathOf(scene);
@@ -66,6 +72,16 @@ function textHitAt(scene, read, cx, cy) {
66
72
  }
67
73
  return null;
68
74
  }
75
+ /** The nodes the pick index boxes over `(cx, cy)`, as shown and in render order, or null when it has no answer. */
76
+ function indexedOrderAt(scene, cx, cy) {
77
+ const broad = { box: {
78
+ x: cx,
79
+ y: cy,
80
+ width: 0,
81
+ height: 0
82
+ } };
83
+ return indexedCandidates(scene, AUTO_POSE_DESCRIPTOR, () => broad);
84
+ }
69
85
  /** Whether `(x, y)` is within the bounds of one of `path`'s parts: a glyph's box, so a stem too thin to land on and the
70
86
  * hole in an o still take the click. */
71
87
  function onAGlyph(path, x, y) {
@@ -0,0 +1 @@
1
+ {"version":3,"file":"textHitAt.js","names":[],"sources":["../../../src/domains/text/textHitAt.ts"],"sourcesContent":["import type { Scene } from '../../core/scene/types';\nimport type { RectPose } from 'core/geometry/unionBounds';\nimport { boundsOfPath, pathContainsPoint, type Path } from '@weasel-js/geom';\nimport { sceneDerivedPathOf, scenePaintedPoseOf } from '../../canvas/derivedPath';\nimport { derivedPartsOf } from '../../core/scene/derivedParts';\nimport { nodesAsShown, shownLookup } from '../../core/scene/nodeAsShown';\nimport { effectSourceFor } from '../pathEffects/effectSources';\nimport { targetTransformOf, worldToLocal } from 'core/geometry/targetFrame';\nimport { caretIndexAt, pointInTextPose, type StyledRun, type TextStyle, type TextVerticalAlign } from '@weasel-js/text';\nimport { textBoxOf } from '../../canvas/nodeShapes/textPainter';\nimport { AUTO_POSE_DESCRIPTOR } from 'core/geometry/autoPoseDescriptor';\nimport { indexedCandidates } from '../../canvas/pickIndex/scenePickIndex';\n\n/** How a node's text fields are read from its `data`. */\nexport interface TextReads<TData> {\n isText(data: TData): boolean;\n text(data: TData): string;\n style(data: TData): TextStyle | undefined;\n runs(data: TData): readonly StyledRun[] | undefined;\n verticalAlign(data: TData): TextVerticalAlign | undefined;\n padding(data: TData): number | undefined;\n}\n\n/**\n * The top-most unlocked text node at world `(cx, cy)`, and the caret there.\n *\n * `pointInTextPose` and `caretIndexAt` work in the node's own box, so the\n * point is taken back through the frame the node is drawn in. The whole box\n * takes the point, padding included; the caret is placed in the inset one.\n *\n * A path effect hiding a text source answers for it within the bounds of any\n * glyph it lays, counters included, with the caret at the end of the text.\n * A curved glyph takes a double-click anywhere in its axis-aligned box, wider\n * than the ink selection picks.\n *\n * The scene's pick index, read through `AUTO_POSE_DESCRIPTOR` because the\n * text box is read as a rect, narrows the walk to the nodes boxed over the\n * point when it is current; the answer is the full walk's either way.\n */\nexport function textHitAt<TData, TPose extends RectPose>(\n scene: Scene<TData, string, TPose>,\n read: TextReads<TData>,\n cx: number,\n cy: number,\n): { id: string; caret: number } | null {\n // renderOrder() is back-to-front, so iterate in reverse and stop on the first hit.\n // Hit as painted: an open edit's draft is what the glyphs and the caret are laid out from.\n const order = indexedOrderAt(scene, cx, cy) ?? nodesAsShown(scene.overrides, scene.renderOrderNodes());\n const shown = shownLookup(scene);\n const paintedPoseOf = scenePaintedPoseOf(scene);\n const derivedPathOf = sceneDerivedPathOf(scene);\n for (let i = order.length - 1; i >= 0; i--) {\n const node = order[i];\n if (scene.isLocked(node.id) || scene.isHidden(node.id)) continue;\n if (!read.isText(node.data)) {\n const source = effectSourceFor(scene, node.id, 'text');\n const text = source === null || scene.isLocked(source) ? undefined : shown.get(source);\n if (text === undefined || !read.isText(text.data)) continue;\n const path = derivedPathOf(node);\n if (path !== null && onAGlyph(path, cx, cy)) return { id: String(source), caret: read.text(text.data).length };\n continue;\n }\n const at = paintedPoseOf(node);\n const p = worldToLocal(targetTransformOf(at), cx, cy);\n if (!p) continue;\n const pose = {\n ...textBoxOf({ padding: read.padding(node.data) }, at),\n text: read.text(node.data),\n runs: read.runs(node.data) as StyledRun[] | undefined,\n style: read.style(node.data),\n verticalAlign: read.verticalAlign(node.data),\n };\n const box = { x: at.x, y: at.y, width: at.width, height: at.height };\n if (!pointInTextPose(p.x, p.y, { ...pose, ...box })) continue;\n return { id: String(node.id), caret: caretIndexAt(p.x, p.y, pose) };\n }\n return null;\n}\n\ntype ShownNode<TData, TPose> = ReturnType<Scene<TData, string, TPose>['renderOrderNodes']>[number];\n\n/** The nodes the pick index boxes over `(cx, cy)`, as shown and in render order, or null when it has no answer. */\nfunction indexedOrderAt<TData, TPose>(\n scene: Scene<TData, string, TPose>,\n cx: number,\n cy: number,\n): readonly ShownNode<TData, TPose>[] | null {\n const broad = { box: { x: cx, y: cy, width: 0, height: 0 } };\n const found = indexedCandidates(scene as never, AUTO_POSE_DESCRIPTOR, () => broad);\n return found as unknown as readonly ShownNode<TData, TPose>[] | null;\n}\n\n/** Whether `(x, y)` is within the bounds of one of `path`'s parts: a glyph's box, so a stem too thin to land on and the\n * hole in an o still take the click. */\nfunction onAGlyph(path: Path, x: number, y: number): boolean {\n const parts = derivedPartsOf(path);\n if (parts === undefined) return pathContainsPoint(path, x, y);\n return parts.some(({ path: part }) => {\n const b = boundsOfPath(part);\n return x >= b.x && x <= b.x + b.width && y >= b.y && y <= b.y + b.height;\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCA,SAAgB,UACd,OACA,MACA,IACA,IACsC;CAGtC,MAAM,QAAQ,eAAe,OAAO,IAAI,EAAE,KAAK,aAAa,MAAM,WAAW,MAAM,iBAAiB,CAAC;CACrG,MAAM,QAAQ,YAAY,KAAK;CAC/B,MAAM,gBAAgB,mBAAmB,KAAK;CAC9C,MAAM,gBAAgB,mBAAmB,KAAK;CAC9C,KAAK,IAAI,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK;EAC1C,MAAM,OAAO,MAAM;EACnB,IAAI,MAAM,SAAS,KAAK,EAAE,KAAK,MAAM,SAAS,KAAK,EAAE,GAAG;EACxD,IAAI,CAAC,KAAK,OAAO,KAAK,IAAI,GAAG;GAC3B,MAAM,SAAS,gBAAgB,OAAO,KAAK,IAAI,MAAM;GACrD,MAAM,OAAO,WAAW,QAAQ,MAAM,SAAS,MAAM,IAAI,SAAY,MAAM,IAAI,MAAM;GACrF,IAAI,SAAS,UAAa,CAAC,KAAK,OAAO,KAAK,IAAI,GAAG;GACnD,MAAM,OAAO,cAAc,IAAI;GAC/B,IAAI,SAAS,QAAQ,SAAS,MAAM,IAAI,EAAE,GAAG,OAAO;IAAE,IAAI,OAAO,MAAM;IAAG,OAAO,KAAK,KAAK,KAAK,IAAI,CAAC,CAAC;GAAO;GAC7G;EACF;EACA,MAAM,KAAK,cAAc,IAAI;EAC7B,MAAM,IAAI,aAAa,kBAAkB,EAAE,GAAG,IAAI,EAAE;EACpD,IAAI,CAAC,GAAG;EACR,MAAM,OAAO;GACX,GAAG,UAAU,EAAE,SAAS,KAAK,QAAQ,KAAK,IAAI,EAAE,GAAG,EAAE;GACrD,MAAM,KAAK,KAAK,KAAK,IAAI;GACzB,MAAM,KAAK,KAAK,KAAK,IAAI;GACzB,OAAO,KAAK,MAAM,KAAK,IAAI;GAC3B,eAAe,KAAK,cAAc,KAAK,IAAI;EAC7C;EACA,MAAM,MAAM;GAAE,GAAG,GAAG;GAAG,GAAG,GAAG;GAAG,OAAO,GAAG;GAAO,QAAQ,GAAG;EAAO;EACnE,IAAI,CAAC,gBAAgB,EAAE,GAAG,EAAE,GAAG;GAAE,GAAG;GAAM,GAAG;EAAI,CAAC,GAAG;EACrD,OAAO;GAAE,IAAI,OAAO,KAAK,EAAE;GAAG,OAAO,aAAa,EAAE,GAAG,EAAE,GAAG,IAAI;EAAE;CACpE;CACA,OAAO;AACT;;AAKA,SAAS,eACP,OACA,IACA,IAC2C;CAC3C,MAAM,QAAQ,EAAE,KAAK;EAAE,GAAG;EAAI,GAAG;EAAI,OAAO;EAAG,QAAQ;CAAE,EAAE;CAE3D,OADc,kBAAkB,OAAgB,4BAA4B,KACrE;AACT;;;AAIA,SAAS,SAAS,MAAY,GAAW,GAAoB;CAC3D,MAAM,QAAQ,eAAe,IAAI;CACjC,IAAI,UAAU,QAAW,OAAO,kBAAkB,MAAM,GAAG,CAAC;CAC5D,OAAO,MAAM,MAAM,EAAE,MAAM,WAAW;EACpC,MAAM,IAAI,aAAa,IAAI;EAC3B,OAAO,KAAK,EAAE,KAAK,KAAK,EAAE,IAAI,EAAE,SAAS,KAAK,EAAE,KAAK,KAAK,EAAE,IAAI,EAAE;CACpE,CAAC;AACH"}
@@ -2,7 +2,7 @@ import { clippedToBox } from "../../canvas/nodeShapes/textPainter.js";
2
2
  import { textCommandFromPose } from "@weasel-js/renderer";
3
3
  import { runsToPlainText } from "@weasel-js/text";
4
4
 
5
- //#region src/features/text/textLayer.ts
5
+ //#region src/domains/text/textLayer.ts
6
6
  /** Build a `RenderLayer` that emits one `TextDrawCommand` per text node. */
7
7
  function createTextLayer(opts) {
8
8
  const { id = "text", label = "Text", getTexts, getPose, isHidden, clipToBounds = false } = opts;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"textLayer.js","names":[],"sources":["../../../src/domains/text/textLayer.ts"],"sourcesContent":["/**\n * Text RenderLayer for text that is not in a scene: one `textCommandFromPose`\n * command per item, the same command the `kit:text` node painter emits, so an\n * item lays out — and wraps, or doesn't, per its style — as a node would.\n *\n * The GL renderer uses MSDF text. The resolved style's `fontFamily` must\n * be registered via `registerFont(family, variant, metricsUrl, atlasUrl)`\n * from the renderer module *before* the GL backend dispatches this layer.\n * Unregistered families render with a warning and no glyphs (see\n * `TextDrawCommand`'s contract).\n */\n\nimport { type DrawCommand } from '@weasel-js/renderer';\nimport type { TextPose } from '@weasel-js/text';\n\nimport type { RenderLayer } from 'core/layers/render';\nimport { textCommandFromPose } from '@weasel-js/renderer';\nimport { runsToPlainText } from '@weasel-js/text';\nimport { clippedToBox } from '../../canvas/nodeShapes/textPainter';\n\n\n/** Options for `createTextLayer`. */\nexport interface CreateTextLayerOpts<T> {\n id?: string;\n label?: string;\n getTexts: () => readonly T[];\n getPose: (node: T) => TextPose;\n /** Optional per-node hide hook (e.g., suppress while editing). */\n isHidden?: (node: T) => boolean;\n /** When `true`, each text command is wrapped in a clipped group so any\n * overflow beyond the pose's `(width × height)` is hidden. Default\n * `false` (legacy: text can spill outside the declared bounds). Opt-in\n * so existing consumers who rely on overflow keep working. */\n clipToBounds?: boolean;\n}\n\n/** Build a `RenderLayer` that emits one `TextDrawCommand` per text node. */\nexport function createTextLayer<T>(opts: CreateTextLayerOpts<T>): RenderLayer<unknown> {\n const { id = 'text', label = 'Text', getTexts, getPose, isHidden, clipToBounds = false } = opts;\n return {\n id,\n label,\n draw: (_data, view) => {\n // A world-space layer, so a `{ px }` size in a pose's style resolves\n // against the live camera. Radial in effect — one factor for both axes.\n const scale = (view.scale.x + view.scale.y) / 2;\n const children: DrawCommand[] = [];\n for (const node of getTexts()) {\n if (isHidden?.(node)) continue;\n const pose = getPose(node);\n if (pose.runs && pose.runs.length > 0 && runsToPlainText(pose.runs) !== pose.text) {\n throw new Error(\n `weasel createTextLayer: TextPose invariant violated — ` +\n `runsToPlainText(runs) !== text. Either omit \\`runs\\` or keep it ` +\n `synchronized with \\`text\\`.`,\n );\n }\n const textCmd = textCommandFromPose(pose, scale);\n children.push(clipToBounds ? clippedToBox(textCmd, pose) : textCmd);\n }\n // World-space commands; drawLayers wraps in viewToMat3 automatically.\n return children;\n },\n };\n}\n"],"mappings":";;;;;;AAqCA,SAAgB,gBAAmB,MAAoD;CACrF,MAAM,EAAE,KAAK,QAAQ,QAAQ,QAAQ,UAAU,SAAS,UAAU,eAAe,UAAU;CAC3F,OAAO;EACL;EACA;EACA,OAAO,OAAO,SAAS;GAGrB,MAAM,SAAS,KAAK,MAAM,IAAI,KAAK,MAAM,KAAK;GAC9C,MAAM,WAA0B,CAAC;GACjC,KAAK,MAAM,QAAQ,SAAS,GAAG;IAC7B,IAAI,WAAW,IAAI,GAAG;IACtB,MAAM,OAAO,QAAQ,IAAI;IACzB,IAAI,KAAK,QAAQ,KAAK,KAAK,SAAS,KAAK,gBAAgB,KAAK,IAAI,MAAM,KAAK,MAC3E,MAAM,IAAI,MACR,+IAGF;IAEF,MAAM,UAAU,oBAAoB,MAAM,KAAK;IAC/C,SAAS,KAAK,eAAe,aAAa,SAAS,IAAI,IAAI,OAAO;GACpE;GAEA,OAAO;EACT;CACF;AACF"}
@@ -5,6 +5,7 @@ import { scenePaintedPoseOf } from "../../canvas/derivedPath.js";
5
5
  import { findMountedCanvas } from "../../canvas/mountedCanvases.js";
6
6
  import { drawOneEditing } from "./editingDrawOne.js";
7
7
  import { textHitAt } from "./textHitAt.js";
8
+ import { registerHostTextEditor } from "./hostTextEditors.js";
8
9
  import { editedData, sceneTextDrafts } from "./sceneTextDrafts.js";
9
10
  import { poseFrameOf } from "@weasel-js/geom";
10
11
  import { useCallback, useEffect, useMemo, useRef } from "react";
@@ -14,7 +15,7 @@ import { resolveScreenLength } from "@weasel-js/paint";
14
15
  import { isTextData } from "@weasel-js/text";
15
16
  import { useTextEdit } from "@weasel-js/text-dom";
16
17
 
17
- //#region src/features/text/useSceneTextEdit.ts
18
+ //#region src/domains/text/useSceneTextEdit.ts
18
19
  /**
19
20
  * `useSceneTextEdit` — scene-aware wrapper around `useTextEdit`: scene
20
21
  * lookups for text / style / runs / screen-pose, and
@@ -43,8 +44,15 @@ function projectionsOf(opts) {
43
44
  };
44
45
  }
45
46
  /** Wire double-click-to-edit text on a scene: mounts a DOM editing overlay
46
- * over the node being edited and writes the result back as an undoable op. */
47
+ * over the node being edited and writes the result back as an undoable op.
48
+ * `<SceneCanvas>`'s own editor stands down for any canvas inside `container`. */
47
49
  function useSceneTextEdit(scene, container, options = {}) {
50
+ useEffect(() => container ? registerHostTextEditor(container) : void 0, [container]);
51
+ return useSceneTextEditor(scene, container, options);
52
+ }
53
+ /** `useSceneTextEdit` without announcing itself as a host's editor: what
54
+ * `<SceneCanvas>` mounts as its own. */
55
+ function useSceneTextEditor(scene, container, options = {}) {
48
56
  const sceneRef = useLatest(scene);
49
57
  const optsRef = useLatest(options);
50
58
  const containerRef = useLatest(container);
@@ -225,5 +233,5 @@ function shownTextPaint(scene, node) {
225
233
  }
226
234
 
227
235
  //#endregion
228
- export { useSceneTextEdit };
236
+ export { useSceneTextEdit, useSceneTextEditor };
229
237
  //# sourceMappingURL=useSceneTextEdit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSceneTextEdit.js","names":[],"sources":["../../../src/domains/text/useSceneTextEdit.ts"],"sourcesContent":["/**\n * `useSceneTextEdit` — scene-aware wrapper around `useTextEdit`: scene\n * lookups for text / style / runs / screen-pose, and\n * `scene.update(id, { data })` for commits. The overlay and a double-click\n * both read the pose a node is painted at, so text inside a container whose\n * pose is a frame is edited where it is drawn.\n *\n * Defaults assume the node's `data` carries `text` / `style` / `runs`\n * directly. For custom data shapes, override any of the optional projections.\n * Consumers with non-rect poses should drop down to raw `useTextEdit` and\n * supply their own `getScreenPose`.\n */\nimport { useCallback, useEffect, useMemo, useRef } from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport { asNodeId } from '../../core/scene/types';\nimport { scenePaintedPoseOf } from '../../canvas/derivedPath';\nimport type { Scene } from '../../core/scene/types';\nimport { drawOneEditing, type EditingDrawOne } from './editingDrawOne';\nimport { textHitAt } from './textHitAt';\nimport { registerHostTextEditor } from './hostTextEditors';\nimport { editedData, sceneTextDrafts } from './sceneTextDrafts';\nimport type { Stroke, Fill, Inherit } from '@weasel-js/paint';\nimport { resolveScreenLength } from '@weasel-js/paint';\nimport { inheritedPaintIn } from '../../canvas/inheritedPaints';\nimport { withInheritedPaint } from '../../core/scene/inheritedPaint';\nimport { clientToCanvas } from '@weasel-js/routing';\nimport { findMountedCanvas, type MountedCanvas } from '../../canvas/mountedCanvases';\nimport type { View } from '../../core/viewport/view';\nimport type { RectPose } from 'core/geometry/unionBounds';\nimport { poseFrameOf } from '@weasel-js/geom';\nimport { isTextData, type StyledRun, type TextPaint, type TextStyle, type TextVerticalAlign } from '@weasel-js/text';\nimport { textBoxOf, type TextOverflow } from '../../canvas/nodeShapes/textPainter';\nimport { useTextEdit, type TextEditClipRect, type TextEditScreenPose, type UseTextEditReturn } from '@weasel-js/text-dom';\n\n/** Shape the default projections expect `data` to satisfy. All fields\n * optional so any object satisfies the constraint — projections fill in\n * the gaps for non-default shapes. */\nexport interface DefaultTextData {\n text?: string;\n style?: TextStyle;\n runs?: readonly StyledRun[];\n fill?: Fill | Inherit | null;\n stroke?: Stroke | Inherit | null;\n verticalAlign?: TextVerticalAlign;\n padding?: number;\n overflow?: TextOverflow;\n}\n\n/** All-optional projections + fontSize fallback. */\nexport interface UseSceneTextEditOptions<TData> {\n /**\n * Is this node text? A double-click opens an edit only on a node that\n * answers yes, and passes through any other to the text under it. Default:\n * `isTextData`, the test `inferredNodeRouting` classifies a text node by. Pass one when `getText` reads some other shape.\n */\n isText?: (data: TData) => boolean;\n /** Read plain text from `data`. Default: `data.text ?? ''`. */\n getText?: (data: TData) => string;\n /** Read style from `data`. Default: `data.style`. */\n getStyle?: (data: TData) => TextStyle | undefined;\n /** Read the node's paint from `data`. Default: `data.fill` / `data.stroke`. */\n getPaint?: (data: TData) => TextPaint | undefined;\n /** Read rich-text runs from `data`. Default: `data.runs`. */\n getRuns?: (data: TData) => readonly StyledRun[] | undefined;\n /** Read the box vertical alignment from `data`. Default: `data.verticalAlign`. */\n getVerticalAlign?: (data: TData) => TextVerticalAlign | undefined;\n /** Read the inset from the node's box to its text from `data`. Default: `data.padding`. */\n getPadding?: (data: TData) => number | undefined;\n /** Read whether the node cuts its text at its box from `data`, which the\n * overlay then does too. Default: `data.overflow`. */\n getOverflow?: (data: TData) => TextOverflow | undefined;\n /** Produce updated data with new text. Default: `{ ...data, text }`. */\n setText?: (data: TData, text: string) => TData;\n /** Produce updated data with new runs. Default: `{ ...data, runs }`. */\n setRuns?: (data: TData, runs: StyledRun[]) => TData;\n /**\n * Produce updated data with a new node-wide style. Default:\n * `{ ...data, style }`. Written only when a range toggle has to *clear* a\n * flag the node itself sets — the run algebra is additive, so that edit\n * lowers the node flag and raises it on the runs outside the range. See\n * `useTextEdit`'s `setStyle`.\n *\n * An edit's text, runs, and style are folded through these three and\n * written as one `scene.update`, so a commit is one undo step.\n */\n setStyle?: (data: TData, style: TextStyle) => TData;\n /** Fallback fontSize when `style.fontSize` is unset. Default `16`. */\n defaultFontSize?: number;\n /**\n * The view to project through. Omit it and the view is read live from the\n * weasel canvas mounted inside `container` (the one a double-click landed\n * on, when there are several). With no canvas there, the node's world box\n * is passed through as screen pixels.\n *\n * A thunk is re-read on every projection; a plain `View` is the value from\n * the render that supplied it.\n */\n view?: View | (() => View);\n /**\n * Forwarded to `useTextEdit`: is `el` part of the editor's own chrome?\n * Focus moving into it does not end the edit. Wire it to whatever renders\n * the character controls.\n */\n isEditorChrome?: (el: Element) => boolean;\n /** Forwarded to `useTextEdit`: what Escape does to the edit. */\n escape?: 'cancel' | 'commit';\n /** Forwarded to `useTextEdit`: how long an edit waits for its face. */\n fontHold?: number;\n /** Forwarded to `useTextEdit`: called as an edit of the node `id` opens,\n * whichever way it was started. */\n onStart?: (id: string) => void;\n /** Forwarded to `useTextEdit`: called as the edit of `id` closes, after\n * any commit has been written to the scene. */\n onEnd?: (id: string) => void;\n}\n\nfunction resolveView(view: View | (() => View) | undefined): View | undefined {\n return typeof view === 'function' ? view() : view;\n}\n\n/** What `onDoubleClick` reads of the event: a React or a DOM `MouseEvent`. */\nexport type TextEditDoubleClick = Pick<MouseEvent, 'target' | 'clientX' | 'clientY'>;\n\n/** Return shape extends `UseTextEditReturn` with an `onDoubleClick`\n * binding for the canvas container. Double-clicking inside a text\n * node enters edit mode with the caret seeded at the clicked glyph. */\nexport interface UseSceneTextEditReturn extends UseTextEditReturn {\n /** Bind to the element wrapping `<SceneCanvas>` (the same element\n * passed as `container`, or any ancestor that receives the dblclick\n * bubble). Starts an edit on the top-most text node under the cursor. */\n onDoubleClick: (e: TextEditDoubleClick) => void;\n /** The `isText` option asked of the node with this id: what\n * `TextEditDep.isTextNode` wants, so both ways into an edit agree. */\n isTextNode: (id: string) => boolean;\n /**\n * `defaultDrawOne`, less the glyphs of the node being edited: the overlay\n * stands in for its text, and the rest of the node, a `background` among\n * it, goes on painting. Pass it as `layers.scene.drawOne`, with\n * `paintBounds: defaultPaintBounds`, in place of an `alphaFor` that hides\n * the whole node.\n */\n drawOne: EditingDrawOne;\n}\n\n/** How each field is read from a node's `data`: the option's projection, else the default shape. */\nfunction projectionsOf<TData extends DefaultTextData>(opts: { readonly current: UseSceneTextEditOptions<TData> }) {\n return {\n isText: (data: TData): boolean =>\n opts.current.isText ? opts.current.isText(data) : isTextData(data),\n text: (data: TData): string =>\n opts.current.getText ? opts.current.getText(data) : (data.text ?? ''),\n style: (data: TData): TextStyle | undefined =>\n opts.current.getStyle ? opts.current.getStyle(data) : data.style,\n runs: (data: TData): readonly StyledRun[] | undefined =>\n opts.current.getRuns ? opts.current.getRuns(data) : data.runs,\n verticalAlign: (data: TData): TextVerticalAlign | undefined =>\n opts.current.getVerticalAlign ? opts.current.getVerticalAlign(data) : data.verticalAlign,\n padding: (data: TData): number | undefined =>\n opts.current.getPadding ? opts.current.getPadding(data) : data.padding,\n overflow: (data: TData): TextOverflow | undefined =>\n opts.current.getOverflow ? opts.current.getOverflow(data) : data.overflow,\n };\n}\n\n/** Wire double-click-to-edit text on a scene: mounts a DOM editing overlay\n * over the node being edited and writes the result back as an undoable op.\n * `<SceneCanvas>`'s own editor stands down for any canvas inside `container`. */\nexport function useSceneTextEdit<\n TData extends DefaultTextData,\n TLayer extends string,\n TPose extends RectPose = RectPose,\n>(\n scene: Scene<TData, TLayer, TPose>,\n container: HTMLElement | null,\n options: UseSceneTextEditOptions<TData> = {},\n): UseSceneTextEditReturn {\n useEffect(() => (container ? registerHostTextEditor(container) : undefined), [container]);\n return useSceneTextEditor(scene, container, options);\n}\n\n/** `useSceneTextEdit` without announcing itself as a host's editor: what\n * `<SceneCanvas>` mounts as its own. */\nexport function useSceneTextEditor<\n TData extends DefaultTextData,\n TLayer extends string,\n TPose extends RectPose = RectPose,\n>(\n scene: Scene<TData, TLayer, TPose>,\n container: HTMLElement | null,\n options: UseSceneTextEditOptions<TData> = {},\n): UseSceneTextEditReturn {\n // Refs, so the useTextEdit callbacks always see the current values\n // without re-binding.\n const sceneRef = useLatest(scene);\n const optsRef = useLatest(options);\n const containerRef = useLatest(container);\n // The last double-click's target, so the overlay projects through the\n // canvas the edit was opened on.\n const openedOnRef = useRef<EventTarget | null>(null);\n const drafts = useMemo(() => sceneTextDrafts(sceneRef, optsRef), [sceneRef, optsRef]);\n useEffect(() => () => drafts.close(), [drafts]);\n\n // The canvas's box in container pixels is both the origin its view projects\n // from and the region its glyphs are clipped to.\n const surface = (): { canvas: MountedCanvas; box: TextEditClipRect } | null => {\n const root = containerRef.current;\n const canvas = root ? findMountedCanvas(root, openedOnRef.current) : null;\n if (!root || !canvas) return null;\n const a = canvas.element.getBoundingClientRect();\n const b = root.getBoundingClientRect();\n return {\n canvas,\n box: {\n x: a.left - b.left - root.clientLeft + root.scrollLeft,\n y: a.top - b.top - root.clientTop + root.scrollTop,\n width: a.width,\n height: a.height,\n },\n };\n };\n\n const read = useMemo(() => projectionsOf(optsRef), [optsRef]);\n\n const edit = useTextEdit({\n container,\n isEditorChrome: (el) => optsRef.current.isEditorChrome?.(el) ?? false,\n escape: options.escape,\n fontHold: options.fontHold,\n onStart: (id) => optsRef.current.onStart?.(id),\n onEnd: (id) => {\n drafts.end(id);\n optsRef.current.onEnd?.(id);\n },\n onDraft: drafts.draft,\n getText: (id) => {\n const node = sceneRef.current.get(asNodeId(id));\n return node ? read.text(node.data) : '';\n },\n getStyle: (id) => {\n const node = sceneRef.current.get(asNodeId(id));\n return node ? read.style(node.data) : undefined;\n },\n getPaint: (id) => {\n const node = sceneRef.current.get(asNodeId(id));\n if (!node) return undefined;\n const get = optsRef.current.getPaint;\n return get ? get(node.data) : shownTextPaint(sceneRef.current, node);\n },\n getScreenPose: (id) => {\n const scene = sceneRef.current;\n const node = scene.get(asNodeId(id));\n if (!node) return null;\n const at = surface();\n const view = resolveView(optsRef.current.view) ?? at?.canvas.getView();\n return screenPoseOf(node.data, scenePaintedPoseOf(scene)(node), read, view, at?.box, optsRef.current.defaultFontSize);\n },\n getClipRect: () => surface()?.box ?? null,\n getRuns: (id) => {\n const node = sceneRef.current.get(asNodeId(id));\n return node ? read.runs(node.data) : undefined;\n },\n commit: (id, { text, runs, style }) => {\n const nid = asNodeId(id);\n const node = sceneRef.current.get(nid);\n if (!node) return;\n drafts.commit(id, editedData(node.data, { text, runs, style }, optsRef.current));\n },\n });\n\n const onDoubleClick = useCallback((e: TextEditDoubleClick) => {\n // The dblclick bubbles from the canvas up to any wrapping container; the\n // element it started on is what the click coordinates are relative to.\n const canvas = e.target instanceof HTMLElement ? e.target : null;\n if (!canvas) return;\n // Inside the editor a double-click selects a word, and its target is a run\n // span whose box the click would be measured against as if it were a canvas.\n if (canvas.closest('[contenteditable=\"true\"]')) return;\n\n // The click is un-projected here; `textHitAt` takes it back through the\n // frame the node is drawn in.\n const [canvasX, canvasY] = clientToCanvas(canvas, e.clientX, e.clientY);\n const root = containerRef.current;\n const view = resolveView(optsRef.current.view)\n ?? (root ? findMountedCanvas(root, canvas)?.getView() : undefined);\n const cx = view ? canvasX / view.scale.x + view.x : canvasX;\n const cy = view ? canvasY / view.scale.y + view.y : canvasY;\n const hit = textHitAt(sceneRef.current, read, cx, cy);\n if (!hit) return;\n openedOnRef.current = canvas;\n edit.startEdit(hit.id, { caret: hit.caret });\n }, [edit, containerRef, optsRef, read, sceneRef]);\n\n const isTextNode = useCallback((id: string): boolean => {\n const node = sceneRef.current.get(asNodeId(id));\n return node !== undefined && read.isText(node.data);\n }, [read, sceneRef]);\n\n const drawOne = useMemo(() => drawOneEditing(edit.editingId), [edit.editingId]);\n return { ...edit, onDoubleClick, isTextNode, drawOne };\n}\n\n/** The overlay's pose for a node painted at `painted`: its text box, inset\n * about the box center, which is all the overlay's frame needs, projected\n * through `view`. */\nfunction screenPoseOf<TData extends DefaultTextData>(\n d: TData,\n painted: RectPose,\n read: ReturnType<typeof projectionsOf<TData>>,\n view: View | undefined,\n origin: TextEditClipRect | undefined,\n defaultFontSize = 16,\n): TextEditScreenPose {\n const inset = textBoxOf({ padding: read.padding(d) }, painted);\n const box = screenBoxOf({ ...painted, ...inset }, view, origin);\n return {\n ...box,\n // Against `zoom`, the same factor the overlay scales back up by, so a\n // `{ px }` size arrives on screen at the pixel count it names.\n fontSize: resolveScreenLength(read.style(d)?.fontSize ?? defaultFontSize, box.zoom),\n verticalAlign: read.verticalAlign(d),\n ...(read.overflow(d) === 'clip' ? { clip: clipToBox(inset, painted) } : {}),\n };\n}\n\n/** How far `box` reaches past `inner` on each side: the overlay's cut at\n * `box` when its own pose is `inner`. */\nfunction clipToBox(inner: RectPose, box: RectPose): NonNullable<TextEditScreenPose['clip']> {\n return {\n top: inner.y - box.y,\n left: inner.x - box.x,\n bottom: box.y + box.height - (inner.y + inner.height),\n right: box.x + box.width - (inner.x + inner.width),\n };\n}\n\n/**\n * `pose`'s box as the overlay takes it, `origin` being the canvas's corner in\n * container pixels. Only the box center is projected, since the overlay's\n * frame is about it. Width and height stay in world units and reach the\n * screen through the overlay's own `scale(zoom)` — see\n * `TextEditScreenPose.zoom`.\n */\nfunction screenBoxOf(\n pose: RectPose,\n view: View | undefined,\n origin: { x: number; y: number } | undefined,\n): Pick<TextEditScreenPose, 'x' | 'y' | 'width' | 'height' | 'frame'> & { zoom: number } {\n const zoom = view ? view.scale.x : 1;\n const zoomY = view ? view.scale.y : 1;\n const cx = (origin?.x ?? 0) + (pose.x + pose.width / 2 - (view?.x ?? 0)) * zoom;\n const cy = (origin?.y ?? 0) + (pose.y + pose.height / 2 - (view?.y ?? 0)) * zoomY;\n return {\n x: cx - (pose.width * zoom) / 2,\n y: cy - (pose.height * zoom) / 2,\n width: pose.width,\n height: pose.height,\n zoom,\n frame: frameLinearOf(pose, zoomY / zoom),\n };\n}\n\n/** The linear part of the frame `pose` is drawn in, as the overlay takes it.\n * `stretch` is the view's y scale over its x scale: the overlay scales both\n * axes by the x scale, so the frame carries what is left of y. */\nfunction frameLinearOf(pose: unknown, stretch: number): TextEditScreenPose['frame'] {\n const m = poseFrameOf(pose);\n if (!m && stretch === 1) return undefined;\n const [a, b, c, d] = m ?? [1, 0, 0, 1];\n return [a, b * stretch, c, d * stretch];\n}\n\n/** A text node's paint as the canvas paints it: an inherited one is its container's. */\nfunction shownTextPaint<TData extends DefaultTextData>(\n scene: Scene<TData, string, unknown>,\n node: NonNullable<ReturnType<Scene<TData, string, unknown>['get']>>,\n): TextPaint {\n const { fill, stroke } = withInheritedPaint(node.data, inheritedPaintIn(scene, node));\n return { fill, stroke };\n}\n"],"mappings":"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"}
@@ -2,7 +2,7 @@ import { ZOOM_FLOOR } from "../../core/viewport/zoomBounds.js";
2
2
  import { normalizeView } from "../../core/viewport/view.js";
3
3
  import { fitViewToBounds } from "../../core/viewport/fitViewToBounds.js";
4
4
 
5
- //#region src/features/viewports/aim.ts
5
+ //#region src/domains/viewports/aim.ts
6
6
  /** The aim of view `viewId` (`null`: a surface's root) showing world rect `rect`. */
7
7
  function rectAim(viewId, rect) {
8
8
  return {
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aim.js","names":[],"sources":["../../../src/domains/viewports/aim.ts"],"sourcesContent":["import { fitViewToBounds, type Bounds } from 'core/viewport/fitViewToBounds';\nimport type { Dims } from 'core/layers/render';\nimport { normalizeView, type View } from 'core/viewport/view';\nimport { ZOOM_FLOOR } from 'core/viewport/zoomBounds';\n\n/** A point in a view's world. A 2D world is the plane z = 0, so `z` is absent there. */\nexport interface AimPoint {\n x: number;\n y: number;\n z?: number;\n}\n\n/** A closed outline in a frame's CSS px, from the frame's top-left; at least 3 points. */\nexport type AimOutline = readonly { x: number; y: number }[];\n\n/** A target view, as an aim is drawn into it: its world, and its frame. */\nexport interface AimSpace {\n /** The view whose frame this is; `null` for a surface's root view. */\n readonly viewId: string | null;\n /** `'2d'` for core's views; any other value is its owner's (kernel3d: `'3d'`). */\n readonly kind: string;\n readonly size: { width: number; height: number };\n /** World point to frame px; `null` where the point does not draw (behind a 3D camera). */\n project(p: AimPoint): { x: number; y: number } | null;\n /** Frame px to the world point under it; `null` where there is none (a 3D ray missing its plane). */\n unproject(px: { x: number; y: number }): AimPoint | null;\n}\n\n/** What a viewport shows, read in the world of the view it names. */\nexport interface ViewAim<TExtent = unknown> {\n readonly viewId: string | null;\n /** Discriminates `extent`: `'rect'` for core's 2D views. */\n readonly kind: string;\n /** Where the aim lies in its world, in its owner's own terms. */\n readonly extent: TExtent;\n /** The aim's outline in `space`'s frame, or `null` when it cannot be drawn there. */\n outlineIn(space: AimSpace): AimOutline | null;\n /** This aim moved so the world point under frame px `from` in `space` ends up under `to`; `null` when either misses. */\n pannedIn(space: AimSpace, from: { x: number; y: number }, to: { x: number; y: number }): ViewAim<TExtent> | null;\n}\n\n/** Core's 2D aim: a world rect a view shows. */\nexport type RectAim = ViewAim<Bounds> & { readonly kind: 'rect' };\n\n/** The aim of view `viewId` (`null`: a surface's root) showing world rect `rect`. */\nexport function rectAim(viewId: string | null, rect: Bounds): RectAim {\n return {\n viewId,\n kind: 'rect',\n extent: rect,\n outlineIn(space) {\n const { x, y, width, height } = rect;\n const out: { x: number; y: number }[] = [];\n for (const corner of [\n { x, y },\n { x: x + width, y },\n { x: x + width, y: y + height },\n { x, y: y + height },\n ]) {\n const p = space.project(corner);\n if (!p) return null;\n out.push(p);\n }\n return out;\n },\n pannedIn(space, from, to) {\n const a = space.unproject(from);\n const b = space.unproject(to);\n if (!a || !b) return null;\n return rectAim(viewId, { ...rect, x: rect.x + (b.x - a.x), y: rect.y + (b.y - a.y) });\n },\n };\n}\n\n/** Whether `aim` is core's 2D rect aim, so its `extent` is a world rect. */\nexport function isRectAim(aim: ViewAim): aim is RectAim {\n return aim.kind === 'rect';\n}\n\n/** A 2D view's frame: `view` is its camera and `size` its frame; frame px are measured from the frame's top-left. */\nexport function aimSpace2d(viewId: string | null, view: View, size: Dims): AimSpace {\n return {\n viewId,\n kind: '2d',\n size: { width: size.width, height: size.height },\n project: (p) => ({ x: (p.x - view.x) * view.scale.x, y: (p.y - view.y) * view.scale.y }),\n unproject: (px) => ({ x: px.x / view.scale.x + view.x, y: px.y / view.scale.y + view.y }),\n };\n}\n\nconst UNUSED_CURRENT: View = { x: 0, y: 0, scale: { x: 1, y: 1 } };\n\n/**\n * The camera that shows `rect` in a frame of `size`: `'exact'` per axis (the inverse of `viewportWorldRect`),\n * `'contain'` uniform and centered. Not clamped to a zoom range, and finite for an empty rect or frame. Normalized.\n */\nexport function viewForAim(rect: Bounds, size: Dims, fit: 'exact' | 'contain'): View {\n if (!(rect.width > 0 && rect.height > 0 && size.width > 0 && size.height > 0)) {\n return normalizeView({ x: rect.x, y: rect.y, scale: { x: ZOOM_FLOOR, y: ZOOM_FLOOR } });\n }\n return normalizeView(fitViewToBounds(rect, size, UNUSED_CURRENT, {\n mode: fit === 'exact' ? 'stretch' : 'contain', padding: 0, minScale: 0, maxScale: Infinity,\n }));\n}\n"],"mappings":";;;;;;AA6CA,SAAgB,QAAQ,QAAuB,MAAuB;CACpE,OAAO;EACL;EACA,MAAM;EACN,QAAQ;EACR,UAAU,OAAO;GACf,MAAM,EAAE,GAAG,GAAG,OAAO,WAAW;GAChC,MAAM,MAAkC,CAAC;GACzC,KAAK,MAAM,UAAU;IACnB;KAAE;KAAG;IAAE;IACP;KAAE,GAAG,IAAI;KAAO;IAAE;IAClB;KAAE,GAAG,IAAI;KAAO,GAAG,IAAI;IAAO;IAC9B;KAAE;KAAG,GAAG,IAAI;IAAO;GACrB,GAAG;IACD,MAAM,IAAI,MAAM,QAAQ,MAAM;IAC9B,IAAI,CAAC,GAAG,OAAO;IACf,IAAI,KAAK,CAAC;GACZ;GACA,OAAO;EACT;EACA,SAAS,OAAO,MAAM,IAAI;GACxB,MAAM,IAAI,MAAM,UAAU,IAAI;GAC9B,MAAM,IAAI,MAAM,UAAU,EAAE;GAC5B,IAAI,CAAC,KAAK,CAAC,GAAG,OAAO;GACrB,OAAO,QAAQ,QAAQ;IAAE,GAAG;IAAM,GAAG,KAAK,KAAK,EAAE,IAAI,EAAE;IAAI,GAAG,KAAK,KAAK,EAAE,IAAI,EAAE;GAAG,CAAC;EACtF;CACF;AACF;;AAGA,SAAgB,UAAU,KAA8B;CACtD,OAAO,IAAI,SAAS;AACtB;;AAGA,SAAgB,WAAW,QAAuB,MAAY,MAAsB;CAClF,OAAO;EACL;EACA,MAAM;EACN,MAAM;GAAE,OAAO,KAAK;GAAO,QAAQ,KAAK;EAAO;EAC/C,UAAU,OAAO;GAAE,IAAI,EAAE,IAAI,KAAK,KAAK,KAAK,MAAM;GAAG,IAAI,EAAE,IAAI,KAAK,KAAK,KAAK,MAAM;EAAE;EACtF,YAAY,QAAQ;GAAE,GAAG,GAAG,IAAI,KAAK,MAAM,IAAI,KAAK;GAAG,GAAG,GAAG,IAAI,KAAK,MAAM,IAAI,KAAK;EAAE;CACzF;AACF;AAEA,IAAM,iBAAuB;CAAE,GAAG;CAAG,GAAG;CAAG,OAAO;EAAE,GAAG;EAAG,GAAG;CAAE;AAAE;;;;;AAMjE,SAAgB,WAAW,MAAc,MAAY,KAAgC;CACnF,IAAI,EAAE,KAAK,QAAQ,KAAK,KAAK,SAAS,KAAK,KAAK,QAAQ,KAAK,KAAK,SAAS,IACzE,OAAO,cAAc;EAAE,GAAG,KAAK;EAAG,GAAG,KAAK;EAAG,OAAO;GAAE,GAAG;GAAY,GAAG;EAAW;CAAE,CAAC;CAExF,OAAO,cAAc,gBAAgB,MAAM,MAAM,gBAAgB;EAC/D,MAAM,QAAQ,UAAU,YAAY;EAAW,SAAS;EAAG,UAAU;EAAG,UAAU;CACpF,CAAC,CAAC;AACJ"}
@@ -1,7 +1,7 @@
1
1
  import { aimSpace2d } from "./aim.js";
2
2
  import { pointInPolygon, pointSegmentDist2, polygonFromPoints } from "@weasel-js/geom";
3
3
 
4
- //#region src/features/viewports/viewMarker.ts
4
+ //#region src/domains/viewports/viewMarker.ts
5
5
  /** The marker style a {@link ViewMarkerProps.style} fills in from: the minimap indicator's. */
6
6
  var VIEW_MARKER_DEFAULT_STYLE = {
7
7
  stroke: "#ffffff",
@@ -19,6 +19,10 @@ function createViewMarkerLayer(props, host) {
19
19
  id: `view-marker:${id}`,
20
20
  label: `View marker ${id}`,
21
21
  space: "screen",
22
+ subscribe: (listener) => {
23
+ const { source } = props();
24
+ return source !== null && typeof source === "object" && source.subscribe ? source.subscribe(listener) : () => {};
25
+ },
22
26
  draw: (_data, view, dims) => {
23
27
  const { source, target, style } = props();
24
28
  if (source === target) return [];
@@ -42,10 +46,13 @@ function createViewMarkerLayer(props, host) {
42
46
  }
43
47
  /** A press this many screen px outside the outline still counts. */
44
48
  var VIEW_MARKER_HIT_SLOP_PX = 4;
49
+ /** The share of its frame a marker can cover and still grab its interior. */
50
+ var INTERIOR_MAX_COVER = .75;
45
51
  /**
46
52
  * The grab for a press at frame px `at` in `space`, the marker's target, or
47
53
  * `null` when the marker is not interactive, its source has no writer or no
48
- * aim, or `at` is not on its outline or within {@link VIEW_MARKER_HIT_SLOP_PX} of it. A surface with views
54
+ * aim, or `at` is not on its outline or within {@link VIEW_MARKER_HIT_SLOP_PX} of it — of its rim alone, for a
55
+ * marker covering most of its frame (see {@link ViewMarkerProps.interactive}). A surface with views
49
56
  * calls this itself; a canvas with none, such as a detached minimap, calls it to route a press to its marker.
50
57
  */
51
58
  function grabViewMarker(props, host, space, at) {
@@ -57,7 +64,9 @@ function grabViewMarker(props, host, space, at) {
57
64
  if (!write) return null;
58
65
  const startAim = markerSourceAim(source, host);
59
66
  const outline = startAim?.outlineIn(space);
60
- if (!startAim || !outline || !outlineHolds(outline, at, VIEW_MARKER_HIT_SLOP_PX)) return null;
67
+ if (!startAim || !outline) return null;
68
+ const interior = polygonArea(outline) <= INTERIOR_MAX_COVER * space.size.width * space.size.height;
69
+ if (!outlineHolds(outline, at, VIEW_MARKER_HIT_SLOP_PX, interior)) return null;
61
70
  return {
62
71
  markerId: id,
63
72
  moveTo: (world) => {
@@ -67,8 +76,17 @@ function grabViewMarker(props, host, space, at) {
67
76
  }
68
77
  };
69
78
  }
70
- function outlineHolds(outline, p, slop) {
71
- if (pointInPolygon(outline.flatMap((q) => [q.x, q.y]), p.x, p.y)) return true;
79
+ function polygonArea(outline) {
80
+ let twice = 0;
81
+ for (let i = 0; i < outline.length; i++) {
82
+ const a = outline[i];
83
+ const b = outline[(i + 1) % outline.length];
84
+ twice += a.x * b.y - b.x * a.y;
85
+ }
86
+ return Math.abs(twice) / 2;
87
+ }
88
+ function outlineHolds(outline, p, slop, interior) {
89
+ if (interior && pointInPolygon(outline.flatMap((q) => [q.x, q.y]), p.x, p.y)) return true;
72
90
  for (let i = 0; i < outline.length; i++) {
73
91
  const a = outline[i];
74
92
  const b = outline[(i + 1) % outline.length];
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewMarker.js","names":[],"sources":["../../../src/domains/viewports/viewMarker.ts"],"sourcesContent":["import { pointInPolygon, pointSegmentDist2, polygonFromPoints } from '@weasel-js/geom';\nimport type { DrawCommand } from '@weasel-js/renderer';\nimport type { RenderLayer } from 'core/layers/render';\nimport { aimSpace2d, type AimOutline, type AimSpace, type ViewAim } from './aim';\n\n/** An aim handed in by value (another surface's view, a 3D pane), with the writer that moves it. */\nexport interface AimSource {\n aim(): ViewAim | null;\n setAim?(aim: ViewAim): void;\n /** Tells a marker drawing this aim that it changed, so the marker repaints. Without it the marker\n * repaints only when something else asks for a redraw. Returns an unsubscribe. */\n subscribe?(listener: () => void): () => void;\n}\n\n/** How a marker's outline is stroked, in screen px. */\nexport interface ViewMarkerStyle {\n stroke?: string;\n width?: number;\n dash?: readonly number[];\n}\n\n/** One view's aim, drawn inside another view's frame. */\nexport interface ViewMarkerProps {\n /** Unique among the markers on one surface. */\n id: string;\n /** Whose aim: a view id on this surface, `null` for the root view, or an aim by value. */\n source: string | null | AimSource;\n /** Whose frame it is drawn in, after that view's layers: a view id, or `null` for the root. */\n target: string | null;\n /** Screen px. Default: stroke `#ffffff`, width 1, dash [2, 3]. */\n style?: ViewMarkerStyle;\n /** Default true: dragging the marker edits the source's aim. A source with no writer is never interactive.\n * A marker grabs its interior plus {@link VIEW_MARKER_HIT_SLOP_PX} while it covers at most three quarters\n * of its frame, and past that only a band that wide either side of its rim, so the frame keeps its presses. */\n interactive?: boolean;\n}\n\n/** The marker style a {@link ViewMarkerProps.style} fills in from: the minimap indicator's. */\nexport const VIEW_MARKER_DEFAULT_STYLE: Required<ViewMarkerStyle> = { stroke: '#ffffff', width: 1, dash: [2, 3] };\n\n/** What a marker resolves aims through: the surface's views. */\nexport interface ViewMarkerHost {\n aimOf(id: string | null): ViewAim | null;\n setAim(id: string | null, aim: ViewAim): boolean;\n}\n\n/** The aim `source` names: a view's through `host`, or one handed in by value. */\nexport function markerSourceAim(source: ViewMarkerProps['source'], host: ViewMarkerHost): ViewAim | null {\n return source === null || typeof source === 'string' ? host.aimOf(source) : source.aim();\n}\n\n/** A screen-space layer, id `view-marker:<id>`, drawing `props`'s source aim in the frame it is handed. */\nexport function createViewMarkerLayer(props: () => ViewMarkerProps, host: ViewMarkerHost): RenderLayer<unknown> {\n const { id } = props();\n return {\n id: `view-marker:${id}`,\n label: `View marker ${id}`,\n space: 'screen',\n // Read when the canvas subscribes: a source swapped later is heard once it next resubscribes its layers.\n subscribe: (listener) => {\n const { source } = props();\n return source !== null && typeof source === 'object' && source.subscribe ? source.subscribe(listener) : () => {};\n },\n draw: (_data, view, dims): DrawCommand[] => {\n const { source, target, style } = props();\n if (source === target) return [];\n const aim = markerSourceAim(source, host);\n const outline = aim?.outlineIn(aimSpace2d(target, view, dims));\n if (!outline) return [];\n const d = VIEW_MARKER_DEFAULT_STYLE;\n return [{\n kind: 'path',\n path: polygonFromPoints(outline),\n stroke: {\n paint: { kind: 'solid', color: style?.stroke ?? d.stroke },\n width: style?.width ?? d.width,\n dash: [...(style?.dash ?? d.dash)],\n },\n }];\n },\n };\n}\n\n/** A press on a marker, held for the drag: where it started, and the aim it started from. */\nexport interface ViewMarkerGrab {\n readonly markerId: string;\n /** Pan the source so the world point under the press ends up under `world`, a point in the target's world. */\n moveTo(world: { x: number; y: number }): void;\n}\n\n/** A press this many screen px outside the outline still counts. */\nexport const VIEW_MARKER_HIT_SLOP_PX = 4;\n\n/** The share of its frame a marker can cover and still grab its interior. */\nconst INTERIOR_MAX_COVER = 0.75;\n\n/**\n * The grab for a press at frame px `at` in `space`, the marker's target, or\n * `null` when the marker is not interactive, its source has no writer or no\n * aim, or `at` is not on its outline or within {@link VIEW_MARKER_HIT_SLOP_PX} of it — of its rim alone, for a\n * marker covering most of its frame (see {@link ViewMarkerProps.interactive}). A surface with views\n * calls this itself; a canvas with none, such as a detached minimap, calls it to route a press to its marker.\n */\nexport function grabViewMarker(\n props: ViewMarkerProps,\n host: ViewMarkerHost,\n space: AimSpace,\n at: { x: number; y: number },\n): ViewMarkerGrab | null {\n const { id, source, target, interactive } = props;\n if (interactive === false || source === target) return null;\n const write = source === null || typeof source === 'string'\n ? (aim: ViewAim) => { host.setAim(source, aim); }\n : source.setAim?.bind(source);\n if (!write) return null;\n const startAim = markerSourceAim(source, host);\n const outline = startAim?.outlineIn(space);\n if (!startAim || !outline) return null;\n const interior = polygonArea(outline) <= INTERIOR_MAX_COVER * space.size.width * space.size.height;\n if (!outlineHolds(outline, at, VIEW_MARKER_HIT_SLOP_PX, interior)) return null;\n return {\n markerId: id,\n moveTo: (world) => {\n const to = space.project(world);\n const next = to && startAim.pannedIn(space, at, to);\n if (next) write(next);\n },\n };\n}\n\nfunction polygonArea(outline: AimOutline): number {\n let twice = 0;\n for (let i = 0; i < outline.length; i++) {\n const a = outline[i]!;\n const b = outline[(i + 1) % outline.length]!;\n twice += a.x * b.y - b.x * a.y;\n }\n return Math.abs(twice) / 2;\n}\n\nfunction outlineHolds(outline: AimOutline, p: { x: number; y: number }, slop: number, interior: boolean): boolean {\n if (interior && pointInPolygon(outline.flatMap((q) => [q.x, q.y]), p.x, p.y)) return true;\n for (let i = 0; i < outline.length; i++) {\n const a = outline[i]!;\n const b = outline[(i + 1) % outline.length]!;\n if (pointSegmentDist2(p.x, p.y, a.x, a.y, b.x, b.y) <= slop * slop) return true;\n }\n return false;\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { hitPredicate } from "@weasel-js/gestures";
2
2
 
3
- //#region src/features/viewports/viewMarkerDrag.ts
3
+ //#region src/domains/viewports/viewMarkerDrag.ts
4
4
  /** The action id a drag on a view marker routes to. */
5
5
  var VIEW_MARKER_DRAG = "view.marker.drag";
6
6
  /** The id of the entry {@link createViewMarkerContribution} builds. */
@@ -31,8 +31,8 @@ function viewMarkerDragAction() {
31
31
  }
32
32
  /**
33
33
  * The always-on entry (id `'view-marker'`) that binds a drag on any marker to {@link VIEW_MARKER_DRAG}.
34
- * A `<SceneCanvas>` installs it unless `actions={null}` or a `tools` takeover leaves it out; to turn marker
35
- * dragging back on there, pass it in `ambient`, or in the takeover's own `useTools` ambient list.
34
+ * A `<SceneCanvas>` installs it, beside a `tools` takeover too, unless `actions={null}` leaves it out; to turn
35
+ * marker dragging back on there, pass it in `ambient`.
36
36
  */
37
37
  function createViewMarkerContribution() {
38
38
  return {
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewMarkerDrag.js","names":[],"sources":["../../../src/domains/viewports/viewMarkerDrag.ts"],"sourcesContent":["import { hitPredicate } from '@weasel-js/gestures';\nimport type { Action, InvocationCtx } from '@weasel-js/routing';\nimport type { SurfaceContribution } from 'canvas/surfaceContribution';\nimport type { ViewMarkerGrab } from './viewMarker';\n\n/** The action id a drag on a view marker routes to. */\nexport const VIEW_MARKER_DRAG = 'view.marker.drag';\n\n/** The id of the entry {@link createViewMarkerContribution} builds. */\nexport const VIEW_MARKER_ENTRY_ID = 'view-marker';\n\nconst MARKER_KIND_PREFIX = 'layer:view-marker:';\n\nfunction grabIn(affordance: unknown): ViewMarkerGrab | null {\n return (affordance as { payload?: ViewMarkerGrab } | null | undefined)?.payload ?? null;\n}\n\n/** Drags the grabbed marker. There is nothing to commit, so a cancel ends where a release does. */\nexport function viewMarkerDragAction(): Action {\n return {\n id: VIEW_MARKER_DRAG,\n label: 'View marker — drag',\n invoker: {\n timing: 'ongoing',\n start: (ctx: InvocationCtx) => {\n const grab = grabIn(ctx.drag?.affordance);\n if (!grab) return {};\n return {\n kind: VIEW_MARKER_DRAG,\n onMove: (m: InvocationCtx) => { grab.moveTo(m.drag?.current ?? m.world); },\n };\n },\n },\n };\n}\n\n/**\n * The always-on entry (id `'view-marker'`) that binds a drag on any marker to {@link VIEW_MARKER_DRAG}.\n * A `<SceneCanvas>` installs it, beside a `tools` takeover too, unless `actions={null}` leaves it out; to turn\n * marker dragging back on there, pass it in `ambient`.\n */\nexport function createViewMarkerContribution(): SurfaceContribution {\n return {\n id: VIEW_MARKER_ENTRY_ID,\n eligibility: { always: true },\n actions: [viewMarkerDragAction()],\n bindings: [{\n spec: {\n kind: 'drag',\n target: { kindOf: hitPredicate((hit) => (hit as { kind?: string } | null | undefined)?.kind?.startsWith(MARKER_KIND_PREFIX) ?? false) },\n },\n actionId: VIEW_MARKER_DRAG,\n opts: { id: 'view-marker.drag' },\n }],\n };\n}\n"],"mappings":";;;;AAMA,IAAa,mBAAmB;;AAGhC,IAAa,uBAAuB;AAEpC,IAAM,qBAAqB;AAE3B,SAAS,OAAO,YAA4C;CAC1D,OAAQ,YAAgE,WAAW;AACrF;;AAGA,SAAgB,uBAA+B;CAC7C,OAAO;EACL,IAAI;EACJ,OAAO;EACP,SAAS;GACP,QAAQ;GACR,QAAQ,QAAuB;IAC7B,MAAM,OAAO,OAAO,IAAI,MAAM,UAAU;IACxC,IAAI,CAAC,MAAM,OAAO,CAAC;IACnB,OAAO;KACL,MAAM;KACN,SAAS,MAAqB;MAAE,KAAK,OAAO,EAAE,MAAM,WAAW,EAAE,KAAK;KAAG;IAC3E;GACF;EACF;CACF;AACF;;;;;;AAOA,SAAgB,+BAAoD;CAClE,OAAO;EACL,IAAI;EACJ,aAAa,EAAE,QAAQ,KAAK;EAC5B,SAAS,CAAC,qBAAqB,CAAC;EAChC,UAAU,CAAC;GACT,MAAM;IACJ,MAAM;IACN,QAAQ,EAAE,QAAQ,cAAc,QAAS,KAA8C,MAAM,WAAW,kBAAkB,KAAK,KAAK,EAAE;GACxI;GACA,UAAU;GACV,MAAM,EAAE,IAAI,mBAAmB;EACjC,CAAC;CACH;AACF"}
@@ -1,4 +1,4 @@
1
- //#region src/features/viewports/viewResolver.ts
1
+ //#region src/domains/viewports/viewResolver.ts
2
2
  /** Right and bottom edges are exclusive, so neighbouring views never both
3
3
  * claim a pixel. */
4
4
  var inside = (r, x, y) => x >= r.x && x < r.x + r.w && y >= r.y && y < r.y + r.h;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewResolver.js","names":[],"sources":["../../../src/domains/viewports/viewResolver.ts"],"sourcesContent":["import type { View } from 'core/viewport/view';\n\n/**\n * A view the resolver can route input to: a camera and the rect it paints\n * into, in canvas CSS-pixel space. A viewport node supplies one of these per\n * frame; so could any other per-view surface.\n */\nexport interface ResolvableView {\n id: string;\n view: View;\n rect: { x: number; y: number; w: number; h: number };\n /** The part of `rect` the view is seen through, in the same space — a view\n * nested in another is cut to it. Omitted, the whole rect. */\n clip?: { x: number; y: number; w: number; h: number };\n}\n\ntype Rect = { x: number; y: number; w: number; h: number };\n\n/** Right and bottom edges are exclusive, so neighbouring views never both\n * claim a pixel. */\nconst inside = (r: Rect, x: number, y: number): boolean =>\n x >= r.x && x < r.x + r.w && y >= r.y && y < r.y + r.h;\n\n/** Whether `v` takes a canvas-local point: inside its rect and its clip. */\nexport function viewContains(v: ResolvableView, x: number, y: number): boolean {\n return inside(v.rect, x, y) && (v.clip === undefined || inside(v.clip, x, y));\n}\n\n/**\n * The view an input event belongs to. `id` is `null` for the root view —\n * the whole canvas, when no viewport contains the point.\n */\nexport interface ViewTarget {\n id: string | null;\n view: View;\n /** The point asked about, in that view's world. */\n world: { x: number; y: number };\n}\n\n/**\n * Routes client points to views, and holds a captured pointer on the view its\n * gesture started in.\n *\n * Stickiness is the point. A drag that leaves its view's rect — over a\n * neighbouring view, or off the canvas — must keep reporting coordinates in\n * the space it began in, or a marquee crossing a panel edge silently starts\n * measuring against the wrong camera.\n */\nexport interface ViewResolver {\n /** Resolve and pin `pointerId`. Call on pointerdown. */\n begin(pointerId: number, clientX: number, clientY: number): ViewTarget;\n /**\n * The view for a point. A pinned `pointerId` gets its pinned view, looked\n * up fresh so a rect that moved mid-gesture is honored. Pass `null` for\n * input with no pointer to capture — wheel, hover, keys.\n */\n at(pointerId: number | null, clientX: number, clientY: number): ViewTarget;\n /** Release `pointerId`. Call on pointerup and pointercancel. */\n end(pointerId: number): void;\n /** Release every pin. */\n clear(): void;\n}\n\n/**\n * Options for `createViewResolver`. Each field is a function called afresh on\n * every query, so views and the canvas may move between events.\n */\nexport interface CreateViewResolverOpts {\n /** Candidate views in paint order — the last one containing a point wins,\n * so a nested view listed after the one around it is the one a point\n * inside both reaches. */\n views: () => readonly ResolvableView[];\n /** The camera for points no view claims. */\n root: () => View;\n /** A client point in the canvas's own CSS pixels, from its top-left. */\n toLocal: (clientX: number, clientY: number) => { x: number; y: number };\n /**\n * Whether something painted over every view claims a canvas-local point —\n * chrome on the surface's own frame. Such a point resolves to the root even\n * inside a view's rect. Asked only where a view would otherwise win.\n */\n occluded?: (x: number, y: number) => boolean;\n}\n\n/** Build a `ViewResolver` over `opts.views`, falling back to `opts.root`. */\nexport function createViewResolver(opts: CreateViewResolverOpts): ViewResolver {\n const { views, root, toLocal, occluded } = opts;\n const pinned = new Map<number, string | null>();\n\n type Local = { x: number; y: number };\n /** `at`, a canvas point `from` its surface's top-left, through `view`. */\n const worldOf = (at: Local, from: Local, view: View): Local => ({\n x: (at.x - from.x) / view.scale.x + view.x,\n y: (at.y - from.y) / view.scale.y + view.y,\n });\n\n function rootTarget(at: Local): ViewTarget {\n const view = root();\n return { id: null, view, world: worldOf(at, { x: 0, y: 0 }, view) };\n }\n\n function target(v: ResolvableView, at: Local): ViewTarget {\n return { id: v.id, view: v.view, world: worldOf(at, v.rect, v.view) };\n }\n\n function hit({ x, y }: Local): ResolvableView | undefined {\n const list = views();\n for (let i = list.length - 1; i >= 0; i--) {\n if (viewContains(list[i]!, x, y)) {\n return occluded?.(x, y) ? undefined : list[i];\n }\n }\n return undefined;\n }\n\n function fresh(at: Local): ViewTarget {\n const v = hit(at);\n return v ? target(v, at) : rootTarget(at);\n }\n\n return {\n begin(pointerId, clientX, clientY) {\n const t = fresh(toLocal(clientX, clientY));\n pinned.set(pointerId, t.id);\n return t;\n },\n at(pointerId, clientX, clientY) {\n const at = toLocal(clientX, clientY);\n if (pointerId !== null && pinned.has(pointerId)) {\n const id = pinned.get(pointerId)!;\n if (id === null) return rootTarget(at);\n const v = views().find((c) => c.id === id);\n if (v) return target(v, at);\n pinned.delete(pointerId);\n }\n return fresh(at);\n },\n end(pointerId) {\n pinned.delete(pointerId);\n },\n clear() {\n pinned.clear();\n },\n };\n}\n"],"mappings":";;;AAoBA,IAAM,UAAU,GAAS,GAAW,MAClC,KAAK,EAAE,KAAK,IAAI,EAAE,IAAI,EAAE,KAAK,KAAK,EAAE,KAAK,IAAI,EAAE,IAAI,EAAE;;AAGvD,SAAgB,aAAa,GAAmB,GAAW,GAAoB;CAC7E,OAAO,OAAO,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,SAAS,UAAa,OAAO,EAAE,MAAM,GAAG,CAAC;AAC7E;;AA2DA,SAAgB,mBAAmB,MAA4C;CAC7E,MAAM,EAAE,OAAO,MAAM,SAAS,aAAa;CAC3C,MAAM,yBAAS,IAAI,IAA2B;;CAI9C,MAAM,WAAW,IAAW,MAAa,UAAuB;EAC9D,IAAI,GAAG,IAAI,KAAK,KAAK,KAAK,MAAM,IAAI,KAAK;EACzC,IAAI,GAAG,IAAI,KAAK,KAAK,KAAK,MAAM,IAAI,KAAK;CAC3C;CAEA,SAAS,WAAW,IAAuB;EACzC,MAAM,OAAO,KAAK;EAClB,OAAO;GAAE,IAAI;GAAM;GAAM,OAAO,QAAQ,IAAI;IAAE,GAAG;IAAG,GAAG;GAAE,GAAG,IAAI;EAAE;CACpE;CAEA,SAAS,OAAO,GAAmB,IAAuB;EACxD,OAAO;GAAE,IAAI,EAAE;GAAI,MAAM,EAAE;GAAM,OAAO,QAAQ,IAAI,EAAE,MAAM,EAAE,IAAI;EAAE;CACtE;CAEA,SAAS,IAAI,EAAE,GAAG,KAAwC;EACxD,MAAM,OAAO,MAAM;EACnB,KAAK,IAAI,IAAI,KAAK,SAAS,GAAG,KAAK,GAAG,KACpC,IAAI,aAAa,KAAK,IAAK,GAAG,CAAC,GAC7B,OAAO,WAAW,GAAG,CAAC,IAAI,SAAY,KAAK;CAIjD;CAEA,SAAS,MAAM,IAAuB;EACpC,MAAM,IAAI,IAAI,EAAE;EAChB,OAAO,IAAI,OAAO,GAAG,EAAE,IAAI,WAAW,EAAE;CAC1C;CAEA,OAAO;EACL,MAAM,WAAW,SAAS,SAAS;GACjC,MAAM,IAAI,MAAM,QAAQ,SAAS,OAAO,CAAC;GACzC,OAAO,IAAI,WAAW,EAAE,EAAE;GAC1B,OAAO;EACT;EACA,GAAG,WAAW,SAAS,SAAS;GAC9B,MAAM,KAAK,QAAQ,SAAS,OAAO;GACnC,IAAI,cAAc,QAAQ,OAAO,IAAI,SAAS,GAAG;IAC/C,MAAM,KAAK,OAAO,IAAI,SAAS;IAC/B,IAAI,OAAO,MAAM,OAAO,WAAW,EAAE;IACrC,MAAM,IAAI,MAAM,CAAC,CAAC,MAAM,MAAM,EAAE,OAAO,EAAE;IACzC,IAAI,GAAG,OAAO,OAAO,GAAG,EAAE;IAC1B,OAAO,OAAO,SAAS;GACzB;GACA,OAAO,MAAM,EAAE;EACjB;EACA,IAAI,WAAW;GACb,OAAO,OAAO,SAAS;EACzB;EACA,QAAQ;GACN,OAAO,MAAM;EACf;CACF;AACF"}
@@ -4,7 +4,7 @@ import { backgroundFillCommands } from "../../core/layers/backgroundFill.js";
4
4
  import { normalizeView } from "../../core/viewport/view.js";
5
5
  import { mat3 } from "@weasel-js/geom";
6
6
 
7
- //#region src/features/viewports/viewportLayer.ts
7
+ //#region src/domains/viewports/viewportLayer.ts
8
8
  /** Build a layer that renders other layers through a second view, inside a
9
9
  * sub-region of the canvas — a minimap, an inset, a magnifier. */
10
10
  function createViewportLayer(opts) {
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewportLayer.js","names":[],"sources":["../../../src/domains/viewports/viewportLayer.ts"],"sourcesContent":["import type { DrawCommand, GroupDrawCommand } from '@weasel-js/renderer';\nimport { drawOneLayer, subscribeToSources, type Dims, type RenderLayer } from 'core/layers/render';\nimport { backgroundFillCommands } from 'core/layers/backgroundFill';\nimport { normalizeView, type View } from 'core/viewport/view';\nimport { mat3, type GlMat3 } from '@weasel-js/geom';\nimport type { ResolvableView } from './viewResolver';\n\n/**\n * A \"viewport node\" — a screen-space rectangle on the outer canvas that\n * re-renders one or more `source` layers through an *inner* `View`, then\n * clips them to its rect. The same source layer can be rendered through\n * multiple viewports without duplicating data.\n *\n * **Inner view semantics.** The source layers draw as if the inner view\n * filled the screen at world-origin. The viewport then translates the result\n * so that the inner view's origin lands at `bounds.x, bounds.y` and clips to\n * `(bounds.w, bounds.h)`.\n *\n * **This layer paints; it does not take input.** `<CanvasView>` is built on\n * it and routes the pointer stream through the kit's gesture dispatcher —\n * declare one of those for a viewport that should be clickable. `resolvable`\n * is the seam it uses: feed it to `createViewResolver` to route input to a\n * raw viewport yourself.\n *\n * **Screen-space source layers** (e.g., debug overlays, selection chrome)\n * draw in the viewport's own CSS-pixel space: their coords are relative to\n * the rect's top-left, and they clip to it, rather than to the outer canvas.\n */\nexport interface CreateViewportLayerOpts<TData, TSource = TData> {\n id: string;\n label: string;\n /** Layers re-rendered through `view`. Each receives `(data, view, dims)`\n * exactly as the outer Canvas would call it.\n *\n * Pass a thunk for a stack that is assembled elsewhere — it is read fresh\n * on every `draw`, the same way a thunked `view` is. */\n source: readonly RenderLayer<TSource>[] | (() => readonly RenderLayer<TSource>[]);\n /**\n * The `data` the source layers receive, derived from what the outer canvas\n * passed down. Omit and they get the outer canvas's own. A viewport showing\n * the same scene through a second camera wants its own per-view helpers\n * here — selection, chrome state and gesture previews.\n */\n data?: (outer: TData) => TSource;\n /**\n * The inner view. Pass a thunk for a camera that moves — it is read fresh\n * on every `draw` and `resolvable`, so the two cannot\n * disagree about where the viewport is looking mid-gesture.\n *\n * The thunk receives the outer view and dims, so a derived camera\n * (parallax, node-anchored scroll) is a function of the one hosting it.\n */\n view: View | ((outer: View, dims: Dims) => View);\n /** Where on the outer canvas this viewport is painted, in screen-space\n * CSS pixels. Recomputed every frame. */\n bounds: (outer: View, dims: Dims) => { x: number; y: number; w: number; h: number };\n /** The part of the outer canvas the viewport shows through, in the same\n * space as `bounds` and recomputed with it — the rect of a viewport it is\n * nested in. Omit to show all of `bounds`. */\n clip?: (outer: View, dims: Dims) => { x: number; y: number; w: number; h: number };\n /** Optional opaque background painted before the source layers. */\n background?: string;\n}\n\n/**\n * A {@link RenderLayer} that can also describe itself as a routing target.\n * Returned by {@link createViewportLayer}.\n */\nexport interface ViewportLayer<TData> extends RenderLayer<TData> {\n /**\n * This viewport as a routing candidate for {@link createViewResolver} —\n * its inner view and the rect it paints into for the given outer frame.\n *\n * Pass the `outer` view and `dims` the frame was drawn with: `bounds` is a\n * pure function of those, so this reproduces the exact rect that was\n * painted rather than a remembered one.\n */\n resolvable(outer: View, dims: Dims): ResolvableView;\n}\n\n/** Build a layer that renders other layers through a second view, inside a\n * sub-region of the canvas — a minimap, an inset, a magnifier. */\nexport function createViewportLayer<TData, TSource = TData>(\n opts: CreateViewportLayerOpts<TData, TSource>,\n): ViewportLayer<TData> {\n const { id, label, source, view, bounds, clip: clipAt, background, data: sourceData } = opts;\n const fixedView = typeof view === 'function' ? null : normalizeView(view);\n const viewAt = (outer: View, dims: Dims): View =>\n fixedView ?? normalizeView((view as (outer: View, dims: Dims) => View)(outer, dims));\n const sourceAt = typeof source === 'function' ? source : () => source;\n return {\n id,\n label,\n space: 'screen',\n subscribe: subscribeToSources(sourceAt),\n resolvable(outer, dims) {\n const rect = bounds(outer, dims);\n return clipAt\n ? { id, view: viewAt(outer, dims), rect, clip: clipAt(outer, dims) }\n : { id, view: viewAt(outer, dims), rect };\n },\n draw: (data, outerView, dims): DrawCommand[] => {\n const b = bounds(outerView, dims);\n // Inner content drawn through `view`, then translated so the inner\n // view's screen origin lines up with the viewport top-left.\n const innerDims = { width: b.w, height: b.h, dpr: dims.dpr };\n const children: DrawCommand[] = background\n ? backgroundFillCommands({ kind: 'solid', color: background }, innerDims)\n : [];\n // The cast holds up because `TSource` defaults to `TData`: without a\n // thunk the two are the same type by construction.\n const innerData = sourceData ? sourceData(data) : (data as unknown as TSource);\n const v = viewAt(outerView, dims);\n for (const layer of sourceAt()) {\n for (const c of drawOneLayer(layer, innerData, v, innerDims)) children.push(c);\n }\n const transform: GlMat3 = mat3.translated(mat3.identity(), b.x, b.y);\n const group: GroupDrawCommand = {\n kind: 'group',\n transform,\n clip: clipAt ? clipWithin(b, clipAt(outerView, dims)) : { kind: 'rect', x: 0, y: 0, width: b.w, height: b.h },\n children,\n };\n return [group];\n },\n };\n}\n\ntype Rect = { x: number; y: number; w: number; h: number };\n\n/** `clip` cut to `b`, measured from `b`'s top-left. */\nfunction clipWithin(b: Rect, clip: Rect): { kind: 'rect'; x: number; y: number; width: number; height: number } {\n const x0 = Math.max(b.x, clip.x);\n const y0 = Math.max(b.y, clip.y);\n const x1 = Math.min(b.x + b.w, clip.x + clip.w);\n const y1 = Math.min(b.y + b.h, clip.y + clip.h);\n return { kind: 'rect', x: x0 - b.x, y: y0 - b.y, width: Math.max(0, x1 - x0), height: Math.max(0, y1 - y0) };\n}\n"],"mappings":";;;;;;;;;AAkFA,SAAgB,oBACd,MACsB;CACtB,MAAM,EAAE,IAAI,OAAO,QAAQ,MAAM,QAAQ,MAAM,QAAQ,YAAY,MAAM,eAAe;CACxF,MAAM,YAAY,OAAO,SAAS,aAAa,OAAO,cAAc,IAAI;CACxE,MAAM,UAAU,OAAa,SAC3B,aAAa,cAAe,KAA2C,OAAO,IAAI,CAAC;CACrF,MAAM,WAAW,OAAO,WAAW,aAAa,eAAe;CAC/D,OAAO;EACL;EACA;EACA,OAAO;EACP,WAAW,mBAAmB,QAAQ;EACtC,WAAW,OAAO,MAAM;GACtB,MAAM,OAAO,OAAO,OAAO,IAAI;GAC/B,OAAO,SACH;IAAE;IAAI,MAAM,OAAO,OAAO,IAAI;IAAG;IAAM,MAAM,OAAO,OAAO,IAAI;GAAE,IACjE;IAAE;IAAI,MAAM,OAAO,OAAO,IAAI;IAAG;GAAK;EAC5C;EACA,OAAO,MAAM,WAAW,SAAwB;GAC9C,MAAM,IAAI,OAAO,WAAW,IAAI;GAGhC,MAAM,YAAY;IAAE,OAAO,EAAE;IAAG,QAAQ,EAAE;IAAG,KAAK,KAAK;GAAI;GAC3D,MAAM,WAA0B,aAC5B,uBAAuB;IAAE,MAAM;IAAS,OAAO;GAAW,GAAG,SAAS,IACtE,CAAC;GAGL,MAAM,YAAY,aAAa,WAAW,IAAI,IAAK;GACnD,MAAM,IAAI,OAAO,WAAW,IAAI;GAChC,KAAK,MAAM,SAAS,SAAS,GAC3B,KAAK,MAAM,KAAK,aAAa,OAAO,WAAW,GAAG,SAAS,GAAG,SAAS,KAAK,CAAC;GAS/E,OAAO,CAAC;IALN,MAAM;IACN,WAHwB,KAAK,WAAW,KAAK,SAAS,GAAG,EAAE,GAAG,EAAE,CAGhE;IACA,MAAM,SAAS,WAAW,GAAG,OAAO,WAAW,IAAI,CAAC,IAAI;KAAE,MAAM;KAAQ,GAAG;KAAG,GAAG;KAAG,OAAO,EAAE;KAAG,QAAQ,EAAE;IAAE;IAC5G;GAEM,CAAK;EACf;CACF;AACF;;AAKA,SAAS,WAAW,GAAS,MAAmF;CAC9G,MAAM,KAAK,KAAK,IAAI,EAAE,GAAG,KAAK,CAAC;CAC/B,MAAM,KAAK,KAAK,IAAI,EAAE,GAAG,KAAK,CAAC;CAC/B,MAAM,KAAK,KAAK,IAAI,EAAE,IAAI,EAAE,GAAG,KAAK,IAAI,KAAK,CAAC;CAC9C,MAAM,KAAK,KAAK,IAAI,EAAE,IAAI,EAAE,GAAG,KAAK,IAAI,KAAK,CAAC;CAC9C,OAAO;EAAE,MAAM;EAAQ,GAAG,KAAK,EAAE;EAAG,GAAG,KAAK,EAAE;EAAG,OAAO,KAAK,IAAI,GAAG,KAAK,EAAE;EAAG,QAAQ,KAAK,IAAI,GAAG,KAAK,EAAE;CAAE;AAC7G"}