@weasel-js/core 1.13.0-rc.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 (745) hide show
  1. package/CHANGELOG.md +408 -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 +17 -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/buildSceneLayer.js +1 -1
  133. package/dist/canvas/buildSceneLayer.js.map +1 -1
  134. package/dist/canvas/buildSceneTree.js +5 -3
  135. package/dist/canvas/buildSceneTree.js.map +1 -1
  136. package/dist/canvas/canvasRenderer.js +6 -2
  137. package/dist/canvas/canvasRenderer.js.map +1 -1
  138. package/dist/canvas/deps/editAnchors.js +4 -4
  139. package/dist/canvas/deps/editAnchors.js.map +1 -1
  140. package/dist/canvas/deps/enteredGroup.js +14 -5
  141. package/dist/canvas/deps/enteredGroup.js.map +1 -1
  142. package/dist/canvas/deps/ingestion.js +5 -1
  143. package/dist/canvas/deps/ingestion.js.map +1 -1
  144. package/dist/canvas/deps/insert.js.map +1 -1
  145. package/dist/canvas/deps/insertSite.js +5 -3
  146. package/dist/canvas/deps/insertSite.js.map +1 -1
  147. package/dist/canvas/deps/nodeAtPoint.js +1 -12
  148. package/dist/canvas/deps/nodeAtPoint.js.map +1 -1
  149. package/dist/canvas/deps/pathEditCommits.js +3 -3
  150. package/dist/canvas/deps/pathEditCommits.js.map +1 -1
  151. package/dist/canvas/deps/slice.js +16 -2
  152. package/dist/canvas/deps/slice.js.map +1 -1
  153. package/dist/canvas/deps/snap.js +5 -5
  154. package/dist/canvas/deps/snap.js.map +1 -1
  155. package/dist/canvas/deps/textEdit.js +5 -6
  156. package/dist/canvas/deps/textEdit.js.map +1 -1
  157. package/dist/canvas/derivedPath.js +8 -3
  158. package/dist/canvas/derivedPath.js.map +1 -1
  159. package/dist/canvas/getNodeAtPoint.js +2 -2
  160. package/dist/canvas/getNodeAtPoint.js.map +1 -1
  161. package/dist/canvas/insertPreviewExtent.js.map +1 -1
  162. package/dist/canvas/layerSlots.js.map +1 -1
  163. package/dist/canvas/nodeShapes/measurePainter.js +1 -1
  164. package/dist/canvas/nodeShapes/measurePainter.js.map +1 -1
  165. package/dist/canvas/nodeShapes/strokedBounds.js +5 -3
  166. package/dist/canvas/nodeShapes/strokedBounds.js.map +1 -1
  167. package/dist/canvas/overlayRoleStrokes.js +6 -0
  168. package/dist/canvas/overlayRoleStrokes.js.map +1 -1
  169. package/dist/canvas/pickIndex/scenePickIndex.js +10 -12
  170. package/dist/canvas/pickIndex/scenePickIndex.js.map +1 -1
  171. package/dist/canvas/sceneAdapter.js +10 -19
  172. package/dist/canvas/sceneAdapter.js.map +1 -1
  173. package/dist/canvas/sceneClipboard.js +17 -4
  174. package/dist/canvas/sceneClipboard.js.map +1 -1
  175. package/dist/canvas/sceneViewRender.js +7 -5
  176. package/dist/canvas/sceneViewRender.js.map +1 -1
  177. package/dist/canvas/surfaceContribution.js.map +1 -1
  178. package/dist/canvas/useCanvasPaint.js +12 -4
  179. package/dist/canvas/useCanvasPaint.js.map +1 -1
  180. package/dist/canvas/useHitObserver.js +1 -1
  181. package/dist/canvas/useHitObserver.js.map +1 -1
  182. package/dist/canvas/useViewAim.js +1 -1
  183. package/dist/canvas/useViewAim.js.map +1 -1
  184. package/dist/canvas/useViewHelpers.js +8 -5
  185. package/dist/canvas/useViewHelpers.js.map +1 -1
  186. package/dist/canvas/useViewNesting.js +39 -12
  187. package/dist/canvas/useViewNesting.js.map +1 -1
  188. package/dist/canvas/viewInputs.js.map +1 -1
  189. package/dist/canvas/viewMarkers.js +2 -2
  190. package/dist/canvas/viewMarkers.js.map +1 -1
  191. package/dist/canvas/viewRegistry.js +2 -2
  192. package/dist/canvas/viewRegistry.js.map +1 -1
  193. package/dist/canvas/viewSnap.js +67 -0
  194. package/dist/canvas/viewSnap.js.map +1 -0
  195. package/dist/clipboard.d.ts +6 -12
  196. package/dist/{clone-qb2e1F5p.d.ts → clone-Bz4iqGfE.d.ts} +10 -5
  197. package/dist/clone.d.ts +5 -5
  198. package/dist/core/geometry/booleanOfPaths.js +23 -0
  199. package/dist/core/geometry/booleanOfPaths.js.map +1 -0
  200. package/dist/core/geometry/pathPoseDescriptor.js +12 -9
  201. package/dist/core/geometry/pathPoseDescriptor.js.map +1 -1
  202. package/dist/core/geometry/poseDescriptor.js +12 -3
  203. package/dist/core/geometry/poseDescriptor.js.map +1 -1
  204. package/dist/core/geometry/targetFrame.js +2 -13
  205. package/dist/core/geometry/targetFrame.js.map +1 -1
  206. package/dist/core/layers/render.js +2 -1
  207. package/dist/core/layers/render.js.map +1 -1
  208. package/dist/core/layers/retain.js +5 -3
  209. package/dist/core/layers/retain.js.map +1 -1
  210. package/dist/core/ops/setPath.js +3 -0
  211. package/dist/core/ops/setPath.js.map +1 -1
  212. package/dist/core/paint/meshKindLazy.js.map +1 -1
  213. package/dist/core/paint/paintKindEntry.js.map +1 -1
  214. package/dist/core/paint/paintKinds.js +8 -1
  215. package/dist/core/paint/paintKinds.js.map +1 -1
  216. package/dist/core/scene/NodeProperties.js.map +1 -1
  217. package/dist/core/scene/NodeRouting.js.map +1 -1
  218. package/dist/core/scene/booleanGroup.js +33 -0
  219. package/dist/core/scene/booleanGroup.js.map +1 -0
  220. package/dist/core/scene/booleanOfChildren.js +53 -0
  221. package/dist/core/scene/booleanOfChildren.js.map +1 -0
  222. package/dist/core/scene/carryDescendants.js +18 -6
  223. package/dist/core/scene/carryDescendants.js.map +1 -1
  224. package/dist/core/scene/composeFrame.js +42 -5
  225. package/dist/core/scene/composeFrame.js.map +1 -1
  226. package/dist/core/scene/composePose.js +35 -16
  227. package/dist/core/scene/composePose.js.map +1 -1
  228. package/dist/core/scene/depMemo.js +36 -18
  229. package/dist/core/scene/depMemo.js.map +1 -1
  230. package/dist/core/scene/derivedParts.js +2 -23
  231. package/dist/core/scene/derivedParts.js.map +1 -1
  232. package/dist/core/scene/effectivePose.js +10 -0
  233. package/dist/core/scene/effectivePose.js.map +1 -1
  234. package/dist/core/scene/historyForm.js +5 -4
  235. package/dist/core/scene/historyForm.js.map +1 -1
  236. package/dist/core/scene/kitOps.js +5 -71
  237. package/dist/core/scene/kitOps.js.map +1 -1
  238. package/dist/core/scene/kitRegistry.js +5 -0
  239. package/dist/core/scene/kitRegistry.js.map +1 -1
  240. package/dist/core/scene/layerOps.js +82 -0
  241. package/dist/core/scene/layerOps.js.map +1 -0
  242. package/dist/core/scene/layoutPass.js +14 -12
  243. package/dist/core/scene/layoutPass.js.map +1 -1
  244. package/dist/core/scene/lockGuard.js +9 -3
  245. package/dist/core/scene/lockGuard.js.map +1 -1
  246. package/dist/core/scene/nestedHit.js +5 -5
  247. package/dist/core/scene/nestedHit.js.map +1 -1
  248. package/dist/core/scene/nodeFlags.js +8 -2
  249. package/dist/core/scene/nodeFlags.js.map +1 -1
  250. package/dist/core/scene/nodeGates.js +31 -2
  251. package/dist/core/scene/nodeGates.js.map +1 -1
  252. package/dist/core/scene/pathEffects/effects.js +22 -3
  253. package/dist/core/scene/pathEffects/effects.js.map +1 -1
  254. package/dist/core/scene/pathEffects/textAlong.js +16 -3
  255. package/dist/core/scene/pathEffects/textAlong.js.map +1 -1
  256. package/dist/core/scene/pathEffects/textAlongBands.js +65 -0
  257. package/dist/core/scene/pathEffects/textAlongBands.js.map +1 -0
  258. package/dist/core/scene/reconcileSpecs.js +1 -1
  259. package/dist/core/scene/reconcileSpecs.js.map +1 -1
  260. package/dist/core/scene/scene.js +11 -5
  261. package/dist/core/scene/scene.js.map +1 -1
  262. package/dist/core/scene/sceneEdits.js +5 -2
  263. package/dist/core/scene/sceneEdits.js.map +1 -1
  264. package/dist/core/scene/scenePoseAdapter.js +10 -1
  265. package/dist/core/scene/scenePoseAdapter.js.map +1 -1
  266. package/dist/core/scene/scrollOffsets.js +93 -0
  267. package/dist/core/scene/scrollOffsets.js.map +1 -0
  268. package/dist/core/scene/serialization.js +15 -5
  269. package/dist/core/scene/serialization.js.map +1 -1
  270. package/dist/core/scene/statedFields.js +2 -1
  271. package/dist/core/scene/statedFields.js.map +1 -1
  272. package/dist/core/scene/strategyView.js +35 -0
  273. package/dist/core/scene/strategyView.js.map +1 -0
  274. package/dist/core/scene/types.js.map +1 -1
  275. package/dist/core/scene/useScene.js +2 -0
  276. package/dist/core/scene/useScene.js.map +1 -1
  277. package/dist/core/scene/worldSave.js +87 -0
  278. package/dist/core/scene/worldSave.js.map +1 -0
  279. package/dist/core/selection/selectionTarget.js +1 -1
  280. package/dist/core/selection/selectionTarget.js.map +1 -1
  281. package/dist/{cornerHandles-CNCb7aTY.d.ts → cornerHandles-DpmQM_z5.d.ts} +1 -1
  282. package/dist/{depSchema-B1RS-IJx.d.ts → depSchema-C3BwxitB.d.ts} +130 -104
  283. package/dist/devtools-protocol.d.ts +2 -2
  284. package/dist/devtools.d.ts +2 -2
  285. package/dist/{features → domains}/chrome-caps/buildChromeCtx.js +1 -1
  286. package/dist/domains/chrome-caps/buildChromeCtx.js.map +1 -0
  287. package/dist/{features → domains}/chrome-caps/conditions.js +1 -1
  288. package/dist/domains/chrome-caps/conditions.js.map +1 -0
  289. package/dist/{features → domains}/chrome-caps/defaults.js +1 -1
  290. package/dist/domains/chrome-caps/defaults.js.map +1 -0
  291. package/dist/{features → domains}/chrome-caps/resolve.js +1 -1
  292. package/dist/domains/chrome-caps/resolve.js.map +1 -0
  293. package/dist/{features → domains}/chrome-caps/useChromeCaps.js +1 -1
  294. package/dist/domains/chrome-caps/useChromeCaps.js.map +1 -0
  295. package/dist/{features → domains}/chrome-caps/useHoverTracking.js +1 -1
  296. package/dist/domains/chrome-caps/useHoverTracking.js.map +1 -0
  297. package/dist/{features → domains}/ingestion/contentHandlers.js +20 -3
  298. package/dist/domains/ingestion/contentHandlers.js.map +1 -0
  299. package/dist/domains/ingestion/ghost.js +56 -0
  300. package/dist/domains/ingestion/ghost.js.map +1 -0
  301. package/dist/{features → domains}/ingestion/imageHandler.js +1 -1
  302. package/dist/domains/ingestion/imageHandler.js.map +1 -0
  303. package/dist/domains/ingestion/kitHandlers.js +22 -0
  304. package/dist/domains/ingestion/kitHandlers.js.map +1 -0
  305. package/dist/{features → domains}/ingestion/openFilePicker.js +1 -1
  306. package/dist/domains/ingestion/openFilePicker.js.map +1 -0
  307. package/dist/{features → domains}/ingestion/placement.js +1 -1
  308. package/dist/domains/ingestion/placement.js.map +1 -0
  309. package/dist/{features → domains}/ingestion/svgHandler.js +1 -1
  310. package/dist/domains/ingestion/svgHandler.js.map +1 -0
  311. package/dist/{features → domains}/ingestion/textHandler.js +9 -7
  312. package/dist/domains/ingestion/textHandler.js.map +1 -0
  313. package/dist/{features → domains}/ingestion/useCanvasIngestion.js +13 -15
  314. package/dist/domains/ingestion/useCanvasIngestion.js.map +1 -0
  315. package/dist/{features → domains}/ingestion/weaselJsonHandler.js +1 -1
  316. package/dist/domains/ingestion/weaselJsonHandler.js.map +1 -0
  317. package/dist/{features → domains}/longPress/longPressFeedback.js +1 -1
  318. package/dist/domains/longPress/longPressFeedback.js.map +1 -0
  319. package/dist/{features → domains}/measure/actionIds.js +1 -1
  320. package/dist/domains/measure/actionIds.js.map +1 -0
  321. package/dist/{features → domains}/measure/measureCommands.js +1 -1
  322. package/dist/domains/measure/measureCommands.js.map +1 -0
  323. package/dist/{features → domains}/measure/measureContribution.js +1 -1
  324. package/dist/domains/measure/measureContribution.js.map +1 -0
  325. package/dist/{features → domains}/measure/measureEdits.js +1 -1
  326. package/dist/domains/measure/measureEdits.js.map +1 -0
  327. package/dist/{features → domains}/measure/measureGestures.js +4 -7
  328. package/dist/domains/measure/measureGestures.js.map +1 -0
  329. package/dist/{features → domains}/measure/measureHit.js +1 -1
  330. package/dist/domains/measure/measureHit.js.map +1 -0
  331. package/dist/{features → domains}/measure/measureLayer.js +1 -1
  332. package/dist/domains/measure/measureLayer.js.map +1 -0
  333. package/dist/{features → domains}/measure/measurement.js +1 -1
  334. package/dist/domains/measure/measurement.js.map +1 -0
  335. package/dist/{features → domains}/measure/measurements.js +2 -2
  336. package/dist/domains/measure/measurements.js.map +1 -0
  337. package/dist/{features → domains}/modes/modeLayers.js +1 -1
  338. package/dist/domains/modes/modeLayers.js.map +1 -0
  339. package/dist/{features → domains}/modes/modeTriggers.js +3 -6
  340. package/dist/domains/modes/modeTriggers.js.map +1 -0
  341. package/dist/{features → domains}/pathEffects/effectSources.js +1 -1
  342. package/dist/domains/pathEffects/effectSources.js.map +1 -0
  343. package/dist/{features → domains}/pathEffects/index.js +1 -1
  344. package/dist/domains/pathEffects/index.js.map +1 -0
  345. package/dist/{features → domains}/pathEffects/readSource.js +1 -1
  346. package/dist/domains/pathEffects/readSource.js.map +1 -0
  347. package/dist/{features → domains}/pathEffects/textItems.js +11 -2
  348. package/dist/domains/pathEffects/textItems.js.map +1 -0
  349. package/dist/{features → domains}/pathEffects/useEffectSourcesLayer.js +1 -1
  350. package/dist/domains/pathEffects/useEffectSourcesLayer.js.map +1 -0
  351. package/dist/{features → domains}/paths/anchorData.js +1 -1
  352. package/dist/domains/paths/anchorData.js.map +1 -0
  353. package/dist/{features → domains}/paths/anchorMarkers.js +9 -7
  354. package/dist/domains/paths/anchorMarkers.js.map +1 -0
  355. package/dist/{features → domains}/paths/editablePath.js +1 -1
  356. package/dist/domains/paths/editablePath.js.map +1 -0
  357. package/dist/{features → domains}/paths/index.js +3 -3
  358. package/dist/{features → domains}/paths/originProjection.js +1 -1
  359. package/dist/domains/paths/originProjection.js.map +1 -0
  360. package/dist/{features → domains}/paths/pathEditExtension.js +1 -1
  361. package/dist/domains/paths/pathEditExtension.js.map +1 -0
  362. package/dist/{features → domains}/paths/pathEditState.js +1 -1
  363. package/dist/domains/paths/pathEditState.js.map +1 -0
  364. package/dist/{features → domains}/paths/pathEditingOverlayLayer.js +1 -1
  365. package/dist/domains/paths/pathEditingOverlayLayer.js.map +1 -0
  366. package/dist/{features → domains}/paths/pathLayer.js +1 -1
  367. package/dist/domains/paths/pathLayer.js.map +1 -0
  368. package/dist/{features → domains}/paths/pathRepresentation.js +1 -1
  369. package/dist/domains/paths/pathRepresentation.js.map +1 -0
  370. package/dist/{features → domains}/paths/pathSnap/glyphs.js +1 -1
  371. package/dist/domains/paths/pathSnap/glyphs.js.map +1 -0
  372. package/dist/{features → domains}/paths/pathSnap/pathSnapSource.js +1 -1
  373. package/dist/domains/paths/pathSnap/pathSnapSource.js.map +1 -0
  374. package/dist/{features → domains}/paths/pathSnap/sceneIndex.js +1 -1
  375. package/dist/domains/paths/pathSnap/sceneIndex.js.map +1 -0
  376. package/dist/{features → domains}/paths/pathSnap/snapGeometry.js +3 -2
  377. package/dist/domains/paths/pathSnap/snapGeometry.js.map +1 -0
  378. package/dist/{features → domains}/paths/penPreviewLayer.js +1 -1
  379. package/dist/domains/paths/penPreviewLayer.js.map +1 -0
  380. package/dist/{features → domains}/paths/shapeGenerator.js +6 -4
  381. package/dist/domains/paths/shapeGenerator.js.map +1 -0
  382. package/dist/{features → domains}/paths/strokeOutline.js +1 -1
  383. package/dist/domains/paths/strokeOutline.js.map +1 -0
  384. package/dist/{features → domains}/paths/useAnchorFields.js +1 -1
  385. package/dist/domains/paths/useAnchorFields.js.map +1 -0
  386. package/dist/{features → domains}/paths/usePathEdit.js +1 -1
  387. package/dist/domains/paths/usePathEdit.js.map +1 -0
  388. package/dist/{features → domains}/poseRun/usePoseRun.js +1 -1
  389. package/dist/domains/poseRun/usePoseRun.js.map +1 -0
  390. package/dist/{features → domains}/selection/enteredGroupFrame.js +49 -10
  391. package/dist/domains/selection/enteredGroupFrame.js.map +1 -0
  392. package/dist/{features → domains}/selection/enteredGroupScope.js +1 -1
  393. package/dist/domains/selection/enteredGroupScope.js.map +1 -0
  394. package/dist/domains/selection/enteredOperands.js +29 -0
  395. package/dist/domains/selection/enteredOperands.js.map +1 -0
  396. package/dist/{features → domains}/selection/handleCommands.js +1 -1
  397. package/dist/domains/selection/handleCommands.js.map +1 -0
  398. package/dist/{features → domains}/selection/overlay.js +1 -1
  399. package/dist/domains/selection/overlay.js.map +1 -0
  400. package/dist/{features → domains}/selection/selectionPose.js +1 -1
  401. package/dist/domains/selection/selectionPose.js.map +1 -0
  402. package/dist/{features → domains}/snap/angle2d.js +1 -1
  403. package/dist/domains/snap/angle2d.js.map +1 -0
  404. package/dist/{features → domains}/snap/debugCandidates.js +1 -1
  405. package/dist/domains/snap/debugCandidates.js.map +1 -0
  406. package/dist/{features → domains}/snap/gather.js +1 -1
  407. package/dist/domains/snap/gather.js.map +1 -0
  408. package/dist/{features → domains}/snap/inputSubspace.js +1 -1
  409. package/dist/domains/snap/inputSubspace.js.map +1 -0
  410. package/dist/{features → domains}/snap/lazySession.js +1 -1
  411. package/dist/domains/snap/lazySession.js.map +1 -0
  412. package/dist/{features → domains}/snap/marks.js +1 -1
  413. package/dist/domains/snap/marks.js.map +1 -0
  414. package/dist/{features → domains}/snap/outcomeStore.js +1 -1
  415. package/dist/domains/snap/outcomeStore.js.map +1 -0
  416. package/dist/{features → domains}/snap/pointSnap.js +1 -1
  417. package/dist/domains/snap/pointSnap.js.map +1 -0
  418. package/dist/{features → domains}/snap/probes2d.js +1 -1
  419. package/dist/domains/snap/probes2d.js.map +1 -0
  420. package/dist/{features → domains}/snap/readoutDraw.js +1 -1
  421. package/dist/domains/snap/readoutDraw.js.map +1 -0
  422. package/dist/{features → domains}/snap/readoutLayer.js +1 -1
  423. package/dist/domains/snap/readoutLayer.js.map +1 -0
  424. package/dist/{features → domains}/snap/registry.js +1 -1
  425. package/dist/domains/snap/registry.js.map +1 -0
  426. package/dist/{features → domains}/snap/resizeBox.js +1 -1
  427. package/dist/domains/snap/resizeBox.js.map +1 -0
  428. package/dist/{features → domains}/snap/roles.js +1 -1
  429. package/dist/domains/snap/roles.js.map +1 -0
  430. package/dist/{features → domains}/snap/rotationSource.js +1 -1
  431. package/dist/domains/snap/rotationSource.js.map +1 -0
  432. package/dist/{features → domains}/snap/sceneReads.js +24 -3
  433. package/dist/domains/snap/sceneReads.js.map +1 -0
  434. package/dist/{features → domains}/snap/sceneSession2d.js +3 -2
  435. package/dist/domains/snap/sceneSession2d.js.map +1 -0
  436. package/dist/{features → domains}/snap/screen.js +1 -1
  437. package/dist/domains/snap/screen.js.map +1 -0
  438. package/dist/{features → domains}/snap/session2d.js +3 -2
  439. package/dist/domains/snap/session2d.js.map +1 -0
  440. package/dist/{features → domains}/snap/spanSnap.js +1 -1
  441. package/dist/domains/snap/spanSnap.js.map +1 -0
  442. package/dist/{features → domains}/snap/stepAngles.js +1 -1
  443. package/dist/domains/snap/stepAngles.js.map +1 -0
  444. package/dist/{features → domains}/text/editingDrawOne.js +4 -4
  445. package/dist/domains/text/editingDrawOne.js.map +1 -0
  446. package/dist/domains/text/hostTextEditors.js +24 -0
  447. package/dist/domains/text/hostTextEditors.js.map +1 -0
  448. package/dist/{features → domains}/text/index.js +1 -1
  449. package/dist/{features → domains}/text/sceneTextDrafts.js +1 -1
  450. package/dist/domains/text/sceneTextDrafts.js.map +1 -0
  451. package/dist/{features → domains}/text/textHitAt.js +18 -2
  452. package/dist/domains/text/textHitAt.js.map +1 -0
  453. package/dist/{features → domains}/text/textLayer.js +1 -1
  454. package/dist/domains/text/textLayer.js.map +1 -0
  455. package/dist/{features → domains}/text/useSceneTextEdit.js +11 -3
  456. package/dist/domains/text/useSceneTextEdit.js.map +1 -0
  457. package/dist/{features → domains}/viewports/aim.js +1 -1
  458. package/dist/domains/viewports/aim.js.map +1 -0
  459. package/dist/{features → domains}/viewports/viewMarker.js +23 -5
  460. package/dist/domains/viewports/viewMarker.js.map +1 -0
  461. package/dist/{features → domains}/viewports/viewMarkerDrag.js +3 -3
  462. package/dist/domains/viewports/viewMarkerDrag.js.map +1 -0
  463. package/dist/{features → domains}/viewports/viewResolver.js +1 -1
  464. package/dist/domains/viewports/viewResolver.js.map +1 -0
  465. package/dist/{features → domains}/viewports/viewportLayer.js +3 -2
  466. package/dist/domains/viewports/viewportLayer.js.map +1 -0
  467. package/dist/icons.table.js +1 -1
  468. package/dist/icons.table.js.map +1 -1
  469. package/dist/icons.table.json +106 -6
  470. package/dist/index.d.ts +1445 -1053
  471. package/dist/index.js +90 -77
  472. package/dist/interactions/actions/admitsChild.js +6 -2
  473. package/dist/interactions/actions/admitsChild.js.map +1 -1
  474. package/dist/interactions/actions/align/align.js +1 -4
  475. package/dist/interactions/actions/align/align.js.map +1 -1
  476. package/dist/interactions/actions/booleans/booleanGroupAction.js +66 -0
  477. package/dist/interactions/actions/booleans/booleanGroupAction.js.map +1 -0
  478. package/dist/interactions/actions/booleans/keymap.js +52 -0
  479. package/dist/interactions/actions/booleans/keymap.js.map +1 -0
  480. package/dist/interactions/actions/booleans/operands.js +1 -1
  481. package/dist/interactions/actions/booleans/pieces.js +4 -11
  482. package/dist/interactions/actions/booleans/pieces.js.map +1 -1
  483. package/dist/interactions/actions/clipboard/action.js +3 -7
  484. package/dist/interactions/actions/clipboard/action.js.map +1 -1
  485. package/dist/interactions/actions/clipboard/clipboardOps.js +1 -1
  486. package/dist/interactions/actions/clipboard/clipboardOps.js.map +1 -1
  487. package/dist/interactions/actions/clipboard/materializePaste.js.map +1 -1
  488. package/dist/interactions/actions/defaults/anchorEditing.js +6 -6
  489. package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
  490. package/dist/interactions/actions/defaults/bendSegment.js +1 -1
  491. package/dist/interactions/actions/defaults/bendSegment.js.map +1 -1
  492. package/dist/interactions/actions/defaults/containerScroll.js +170 -0
  493. package/dist/interactions/actions/defaults/containerScroll.js.map +1 -0
  494. package/dist/interactions/actions/defaults/contextMenu.js +2 -5
  495. package/dist/interactions/actions/defaults/contextMenu.js.map +1 -1
  496. package/dist/interactions/actions/defaults/delete.js +1 -1
  497. package/dist/interactions/actions/defaults/delete.js.map +1 -1
  498. package/dist/interactions/actions/defaults/duplicate.js +1 -3
  499. package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
  500. package/dist/interactions/actions/defaults/enterGroup.js +7 -12
  501. package/dist/interactions/actions/defaults/enterGroup.js.map +1 -1
  502. package/dist/interactions/actions/defaults/enterPathEdit.js +4 -8
  503. package/dist/interactions/actions/defaults/enterPathEdit.js.map +1 -1
  504. package/dist/interactions/actions/defaults/group.js +12 -24
  505. package/dist/interactions/actions/defaults/group.js.map +1 -1
  506. package/dist/interactions/actions/defaults/ingest.js +18 -10
  507. package/dist/interactions/actions/defaults/ingest.js.map +1 -1
  508. package/dist/interactions/actions/defaults/insertPathAnchor.js +3 -3
  509. package/dist/interactions/actions/defaults/insertPathAnchor.js.map +1 -1
  510. package/dist/interactions/actions/defaults/nudgeSteps.js +3 -21
  511. package/dist/interactions/actions/defaults/nudgeSteps.js.map +1 -1
  512. package/dist/interactions/actions/defaults/pathEditMarks.js.map +1 -1
  513. package/dist/interactions/actions/defaults/selectDeep.js +2 -2
  514. package/dist/interactions/actions/defaults/selectDeep.js.map +1 -1
  515. package/dist/interactions/actions/distribute/distribute.js.map +1 -1
  516. package/dist/interactions/actions/dropVerdict.js +9 -0
  517. package/dist/interactions/actions/dropVerdict.js.map +1 -0
  518. package/dist/interactions/actions/edit-anchors/action.js +2 -2
  519. package/dist/interactions/actions/edit-anchors/action.js.map +1 -1
  520. package/dist/interactions/actions/edit-anchors/extensionDrags.js.map +1 -1
  521. package/dist/interactions/actions/edit-anchors/geometry.js +8 -6
  522. package/dist/interactions/actions/edit-anchors/geometry.js.map +1 -1
  523. package/dist/interactions/actions/edit-anchors/handleDrag.js.map +1 -1
  524. package/dist/interactions/actions/edit-anchors/preview.js +1 -1
  525. package/dist/interactions/actions/edit-anchors/preview.js.map +1 -1
  526. package/dist/interactions/actions/flip/action.js +3 -2
  527. package/dist/interactions/actions/flip/action.js.map +1 -1
  528. package/dist/interactions/actions/geometryProjection.js +8 -3
  529. package/dist/interactions/actions/geometryProjection.js.map +1 -1
  530. package/dist/interactions/actions/gestureFrames.js +1 -0
  531. package/dist/interactions/actions/gestureFrames.js.map +1 -1
  532. package/dist/interactions/actions/insert/action.js +6 -2
  533. package/dist/interactions/actions/insert/action.js.map +1 -1
  534. package/dist/interactions/actions/insert/frame.js +1 -1
  535. package/dist/interactions/actions/insert/frame.js.map +1 -1
  536. package/dist/interactions/actions/insert/placeAction.js +6 -8
  537. package/dist/interactions/actions/insert/placeAction.js.map +1 -1
  538. package/dist/interactions/actions/insert/session.js +2 -2
  539. package/dist/interactions/actions/insert/session.js.map +1 -1
  540. package/dist/interactions/actions/move/action.js +6 -15
  541. package/dist/interactions/actions/move/action.js.map +1 -1
  542. package/dist/interactions/actions/move/behaviors/layoutDrop.js +12 -2
  543. package/dist/interactions/actions/move/behaviors/layoutDrop.js.map +1 -1
  544. package/dist/interactions/actions/move/behaviors/reparentOnDrop.js +29 -10
  545. package/dist/interactions/actions/move/behaviors/reparentOnDrop.js.map +1 -1
  546. package/dist/interactions/actions/move/behaviors/snapToContainer.js +42 -6
  547. package/dist/interactions/actions/move/behaviors/snapToContainer.js.map +1 -1
  548. package/dist/interactions/actions/move/capture.js +4 -8
  549. package/dist/interactions/actions/move/capture.js.map +1 -1
  550. package/dist/interactions/actions/move/commit/index.js.map +1 -1
  551. package/dist/interactions/actions/move/commit/layoutDrop.js +4 -3
  552. package/dist/interactions/actions/move/commit/layoutDrop.js.map +1 -1
  553. package/dist/interactions/actions/move/commit/layoutRelease.js +15 -9
  554. package/dist/interactions/actions/move/commit/layoutRelease.js.map +1 -1
  555. package/dist/interactions/actions/move/commit/reparent.js +19 -13
  556. package/dist/interactions/actions/move/commit/reparent.js.map +1 -1
  557. package/dist/interactions/actions/move/delta.js +51 -5
  558. package/dist/interactions/actions/move/delta.js.map +1 -1
  559. package/dist/interactions/actions/move/frame.js +8 -4
  560. package/dist/interactions/actions/move/frame.js.map +1 -1
  561. package/dist/interactions/actions/move/layout/container.js +19 -9
  562. package/dist/interactions/actions/move/layout/container.js.map +1 -1
  563. package/dist/interactions/actions/move/layout/dragged.js +2 -2
  564. package/dist/interactions/actions/move/layout/dragged.js.map +1 -1
  565. package/dist/interactions/actions/move/layout/dropPass.js +36 -18
  566. package/dist/interactions/actions/move/layout/dropPass.js.map +1 -1
  567. package/dist/interactions/actions/move/layout/exits.js +2 -2
  568. package/dist/interactions/actions/move/layout/exits.js.map +1 -1
  569. package/dist/interactions/actions/move/layout/glide.js +5 -5
  570. package/dist/interactions/actions/move/layout/glide.js.map +1 -1
  571. package/dist/interactions/actions/move/layout/view.js +55 -0
  572. package/dist/interactions/actions/move/layout/view.js.map +1 -0
  573. package/dist/interactions/actions/move/refusal.js +52 -0
  574. package/dist/interactions/actions/move/refusal.js.map +1 -0
  575. package/dist/interactions/actions/move/session.js +9 -8
  576. package/dist/interactions/actions/move/session.js.map +1 -1
  577. package/dist/interactions/actions/move/translate.js +10 -4
  578. package/dist/interactions/actions/move/translate.js.map +1 -1
  579. package/dist/interactions/actions/moveNodes.js +21 -5
  580. package/dist/interactions/actions/moveNodes.js.map +1 -1
  581. package/dist/interactions/actions/pathCommands/pathNodes.js +3 -3
  582. package/dist/interactions/actions/pathCommands/pathNodes.js.map +1 -1
  583. package/dist/interactions/actions/planeInput.js +14 -13
  584. package/dist/interactions/actions/planeInput.js.map +1 -1
  585. package/dist/interactions/actions/poseFrame.js +21 -8
  586. package/dist/interactions/actions/poseFrame.js.map +1 -1
  587. package/dist/interactions/actions/resize/action.js +1 -1
  588. package/dist/interactions/actions/resize/action.js.map +1 -1
  589. package/dist/interactions/actions/resize/aspect.js +1 -1
  590. package/dist/interactions/actions/resize/aspect.js.map +1 -1
  591. package/dist/interactions/actions/resize/commit.js +10 -1
  592. package/dist/interactions/actions/resize/commit.js.map +1 -1
  593. package/dist/interactions/actions/resize/frame.js +3 -2
  594. package/dist/interactions/actions/resize/frame.js.map +1 -1
  595. package/dist/interactions/actions/resize/held.js +44 -0
  596. package/dist/interactions/actions/resize/held.js.map +1 -0
  597. package/dist/interactions/actions/resize/session.js +3 -1
  598. package/dist/interactions/actions/resize/session.js.map +1 -1
  599. package/dist/interactions/actions/resolveOverlays.js +9 -0
  600. package/dist/interactions/actions/resolveOverlays.js.map +1 -1
  601. package/dist/interactions/actions/resolvePreviews.js +23 -7
  602. package/dist/interactions/actions/resolvePreviews.js.map +1 -1
  603. package/dist/interactions/actions/rotate/action.js +1 -1
  604. package/dist/interactions/actions/rotate/action.js.map +1 -1
  605. package/dist/interactions/actions/rotate/frame.js +1 -1
  606. package/dist/interactions/actions/rotate/frame.js.map +1 -1
  607. package/dist/interactions/actions/rotate/session.js +2 -2
  608. package/dist/interactions/actions/rotate/session.js.map +1 -1
  609. package/dist/interactions/actions/selectionSubtree.js +2 -4
  610. package/dist/interactions/actions/selectionSubtree.js.map +1 -1
  611. package/dist/interactions/actions/spatialReference.js +4 -9
  612. package/dist/interactions/actions/spatialReference.js.map +1 -1
  613. package/dist/interactions/gestures/dragRadial/dragRadial.js.map +1 -1
  614. package/dist/interactions/gestures/dragRect/dragRect.js.map +1 -1
  615. package/dist/kitSettingsSchema.js +2 -2
  616. package/dist/kitSettingsSchema.js.map +1 -1
  617. package/dist/{math-C_elShP_.d.ts → math-B_unOnF4.d.ts} +2 -2
  618. package/dist/math.d.ts +3 -3
  619. package/dist/move.d.ts +6 -6
  620. package/dist/{options-ClEm3SEl.d.ts → options-DAjkhbcs.d.ts} +5 -5
  621. package/dist/{poseDescriptor-DN2yfnq_.d.ts → poseDescriptor-CvSxVhCd.d.ts} +17 -4
  622. package/dist/resize.d.ts +6 -6
  623. package/dist/style.css +14 -0
  624. package/dist/test-seams.d.ts +11 -15
  625. package/dist/test-seams.js +1 -1
  626. package/dist/tools/builtin/eyedropper/useEyedropperTool.js +3 -6
  627. package/dist/tools/builtin/eyedropper/useEyedropperTool.js.map +1 -1
  628. package/dist/tools/builtin/line/useLineTool.js.map +1 -1
  629. package/dist/tools/builtin/measure/useMeasureTool.js +2 -2
  630. package/dist/tools/builtin/measure/useMeasureTool.js.map +1 -1
  631. package/dist/tools/builtin/pen/penHandle.js +2 -2
  632. package/dist/tools/builtin/pen/penHandle.js.map +1 -1
  633. package/dist/tools/builtin/pen/usePenTool.js +13 -19
  634. package/dist/tools/builtin/pen/usePenTool.js.map +1 -1
  635. package/dist/tools/builtin/polygon/usePolygonTool.js +1 -1
  636. package/dist/tools/builtin/polygon/usePolygonTool.js.map +1 -1
  637. package/dist/tools/builtin/select/groupEntry.js +1 -1
  638. package/dist/tools/builtin/select/groupEntry.js.map +1 -1
  639. package/dist/tools/builtin/select/selectionContributions.js +1 -1
  640. package/dist/tools/builtin/select/selectionContributions.js.map +1 -1
  641. package/dist/tools/builtin/select/useSelectTool.js +7 -7
  642. package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
  643. package/dist/tools/builtin/slice/useSliceTool.js +5 -8
  644. package/dist/tools/builtin/slice/useSliceTool.js.map +1 -1
  645. package/dist/tools/builtin/star/useStarTool.js +1 -1
  646. package/dist/tools/builtin/star/useStarTool.js.map +1 -1
  647. package/dist/tools/index.js +2 -1
  648. package/dist/{types-49uYJN3j.d.ts → types-A_sNJYRd.d.ts} +8 -0
  649. package/dist/{types-BEdRIhNc.d.ts → types-DW14eu9B.d.ts} +131 -36
  650. package/dist/{types-p2alu-R1.d.ts → types-VCtfchp-.d.ts} +4 -6
  651. package/dist/util/color.js +12 -14
  652. package/dist/util/color.js.map +1 -1
  653. package/dist/version.js +1 -1
  654. package/package.json +25 -24
  655. package/dist/canvas/poseCascade.js +0 -65
  656. package/dist/canvas/poseCascade.js.map +0 -1
  657. package/dist/features/chrome-caps/buildChromeCtx.js.map +0 -1
  658. package/dist/features/chrome-caps/conditions.js.map +0 -1
  659. package/dist/features/chrome-caps/defaults.js.map +0 -1
  660. package/dist/features/chrome-caps/resolve.js.map +0 -1
  661. package/dist/features/chrome-caps/useChromeCaps.js.map +0 -1
  662. package/dist/features/chrome-caps/useHoverTracking.js.map +0 -1
  663. package/dist/features/ingestion/contentHandlers.js.map +0 -1
  664. package/dist/features/ingestion/imageHandler.js.map +0 -1
  665. package/dist/features/ingestion/openFilePicker.js.map +0 -1
  666. package/dist/features/ingestion/placement.js.map +0 -1
  667. package/dist/features/ingestion/registerKitHandlers.js +0 -43
  668. package/dist/features/ingestion/registerKitHandlers.js.map +0 -1
  669. package/dist/features/ingestion/svgHandler.js.map +0 -1
  670. package/dist/features/ingestion/textHandler.js.map +0 -1
  671. package/dist/features/ingestion/useCanvasIngestion.js.map +0 -1
  672. package/dist/features/ingestion/weaselJsonHandler.js.map +0 -1
  673. package/dist/features/longPress/longPressFeedback.js.map +0 -1
  674. package/dist/features/measure/actionIds.js.map +0 -1
  675. package/dist/features/measure/measureCommands.js.map +0 -1
  676. package/dist/features/measure/measureContribution.js.map +0 -1
  677. package/dist/features/measure/measureEdits.js.map +0 -1
  678. package/dist/features/measure/measureGestures.js.map +0 -1
  679. package/dist/features/measure/measureHit.js.map +0 -1
  680. package/dist/features/measure/measureLayer.js.map +0 -1
  681. package/dist/features/measure/measurement.js.map +0 -1
  682. package/dist/features/measure/measurements.js.map +0 -1
  683. package/dist/features/modes/modeLayers.js.map +0 -1
  684. package/dist/features/modes/modeTriggers.js.map +0 -1
  685. package/dist/features/pathEffects/effectSources.js.map +0 -1
  686. package/dist/features/pathEffects/index.js.map +0 -1
  687. package/dist/features/pathEffects/readSource.js.map +0 -1
  688. package/dist/features/pathEffects/textItems.js.map +0 -1
  689. package/dist/features/pathEffects/useEffectSourcesLayer.js.map +0 -1
  690. package/dist/features/paths/anchorData.js.map +0 -1
  691. package/dist/features/paths/anchorMarkers.js.map +0 -1
  692. package/dist/features/paths/editablePath.js.map +0 -1
  693. package/dist/features/paths/originProjection.js.map +0 -1
  694. package/dist/features/paths/pathEditExtension.js.map +0 -1
  695. package/dist/features/paths/pathEditState.js.map +0 -1
  696. package/dist/features/paths/pathEditingOverlayLayer.js.map +0 -1
  697. package/dist/features/paths/pathLayer.js.map +0 -1
  698. package/dist/features/paths/pathRepresentation.js.map +0 -1
  699. package/dist/features/paths/pathSnap/glyphs.js.map +0 -1
  700. package/dist/features/paths/pathSnap/pathSnapSource.js.map +0 -1
  701. package/dist/features/paths/pathSnap/sceneIndex.js.map +0 -1
  702. package/dist/features/paths/pathSnap/snapGeometry.js.map +0 -1
  703. package/dist/features/paths/penPreviewLayer.js.map +0 -1
  704. package/dist/features/paths/shapeGenerator.js.map +0 -1
  705. package/dist/features/paths/strokeOutline.js.map +0 -1
  706. package/dist/features/paths/useAnchorFields.js.map +0 -1
  707. package/dist/features/paths/usePathEdit.js.map +0 -1
  708. package/dist/features/poseRun/usePoseRun.js.map +0 -1
  709. package/dist/features/selection/enteredGroupFrame.js.map +0 -1
  710. package/dist/features/selection/enteredGroupScope.js.map +0 -1
  711. package/dist/features/selection/handleCommands.js.map +0 -1
  712. package/dist/features/selection/overlay.js.map +0 -1
  713. package/dist/features/selection/selectionPose.js.map +0 -1
  714. package/dist/features/snap/angle2d.js.map +0 -1
  715. package/dist/features/snap/debugCandidates.js.map +0 -1
  716. package/dist/features/snap/gather.js.map +0 -1
  717. package/dist/features/snap/inputSubspace.js.map +0 -1
  718. package/dist/features/snap/lazySession.js.map +0 -1
  719. package/dist/features/snap/marks.js.map +0 -1
  720. package/dist/features/snap/outcomeStore.js.map +0 -1
  721. package/dist/features/snap/pointSnap.js.map +0 -1
  722. package/dist/features/snap/probes2d.js.map +0 -1
  723. package/dist/features/snap/readoutDraw.js.map +0 -1
  724. package/dist/features/snap/readoutLayer.js.map +0 -1
  725. package/dist/features/snap/registry.js.map +0 -1
  726. package/dist/features/snap/resizeBox.js.map +0 -1
  727. package/dist/features/snap/roles.js.map +0 -1
  728. package/dist/features/snap/rotationSource.js.map +0 -1
  729. package/dist/features/snap/sceneReads.js.map +0 -1
  730. package/dist/features/snap/sceneSession2d.js.map +0 -1
  731. package/dist/features/snap/screen.js.map +0 -1
  732. package/dist/features/snap/session2d.js.map +0 -1
  733. package/dist/features/snap/spanSnap.js.map +0 -1
  734. package/dist/features/snap/stepAngles.js.map +0 -1
  735. package/dist/features/text/editingDrawOne.js.map +0 -1
  736. package/dist/features/text/sceneTextDrafts.js.map +0 -1
  737. package/dist/features/text/textHitAt.js.map +0 -1
  738. package/dist/features/text/textLayer.js.map +0 -1
  739. package/dist/features/text/useSceneTextEdit.js.map +0 -1
  740. package/dist/features/viewports/aim.js.map +0 -1
  741. package/dist/features/viewports/viewMarker.js.map +0 -1
  742. package/dist/features/viewports/viewMarkerDrag.js.map +0 -1
  743. package/dist/features/viewports/viewResolver.js.map +0 -1
  744. package/dist/features/viewports/viewportLayer.js.map +0 -1
  745. /package/dist/{features → domains}/chrome-caps/index.js +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conditions.js","names":[],"sources":["../../../src/domains/chrome-caps/conditions.ts"],"sourcesContent":["import type { Condition } from './types';\nimport type { Rule } from '@weasel-js/routing';\nimport type { RuleCtx } from '@weasel-js/routing';\nimport { evaluate, ALWAYS, NEVER } from '@weasel-js/routing';\nimport type { ModifierState } from '../../interactions/gestures/types';\n\n/**\n * Promote a Rule tree to a fluent Condition.\n *\n * **Chain semantics: strict left-to-right.** No boolean precedence.\n * `focused.or(hovering).and(selectionIs(1))` evaluates\n * `((focused || hovering) && selectionIs(1))`. For grouped disjunction, use\n * the top-level `or` / `and` / `not` helpers or name intermediates.\n *\n * Combinators flatten same-kind nesting on construction so `a.and(b).and(c)`\n * produces `{ all: [a, b, c] }`.\n */\nexport function cond(rule: Rule): Condition {\n const fn = (ctx: RuleCtx) => evaluate(rule, ctx);\n const condition = fn as Condition;\n (condition as { rule: Rule }).rule = rule;\n condition.and = (other) => cond(combineAll(rule, asRule(other)));\n condition.or = (other) => cond(combineAny(rule, asRule(other)));\n condition.andNot = (other) => cond(combineAll(rule, { not: asRule(other) }));\n condition.orNot = (other) => cond(combineAny(rule, { not: asRule(other) }));\n return condition;\n}\n\nfunction asRule(value: Condition | Rule): Rule {\n return typeof value === 'function' ? (value as Condition).rule : value;\n}\n\nfunction combineAll(a: Rule, b: Rule): Rule {\n const left = 'all' in a ? a.all : [a];\n const right = 'all' in b ? b.all : [b];\n return { all: [...left, ...right] };\n}\n\nfunction combineAny(a: Rule, b: Rule): Rule {\n const left = 'any' in a ? a.any : [a];\n const right = 'any' in b ? b.any : [b];\n return { any: [...left, ...right] };\n}\n\n/** Inline-lambda escape hatch. Wraps the closure as a `when` rule node. */\nexport function when(fn: (ctx: RuleCtx) => boolean): Condition {\n return cond({ when: fn });\n}\n\n// ─── Variadic helpers (for grouped expressions) ─────────────────────\n\n/** `(a && b && c && ...)` — useful when chain mixing makes left-to-right\n * semantics ambiguous. */\nexport function and(...cs: (Condition | Rule)[]): Condition {\n return cond({ all: cs.map(asRule) });\n}\n\n/** `(a || b || c || ...)` — preferred over `.or` chains when mixed with\n * `.and` to make grouping explicit. */\nexport function or(...cs: (Condition | Rule)[]): Condition {\n return cond({ any: cs.map(asRule) });\n}\n\n/** `!c` — top-level negation. Most rules use `.andNot()` / `.orNot()`\n * instead; this is for the lead-with-negation case. */\nexport function not(c: Condition | Rule): Condition {\n return cond({ not: asRule(c) });\n}\n\n// ─── Constant atoms ─────────────────────────────────────────────────\n\n/** Always true. The fallback for any chrome id without a registered rule. */\nexport const always: Condition = cond(ALWAYS);\n\n/** Always false. Useful for \"turn this chrome off entirely\":\n * `chromeVisibility={{ 'snap.guides': never }}`. */\nexport const never: Condition = cond(NEVER);\n\n// ─── Focus / action atoms ───────────────────────────────────────────\n\n/** Canvas surface currently focused. */\nexport const focused: Condition = cond({ focused: true });\n\n/** Any action currently in flight (read: user is gesturing). */\nexport const gesturing: Condition = cond({ gesturing: true });\n\n/** A path is currently in anchor-edit mode. */\nexport const editingAnchors: Condition = cond({ editingAnchors: true });\n\n/** Current selection is resizable (every selected node passes the consumer's\n * `selectTool.resize.resizable` predicate). Absent predicate → resizable. */\nexport const resizable: Condition = cond({ resizable: true });\n\n/** Active action's kind matches `kind` (e.g. `actionIs('move')`). */\nexport const actionIs = (kind: string): Condition => cond({ actionIs: kind });\n\n// ─── Selection atoms ────────────────────────────────────────────────\n\n/** Nothing selected. */\nexport const selectionEmpty: Condition = cond({ selection: { empty: true } });\n\n/** Exactly `n` items selected. */\nexport const selectionIs = (n: number): Condition => cond({ selection: { is: n } });\n\n/** At least `n` items selected. `selectionAtLeast(1)` is the common\n * \"something is selected\" check. */\nexport const selectionAtLeast = (n: number): Condition => cond({ selection: { atLeast: n } });\n\n/** Multi-mode handle behavior active (single shared union-bounds\n * handle vs per-item handles). */\nexport const multiActive: Condition = cond({ when: (c) => c.multiActive });\n\n// ─── Hover atoms ────────────────────────────────────────────────────\n\n/** Pointer is hovering some node. */\nexport const hovering: Condition = cond({ hovering: true });\n\n/** Pointer is hovering a node that is currently selected. */\nexport const hoveringSelected: Condition = cond({ hoveringSelected: true });\n\n// ─── Modifier atoms ─────────────────────────────────────────────────\n\n/** Named modifier key currently held. */\nexport const modifierHeld = (m: keyof ModifierState): Condition => cond({ modifierHeld: m });\n\n// ─── View atoms ─────────────────────────────────────────────────────\n\n/** View scale (uniform or geometric mean for non-uniform) is at least\n * `z`. Useful for \"hide hairline chrome at low zoom\" rules. */\nexport const zoomAtLeast = (z: number): Condition => cond({ zoomAtLeast: z });\n\n// ─── Mode + capability atoms ───────────────────────────────────────\n\n/** Active mode equals `m`. */\nexport const modeIs = (m: string): Condition => cond({ mode: m });\n\n/** Active mode is one of `modes`. */\nexport const modeIn = (modes: readonly string[]): Condition => cond({ mode: { in: modes } });\n\n/** Active mode is NOT `m`. */\nexport const modeNot = (m: string): Condition => cond({ mode: { not: m } });\n\n/** Active mode's `allows` includes capability `cap`. */\nexport const capabilityIs = (cap: string): Condition => cond({ capability: cap });\n\n/** Active mode's `allows` includes ANY of `caps` (OR). */\nexport const capabilityIn = (caps: readonly string[]): Condition => cond({ capability: { in: caps } });\n\n/** Active mode's `allows` includes ALL of `caps` (AND). */\nexport const capabilityAll = (caps: readonly string[]): Condition => cond({ capability: [...caps] });\n\n/** Active mode's `allows` does NOT include capability `cap`. */\nexport const capabilityNot = (cap: string): Condition => cond({ capability: { not: cap } });\n\n// ─── Device atoms ───────────────────────────────────────────────────\n\n/** Primary pointer is imprecise (touch, most styluses). Absent device\n * profile → false. */\nexport const coarsePointer: Condition = cond({ coarsePointer: true });\n\n/** Primary pointer can hover. Absent device profile → true. */\nexport const canHover: Condition = cond({ canHover: true });\n"],"mappings":";;;;;;;;;;;;;;AAiBA,SAAgB,KAAK,MAAuB;CAC1C,MAAM,MAAM,QAAiB,SAAS,MAAM,GAAG;CAC/C,MAAM,YAAY;CAClB,UAA8B,OAAO;CACrC,UAAU,OAAU,UAAU,KAAK,WAAW,MAAM,OAAO,KAAK,CAAC,CAAC;CAClE,UAAU,MAAU,UAAU,KAAK,WAAW,MAAM,OAAO,KAAK,CAAC,CAAC;CAClE,UAAU,UAAU,UAAU,KAAK,WAAW,MAAM,EAAE,KAAK,OAAO,KAAK,EAAE,CAAC,CAAC;CAC3E,UAAU,SAAU,UAAU,KAAK,WAAW,MAAM,EAAE,KAAK,OAAO,KAAK,EAAE,CAAC,CAAC;CAC3E,OAAO;AACT;AAEA,SAAS,OAAO,OAA+B;CAC7C,OAAO,OAAO,UAAU,aAAc,MAAoB,OAAO;AACnE;AAEA,SAAS,WAAW,GAAS,GAAe;CAC1C,MAAM,OAAO,SAAS,IAAI,EAAE,MAAM,CAAC,CAAC;CACpC,MAAM,QAAQ,SAAS,IAAI,EAAE,MAAM,CAAC,CAAC;CACrC,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM,GAAG,KAAK,EAAE;AACpC;AAEA,SAAS,WAAW,GAAS,GAAe;CAC1C,MAAM,OAAO,SAAS,IAAI,EAAE,MAAM,CAAC,CAAC;CACpC,MAAM,QAAQ,SAAS,IAAI,EAAE,MAAM,CAAC,CAAC;CACrC,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM,GAAG,KAAK,EAAE;AACpC;;AAGA,SAAgB,KAAK,IAA0C;CAC7D,OAAO,KAAK,EAAE,MAAM,GAAG,CAAC;AAC1B;;;AAMA,SAAgB,IAAI,GAAG,IAAqC;CAC1D,OAAO,KAAK,EAAE,KAAK,GAAG,IAAI,MAAM,EAAE,CAAC;AACrC;;;AAIA,SAAgB,GAAG,GAAG,IAAqC;CACzD,OAAO,KAAK,EAAE,KAAK,GAAG,IAAI,MAAM,EAAE,CAAC;AACrC;;;AAIA,SAAgB,IAAI,GAAgC;CAClD,OAAO,KAAK,EAAE,KAAK,OAAO,CAAC,EAAE,CAAC;AAChC;;AAKA,IAAa,SAAoB,KAAK,MAAM;;;AAI5C,IAAa,QAAmB,KAAK,KAAK;;AAK1C,IAAa,UAAqB,KAAK,EAAE,SAAS,KAAK,CAAC;;AAGxD,IAAa,YAAuB,KAAK,EAAE,WAAW,KAAK,CAAC;;AAG5D,IAAa,iBAA4B,KAAK,EAAE,gBAAgB,KAAK,CAAC;;;AAItE,IAAa,YAAuB,KAAK,EAAE,WAAW,KAAK,CAAC;;AAG5D,IAAa,YAAY,SAA4B,KAAK,EAAE,UAAU,KAAK,CAAC;;AAK5E,IAAa,iBAA4B,KAAK,EAAE,WAAW,EAAE,OAAO,KAAK,EAAE,CAAC;;AAG5E,IAAa,eAAe,MAAyB,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,CAAC;;;AAIlF,IAAa,oBAAoB,MAAyB,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,CAAC;;;AAI5F,IAAa,cAAyB,KAAK,EAAE,OAAO,MAAM,EAAE,YAAY,CAAC;;AAKzE,IAAa,WAAsB,KAAK,EAAE,UAAU,KAAK,CAAC;;AAG1D,IAAa,mBAA8B,KAAK,EAAE,kBAAkB,KAAK,CAAC;;AAK1E,IAAa,gBAAgB,MAAsC,KAAK,EAAE,cAAc,EAAE,CAAC;;;AAM3F,IAAa,eAAe,MAAyB,KAAK,EAAE,aAAa,EAAE,CAAC;;AAK5E,IAAa,UAAU,MAAyB,KAAK,EAAE,MAAM,EAAE,CAAC;;AAGhE,IAAa,UAAU,UAAwC,KAAK,EAAE,MAAM,EAAE,IAAI,MAAM,EAAE,CAAC;;AAG3F,IAAa,WAAW,MAAyB,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;;AAG1E,IAAa,gBAAgB,QAA2B,KAAK,EAAE,YAAY,IAAI,CAAC;;AAGhF,IAAa,gBAAgB,SAAuC,KAAK,EAAE,YAAY,EAAE,IAAI,KAAK,EAAE,CAAC;;AAGrG,IAAa,iBAAiB,SAAuC,KAAK,EAAE,YAAY,CAAC,GAAG,IAAI,EAAE,CAAC;;AAGnG,IAAa,iBAAiB,QAA2B,KAAK,EAAE,YAAY,EAAE,KAAK,IAAI,EAAE,CAAC;;;AAM1F,IAAa,gBAA2B,KAAK,EAAE,eAAe,KAAK,CAAC;;AAGpE,IAAa,WAAsB,KAAK,EAAE,UAAU,KAAK,CAAC"}
@@ -1,4 +1,4 @@
1
- //#region src/features/chrome-caps/defaults.ts
1
+ //#region src/domains/chrome-caps/defaults.ts
2
2
  /**
3
3
  * Kit-shipped defaults. Merged with the consumer's `chromeVisibility` map
4
4
  * at resolve time; consumer entries take precedence per id.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.js","names":[],"sources":["../../../src/domains/chrome-caps/defaults.ts"],"sourcesContent":["import type { VisibilityRules } from './types';\nimport type { Rule } from '@weasel-js/routing';\n\n/**\n * Kit-shipped defaults. Merged with the consumer's `chromeVisibility` map\n * at resolve time; consumer entries take precedence per id.\n *\n * Gating is written in terms of **capabilities, not mode ids**. A\n * capability rule keeps holding when a new mode is added that permits the\n * same thing; a mode rule has to be found and edited every time.\n */\nexport const defaultVisibilityRules: VisibilityRules = {\n // Off whenever an anchor-editing overlay has taken over the visuals.\n 'selection.outline': {\n all: [\n { selection: { atLeast: 1 } },\n { capability: { not: 'edits-anchors' } },\n ],\n } as Rule,\n\n // Handles hide during an action so the moving / resizing object isn't\n // visually crowded; they reappear on commit.\n 'selection.resize-handles': {\n all: [\n { selection: { atLeast: 1 } },\n { not: { gesturing: true } },\n { capability: 'transforms-selection' },\n { resizable: true },\n ],\n } as Rule,\n\n 'selection.rotation-handle': {\n all: [\n { selection: { atLeast: 1 } },\n { focused: true },\n { not: { gesturing: true } },\n { capability: 'transforms-selection' },\n ],\n } as Rule,\n\n // Transient action chrome — only shown during the matching action.\n 'action.marquee': { actionIs: 'marquee' } as Rule,\n 'action.lasso': { actionIs: 'lasso' } as Rule,\n 'action.move-ghosts': { actionIs: 'move' } as Rule,\n\n // Path-edit chrome — gated on there actually being an edited path, not on\n // `{ mode: 'path-edit' }`: consumers with no mode registry report mode\n // `'normal'`, and one defining its own anchor-editing mode uses another id.\n // A state selector rather than a `capability:` one on purpose: the\n // capability says the user *may* edit anchors, which is not the same as\n // a path *being* edited.\n 'path-edit.anchors': { editingAnchors: true } as Rule,\n 'path-edit.overlay': { editingAnchors: true } as Rule,\n\n // Snap system — guides during any action; targets consumer-driven for now.\n 'snap.guides': { gesturing: true } as Rule,\n // 'snap.targets' — no default; consumer-driven for now.\n\n // 'grid' / 'debug.*' — no default; visibility is driven by dedicated\n // boolean props on <SceneCanvas>.\n};\n"],"mappings":";;;;;;;;;AAWA,IAAa,yBAA0C;CAErD,qBAAqB,EACnB,KAAK,CACH,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,GAC5B,EAAE,YAAY,EAAE,KAAK,gBAAgB,EAAE,CACzC,EACF;CAIA,4BAA4B,EAC1B,KAAK;EACH,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE;EAC5B,EAAE,KAAK,EAAE,WAAW,KAAK,EAAE;EAC3B,EAAE,YAAY,uBAAuB;EACrC,EAAE,WAAW,KAAK;CACpB,EACF;CAEA,6BAA6B,EAC3B,KAAK;EACH,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE;EAC5B,EAAE,SAAS,KAAK;EAChB,EAAE,KAAK,EAAE,WAAW,KAAK,EAAE;EAC3B,EAAE,YAAY,uBAAuB;CACvC,EACF;CAGA,kBAAsB,EAAE,UAAU,UAAU;CAC5C,gBAAsB,EAAE,UAAU,QAAQ;CAC1C,sBAAsB,EAAE,UAAU,OAAO;CAQzC,qBAAqB,EAAE,gBAAgB,KAAK;CAC5C,qBAAqB,EAAE,gBAAgB,KAAK;CAG5C,eAAe,EAAE,WAAW,KAAK;AAKnC"}
@@ -2,7 +2,7 @@ import { viewZoom } from "../../core/viewport/view.js";
2
2
  import { defaultVisibilityRules } from "./defaults.js";
3
3
  import { DEFAULT_ALLOWED_CAPABILITIES, evaluate } from "@weasel-js/routing";
4
4
 
5
- //#region src/features/chrome-caps/resolve.ts
5
+ //#region src/domains/chrome-caps/resolve.ts
6
6
  /**
7
7
  * Build the per-frame visibility check: consumer rules merged on top of
8
8
  * the kit defaults, evaluated against `ctx`.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.js","names":[],"sources":["../../../src/domains/chrome-caps/resolve.ts"],"sourcesContent":["import type { ChromeCtx, ChromeId, VisibilityRules } from './types';\nimport type { RuleCtx } from '@weasel-js/routing';\nimport { DEFAULT_ALLOWED_CAPABILITIES } from '@weasel-js/routing';\nimport { evaluate } from '@weasel-js/routing';\nimport { defaultVisibilityRules } from './defaults';\nimport { viewZoom } from 'core/viewport/view';\n\n/**\n * Build the per-frame visibility check: consumer rules merged on top of\n * the kit defaults, evaluated against `ctx`.\n *\n * `ChromeCtx` is the legacy shape; surfaces still on it get `mode='normal'`\n * and `DEFAULT_ALLOWED_CAPABILITIES` filled in here, so a caller that never\n * opted into modality behaves exactly like one sitting in normal mode.\n */\nexport function resolveVisibility(\n consumer: VisibilityRules | undefined,\n ctx: ChromeCtx | RuleCtx,\n): (id: ChromeId) => boolean {\n const merged: VisibilityRules = consumer\n ? { ...defaultVisibilityRules, ...consumer }\n : defaultVisibilityRules;\n const ruleCtx: RuleCtx = 'mode' in ctx\n ? ctx\n : {\n ...ctx,\n zoom: viewZoom(ctx.view),\n mode: 'normal',\n allowedCapabilities: DEFAULT_ALLOWED_CAPABILITIES,\n };\n return (id) => {\n const entry = merged[id];\n if (entry === undefined) return true;\n if (typeof entry === 'function') return entry(ruleCtx);\n return evaluate(entry, ruleCtx);\n };\n}\n"],"mappings":";;;;;;;;;;;;;AAeA,SAAgB,kBACd,UACA,KAC2B;CAC3B,MAAM,SAA0B,WAC5B;EAAE,GAAG;EAAwB,GAAG;CAAS,IACzC;CACJ,MAAM,UAAmB,UAAU,MAC/B,MACA;EACA,GAAG;EACH,MAAM,SAAS,IAAI,IAAI;EACvB,MAAM;EACN,qBAAqB;CACvB;CACF,QAAQ,OAAO;EACb,MAAM,QAAQ,OAAO;EACrB,IAAI,UAAU,QAAW,OAAO;EAChC,IAAI,OAAO,UAAU,YAAY,OAAO,MAAM,OAAO;EACrD,OAAO,SAAS,OAAO,OAAO;CAChC;AACF"}
@@ -4,7 +4,7 @@ import { useMemo } from "react";
4
4
  import { useLatest } from "@weasel-js/react";
5
5
  import { DEFAULT_ALLOWED_CAPABILITIES, buildRuleCtx } from "@weasel-js/routing";
6
6
 
7
- //#region src/features/chrome-caps/useChromeCaps.ts
7
+ //#region src/domains/chrome-caps/useChromeCaps.ts
8
8
  /**
9
9
  * The rule context a surface's chrome-visibility rules and action eligibility
10
10
  * are evaluated against, and the hook that keeps one for `<SceneCanvas>`.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useChromeCaps.js","names":[],"sources":["../../../src/domains/chrome-caps/useChromeCaps.ts"],"sourcesContent":["/**\n * The rule context a surface's chrome-visibility rules and action eligibility\n * are evaluated against, and the hook that keeps one for `<SceneCanvas>`.\n */\nimport { useMemo } from 'react';\nimport type { ActiveMode } from '@weasel-js/modes';\nimport { useLatest } from '@weasel-js/react';\nimport {\n buildRuleCtx,\n DEFAULT_ALLOWED_CAPABILITIES,\n type DeviceProfile,\n type ModifierState,\n type RuleCtx,\n} from '@weasel-js/routing';\nimport type { NodeId } from '../../core/scene/types';\nimport { viewZoom, type View } from '../../core/viewport/view';\nimport { resolveVisibility } from './resolve';\nimport type { VisibilityRules } from './types';\n\n/**\n * The part of a rule context that belongs to one view. Everything else a\n * `RuleCtx` carries — mode, capabilities, device, focus — is the surface's and\n * the same for every view drawn on it.\n */\nexport interface ViewRuleInputs {\n selection: readonly NodeId[];\n view: View;\n /** What that view's dispatcher currently has in flight. */\n action: { kind: string | null; id: string | null };\n}\n\n/** The part of a rule context that belongs to the surface. */\nexport interface SurfaceRuleInputs {\n focused: boolean;\n /** The modifier keys held. Absent means none, so `modifierHeld` never matches. */\n modifiers?: ModifierState | undefined;\n hover: NodeId | null;\n /** Absent on a surface with no mode registry, which behaves as normal mode. */\n mode: ActiveMode | undefined;\n /** Whether one node may be resized. Absent means every node may. */\n resizable: ((id: string) => boolean) | undefined;\n editingAnchors: boolean;\n device: DeviceProfile;\n}\n\nconst NO_MODIFIERS = { alt: false, ctrl: false, meta: false, shift: false };\n\n/** One view's rule context on a surface. */\nexport function surfaceRuleCtx(surface: SurfaceRuleInputs, view: ViewRuleInputs): RuleCtx {\n const { resizable } = surface;\n return buildRuleCtx({\n focused: surface.focused,\n selection: view.selection,\n multiActive: view.selection.length > 1,\n modifiers: surface.modifiers ?? NO_MODIFIERS,\n action: view.action,\n hover: surface.hover,\n zoom: viewZoom(view.view),\n mode: surface.mode?.id ?? 'normal',\n // An empty set would make every `capability:` rule false and hide the\n // chrome those rules gate.\n allowedCapabilities: surface.mode?.allowedCapabilities ?? DEFAULT_ALLOWED_CAPABILITIES,\n // Left undefined without a predicate, which `resizable:` reads as resizable.\n selectionResizable: resizable ? view.selection.every((id) => resizable(id)) : undefined,\n editingAnchors: surface.editingAnchors,\n device: surface.device,\n });\n}\n\n/** Where a surface's rule context comes from. Read at the moment a rule is\n * evaluated, never at render. */\nexport interface ChromeCapsSource {\n /** The surface's rule table, over the kit defaults. */\n rules: VisibilityRules | undefined;\n /** The active mode. Absent when no mode registry is wired. */\n getMode: (() => ActiveMode) | undefined;\n /** Absent means the surface always counts as focused. */\n getFocused: (() => boolean) | undefined;\n getHover: () => NodeId | null;\n /** Absent means no modifier key ever counts as held. */\n getModifiers?: (() => ModifierState) | undefined;\n resizable: ((id: string) => boolean) | undefined;\n editingAnchors: () => boolean;\n device: DeviceProfile;\n}\n\n/**\n * A surface's chrome-caps answers for one view. The rules are surface-wide on\n * purpose — one consumer table, one set of kit defaults — but a rule keyed on\n * selection, camera or the in-flight action answers differently per view, so\n * the caller supplies that part and both the paint gate and the dispatcher's\n * eligibility filter read the same context.\n */\nexport interface ChromeCaps {\n /** `undefined` when the surface gates nothing: no mode registry is wired. */\n ruleCtx(inputs: ViewRuleInputs): RuleCtx | undefined;\n isVisible(inputs: ViewRuleInputs): (id: string) => boolean;\n}\n\n/** The chrome-caps answers for a surface. The object keeps its identity for\n * the life of the component and reads `source` as of the last commit. */\nexport function useChromeCaps(source: ChromeCapsSource): ChromeCaps {\n const live = useLatest(source);\n return useMemo(() => {\n const build = (inputs: ViewRuleInputs): RuleCtx => {\n const now = live.current;\n return surfaceRuleCtx({\n focused: now.getFocused ? now.getFocused() : true,\n modifiers: now.getModifiers?.(),\n hover: now.getHover(),\n mode: now.getMode?.(),\n resizable: now.resizable,\n editingAnchors: now.editingAnchors(),\n device: now.device,\n }, inputs);\n };\n return {\n ruleCtx: (inputs) => (live.current.getMode ? build(inputs) : undefined),\n isVisible: (inputs) => resolveVisibility(live.current.rules, build(inputs)),\n };\n }, [live]);\n}\n"],"mappings":";;;;;;;;;;;AA6CA,IAAM,eAAe;CAAE,KAAK;CAAO,MAAM;CAAO,MAAM;CAAO,OAAO;AAAM;;AAG1E,SAAgB,eAAe,SAA4B,MAA+B;CACxF,MAAM,EAAE,cAAc;CACtB,OAAO,aAAa;EAClB,SAAS,QAAQ;EACjB,WAAW,KAAK;EAChB,aAAa,KAAK,UAAU,SAAS;EACrC,WAAW,QAAQ,aAAa;EAChC,QAAQ,KAAK;EACb,OAAO,QAAQ;EACf,MAAM,SAAS,KAAK,IAAI;EACxB,MAAM,QAAQ,MAAM,MAAM;EAG1B,qBAAqB,QAAQ,MAAM,uBAAuB;EAE1D,oBAAoB,YAAY,KAAK,UAAU,OAAO,OAAO,UAAU,EAAE,CAAC,IAAI;EAC9E,gBAAgB,QAAQ;EACxB,QAAQ,QAAQ;CAClB,CAAC;AACH;;;AAkCA,SAAgB,cAAc,QAAsC;CAClE,MAAM,OAAO,UAAU,MAAM;CAC7B,OAAO,cAAc;EACnB,MAAM,SAAS,WAAoC;GACjD,MAAM,MAAM,KAAK;GACjB,OAAO,eAAe;IACpB,SAAS,IAAI,aAAa,IAAI,WAAW,IAAI;IAC7C,WAAW,IAAI,eAAe;IAC9B,OAAO,IAAI,SAAS;IACpB,MAAM,IAAI,UAAU;IACpB,WAAW,IAAI;IACf,gBAAgB,IAAI,eAAe;IACnC,QAAQ,IAAI;GACd,GAAG,MAAM;EACX;EACA,OAAO;GACL,UAAU,WAAY,KAAK,QAAQ,UAAU,MAAM,MAAM,IAAI;GAC7D,YAAY,WAAW,kBAAkB,KAAK,QAAQ,OAAO,MAAM,MAAM,CAAC;EAC5E;CACF,GAAG,CAAC,IAAI,CAAC;AACX"}
@@ -1,7 +1,7 @@
1
1
  import { useCallback, useEffect, useMemo, useRef } from "react";
2
2
  import { useLatest } from "@weasel-js/react";
3
3
 
4
- //#region src/features/chrome-caps/useHoverTracking.ts
4
+ //#region src/domains/chrome-caps/useHoverTracking.ts
5
5
  /** Last-hovered NodeId for chrome-caps rules. */
6
6
  /** Track which node the pointer is over. Returns a getter rather than state,
7
7
  * so a pointer move does not re-render the component. Cleared on
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useHoverTracking.js","names":[],"sources":["../../../src/domains/chrome-caps/useHoverTracking.ts"],"sourcesContent":["/** Last-hovered NodeId for chrome-caps rules. */\n\nimport { useCallback, useEffect, useMemo, useRef } from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport type { NodeId } from '../../core/scene/types';\n\n/** Options for `useHoverTracking`. */\nexport interface UseHoverTrackingArgs {\n /** Topmost id under a client point, or null. */\n nodeAtClientPoint: (clientX: number, clientY: number) => { id: NodeId } | null;\n enabled?: boolean;\n /** Called when the hovered id changes, `null` once the pointer is over no node or has left. */\n onChange?: (id: NodeId | null) => void;\n}\n\n/** What `useHoverTracking` returns. Both members keep their identity. */\nexport interface HoverTracking {\n /** The node the pointer is over, or null. */\n getHover: () => NodeId | null;\n /** Hand it the element to track, or null to stop: a callback ref, so an\n * element that mounts after the hook is tracked from the moment it arrives. */\n canvasRef: (element: HTMLElement | null) => void;\n}\n\n/** Track which node the pointer is over. Returns a getter rather than state,\n * so a pointer move does not re-render the component. Cleared on\n * `pointerleave`. */\nexport function useHoverTracking(args: UseHoverTrackingArgs): HoverTracking {\n const { nodeAtClientPoint, enabled = true } = args;\n const hoverRef = useRef<NodeId | null>(null);\n const nodeAtRef = useLatest(nodeAtClientPoint);\n const onChangeRef = useLatest(args.onChange);\n const elementRef = useRef<HTMLElement | null>(null);\n const enabledRef = useRef(enabled);\n const detachRef = useRef<(() => void) | null>(null);\n\n const hover = useCallback((id: NodeId | null) => {\n if (hoverRef.current === id) return;\n hoverRef.current = id;\n onChangeRef.current?.(id);\n }, [onChangeRef]);\n\n const sync = useCallback(() => {\n detachRef.current?.();\n detachRef.current = null;\n hover(null);\n const el = elementRef.current;\n if (!el || !enabledRef.current) return;\n const onMove = (e: PointerEvent) => hover(nodeAtRef.current(e.clientX, e.clientY)?.id ?? null);\n const onLeave = () => hover(null);\n el.addEventListener('pointermove', onMove);\n el.addEventListener('pointerleave', onLeave);\n detachRef.current = () => {\n el.removeEventListener('pointermove', onMove);\n el.removeEventListener('pointerleave', onLeave);\n };\n }, [nodeAtRef, hover]);\n\n useEffect(() => {\n enabledRef.current = enabled;\n sync();\n return () => {\n detachRef.current?.();\n detachRef.current = null;\n hover(null);\n };\n }, [enabled, sync, hover]);\n\n const canvasRef = useCallback((element: HTMLElement | null) => {\n if (elementRef.current === element) return;\n elementRef.current = element;\n sync();\n }, [sync]);\n const getHover = useCallback(() => hoverRef.current, []);\n\n return useMemo(() => ({ getHover, canvasRef }), [getHover, canvasRef]);\n}\n"],"mappings":";;;;;;;;AA2BA,SAAgB,iBAAiB,MAA2C;CAC1E,MAAM,EAAE,mBAAmB,UAAU,SAAS;CAC9C,MAAM,WAAW,OAAsB,IAAI;CAC3C,MAAM,YAAY,UAAU,iBAAiB;CAC7C,MAAM,cAAc,UAAU,KAAK,QAAQ;CAC3C,MAAM,aAAa,OAA2B,IAAI;CAClD,MAAM,aAAa,OAAO,OAAO;CACjC,MAAM,YAAY,OAA4B,IAAI;CAElD,MAAM,QAAQ,aAAa,OAAsB;EAC/C,IAAI,SAAS,YAAY,IAAI;EAC7B,SAAS,UAAU;EACnB,YAAY,UAAU,EAAE;CAC1B,GAAG,CAAC,WAAW,CAAC;CAEhB,MAAM,OAAO,kBAAkB;EAC7B,UAAU,UAAU;EACpB,UAAU,UAAU;EACpB,MAAM,IAAI;EACV,MAAM,KAAK,WAAW;EACtB,IAAI,CAAC,MAAM,CAAC,WAAW,SAAS;EAChC,MAAM,UAAU,MAAoB,MAAM,UAAU,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,MAAM,IAAI;EAC7F,MAAM,gBAAgB,MAAM,IAAI;EAChC,GAAG,iBAAiB,eAAe,MAAM;EACzC,GAAG,iBAAiB,gBAAgB,OAAO;EAC3C,UAAU,gBAAgB;GACxB,GAAG,oBAAoB,eAAe,MAAM;GAC5C,GAAG,oBAAoB,gBAAgB,OAAO;EAChD;CACF,GAAG,CAAC,WAAW,KAAK,CAAC;CAErB,gBAAgB;EACd,WAAW,UAAU;EACrB,KAAK;EACL,aAAa;GACX,UAAU,UAAU;GACpB,UAAU,UAAU;GACpB,MAAM,IAAI;EACZ;CACF,GAAG;EAAC;EAAS;EAAM;CAAK,CAAC;CAEzB,MAAM,YAAY,aAAa,YAAgC;EAC7D,IAAI,WAAW,YAAY,SAAS;EACpC,WAAW,UAAU;EACrB,KAAK;CACP,GAAG,CAAC,IAAI,CAAC;CACT,MAAM,WAAW,kBAAkB,SAAS,SAAS,CAAC,CAAC;CAEvD,OAAO,eAAe;EAAE;EAAU;CAAU,IAAI,CAAC,UAAU,SAAS,CAAC;AACvE"}
@@ -2,7 +2,7 @@ import { dwarn } from "../../debug/flag.js";
2
2
  import { admitIngestMime } from "@weasel-js/routing";
3
3
  import { UNKNOWN_MIME, mimeMatchesGlob } from "@weasel-js/gestures";
4
4
 
5
- //#region src/features/ingestion/contentHandlers.ts
5
+ //#region src/domains/ingestion/contentHandlers.ts
6
6
  /**
7
7
  * Content-handler registry for external-content ingestion.
8
8
  *
@@ -33,7 +33,19 @@ function unregister(entry) {
33
33
  releases?.pop()?.();
34
34
  if (releases?.length === 0) RELEASES.delete(entry);
35
35
  }
36
- /** Register a content handler. Returns a disposer that removes it. */
36
+ /** Keep the string flavors `entries` name exactly, as registering them would, until the
37
+ * returned disposer runs: for handlers one canvas holds rather than the registry. */
38
+ function admitHandlerMimes(entries) {
39
+ const releases = [...new Set(entries.flatMap(namedMimes))].map(admitIngestMime);
40
+ let released = false;
41
+ return () => {
42
+ if (released) return;
43
+ released = true;
44
+ releases.forEach((release) => release());
45
+ };
46
+ }
47
+ /** Register a content handler for every canvas on the page. Returns a disposer that removes
48
+ * it. A handler for one canvas goes in that canvas's `ingestion.handlers` instead. */
37
49
  function registerContentHandler(entry) {
38
50
  HANDLERS.push(entry);
39
51
  const admits = namedMimes(entry).map(admitIngestMime);
@@ -55,6 +67,11 @@ function byPriority(handlers) {
55
67
  function getContentHandlers() {
56
68
  return byPriority(HANDLERS);
57
69
  }
70
+ /** The handlers an arrival on one canvas routes through: the canvas's own, then the
71
+ * registry's, priority-ordered, so at equal priority the canvas's own run first. */
72
+ function handlersFor(own) {
73
+ return byPriority([...own ?? [], ...HANDLERS]);
74
+ }
58
75
  function entryMatches(entry, item) {
59
76
  const { match } = entry;
60
77
  if (typeof match === "function") try {
@@ -111,5 +128,5 @@ async function runIngest(items, ctx, opts = {}) {
111
128
  }
112
129
 
113
130
  //#endregion
114
- export { couldClaim, getContentHandlers, registerContentHandler, runIngest };
131
+ export { admitHandlerMimes, couldClaim, getContentHandlers, handlersFor, registerContentHandler, runIngest };
115
132
  //# sourceMappingURL=contentHandlers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contentHandlers.js","names":[],"sources":["../../../src/domains/ingestion/contentHandlers.ts"],"sourcesContent":["/**\n * Content-handler registry for external-content ingestion.\n *\n * All three arrival paths (OS drop, clipboard paste, file picker /\n * `CanvasExtensionApi.ingest`) converge here: `runIngest(items, ctx)`\n * partitions the items across registered handlers in priority order — the\n * first matching handler takes every item it matches; leftovers continue\n * down the list.\n *\n * MIME matching delegates to `mimeMatchesGlob` from `@weasel-js/gestures` —\n * the SAME matcher the binding-level `types` filter uses, so a binding that\n * matched can never disagree with the registry about what a glob means.\n */\nimport { mimeMatchesGlob, UNKNOWN_MIME } from '@weasel-js/gestures';\nimport { dwarn } from '../../debug';\nimport type { Scene } from 'core/scene/types';\nimport type { SelectionApi } from 'core/selection/useSelection';\nimport type { Op } from 'core/ops/types';\nimport type { ClipboardIngestCtx, InsertDep, SnapDep, SvgIngestOptions } from 'interactions/actions/depSchema';\nimport { admitIngestMime, type ActionDeps, type IngestItem } from '@weasel-js/routing';\n\n/** Context handed to every content handler for one ingest event. */\nexport interface IngestCtx {\n /** World-space arrival point (drop / pointed imperative ingest); `null`\n * for paste and point-less calls — handlers pick their own policy\n * (the kit image handler centers on the viewport). */\n point: { x: number; y: number } | null;\n /** Every item the arrival carried, the ones other handlers took included:\n * a handler can tell that the string it was given rides beside a file.\n * Absent reads as the items the handler was handed. */\n items?: readonly IngestItem[];\n /** Visible canvas area in world coordinates. */\n viewportWorldRect(): { x: number; y: number; width: number; height: number };\n /** The kit insert dep — id/layer/undoable-op supplied; the canonical way\n * for a handler to mint a node (`insert.commit(bounds, { kind, ... })`). */\n insert: InsertDep;\n /** The canvas's snap dep, for a handler that places what it inserts where a\n * gesture would: begin a session, take `at()`, `end()`. Its points are ctx\n * coordinates: world on a 2D canvas, screen in a 3D host. */\n snap?: SnapDep;\n /** Raw op commit for handlers that build their own ops. */\n applyOps(ops: Op[], label?: string): void;\n scene: Scene<unknown, string, unknown>;\n selection: SelectionApi;\n /** Consumer file→src resolver (SceneCanvas `ingestion.resolveSrc`).\n * When absent, the kit image handler embeds as a `data:` URI. */\n resolveSrc?: (file: File) => Promise<string>;\n /** Kit SVG-handler options (SceneCanvas `ingestion.svg`) — e.g.\n * `{ unpack: unpackSvgFiles }` (from `@weasel-js/svg`) to parse SVG files\n * into scene nodes. */\n svg?: SvgIngestOptions;\n /** Clipboard-paste seam — present when the hosting `SceneCanvas` supplied\n * an adapter with `commitPaste`. `reviver` comes from\n * `SceneCanvasProps.ingestion.clipboard`. Absent ⇒ the kit weasel-JSON\n * handler declines inert (dwarn, nothing ingested) — its matched items\n * were already consumed at match time, so they do NOT fall through;\n * only match-level misses flow on to other handlers. */\n clipboard?: ClipboardIngestCtx;\n /** Set to `true` by the kit weasel-JSON handler when it successfully\n * pastes a payload in this event. `kit:svg`'s `text/plain` SVG fallback\n * reads this to decline the SVG flavor of a copy whose weasel-JSON flavor\n * already ingested (avoids a double-paste when both flavors ride one\n * clipboard event). */\n consumedWeaselPayload?: boolean;\n /** Full action-deps bag, for consumer handlers that need more. */\n deps: ActionDeps;\n}\n\n/** A handler for content arriving by paste, drop or file picker. Handlers are\n * matched by MIME glob or predicate and run highest-priority first; the kit's\n * own register at a low priority so a consumer's handler wins by default. */\nexport interface ContentHandlerEntry {\n /** Stable identifier — used for unregistration and debugging\n * (`'kit:image'`, `'app:csv'`). */\n id: string;\n /** MIME glob(s) (`'image/*'`, `'text/csv'`) or an item predicate. A drop\n * or paste keeps string flavors of any exact MIME named here or in\n * `types` while the handler is registered, so a handler can claim a drag\n * from the app's own UI by a type of its own. */\n match: string | string[] | ((item: IngestItem) => boolean);\n /** For a predicate `match`: the MIME globs it could say yes to. A drag\n * still in flight shows its MIMEs and no contents, so a predicate cannot be\n * asked about it; this is what it is asked instead. Without it, no drag\n * can be refused while this handler is registered. */\n types?: readonly string[];\n /** Higher runs earlier. Kit defaults register at -100 so any consumer\n * handler (default 0) beats them. */\n priority?: number;\n handle(items: IngestItem[], ctx: IngestCtx): void | Promise<void>;\n}\n\nconst HANDLERS: ContentHandlerEntry[] = [];\nconst RELEASES = new Map<ContentHandlerEntry, (() => void)[]>();\n\n/** The exact MIMEs a handler names, for string flavors to be kept. A glob\n * names no one flavor, and `application/octet-stream` is what an untyped\n * file reports, never a string flavor anyone meant. */\nfunction namedMimes(entry: ContentHandlerEntry): string[] {\n const named = [...(typeof entry.match === 'function' ? [] : [entry.match].flat()), ...(entry.types ?? [])];\n return [...new Set(named)].filter((m) => !m.includes('*') && m !== 'application/octet-stream');\n}\n\nfunction unregister(entry: ContentHandlerEntry): void {\n const i = HANDLERS.indexOf(entry);\n if (i < 0) return;\n HANDLERS.splice(i, 1);\n // The same entry may be registered more than once; each registration admitted once.\n const releases = RELEASES.get(entry);\n releases?.pop()?.();\n if (releases?.length === 0) RELEASES.delete(entry);\n}\n\n/** Keep the string flavors `entries` name exactly, as registering them would, until the\n * returned disposer runs: for handlers one canvas holds rather than the registry. */\nexport function admitHandlerMimes(entries: readonly ContentHandlerEntry[]): () => void {\n const releases = [...new Set(entries.flatMap(namedMimes))].map(admitIngestMime);\n let released = false;\n return () => {\n if (released) return;\n released = true;\n releases.forEach((release) => release());\n };\n}\n\n/** Register a content handler for every canvas on the page. Returns a disposer that removes\n * it. A handler for one canvas goes in that canvas's `ingestion.handlers` instead. */\nexport function registerContentHandler(entry: ContentHandlerEntry): () => void {\n HANDLERS.push(entry);\n const admits = namedMimes(entry).map(admitIngestMime);\n const releases = RELEASES.get(entry) ?? [];\n releases.push(() => admits.forEach((release) => release()));\n RELEASES.set(entry, releases);\n let removed = false;\n return () => {\n if (removed) return;\n removed = true;\n unregister(entry);\n };\n}\n\nfunction byPriority(handlers: readonly ContentHandlerEntry[]): ContentHandlerEntry[] {\n return [...handlers].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));\n}\n\n/** Snapshot of registered handlers, priority-ordered (stable within ties —\n * registration order; Array.prototype.sort is stable per ES2019). */\nexport function getContentHandlers(): readonly ContentHandlerEntry[] {\n return byPriority(HANDLERS);\n}\n\n/** The handlers an arrival on one canvas routes through: the canvas's own, then the\n * registry's, priority-ordered, so at equal priority the canvas's own run first. */\nexport function handlersFor(own: readonly ContentHandlerEntry[] | undefined): ContentHandlerEntry[] {\n return byPriority([...(own ?? []), ...HANDLERS]);\n}\n\n/** Options for {@link runIngest}. */\nexport interface RunIngestOptions {\n /**\n * The handlers to route through, in place of the registry. The registry is\n * shared by every canvas on the page, so this is how one canvas routes\n * through its own handlers, and how one arrival path accepts a narrower set\n * of content than the canvas has handlers for.\n * Ordered by `priority` like registered handlers; an item none of them\n * matches is ignored.\n */\n handlers?: readonly ContentHandlerEntry[];\n}\n\nfunction entryMatches(entry: ContentHandlerEntry, item: IngestItem): boolean {\n const { match } = entry;\n if (typeof match === 'function') {\n try {\n return match(item);\n } catch (err) {\n console.warn(`weasel ingest: handler \"${entry.id}\" match predicate threw`, err);\n return false;\n }\n }\n const globs = Array.isArray(match) ? match : [match];\n return globs.some((g) => mimeMatchesGlob(item.mime, g));\n}\n\n/**\n * Whether some handler could claim one of `items`: the registry's, or\n * `handlers`. An item with contents is put to each handler's `match`. One\n * with a MIME alone, from a drag still in flight, is answered from the globs\n * and a predicate's declared `types`; it is never refused when its MIME is\n * `UNKNOWN_MIME`, or while a predicate handler declares no types.\n */\nexport function couldClaim(\n items: readonly IngestItem[],\n handlers: readonly ContentHandlerEntry[] = HANDLERS,\n): boolean {\n return items.some((item) => {\n if ((item as { kind?: string }).kind !== undefined) return handlers.some((h) => entryMatches(h, item));\n if (item.mime === UNKNOWN_MIME) return true;\n return handlers.some((h) => {\n const globs = typeof h.match === 'function' ? h.types : [h.match].flat();\n return globs === undefined || globs.some((g) => mimeMatchesGlob(item.mime, g));\n });\n });\n}\n\n/**\n * Route one ingest event's items through the registry. Handlers are queued\n * as microtasks in priority order and their async work then overlaps (each\n * already owns a disjoint item set); a throwing or rejecting handler\n * `console.warn`s and doesn't block the others. The queue order is a real\n * invariant: a higher-priority handler's `handle` body runs before lower\n * ones start, so ctx-flag coordination (e.g. `consumedWeaselPayload`)\n * works — but only while the writing handler stays synchronous up to the\n * write and the reading handler reads before its first `await`.\n * Unmatched items are ignored with a debug-gated `dwarn('ingest', ...)`.\n */\nexport async function runIngest(\n items: IngestItem[],\n ctx: IngestCtx,\n opts: RunIngestOptions = {},\n): Promise<void> {\n let remaining = items;\n const runs: Promise<void>[] = [];\n const handlers = opts.handlers ? byPriority(opts.handlers) : getContentHandlers();\n\n for (const entry of handlers) {\n if (remaining.length === 0) break;\n const mine = remaining.filter((it) => entryMatches(entry, it));\n if (mine.length === 0) continue;\n remaining = remaining.filter((it) => !mine.includes(it));\n runs.push(\n Promise.resolve()\n .then(() => entry.handle(mine, ctx))\n .catch((err) => {\n console.warn(`weasel ingest: handler \"${entry.id}\" threw`, err);\n }),\n );\n }\n\n if (remaining.length > 0) {\n dwarn(\n 'ingest',\n `no content handler matched ${remaining.length} item(s):`,\n remaining.map((it) => it.mime).join(', '),\n );\n }\n\n await Promise.all(runs);\n}\n\n/** @internal test seam — clear the registry. Not part of the public barrel. */\nexport function _resetContentHandlersForTests(): void {\n while (HANDLERS.length > 0) unregister(HANDLERS[0]!);\n}\n"],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ import { arrivalBounds } from "./placement.js";
2
+ import { couldClaim } from "./contentHandlers.js";
3
+ import { rectPath } from "@weasel-js/geom";
4
+ import { pendingKindOf } from "@weasel-js/routing";
5
+
6
+ //#region src/domains/ingestion/ghost.ts
7
+ /**
8
+ * What a drag still in flight would land as. A browser shows a hovering drag's
9
+ * kinds and MIMEs and nothing else — no contents, no size — so the ghost is a
10
+ * placeholder box per thing the drop would insert, where the handlers would put
11
+ * it, at a size that reads the same at every zoom.
12
+ */
13
+ /** A file's ghost is a square this fraction of the viewport's shorter side;
14
+ * a string's is twice as wide and a quarter as tall, the shape of a line. */
15
+ var GHOST_FRACTION = 1 / 6;
16
+ /**
17
+ * The boxes a drop of `items` would fill: one per file some handler could
18
+ * claim, or, when no file could be claimed, one for the strings, since a drag's
19
+ * string flavors are several spellings of one thing. Cascaded and centered as
20
+ * the kit's handlers place what they insert.
21
+ */
22
+ function ghostBounds(items, at, handlers) {
23
+ const claimable = items.filter((item) => couldClaim([item], handlers));
24
+ const kindOf = (item) => pendingKindOf(item) ?? item.kind;
25
+ const files = claimable.filter((item) => kindOf(item) === "file");
26
+ const hasString = claimable.some((item) => kindOf(item) === "string");
27
+ const view = at.viewportWorldRect();
28
+ const side = Math.min(view.width, view.height) * GHOST_FRACTION;
29
+ return (files.length > 0 ? files.map(() => ({
30
+ width: side,
31
+ height: side
32
+ })) : hasString ? [{
33
+ width: side * 2,
34
+ height: side / 4
35
+ }] : []).map((size, i) => arrivalBounds(at, size, i));
36
+ }
37
+ /**
38
+ * `Action.preview` for an action that ingests what a drop carries: draws
39
+ * {@link ghostBounds} through the `previewPaths` dep. Reads `ingestion` and
40
+ * `previewPaths`, which the action declares.
41
+ */
42
+ function previewIngest(deps, params, handlers) {
43
+ const items = params?.items;
44
+ const { ingestion, previewPaths } = deps;
45
+ if (!items || items.length === 0 || !ingestion || !previewPaths) return void 0;
46
+ const boxes = ghostBounds(items, {
47
+ point: params?.world ?? null,
48
+ viewportWorldRect: () => ingestion.viewportWorldRect()
49
+ }, handlers);
50
+ if (boxes.length === 0) return void 0;
51
+ return previewPaths.show(boxes.map((b) => ({ path: rectPath(b.x, b.y, b.width, b.height) })));
52
+ }
53
+
54
+ //#endregion
55
+ export { ghostBounds, previewIngest };
56
+ //# sourceMappingURL=ghost.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ghost.js","names":[],"sources":["../../../src/domains/ingestion/ghost.ts"],"sourcesContent":["/**\n * What a drag still in flight would land as. A browser shows a hovering drag's\n * kinds and MIMEs and nothing else — no contents, no size — so the ghost is a\n * placeholder box per thing the drop would insert, where the handlers would put\n * it, at a size that reads the same at every zoom.\n */\nimport { rectPath } from '@weasel-js/geom';\nimport { pendingKindOf, type ActionDeps, type IngestItem } from '@weasel-js/routing';\nimport { couldClaim, type ContentHandlerEntry, type IngestCtx } from './contentHandlers';\nimport { arrivalBounds } from './placement';\nimport type { ImmediateParams } from '@weasel-js/routing';\n\ntype Box = { x: number; y: number; width: number; height: number };\n\n/** A file's ghost is a square this fraction of the viewport's shorter side;\n * a string's is twice as wide and a quarter as tall, the shape of a line. */\nconst GHOST_FRACTION = 1 / 6;\n\n/**\n * The boxes a drop of `items` would fill: one per file some handler could\n * claim, or, when no file could be claimed, one for the strings, since a drag's\n * string flavors are several spellings of one thing. Cascaded and centered as\n * the kit's handlers place what they insert.\n */\nexport function ghostBounds(\n items: readonly IngestItem[],\n at: Pick<IngestCtx, 'point' | 'viewportWorldRect'>,\n handlers?: readonly ContentHandlerEntry[],\n): Box[] {\n const claimable = items.filter((item) => couldClaim([item], handlers));\n const kindOf = (item: IngestItem) => pendingKindOf(item) ?? item.kind;\n const files = claimable.filter((item) => kindOf(item) === 'file');\n const hasString = claimable.some((item) => kindOf(item) === 'string');\n const view = at.viewportWorldRect();\n const side = Math.min(view.width, view.height) * GHOST_FRACTION;\n const sizes = files.length > 0\n ? files.map(() => ({ width: side, height: side }))\n : hasString ? [{ width: side * 2, height: side / 4 }] : [];\n return sizes.map((size, i) => arrivalBounds(at, size, i));\n}\n\n/**\n * `Action.preview` for an action that ingests what a drop carries: draws\n * {@link ghostBounds} through the `previewPaths` dep. Reads `ingestion` and\n * `previewPaths`, which the action declares.\n */\nexport function previewIngest(\n deps: ActionDeps,\n params: Record<string, unknown> | undefined,\n handlers?: readonly ContentHandlerEntry[],\n): (() => void) | undefined {\n const items = params?.items as IngestItem[] | undefined;\n const { ingestion, previewPaths } = deps;\n if (!items || items.length === 0 || !ingestion || !previewPaths) return undefined;\n const point = (params as ImmediateParams | undefined)?.world ?? null;\n const boxes = ghostBounds(items, { point, viewportWorldRect: () => ingestion.viewportWorldRect() }, handlers);\n if (boxes.length === 0) return undefined;\n return previewPaths.show(boxes.map((b) => ({ path: rectPath(b.x, b.y, b.width, b.height) })));\n}\n"],"mappings":";;;;;;;;;;;;;;AAgBA,IAAM,iBAAiB,IAAI;;;;;;;AAQ3B,SAAgB,YACd,OACA,IACA,UACO;CACP,MAAM,YAAY,MAAM,QAAQ,SAAS,WAAW,CAAC,IAAI,GAAG,QAAQ,CAAC;CACrE,MAAM,UAAU,SAAqB,cAAc,IAAI,KAAK,KAAK;CACjE,MAAM,QAAQ,UAAU,QAAQ,SAAS,OAAO,IAAI,MAAM,MAAM;CAChE,MAAM,YAAY,UAAU,MAAM,SAAS,OAAO,IAAI,MAAM,QAAQ;CACpE,MAAM,OAAO,GAAG,kBAAkB;CAClC,MAAM,OAAO,KAAK,IAAI,KAAK,OAAO,KAAK,MAAM,IAAI;CAIjD,QAHc,MAAM,SAAS,IACzB,MAAM,WAAW;EAAE,OAAO;EAAM,QAAQ;CAAK,EAAE,IAC/C,YAAY,CAAC;EAAE,OAAO,OAAO;EAAG,QAAQ,OAAO;CAAE,CAAC,IAAI,CAAC,EACpD,CAAM,KAAK,MAAM,MAAM,cAAc,IAAI,MAAM,CAAC,CAAC;AAC1D;;;;;;AAOA,SAAgB,cACd,MACA,QACA,UAC0B;CAC1B,MAAM,QAAQ,QAAQ;CACtB,MAAM,EAAE,WAAW,iBAAiB;CACpC,IAAI,CAAC,SAAS,MAAM,WAAW,KAAK,CAAC,aAAa,CAAC,cAAc,OAAO;CAExE,MAAM,QAAQ,YAAY,OAAO;EAAE,OADpB,QAAwC,SAAS;EACtB,yBAAyB,UAAU,kBAAkB;CAAE,GAAG,QAAQ;CAC5G,IAAI,MAAM,WAAW,GAAG,OAAO;CAC/B,OAAO,aAAa,KAAK,MAAM,KAAK,OAAO,EAAE,MAAM,SAAS,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC;AAC9F"}
@@ -1,6 +1,6 @@
1
1
  import { arrivalBounds } from "./placement.js";
2
2
 
3
- //#region src/features/ingestion/imageHandler.ts
3
+ //#region src/domains/ingestion/imageHandler.ts
4
4
  var VIEWPORT_FIT = .9;
5
5
  var defaultMeasure = async (file) => {
6
6
  const bitmap = await createImageBitmap(file);
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imageHandler.js","names":[],"sources":["../../../src/domains/ingestion/imageHandler.ts"],"sourcesContent":["/**\n * The kit's `image/*` content handler. With no consumer `resolveSrc`, a file\n * embeds as a `data:` URI so the scene stays serializable.\n *\n * Registered at priority -100 so any consumer handler (default 0) can take\n * image files first.\n */\nimport type { ContentHandlerEntry, IngestCtx } from './contentHandlers';\nimport type { IngestItem } from '@weasel-js/routing';\nimport { arrivalBounds } from './placement';\n\nconst VIEWPORT_FIT = 0.9;\n\nexport type Measure = (file: File) => Promise<{ width: number; height: number }>;\ntype ToDataUri = (file: File) => Promise<string>;\n\nconst defaultMeasure: Measure = async (file) => {\n const bitmap = await createImageBitmap(file);\n const size = { width: bitmap.width, height: bitmap.height };\n bitmap.close();\n return size;\n};\n\nconst defaultToDataUri: ToDataUri = (file) =>\n new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as string);\n reader.onerror = () => reject(reader.error);\n reader.readAsDataURL(file);\n });\n\nlet measure: Measure = defaultMeasure;\nlet toDataUri: ToDataUri = defaultToDataUri;\n\n/** @internal Options for {@link embedFilesAsImageNodes} — used by the kit\n * SVG handler, whose files can't go through `createImageBitmap`. */\nexport interface EmbedImageOptions {\n /** Replace the raster measurer for these files (the SVG handler supplies\n * an `Image`-element one). Defaults to the module measurer (seam-aware). */\n measure?: Measure;\n /** Force the data-URI MIME. A sniffed `.svg` whose `File.type` was empty\n * would otherwise embed as `data:application/octet-stream;…`, which\n * browsers refuse to rasterize. No effect on `resolveSrc` output. */\n mimeOverride?: string;\n}\n\n/**\n * @internal The shared embed engine: for each file, measure → resolve a\n * `src` → fit-clamp to 90% of the visible viewport → insert one image node\n * centered on `ctx.point` (or the viewport center), cascading multi-file\n * batches by a fixed offset. A file that fails to measure or resolve is\n * skipped with a `console.warn`; the rest proceed.\n */\nexport async function embedFilesAsImageNodes(\n files: File[],\n ctx: IngestCtx,\n opts: EmbedImageOptions = {},\n): Promise<void> {\n let index = 0;\n for (const file of files) {\n try {\n // Measure first: a corrupt file should fail cheaply here, not after\n // a consumer `resolveSrc` has already uploaded it somewhere.\n const natural = await (opts.measure ?? measure)(file);\n let src = ctx.resolveSrc ? await ctx.resolveSrc(file) : await toDataUri(file);\n if (!ctx.resolveSrc && opts.mimeOverride) {\n src = src.replace(/^data:[^;,]*/, `data:${opts.mimeOverride}`);\n }\n const view = ctx.viewportWorldRect();\n const scale = Math.min(\n 1,\n (view.width * VIEWPORT_FIT) / natural.width,\n (view.height * VIEWPORT_FIT) / natural.height,\n );\n const size = { width: natural.width * scale, height: natural.height * scale };\n ctx.insert.commit(arrivalBounds(ctx, size, index), { kind: 'image', src });\n index++;\n } catch (err) {\n console.warn(`weasel ingest: image \"${file.name}\" failed to load`, err);\n }\n }\n}\n\nexport const kitImageHandler: ContentHandlerEntry = {\n id: 'kit:image',\n match: (item: IngestItem) => item.kind === 'file' && item.mime.startsWith('image/'),\n types: ['image/*'],\n priority: -100,\n handle(items, ctx: IngestCtx) {\n const files = items.flatMap((it) => (it.kind === 'file' ? [it.file] : []));\n return embedFilesAsImageNodes(files, ctx);\n },\n};\n\n/** @internal test seam — override the bitmap measurer (jsdom has no\n * `createImageBitmap`). Not part of the public barrel. */\nexport function __setImageMeasureForTests(fn: Measure): void {\n measure = fn;\n}\n\n/** @internal test seam — override the FileReader embed. */\nexport function __setFileToDataUriForTests(fn: ToDataUri): void {\n toDataUri = fn;\n}\n\n/** @internal test seam — restore default seams. */\nexport function _resetImageHandlerSeamsForTests(): void {\n measure = defaultMeasure;\n toDataUri = defaultToDataUri;\n}\n"],"mappings":";;;AAWA,IAAM,eAAe;AAKrB,IAAM,iBAA0B,OAAO,SAAS;CAC9C,MAAM,SAAS,MAAM,kBAAkB,IAAI;CAC3C,MAAM,OAAO;EAAE,OAAO,OAAO;EAAO,QAAQ,OAAO;CAAO;CAC1D,OAAO,MAAM;CACb,OAAO;AACT;AAEA,IAAM,oBAA+B,SACnC,IAAI,SAAS,SAAS,WAAW;CAC/B,MAAM,SAAS,IAAI,WAAW;CAC9B,OAAO,eAAe,QAAQ,OAAO,MAAgB;CACrD,OAAO,gBAAgB,OAAO,OAAO,KAAK;CAC1C,OAAO,cAAc,IAAI;AAC3B,CAAC;AAEH,IAAI,UAAmB;AACvB,IAAI,YAAuB;;;;;;;;AAqB3B,eAAsB,uBACpB,OACA,KACA,OAA0B,CAAC,GACZ;CACf,IAAI,QAAQ;CACZ,KAAK,MAAM,QAAQ,OACjB,IAAI;EAGF,MAAM,UAAU,OAAO,KAAK,WAAW,QAAA,CAAS,IAAI;EACpD,IAAI,MAAM,IAAI,aAAa,MAAM,IAAI,WAAW,IAAI,IAAI,MAAM,UAAU,IAAI;EAC5E,IAAI,CAAC,IAAI,cAAc,KAAK,cAC1B,MAAM,IAAI,QAAQ,gBAAgB,QAAQ,KAAK,cAAc;EAE/D,MAAM,OAAO,IAAI,kBAAkB;EACnC,MAAM,QAAQ,KAAK,IACjB,GACC,KAAK,QAAQ,eAAgB,QAAQ,OACrC,KAAK,SAAS,eAAgB,QAAQ,MACzC;EACA,MAAM,OAAO;GAAE,OAAO,QAAQ,QAAQ;GAAO,QAAQ,QAAQ,SAAS;EAAM;EAC5E,IAAI,OAAO,OAAO,cAAc,KAAK,MAAM,KAAK,GAAG;GAAE,MAAM;GAAS;EAAI,CAAC;EACzE;CACF,SAAS,KAAK;EACZ,QAAQ,KAAK,yBAAyB,KAAK,KAAK,mBAAmB,GAAG;CACxE;AAEJ;AAEA,IAAa,kBAAuC;CAClD,IAAI;CACJ,QAAQ,SAAqB,KAAK,SAAS,UAAU,KAAK,KAAK,WAAW,QAAQ;CAClF,OAAO,CAAC,SAAS;CACjB,UAAU;CACV,OAAO,OAAO,KAAgB;EAE5B,OAAO,uBADO,MAAM,SAAS,OAAQ,GAAG,SAAS,SAAS,CAAC,GAAG,IAAI,IAAI,CAAC,CACzC,GAAO,GAAG;CAC1C;AACF"}
@@ -0,0 +1,22 @@
1
+ import { kitImageHandler } from "./imageHandler.js";
2
+ import { kitSvgHandler } from "./svgHandler.js";
3
+ import { kitWeaselJsonHandler } from "./weaselJsonHandler.js";
4
+ import { kitTextHandler } from "./textHandler.js";
5
+
6
+ //#region src/domains/ingestion/kitHandlers.ts
7
+ var KIT_CONTENT_HANDLERS = [
8
+ kitImageHandler,
9
+ kitSvgHandler,
10
+ kitWeaselJsonHandler,
11
+ kitTextHandler
12
+ ];
13
+ /** The kit handlers a canvas with `option` routes through. */
14
+ function kitHandlersFor(option = true) {
15
+ if (option === true) return KIT_CONTENT_HANDLERS;
16
+ if (option === false) return [];
17
+ return KIT_CONTENT_HANDLERS.filter((h) => !option.omit.includes(h.id));
18
+ }
19
+
20
+ //#endregion
21
+ export { KIT_CONTENT_HANDLERS, kitHandlersFor };
22
+ //# sourceMappingURL=kitHandlers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kitHandlers.js","names":[],"sources":["../../../src/domains/ingestion/kitHandlers.ts"],"sourcesContent":["/**\n * The kit's default content handlers, which every `<SceneCanvas>` routes through unless its\n * `ingestion.kitHandlers` turns some or all of them off. They belong to the canvas, not to\n * the page-wide registry, so one canvas turning one off leaves the others alone.\n */\nimport type { ContentHandlerEntry } from './contentHandlers';\nimport { kitImageHandler } from './imageHandler';\nimport { kitSvgHandler } from './svgHandler';\nimport { kitTextHandler } from './textHandler';\nimport { kitWeaselJsonHandler } from './weaselJsonHandler';\n\n/** `<SceneCanvas ingestion={{ kitHandlers }}>`: `true` (the default) keeps every kit handler,\n * `false` none, and `{ omit }` all but the ids named (`'kit:image'`, `'kit:svg'`,\n * `'kit:weasel-json'`, `'kit:text'`). */\nexport type KitHandlersOption = boolean | { omit: readonly string[] };\n\nexport const KIT_CONTENT_HANDLERS: readonly ContentHandlerEntry[] = [\n kitImageHandler, kitSvgHandler, kitWeaselJsonHandler, kitTextHandler,\n];\n\n/** The kit handlers a canvas with `option` routes through. */\nexport function kitHandlersFor(option: KitHandlersOption = true): readonly ContentHandlerEntry[] {\n if (option === true) return KIT_CONTENT_HANDLERS;\n if (option === false) return [];\n return KIT_CONTENT_HANDLERS.filter((h) => !option.omit.includes(h.id));\n}\n"],"mappings":";;;;;;AAgBA,IAAa,uBAAuD;CAClE;CAAiB;CAAe;CAAsB;AACxD;;AAGA,SAAgB,eAAe,SAA4B,MAAsC;CAC/F,IAAI,WAAW,MAAM,OAAO;CAC5B,IAAI,WAAW,OAAO,OAAO,CAAC;CAC9B,OAAO,qBAAqB,QAAQ,MAAM,CAAC,OAAO,KAAK,SAAS,EAAE,EAAE,CAAC;AACvE"}
@@ -1,4 +1,4 @@
1
- //#region src/features/ingestion/openFilePicker.ts
1
+ //#region src/domains/ingestion/openFilePicker.ts
2
2
  /**
3
3
  * Show the browser's file picker and resolve with what the user chose —
4
4
  * empty when they cancel. Must be called from a user gesture (a click
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openFilePicker.js","names":[],"sources":["../../../src/domains/ingestion/openFilePicker.ts"],"sourcesContent":["/** Options for {@link openFilePicker}. */\nexport interface OpenFilePickerOptions {\n /** `<input accept>` filter, e.g. `'image/*'` or `'.csv,text/csv'`. */\n accept?: string;\n /** Allow multi-select. Default false. */\n multiple?: boolean;\n}\n\n/**\n * Show the browser's file picker and resolve with what the user chose —\n * empty when they cancel. Must be called from a user gesture (a click\n * handler): browsers block programmatic `input.click()` otherwise. Pairs\n * with `CanvasExtensionApi.ingest`:\n *\n * ```ts\n * const files = await openFilePicker({ accept: 'image/*', multiple: true });\n * canvasRef.current?.ingest(files);\n * ```\n */\nexport function openFilePicker(opts: OpenFilePickerOptions = {}): Promise<File[]> {\n return new Promise((resolve) => {\n const input = document.createElement('input');\n input.type = 'file';\n input.hidden = true;\n if (opts.accept) input.accept = opts.accept;\n if (opts.multiple) input.multiple = true;\n document.body.appendChild(input);\n const finish = (files: File[]) => {\n input.remove();\n resolve(files);\n };\n input.addEventListener('change', () => finish(Array.from(input.files ?? [])), { once: true });\n // Where `cancel` is unsupported, the hidden input leaks until the page\n // unloads.\n input.addEventListener('cancel', () => finish([]), { once: true });\n input.click();\n });\n}\n"],"mappings":";;;;;;;;;;;;AAmBA,SAAgB,eAAe,OAA8B,CAAC,GAAoB;CAChF,OAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,QAAQ,SAAS,cAAc,OAAO;EAC5C,MAAM,OAAO;EACb,MAAM,SAAS;EACf,IAAI,KAAK,QAAQ,MAAM,SAAS,KAAK;EACrC,IAAI,KAAK,UAAU,MAAM,WAAW;EACpC,SAAS,KAAK,YAAY,KAAK;EAC/B,MAAM,UAAU,UAAkB;GAChC,MAAM,OAAO;GACb,QAAQ,KAAK;EACf;EACA,MAAM,iBAAiB,gBAAgB,OAAO,MAAM,KAAK,MAAM,SAAS,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,CAAC;EAG5F,MAAM,iBAAiB,gBAAgB,OAAO,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,CAAC;EACjE,MAAM,MAAM;CACd,CAAC;AACH"}
@@ -1,4 +1,4 @@
1
- //#region src/features/ingestion/placement.ts
1
+ //#region src/domains/ingestion/placement.ts
2
2
  var CASCADE_OFFSET_PX = 24;
3
3
  /**
4
4
  * Where the kit's handlers put arriving content of a given size: centered on
@@ -0,0 +1 @@
1
+ {"version":3,"file":"placement.js","names":[],"sources":["../../../src/domains/ingestion/placement.ts"],"sourcesContent":["import type { IngestCtx } from './contentHandlers';\n\nconst CASCADE_OFFSET_PX = 24;\n\n/**\n * Where the kit's handlers put arriving content of a given size: centered on\n * `ctx.point` (a drop, a pointed ingest) or on the viewport (a paste), with the\n * `index`-th item of one arrival stepped down and right of the first.\n */\nexport function arrivalBounds(\n ctx: Pick<IngestCtx, 'point' | 'viewportWorldRect'>,\n size: { width: number; height: number },\n index: number,\n): { x: number; y: number; width: number; height: number } {\n const view = ctx.viewportWorldRect();\n const center = ctx.point ?? { x: view.x + view.width / 2, y: view.y + view.height / 2 };\n const offset = index * CASCADE_OFFSET_PX;\n return {\n x: center.x - size.width / 2 + offset,\n y: center.y - size.height / 2 + offset,\n width: size.width,\n height: size.height,\n };\n}\n"],"mappings":";AAEA,IAAM,oBAAoB;;;;;;AAO1B,SAAgB,cACd,KACA,MACA,OACyD;CACzD,MAAM,OAAO,IAAI,kBAAkB;CACnC,MAAM,SAAS,IAAI,SAAS;EAAE,GAAG,KAAK,IAAI,KAAK,QAAQ;EAAG,GAAG,KAAK,IAAI,KAAK,SAAS;CAAE;CACtF,MAAM,SAAS,QAAQ;CACvB,OAAO;EACL,GAAG,OAAO,IAAI,KAAK,QAAQ,IAAI;EAC/B,GAAG,OAAO,IAAI,KAAK,SAAS,IAAI;EAChC,OAAO,KAAK;EACZ,QAAQ,KAAK;CACf;AACF"}
@@ -1,6 +1,6 @@
1
1
  import { embedFilesAsImageNodes } from "./imageHandler.js";
2
2
 
3
- //#region src/features/ingestion/svgHandler.ts
3
+ //#region src/domains/ingestion/svgHandler.ts
4
4
  var SVG_MIME = "image/svg+xml";
5
5
  var FALLBACK_WIDTH = 300;
6
6
  var FALLBACK_HEIGHT = 150;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"svgHandler.js","names":[],"sources":["../../../src/domains/ingestion/svgHandler.ts"],"sourcesContent":["/**\n * The kit's `image/svg+xml` content handler.\n *\n * Default: a dropped SVG stays **one scene node** — an embedded image\n * (`data.image.src` as a `data:image/svg+xml;…` URI, source bytes preserved\n * verbatim). Chromium's `createImageBitmap` rejects raw SVG blobs, so this\n * handler measures via an `Image` element instead, falling back to 300×150\n * (the CSS default object size) when the file declares no intrinsic\n * dimensions.\n *\n * `<SceneCanvas ingestion={{ svg: { unpack: unpackSvgFiles } }}>` switches to\n * parsing the file into native scene nodes. The unpacker is injected because\n * the SVG parser lives in `@weasel-js/svg`, which depends on this package.\n *\n * Registered at priority -90: ahead of `kit:image` (-100) so SVG files\n * never hit the raster measure path, still behind any consumer handler\n * (default 0).\n */\nimport type { ContentHandlerEntry, IngestCtx } from './contentHandlers';\nimport type { IngestItem } from '@weasel-js/routing';\nimport { embedFilesAsImageNodes, type Measure } from './imageHandler';\n\nexport const SVG_MIME = 'image/svg+xml';\n\n// CSS default object size — what browsers use for replaced content with no\n// intrinsic dimensions (e.g. a viewBox-only SVG in an <img>).\nconst FALLBACK_WIDTH = 300;\nconst FALLBACK_HEIGHT = 150;\n\n/**\n * True for file items that are SVG by MIME — or by `.svg` extension when\n * the source offered no usable type (an empty `File.type` normalizes to\n * `application/octet-stream` at materialization).\n */\nexport function isSvgFileItem(item: IngestItem): boolean {\n if (item.kind !== 'file') return false;\n if (item.mime === SVG_MIME) return true;\n return item.mime === 'application/octet-stream' && /\\.svg$/i.test(item.file.name);\n}\n\n/**\n * Strict-prefix sniff: could this text be an SVG document? `<svg` (or an XML\n * declaration, then optional whitespace/comments/DOCTYPE, then `<svg`) must\n * open the document — no mid-document substring scans. Exists because Safari drops both custom MIMEs\n * and `image/svg+xml` from async clipboard writes, leaving `text/plain` as\n * the only surviving flavor of an SVG copy.\n */\nexport function sniffSvgText(text: string): boolean {\n let t = text.trimStart();\n if (t.startsWith('<?xml')) {\n const end = t.indexOf('?>');\n if (end < 0) return false;\n t = t.slice(end + 2);\n }\n for (;;) {\n t = t.trimStart();\n if (t.startsWith('<!--')) {\n const end = t.indexOf('-->');\n if (end < 0) return false;\n t = t.slice(end + 3);\n continue;\n }\n // Skipping ANY doctype is safe — a non-SVG doctype's root element fails\n // the final `<svg` check anyway. No internal-subset (`[...]`) handling.\n if (/^<!doctype/i.test(t)) {\n const end = t.indexOf('>');\n if (end < 0) return false;\n t = t.slice(end + 1);\n continue;\n }\n break;\n }\n return /^<svg[\\s>/]/i.test(t);\n}\n\nconst defaultMeasure: Measure = async (file) => {\n const url = URL.createObjectURL(file);\n try {\n const img = new Image();\n img.src = url;\n await img.decode();\n return {\n width: img.naturalWidth || FALLBACK_WIDTH,\n height: img.naturalHeight || FALLBACK_HEIGHT,\n };\n } finally {\n URL.revokeObjectURL(url);\n }\n};\n\nlet measure: Measure = defaultMeasure;\n\nexport const kitSvgHandler: ContentHandlerEntry = {\n id: 'kit:svg',\n // Files by MIME/extension, plus the text/plain fallback flavor: SVG markup\n // pasted as plain text (the only flavor Safari lets an SVG copy keep).\n match: (item) =>\n isSvgFileItem(item)\n || (item.kind === 'string' && item.mime === 'text/plain' && sniffSvgText(item.text)),\n types: [SVG_MIME, 'application/octet-stream', 'text/plain'],\n priority: -90,\n handle(items, ctx: IngestCtx) {\n const files = items.flatMap((it) => (it.kind === 'file' ? [it.file] : []));\n // String items are skipped when the weasel-JSON handler already pasted\n // this event's copy: ingesting both flavors would double-paste.\n // WARNING: this read must stay before any `await` in this handle — the\n // sync-ordering of handler microtasks is what makes the flag visible.\n if (!ctx.consumedWeaselPayload) {\n for (const it of items) {\n if (it.kind === 'string') files.push(new File([it.text], 'pasted.svg', { type: SVG_MIME }));\n }\n }\n if (files.length === 0) return;\n if (ctx.svg?.unpack) return ctx.svg.unpack(files, ctx);\n // Thunk (not the variable) so the test seam is read at call time.\n return embedFilesAsImageNodes(files, ctx, {\n measure: (f) => measure(f),\n mimeOverride: SVG_MIME,\n });\n },\n};\n\n/** @internal test seam — override the Image-element measurer (jsdom can't\n * decode SVG). Not part of the public barrel. */\nexport function __setSvgMeasureForTests(fn: Measure): void {\n measure = fn;\n}\n\n/** @internal test seam — restore the default measurer. */\nexport function _resetSvgHandlerSeamsForTests(): void {\n measure = defaultMeasure;\n}\n"],"mappings":";;;AAsBA,IAAa,WAAW;AAIxB,IAAM,iBAAiB;AACvB,IAAM,kBAAkB;;;;;;AAOxB,SAAgB,cAAc,MAA2B;CACvD,IAAI,KAAK,SAAS,QAAQ,OAAO;CACjC,IAAI,KAAK,SAAS,UAAU,OAAO;CACnC,OAAO,KAAK,SAAS,8BAA8B,UAAU,KAAK,KAAK,KAAK,IAAI;AAClF;;;;;;;;AASA,SAAgB,aAAa,MAAuB;CAClD,IAAI,IAAI,KAAK,UAAU;CACvB,IAAI,EAAE,WAAW,OAAO,GAAG;EACzB,MAAM,MAAM,EAAE,QAAQ,IAAI;EAC1B,IAAI,MAAM,GAAG,OAAO;EACpB,IAAI,EAAE,MAAM,MAAM,CAAC;CACrB;CACA,SAAS;EACP,IAAI,EAAE,UAAU;EAChB,IAAI,EAAE,WAAW,MAAM,GAAG;GACxB,MAAM,MAAM,EAAE,QAAQ,KAAK;GAC3B,IAAI,MAAM,GAAG,OAAO;GACpB,IAAI,EAAE,MAAM,MAAM,CAAC;GACnB;EACF;EAGA,IAAI,cAAc,KAAK,CAAC,GAAG;GACzB,MAAM,MAAM,EAAE,QAAQ,GAAG;GACzB,IAAI,MAAM,GAAG,OAAO;GACpB,IAAI,EAAE,MAAM,MAAM,CAAC;GACnB;EACF;EACA;CACF;CACA,OAAO,eAAe,KAAK,CAAC;AAC9B;AAEA,IAAM,iBAA0B,OAAO,SAAS;CAC9C,MAAM,MAAM,IAAI,gBAAgB,IAAI;CACpC,IAAI;EACF,MAAM,MAAM,IAAI,MAAM;EACtB,IAAI,MAAM;EACV,MAAM,IAAI,OAAO;EACjB,OAAO;GACL,OAAO,IAAI,gBAAgB;GAC3B,QAAQ,IAAI,iBAAiB;EAC/B;CACF,UAAU;EACR,IAAI,gBAAgB,GAAG;CACzB;AACF;AAEA,IAAI,UAAmB;AAEvB,IAAa,gBAAqC;CAChD,IAAI;CAGJ,QAAQ,SACN,cAAc,IAAI,KACd,KAAK,SAAS,YAAY,KAAK,SAAS,gBAAgB,aAAa,KAAK,IAAI;CACpF,OAAO;EAAC;EAAU;EAA4B;CAAY;CAC1D,UAAU;CACV,OAAO,OAAO,KAAgB;EAC5B,MAAM,QAAQ,MAAM,SAAS,OAAQ,GAAG,SAAS,SAAS,CAAC,GAAG,IAAI,IAAI,CAAC,CAAE;EAKzE,IAAI,CAAC,IAAI,uBACP;QAAK,MAAM,MAAM,OACf,IAAI,GAAG,SAAS,UAAU,MAAM,KAAK,IAAI,KAAK,CAAC,GAAG,IAAI,GAAG,cAAc,EAAE,MAAM,SAAS,CAAC,CAAC;EAC5F;EAEF,IAAI,MAAM,WAAW,GAAG;EACxB,IAAI,IAAI,KAAK,QAAQ,OAAO,IAAI,IAAI,OAAO,OAAO,GAAG;EAErD,OAAO,uBAAuB,OAAO,KAAK;GACxC,UAAU,MAAM,QAAQ,CAAC;GACzB,cAAc;EAChB,CAAC;CACH;AACF"}
@@ -1,11 +1,14 @@
1
1
  import { arrivalBounds } from "./placement.js";
2
2
  import { measureTextBounds } from "@weasel-js/text";
3
3
 
4
- //#region src/features/ingestion/textHandler.ts
4
+ //#region src/domains/ingestion/textHandler.ts
5
5
  /**
6
6
  * The kit's `text/plain` content handler: a pasted or dropped string becomes
7
- * a text node. Not registered by default; a canvas takes it through
8
- * `ingestion.handlers`.
7
+ * a text node. Registered by default with the other kit handlers.
8
+ *
9
+ * Strings that arrive beside a file are passed over: there they describe the
10
+ * file (its name, a URL to it), as when a browser drags an image or the OS
11
+ * copies a file, and the file is what was meant.
9
12
  *
10
13
  * The node's box is the string's own laid-out size at the default text style,
11
14
  * so it holds the text exactly and wraps nowhere the string does not break.
@@ -24,6 +27,7 @@ function createTextHandler(opts = {}) {
24
27
  types: ["text/plain"],
25
28
  priority: -110,
26
29
  handle(items, ctx) {
30
+ if ((ctx.items ?? items).some((it) => it.kind === "file")) return;
27
31
  let index = 0;
28
32
  for (const item of items) {
29
33
  if (item.kind !== "string") continue;
@@ -38,10 +42,8 @@ function createTextHandler(opts = {}) {
38
42
  }
39
43
  };
40
44
  }
41
- /**
42
- * Turns pasted or dropped plain text into a text node sized to the string. Registered nowhere by
43
- * default: a canvas opts in with `ingestion={{ handlers: [kitTextHandler] }}`.
44
- */
45
+ /** Turns pasted or dropped plain text into a text node sized to the string. A kit handler, so a
46
+ * canvas with the `ingest` feature takes text unless its `ingestion.kitHandlers` omits it. */
45
47
  var kitTextHandler = createTextHandler();
46
48
 
47
49
  //#endregion
@@ -0,0 +1 @@
1
+ {"version":3,"file":"textHandler.js","names":[],"sources":["../../../src/domains/ingestion/textHandler.ts"],"sourcesContent":["/**\n * The kit's `text/plain` content handler: a pasted or dropped string becomes\n * a text node. Registered by default with the other kit handlers.\n *\n * Strings that arrive beside a file are passed over: there they describe the\n * file (its name, a URL to it), as when a browser drags an image or the OS\n * copies a file, and the file is what was meant.\n *\n * The node's box is the string's own laid-out size at the default text style,\n * so it holds the text exactly and wraps nowhere the string does not break.\n * Placement is the image handler's: on the point, or centered in the viewport.\n *\n * Priority -110, below every kit handler, so SVG markup and a copied node\n * payload arriving as plain text go to the handlers that read them.\n */\nimport { measureTextBounds } from '@weasel-js/text';\nimport type { IngestItem } from '@weasel-js/routing';\nimport type { ContentHandlerEntry, IngestCtx } from './contentHandlers';\nimport { arrivalBounds } from './placement';\n\n/** Options for {@link createTextHandler}. */\nexport interface TextHandlerOptions {\n /** Size of a string's layout box in world units. Default\n * `measureTextBounds`, which reads the registered font. */\n measure?: (text: string) => { width: number; height: number };\n}\n\n/** Build the plain-text content handler; {@link kitTextHandler} is the one\n * with the default measurer. */\nexport function createTextHandler(opts: TextHandlerOptions = {}): ContentHandlerEntry {\n const measure = opts.measure ?? ((text: string) => measureTextBounds(text));\n return {\n id: 'kit:text',\n match: (item: IngestItem) => item.kind === 'string' && item.mime === 'text/plain',\n types: ['text/plain'],\n priority: -110,\n handle(items, ctx: IngestCtx) {\n if ((ctx.items ?? items).some((it) => it.kind === 'file')) return;\n let index = 0;\n for (const item of items) {\n if (item.kind !== 'string') continue;\n const text = item.text.replace(/\\r\\n?/g, '\\n').replace(/\\n+$/, '');\n if (text.trim() === '') continue;\n ctx.insert.commit(arrivalBounds(ctx, measure(text), index), { kind: 'text', text });\n index++;\n }\n },\n };\n}\n\n/** Turns pasted or dropped plain text into a text node sized to the string. A kit handler, so a\n * canvas with the `ingest` feature takes text unless its `ingestion.kitHandlers` omits it. */\nexport const kitTextHandler: ContentHandlerEntry = createTextHandler();\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA6BA,SAAgB,kBAAkB,OAA2B,CAAC,GAAwB;CACpF,MAAM,UAAU,KAAK,aAAa,SAAiB,kBAAkB,IAAI;CACzE,OAAO;EACL,IAAI;EACJ,QAAQ,SAAqB,KAAK,SAAS,YAAY,KAAK,SAAS;EACrE,OAAO,CAAC,YAAY;EACpB,UAAU;EACV,OAAO,OAAO,KAAgB;GAC5B,KAAK,IAAI,SAAS,MAAA,CAAO,MAAM,OAAO,GAAG,SAAS,MAAM,GAAG;GAC3D,IAAI,QAAQ;GACZ,KAAK,MAAM,QAAQ,OAAO;IACxB,IAAI,KAAK,SAAS,UAAU;IAC5B,MAAM,OAAO,KAAK,KAAK,QAAQ,UAAU,IAAI,CAAC,CAAC,QAAQ,QAAQ,EAAE;IACjE,IAAI,KAAK,KAAK,MAAM,IAAI;IACxB,IAAI,OAAO,OAAO,cAAc,KAAK,QAAQ,IAAI,GAAG,KAAK,GAAG;KAAE,MAAM;KAAQ;IAAK,CAAC;IAClF;GACF;EACF;CACF;AACF;;;AAIA,IAAa,iBAAsC,kBAAkB"}
@@ -1,25 +1,25 @@
1
1
  import { dwarn } from "../../debug/flag.js";
2
- import { registerContentHandler } from "./contentHandlers.js";
3
- import { acquireKitContentHandlers } from "./registerKitHandlers.js";
2
+ import { admitHandlerMimes } from "./contentHandlers.js";
3
+ import { kitHandlersFor } from "./kitHandlers.js";
4
4
  import { useCallback, useEffect, useMemo } from "react";
5
5
  import { useLatest } from "@weasel-js/react";
6
6
  import { itemsFromFiles } from "@weasel-js/routing";
7
7
 
8
- //#region src/features/ingestion/useCanvasIngestion.ts
8
+ //#region src/domains/ingestion/useCanvasIngestion.ts
9
9
  /**
10
- * One canvas's share of ingestion: its content handlers for as long as it is mounted, the
10
+ * One canvas's share of ingestion: its content handlers and the kit's it keeps, which run on
11
+ * it alone and admit the string flavors they name while it is mounted, the
11
12
  * clipboard seam pastes re-materialize through, and the imperative `ingest`, which triggers
12
13
  * the `ingest` action so its deps resolve as they do on the dispatcher path.
13
14
  *
14
15
  * `getRegistry` answers null until the canvas's action registry is reachable.
15
16
  */
16
17
  function useCanvasIngestion(adapter, getRegistry, options) {
17
- const handlers = options?.handlers;
18
- useEffect(() => {
19
- const disposers = [acquireKitContentHandlers()];
20
- for (const h of handlers ?? []) disposers.push(registerContentHandler(h));
21
- return () => disposers.forEach((d) => d());
22
- }, [handlers]);
18
+ const own = options?.handlers;
19
+ const kit = options?.kitHandlers;
20
+ const omitted = kit === false ? null : typeof kit === "object" ? kit.omit.join("\n") : "";
21
+ const handlers = useMemo(() => [...own ?? [], ...kitHandlersFor(omitted === null ? false : { omit: omitted.split("\n") })], [own, omitted]);
22
+ useEffect(() => admitHandlerMimes(handlers), [handlers]);
23
23
  const reviver = options?.clipboard?.reviver;
24
24
  const enabled = options?.clipboard?.enabled !== false;
25
25
  const onOmitted = useLatest(options?.clipboard?.onOmitted);
@@ -48,16 +48,14 @@ function useCanvasIngestion(adapter, getRegistry, options) {
48
48
  }
49
49
  const params = {
50
50
  items,
51
- ...point ? {
52
- worldX: point.x,
53
- worldY: point.y
54
- } : {}
51
+ ...point ? { world: point } : {}
55
52
  };
56
53
  const ran = registry.trigger("ingest", params);
57
54
  if (!ran) dwarn("ingest", "CanvasExtensionApi.ingest ran nothing: this canvas has no `ingest` action. Add `ingest` to `features`.");
58
55
  return ran;
59
56
  }, [registryOf]),
60
- clipboard
57
+ clipboard,
58
+ handlers
61
59
  };
62
60
  }
63
61
 
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCanvasIngestion.js","names":[],"sources":["../../../src/domains/ingestion/useCanvasIngestion.ts"],"sourcesContent":["import { useLatest } from '@weasel-js/react';\nimport { itemsFromFiles, type ActionsRegistry, type IngestItem } from '@weasel-js/routing';\nimport type { InsertAdapter } from 'core/adapters/types';\nimport type { ClipboardIngestCtx } from 'interactions/actions/depSchema';\nimport { useCallback, useEffect, useMemo } from 'react';\nimport { dwarn } from '../../debug';\nimport { admitHandlerMimes, type ContentHandlerEntry } from './contentHandlers';\nimport { kitHandlersFor, type KitHandlersOption } from './kitHandlers';\nimport type { ImmediateParams } from '@weasel-js/routing';\n\n/** What `useCanvasIngestion` reads of `<SceneCanvas ingestion>`. */\nexport interface CanvasIngestionOptions {\n handlers?: ContentHandlerEntry[];\n kitHandlers?: KitHandlersOption;\n clipboard?: {\n reviver?: ClipboardIngestCtx['reviver'];\n enabled?: boolean;\n onOmitted?: ClipboardIngestCtx['onOmitted'];\n };\n}\n\nexport interface CanvasIngestion {\n /** `SceneCanvasApi.ingest`. */\n ingest(input: File[] | IngestItem[], point?: { x: number; y: number }): boolean;\n /** For the `ingestion` dep. Absent, so the kit weasel-JSON handler declines, when\n * `clipboard.enabled` is false or the adapter cannot `commitPaste`. */\n clipboard: ClipboardIngestCtx | undefined;\n /** For the `ingestion` dep: this canvas's own handlers and the kit's it keeps. */\n handlers: readonly ContentHandlerEntry[];\n}\n\n/**\n * One canvas's share of ingestion: its content handlers and the kit's it keeps, which run on\n * it alone and admit the string flavors they name while it is mounted, the\n * clipboard seam pastes re-materialize through, and the imperative `ingest`, which triggers\n * the `ingest` action so its deps resolve as they do on the dispatcher path.\n *\n * `getRegistry` answers null until the canvas's action registry is reachable.\n */\nexport function useCanvasIngestion(\n adapter: InsertAdapter<{ id: string }>,\n getRegistry: () => ActionsRegistry | null,\n options: CanvasIngestionOptions | undefined,\n): CanvasIngestion {\n const own = options?.handlers;\n const kit = options?.kitHandlers;\n // Keyed by value, so an inline `{ omit: [...] }` does not rebuild the list every render.\n const omitted = kit === false ? null : typeof kit === 'object' ? kit.omit.join('\\n') : '';\n const handlers = useMemo(\n () => [...(own ?? []), ...kitHandlersFor(omitted === null ? false : { omit: omitted.split('\\n') })],\n [own, omitted],\n );\n useEffect(() => admitHandlerMimes(handlers), [handlers]);\n\n const reviver = options?.clipboard?.reviver;\n const enabled = options?.clipboard?.enabled !== false;\n const onOmitted = useLatest(options?.clipboard?.onOmitted);\n const clipboard = useMemo<ClipboardIngestCtx | undefined>(() => {\n if (!enabled || !adapter.commitPaste) return undefined;\n return {\n adapter,\n ...(reviver ? { reviver } : {}),\n onOmitted: (omitted) => onOmitted.current?.(omitted),\n };\n }, [adapter, enabled, reviver, onOmitted]);\n\n const registryOf = useLatest(getRegistry);\n const ingest = useCallback<CanvasIngestion['ingest']>((input, point) => {\n const items: IngestItem[] = (input as (File | IngestItem)[]).map((entry) =>\n entry instanceof File ? itemsFromFiles([entry])[0] : entry,\n );\n if (items.length === 0) return false;\n const registry = registryOf.current();\n if (!registry) {\n dwarn('ingest', 'CanvasExtensionApi.ingest called before the action registry mounted — call ignored. Defer to an effect or event handler.');\n return false;\n }\n const params: ImmediateParams = { items, ...(point ? { world: point } : {}) };\n const ran = registry.trigger('ingest', params);\n if (!ran) dwarn('ingest', 'CanvasExtensionApi.ingest ran nothing: this canvas has no `ingest` action. Add `ingest` to `features`.');\n return ran;\n }, [registryOf]);\n\n return { ingest, clipboard, handlers };\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAuCA,SAAgB,mBACd,SACA,aACA,SACiB;CACjB,MAAM,MAAM,SAAS;CACrB,MAAM,MAAM,SAAS;CAErB,MAAM,UAAU,QAAQ,QAAQ,OAAO,OAAO,QAAQ,WAAW,IAAI,KAAK,KAAK,IAAI,IAAI;CACvF,MAAM,WAAW,cACT,CAAC,GAAI,OAAO,CAAC,GAAI,GAAG,eAAe,YAAY,OAAO,QAAQ,EAAE,MAAM,QAAQ,MAAM,IAAI,EAAE,CAAC,CAAC,GAClG,CAAC,KAAK,OAAO,CACf;CACA,gBAAgB,kBAAkB,QAAQ,GAAG,CAAC,QAAQ,CAAC;CAEvD,MAAM,UAAU,SAAS,WAAW;CACpC,MAAM,UAAU,SAAS,WAAW,YAAY;CAChD,MAAM,YAAY,UAAU,SAAS,WAAW,SAAS;CACzD,MAAM,YAAY,cAA8C;EAC9D,IAAI,CAAC,WAAW,CAAC,QAAQ,aAAa,OAAO;EAC7C,OAAO;GACL;GACA,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;GAC7B,YAAY,YAAY,UAAU,UAAU,OAAO;EACrD;CACF,GAAG;EAAC;EAAS;EAAS;EAAS;CAAS,CAAC;CAEzC,MAAM,aAAa,UAAU,WAAW;CAiBxC,OAAO;EAAE,QAhBM,aAAwC,OAAO,UAAU;GACtE,MAAM,QAAuB,MAAgC,KAAK,UAChE,iBAAiB,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,KACvD;GACA,IAAI,MAAM,WAAW,GAAG,OAAO;GAC/B,MAAM,WAAW,WAAW,QAAQ;GACpC,IAAI,CAAC,UAAU;IACb,MAAM,UAAU,0HAA0H;IAC1I,OAAO;GACT;GACA,MAAM,SAA0B;IAAE;IAAO,GAAI,QAAQ,EAAE,OAAO,MAAM,IAAI,CAAC;GAAG;GAC5E,MAAM,MAAM,SAAS,QAAQ,UAAU,MAAM;GAC7C,IAAI,CAAC,KAAK,MAAM,UAAU,wGAAwG;GAClI,OAAO;EACT,GAAG,CAAC,UAAU,CAEL;EAAQ;EAAW;CAAS;AACvC"}
@@ -3,7 +3,7 @@ import { materializePaste } from "../../interactions/actions/clipboard/materiali
3
3
  import { WEASEL_CLIPBOARD_MIME, WEASEL_CLIPBOARD_MIME_WEB, extractWeaselClipboardFromSvg, parseWeaselClipboardText, sniffWeaselClipboardText } from "../../interactions/actions/clipboard/wireFormat.js";
4
4
  import { sniffSvgText } from "./svgHandler.js";
5
5
 
6
- //#region src/features/ingestion/weaselJsonHandler.ts
6
+ //#region src/domains/ingestion/weaselJsonHandler.ts
7
7
  /** text/plain SVG whose `<metadata>` carries a verified weasel payload —
8
8
  * the draw→draw DOM-paste flavor (Chromium exposes only text/plain there).
9
9
  * The `"weaselClipboard"` substring guard keeps match cheap on arbitrary
@@ -0,0 +1 @@
1
+ {"version":3,"file":"weaselJsonHandler.js","names":[],"sources":["../../../src/domains/ingestion/weaselJsonHandler.ts"],"sourcesContent":["/**\n * The kit's weasel-JSON clipboard content handler.\n *\n * Consumes `application/x-weasel-clipboard+json` payloads in either MIME\n * spelling (Chromium's async-clipboard API round-trips custom formats with\n * a `web ` prefix), `text/plain` that sniffs as the wire format, and\n * `text/plain` SVG carrying the wire format embedded in a `<metadata>`\n * element (Chromium's DOM paste event exposes ONLY `text/plain`). It replays\n * them as a paste through the hosting canvas's adapter, mirroring\n * `useClipboardOps.paste`'s op construction exactly so OS-arriving pastes\n * and in-memory pastes are indistinguishable in history.\n *\n * Declines when `ctx.clipboard` is absent or the payload fails to parse.\n * `runIngest` consumes items at *match* time, so \"leave it for other\n * handlers\" is the sniff's job: non-weasel `text/plain` never matches.\n */\nimport type { NodeId } from 'core/scene/types';\nimport type { ClipboardSnapshot } from 'core/adapters/types';\nimport { materializePaste } from 'interactions/actions/clipboard/materializePaste';\nimport {\n WEASEL_CLIPBOARD_MIME,\n WEASEL_CLIPBOARD_MIME_WEB,\n sniffWeaselClipboardText,\n parseWeaselClipboardText,\n extractWeaselClipboardFromSvg,\n} from 'interactions/actions/clipboard/wireFormat';\nimport { dwarn } from '../../debug';\nimport type { ContentHandlerEntry, IngestCtx } from './contentHandlers';\n// Direct module import (not the ./index barrel) to avoid a cycle — the\n// barrel re-exports this handler.\nimport { sniffSvgText } from './svgHandler';\n\n/** text/plain SVG whose `<metadata>` carries a verified weasel payload —\n * the draw→draw DOM-paste flavor (Chromium exposes only text/plain there).\n * The `\"weaselClipboard\"` substring guard keeps match cheap on arbitrary\n * external SVG pastes before the extraction regex runs. */\nconst isMetadataBearingSvg = (text: string): boolean =>\n text.includes('\"weaselClipboard\"')\n && sniffSvgText(text)\n && extractWeaselClipboardFromSvg(text) !== null;\n\nexport const kitWeaselJsonHandler: ContentHandlerEntry = {\n id: 'kit:weasel-json',\n // Above the other kit handlers (-100/-90) so weasel payloads never fall\n // through to the SVG/text paths; still below consumer handlers (default 0).\n priority: -50,\n match: (item) =>\n item.kind === 'string'\n && (item.mime === WEASEL_CLIPBOARD_MIME\n || item.mime === WEASEL_CLIPBOARD_MIME_WEB\n || (item.mime === 'text/plain'\n && (sniffWeaselClipboardText(item.text) || isMetadataBearingSvg(item.text)))),\n types: [WEASEL_CLIPBOARD_MIME, WEASEL_CLIPBOARD_MIME_WEB, 'text/plain'],\n handle(items, ctx: IngestCtx) {\n const clipboard = ctx.clipboard;\n if (!clipboard) {\n dwarn('clipboard', 'weasel payload arrived but ctx.clipboard is not wired (ingestion.clipboard disabled or absent) — declining');\n return;\n }\n if (!clipboard.adapter.commitPaste) {\n dwarn('clipboard', 'weasel payload arrived but the clipboard adapter lacks commitPaste — declining');\n return;\n }\n const item = items.find((it) => it.kind === 'string');\n if (!item || item.kind !== 'string') return;\n // The metadata-bearing-SVG flavor: unwrap the embedded payload before\n // parsing. Extraction was already verified at match time, so a null here\n // is the same inert-decline class as a corrupt custom-MIME payload.\n const text = sniffSvgText(item.text)\n ? extractWeaselClipboardFromSvg(item.text)\n : item.text;\n if (text === null) {\n dwarn('clipboard', 'weasel payload vanished from the SVG metadata between match and handle — declining');\n return;\n }\n const nodes = parseWeaselClipboardText(text, clipboard.reviver);\n if (nodes === null) {\n dwarn('clipboard', 'weasel payload unusable — declining (see prior warning if the parse threw)');\n return;\n }\n // `ctx.point` is null for paste, so no dropPoint is threaded — the\n // cascade offset is the placement.\n const snapshot: ClipboardSnapshot = { items: nodes };\n const result = materializePaste(clipboard.adapter, snapshot, {\n currentSelection: ctx.selection.get(),\n onOmitted: clipboard.onOmitted,\n });\n if (!result) return;\n ctx.applyOps(result.ops, 'Paste');\n ctx.selection.set(result.newIds as NodeId[]);\n // So kit:svg's text/plain fallback declines the SVG flavor of this copy.\n // WARNING: do not add an `await` earlier in this handle — the flag must\n // be written before lower-priority handlers' microtasks run.\n ctx.consumedWeaselPayload = true;\n },\n};\n"],"mappings":";;;;;;;;;;AAoCA,IAAM,wBAAwB,SAC5B,KAAK,SAAS,qBAAmB,KAC9B,aAAa,IAAI,KACjB,8BAA8B,IAAI,MAAM;AAE7C,IAAa,uBAA4C;CACvD,IAAI;CAGJ,UAAU;CACV,QAAQ,SACN,KAAK,SAAS,aACV,KAAK,SAAS,yBACb,KAAK,SAAS,6BACb,KAAK,SAAS,iBACZ,yBAAyB,KAAK,IAAI,KAAK,qBAAqB,KAAK,IAAI;CAC/E,OAAO;EAAC;EAAuB;EAA2B;CAAY;CACtE,OAAO,OAAO,KAAgB;EAC5B,MAAM,YAAY,IAAI;EACtB,IAAI,CAAC,WAAW;GACd,MAAM,aAAa,4GAA4G;GAC/H;EACF;EACA,IAAI,CAAC,UAAU,QAAQ,aAAa;GAClC,MAAM,aAAa,gFAAgF;GACnG;EACF;EACA,MAAM,OAAO,MAAM,MAAM,OAAO,GAAG,SAAS,QAAQ;EACpD,IAAI,CAAC,QAAQ,KAAK,SAAS,UAAU;EAIrC,MAAM,OAAO,aAAa,KAAK,IAAI,IAC/B,8BAA8B,KAAK,IAAI,IACvC,KAAK;EACT,IAAI,SAAS,MAAM;GACjB,MAAM,aAAa,oFAAoF;GACvG;EACF;EACA,MAAM,QAAQ,yBAAyB,MAAM,UAAU,OAAO;EAC9D,IAAI,UAAU,MAAM;GAClB,MAAM,aAAa,4EAA4E;GAC/F;EACF;EAGA,MAAM,WAA8B,EAAE,OAAO,MAAM;EACnD,MAAM,SAAS,iBAAiB,UAAU,SAAS,UAAU;GAC3D,kBAAkB,IAAI,UAAU,IAAI;GACpC,WAAW,UAAU;EACvB,CAAC;EACD,IAAI,CAAC,QAAQ;EACb,IAAI,SAAS,OAAO,KAAK,OAAO;EAChC,IAAI,UAAU,IAAI,OAAO,MAAkB;EAI3C,IAAI,wBAAwB;CAC9B;AACF"}
@@ -2,7 +2,7 @@ import { accentColorOf } from "../../canvas/accentColor.js";
2
2
  import { ellipsePath, polylineFromPoints } from "@weasel-js/geom";
3
3
  import { prefersReducedMotion } from "@weasel-js/react";
4
4
 
5
- //#region src/features/longPress/longPressFeedback.ts
5
+ //#region src/domains/longPress/longPressFeedback.ts
6
6
  /** Entry and layer id of the default feedback, so a host can find or order it. */
7
7
  var LONG_PRESS_FEEDBACK_ID = "long-press-feedback";
8
8
  var RING_SEGMENTS = 64;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"longPressFeedback.js","names":["circlePath"],"sources":["../../../src/domains/longPress/longPressFeedback.ts"],"sourcesContent":["/**\n * The default \"press is registering\" feedback for a touch or pen long-press: a\n * ring around the press point that fills over the hold. It reads the `longPress`\n * dep the gesture dispatcher publishes, so it shows only for a press some\n * binding would fire on, and disappears when the press fires, moves past the\n * drag threshold, lifts, or is canceled.\n */\nimport type { LongPressState } from '@weasel-js/routing';\nimport { prefersReducedMotion } from '@weasel-js/react';\nimport type { RenderLayer } from 'core/layers/render';\nimport { ellipsePath, polylineFromPoints, type Path } from '@weasel-js/geom';\nimport type { DrawCommand } from '@weasel-js/renderer';\nimport type { CanvasExtensionApi } from '../../canvas/canvasExtension';\nimport type { ContributionDepReader, SurfaceContribution } from '../../canvas/surfaceContribution';\nimport { accentColorOf } from '../../canvas/accentColor';\n\n/** Entry and layer id of the default feedback, so a host can find or order it. */\nexport const LONG_PRESS_FEEDBACK_ID = 'long-press-feedback';\n\n/** Options for {@link createLongPressFeedbackContribution}. */\nexport interface LongPressFeedbackOptions {\n /** Entry and layer id. Default {@link LONG_PRESS_FEEDBACK_ID}. */\n id?: string;\n /** Ring radius in CSS pixels. Default 22 — clear of a fingertip. */\n radius?: number;\n /** Ring stroke width in CSS pixels. Default 3. */\n width?: number;\n /** Ring color. Default the theme's `--wzl-accent`, read off the canvas\n * element when each press starts. */\n color?: string | (() => string);\n /** How long a press is held before the ring appears, in ms, so an ordinary\n * tap shows nothing. Default 120. */\n delay?: number;\n /** `true` shows a static full ring instead of one that fills, and runs no\n * frame loop. Default `'auto'`: follow `prefers-reduced-motion`, read when\n * each press starts. */\n reducedMotion?: boolean | 'auto';\n}\n\nconst RING_SEGMENTS = 64;\nconst TRACK_OPACITY = 0.3;\n\nconst nowMs = (): number => performance.now();\n\n/** An open arc from 12 o'clock, clockwise on a y-down screen, through `t` of a turn. */\nfunction arcPath(cx: number, cy: number, r: number, t: number): Path {\n const n = Math.max(1, Math.ceil(RING_SEGMENTS * t));\n const pts: { x: number; y: number }[] = [];\n for (let i = 0; i <= n; i++) {\n const a = (i / n) * t * Math.PI * 2;\n pts.push({ x: cx + r * Math.sin(a), y: cy - r * Math.cos(a) });\n }\n return polylineFromPoints(pts);\n}\n\nfunction circlePath(cx: number, cy: number, r: number): Path {\n return ellipsePath({ x: cx - r, y: cy - r, width: 2 * r, height: 2 * r });\n}\n\nconst viewIdOf = (data: unknown): string | null =>\n (data as { viewId?: string | null } | null)?.viewId ?? null;\n\n/**\n * The default long-press feedback, as one entry. `<SceneCanvas>` installs it\n * unless `longPress={{ feedback: false }}`; pass a contribution of your own there to\n * replace it — it reads the same `longPress` dep this one does.\n */\nexport function createLongPressFeedbackContribution(\n opts: LongPressFeedbackOptions = {},\n): SurfaceContribution {\n const id = opts.id ?? LONG_PRESS_FEEDBACK_ID;\n const radius = opts.radius ?? 22;\n const width = opts.width ?? 3;\n const delay = opts.delay ?? 120;\n\n let state: LongPressState | undefined;\n let color = '';\n let reduced = false;\n\n const showing = (): boolean => {\n const p = state?.get();\n return !!p && p.armed;\n };\n\n const layer: RenderLayer<unknown> = {\n id,\n label: 'Long-press feedback',\n space: 'screen',\n draw: (data): DrawCommand[] => {\n const p = state?.get();\n // Screen space is the whole canvas, so the root pass draws the ring\n // wherever the press routed; a view pass drawing it too would double it.\n if (!state || !p || !p.armed || viewIdOf(data) !== null) return [];\n const now = nowMs();\n if (now - p.startedAt < delay) return [];\n const { x, y } = p.local;\n const stroke = (opacity?: number) => ({\n paint: { color, ...(opacity !== undefined ? { opacity } : {}) },\n width: { px: width },\n cap: 'round' as const,\n });\n if (reduced) return [{ kind: 'path', path: circlePath(x, y, radius), stroke: stroke() }];\n return [\n { kind: 'path', path: circlePath(x, y, radius), stroke: stroke(TRACK_OPACITY) },\n { kind: 'path', path: arcPath(x, y, radius, state.progress(now)), stroke: stroke() },\n ];\n },\n };\n\n const attach = (api: CanvasExtensionApi, deps: ContributionDepReader): (() => void) => {\n const store = deps.get('longPress');\n if (!store) return () => {};\n state = store;\n let appear: ReturnType<typeof setTimeout> | undefined;\n\n const onChange = (): void => {\n clearTimeout(appear);\n appear = undefined;\n const p = store.get();\n if (p?.armed) {\n color = typeof opts.color === 'function' ? opts.color() : (opts.color ?? accentColorOf(api.element));\n reduced = opts.reducedMotion === 'auto' || opts.reducedMotion === undefined\n ? prefersReducedMotion()\n : opts.reducedMotion;\n // A static ring changes once, when it appears; nothing needs frames.\n const wait = delay - (nowMs() - p.startedAt);\n if (reduced && wait > 0) appear = setTimeout(() => { api.requestRedraw(); }, wait);\n }\n api.requestRedraw();\n };\n\n const offStore = store.subscribe(onChange);\n onChange();\n return () => {\n offStore();\n clearTimeout(appear);\n state = undefined;\n api.requestRedraw();\n };\n };\n\n return {\n id,\n eligibility: { always: true },\n overlay: layer,\n attach,\n // The filling ring asks for the next frame from each painted one while it shows.\n afterPaint: (ctx) => {\n if (!reduced && showing()) ctx.requestFrame();\n },\n };\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- //#region src/features/measure/actionIds.ts
1
+ //#region src/domains/measure/actionIds.ts
2
2
  /** The ids of the measure feature's actions. */
3
3
  var MEASURE_ACTION_IDS = {
4
4
  /** The measure tool's drag, which adds a measurement. */
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actionIds.js","names":[],"sources":["../../../src/domains/measure/actionIds.ts"],"sourcesContent":["/** The ids of the measure feature's actions. */\nexport const MEASURE_ACTION_IDS = {\n /** The measure tool's drag, which adds a measurement. */\n measure: 'measure',\n /** The measure tool's click, which picks the measurement under it. */\n pickAt: 'measure.pickAt',\n /** A click on a measurement under any tool, which picks it. */\n pick: 'measure.pick',\n /** A drag on a measurement under any tool: a picked end moves that end, the\n * rest moves the whole measurement. */\n drag: 'measure.drag',\n moveBy: 'measure.moveBy',\n rotateBy: 'measure.rotateBy',\n delete: 'measure.delete',\n clear: 'measure.clear',\n unpick: 'measure.unpick',\n} as const;\n"],"mappings":";;AACA,IAAa,qBAAqB;;CAEhC,SAAS;;CAET,QAAQ;;CAER,MAAM;;;CAGN,MAAM;CACN,QAAQ;CACR,UAAU;CACV,QAAQ;CACR,OAAO;CACP,QAAQ;AACV"}
@@ -11,7 +11,7 @@ import { MEASURE_LABELS, deleteMeasureOps } from "./measureEdits.js";
11
11
  import { MEASURE_ACTION_IDS } from "./actionIds.js";
12
12
  import { ActionDisabledReason } from "@weasel-js/routing";
13
13
 
14
- //#region src/features/measure/measureCommands.ts
14
+ //#region src/domains/measure/measureCommands.ts
15
15
  var MOVE_LABEL = "Move by Measurement";
16
16
  var ROTATE_LABEL = "Rotate by Measurement";
17
17
  function current(deps, ms) {