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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (488) hide show
  1. package/CHANGELOG.md +275 -0
  2. package/dist/WeaselProvider.js +1 -1
  3. package/dist/affordances/pathAnchors.js +10 -6
  4. package/dist/affordances/pathAnchors.js.map +1 -1
  5. package/dist/animation/poseTarget.js +46 -15
  6. package/dist/animation/poseTarget.js.map +1 -1
  7. package/dist/animation/poseTransition.js +3 -3
  8. package/dist/animation/poseTransition.js.map +1 -1
  9. package/dist/animation/reflow.js +3 -6
  10. package/dist/animation/reflow.js.map +1 -1
  11. package/dist/animation/rig/bindRig.js +2 -2
  12. package/dist/animation/rig/bindRig.js.map +1 -1
  13. package/dist/animation/rig/skinBinding.js +2 -2
  14. package/dist/animation/rig/skinBinding.js.map +1 -1
  15. package/dist/barrel/canvas.js +5 -1
  16. package/dist/barrel/chrome.js +3 -1
  17. package/dist/barrel/paint.js +4 -4
  18. package/dist/barrel/paths.js +9 -4
  19. package/dist/barrel/renderer.js +3 -2
  20. package/dist/barrel/routing.js +4 -4
  21. package/dist/barrel/scene.js +7 -1
  22. package/dist/barrel/text.js +2 -2
  23. package/dist/barrel/viewport.js +6 -2
  24. package/dist/canvas/Canvas.js +2 -1
  25. package/dist/canvas/Canvas.js.map +1 -1
  26. package/dist/canvas/CanvasView.js +23 -34
  27. package/dist/canvas/CanvasView.js.map +1 -1
  28. package/dist/canvas/ModalityHud.js +1 -1
  29. package/dist/canvas/NodeShape.js +5 -1
  30. package/dist/canvas/NodeShape.js.map +1 -1
  31. package/dist/canvas/SceneCanvas/ActionsProviderIfRoot.js +1 -1
  32. package/dist/canvas/SceneCanvas/DepRegistryProviderIfRoot.js +1 -1
  33. package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js +1 -1
  34. package/dist/canvas/SceneCanvas/SceneCanvasInput.js +2 -0
  35. package/dist/canvas/SceneCanvas/SceneCanvasInput.js.map +1 -1
  36. package/dist/canvas/SceneCanvas/SurfaceDeclarations.js +23 -0
  37. package/dist/canvas/SceneCanvas/SurfaceDeclarations.js.map +1 -0
  38. package/dist/canvas/SceneCanvas/canvasPassThrough.js +2 -1
  39. package/dist/canvas/SceneCanvas/canvasPassThrough.js.map +1 -1
  40. package/dist/canvas/SceneCanvas/defaultNodeRouting.js +2 -2
  41. package/dist/canvas/SceneCanvas/defaultNodeRouting.js.map +1 -1
  42. package/dist/canvas/SceneCanvas/props.js.map +1 -1
  43. package/dist/canvas/SceneCanvas/scenePointQuery.js +27 -5
  44. package/dist/canvas/SceneCanvas/scenePointQuery.js.map +1 -1
  45. package/dist/canvas/SceneCanvas/useActionTunings.js +6 -1
  46. package/dist/canvas/SceneCanvas/useActionTunings.js.map +1 -1
  47. package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js +1 -0
  48. package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js.map +1 -1
  49. package/dist/canvas/SceneCanvas/useCanvasTools.js +10 -2
  50. package/dist/canvas/SceneCanvas/useCanvasTools.js.map +1 -1
  51. package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js +25 -19
  52. package/dist/canvas/SceneCanvas/usePreviewGhostLayer.js.map +1 -1
  53. package/dist/canvas/SceneCanvas/useSceneCanvasHandle.js +9 -3
  54. package/dist/canvas/SceneCanvas/useSceneCanvasHandle.js.map +1 -1
  55. package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js +17 -2
  56. package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js.map +1 -1
  57. package/dist/canvas/SceneCanvas/useSceneCanvasTools.js +2 -0
  58. package/dist/canvas/SceneCanvas/useSceneCanvasTools.js.map +1 -1
  59. package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js +19 -4
  60. package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js.map +1 -1
  61. package/dist/canvas/SceneCanvas/useScenePicking.js +31 -6
  62. package/dist/canvas/SceneCanvas/useScenePicking.js.map +1 -1
  63. package/dist/canvas/SceneCanvas/useSceneSelectTool.js +4 -5
  64. package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
  65. package/dist/canvas/SceneCanvas/useSceneStandardActions.js +7 -4
  66. package/dist/canvas/SceneCanvas/useSceneStandardActions.js.map +1 -1
  67. package/dist/canvas/SceneCanvas.js +30 -12
  68. package/dist/canvas/SceneCanvas.js.map +1 -1
  69. package/dist/canvas/ViewMarker.js +35 -0
  70. package/dist/canvas/ViewMarker.js.map +1 -0
  71. package/dist/canvas/accentColor.js +26 -3
  72. package/dist/canvas/accentColor.js.map +1 -1
  73. package/dist/canvas/buildSceneTree.js +14 -20
  74. package/dist/canvas/buildSceneTree.js.map +1 -1
  75. package/dist/canvas/copiesAbove.js +1 -1
  76. package/dist/canvas/copiesAbove.js.map +1 -1
  77. package/dist/canvas/defaultDrawOne.js +11 -6
  78. package/dist/canvas/defaultDrawOne.js.map +1 -1
  79. package/dist/canvas/deps/areaSelect.js +5 -2
  80. package/dist/canvas/deps/areaSelect.js.map +1 -1
  81. package/dist/canvas/deps/booleans.js +3 -2
  82. package/dist/canvas/deps/booleans.js.map +1 -1
  83. package/dist/canvas/deps/enteredGroup.js +81 -0
  84. package/dist/canvas/deps/enteredGroup.js.map +1 -0
  85. package/dist/canvas/deps/hitTestArea.js +28 -16
  86. package/dist/canvas/deps/hitTestArea.js.map +1 -1
  87. package/dist/canvas/deps/insert.js +10 -38
  88. package/dist/canvas/deps/insert.js.map +1 -1
  89. package/dist/canvas/deps/insertSite.js +39 -0
  90. package/dist/canvas/deps/insertSite.js.map +1 -0
  91. package/dist/canvas/deps/lassoSelect.js +6 -3
  92. package/dist/canvas/deps/lassoSelect.js.map +1 -1
  93. package/dist/canvas/deps/pathEditCommits.js +3 -7
  94. package/dist/canvas/deps/pathEditCommits.js.map +1 -1
  95. package/dist/canvas/deps/pathLeaf.js +1 -1
  96. package/dist/canvas/deps/pathLeaf.js.map +1 -1
  97. package/dist/canvas/deps/slice.js +2 -1
  98. package/dist/canvas/deps/slice.js.map +1 -1
  99. package/dist/canvas/derivedPath.js +14 -6
  100. package/dist/canvas/derivedPath.js.map +1 -1
  101. package/dist/canvas/doubleClickTarget.js +9 -11
  102. package/dist/canvas/doubleClickTarget.js.map +1 -1
  103. package/dist/canvas/drawnBounds.js +26 -9
  104. package/dist/canvas/drawnBounds.js.map +1 -1
  105. package/dist/canvas/inheritedPaints.js +55 -0
  106. package/dist/canvas/inheritedPaints.js.map +1 -0
  107. package/dist/canvas/nodeShapes/derivedPainter.js +15 -16
  108. package/dist/canvas/nodeShapes/derivedPainter.js.map +1 -1
  109. package/dist/canvas/nodeShapes/nodePaint.js +7 -34
  110. package/dist/canvas/nodeShapes/nodePaint.js.map +1 -1
  111. package/dist/canvas/nodeShapes/pathPainter.js +7 -7
  112. package/dist/canvas/nodeShapes/pathPainter.js.map +1 -1
  113. package/dist/canvas/nodeShapes/rectFallbackPainter.js +4 -5
  114. package/dist/canvas/nodeShapes/rectFallbackPainter.js.map +1 -1
  115. package/dist/canvas/nodeShapes/shapePainter.js +9 -9
  116. package/dist/canvas/nodeShapes/shapePainter.js.map +1 -1
  117. package/dist/canvas/nodeShapes/textPainter.js +13 -12
  118. package/dist/canvas/nodeShapes/textPainter.js.map +1 -1
  119. package/dist/canvas/ownClip.js +24 -0
  120. package/dist/canvas/ownClip.js.map +1 -0
  121. package/dist/canvas/paintCull.js.map +1 -1
  122. package/dist/canvas/pickIndex/nodeBroadBox.js +86 -0
  123. package/dist/canvas/pickIndex/nodeBroadBox.js.map +1 -0
  124. package/dist/canvas/pickIndex/scenePickIndex.js +208 -0
  125. package/dist/canvas/pickIndex/scenePickIndex.js.map +1 -0
  126. package/dist/canvas/pickReach.js +3 -3
  127. package/dist/canvas/pickReach.js.map +1 -1
  128. package/dist/canvas/pickWalk.js +17 -91
  129. package/dist/canvas/pickWalk.js.map +1 -1
  130. package/dist/canvas/poseCascade.js +1 -1
  131. package/dist/canvas/poseCascade.js.map +1 -1
  132. package/dist/canvas/publishPreviews.js +97 -0
  133. package/dist/canvas/publishPreviews.js.map +1 -0
  134. package/dist/canvas/renderSceneToPixels.js +1 -1
  135. package/dist/canvas/renderSceneToPixels.js.map +1 -1
  136. package/dist/canvas/sceneAdapter.js +7 -5
  137. package/dist/canvas/sceneAdapter.js.map +1 -1
  138. package/dist/canvas/sceneClipboard.js +17 -10
  139. package/dist/canvas/sceneClipboard.js.map +1 -1
  140. package/dist/canvas/scenePickReads.js +37 -0
  141. package/dist/canvas/scenePickReads.js.map +1 -0
  142. package/dist/canvas/scenePickSource.js +74 -0
  143. package/dist/canvas/scenePickSource.js.map +1 -0
  144. package/dist/canvas/sceneSlotWiring.js +9 -3
  145. package/dist/canvas/sceneSlotWiring.js.map +1 -1
  146. package/dist/canvas/sceneViewRender.js +9 -4
  147. package/dist/canvas/sceneViewRender.js.map +1 -1
  148. package/dist/canvas/shownNodes.js +15 -0
  149. package/dist/canvas/shownNodes.js.map +1 -0
  150. package/dist/canvas/surfaceContribution.js +12 -8
  151. package/dist/canvas/surfaceContribution.js.map +1 -1
  152. package/dist/canvas/useCanvasView.js +4 -2
  153. package/dist/canvas/useCanvasView.js.map +1 -1
  154. package/dist/canvas/useLayerStack.js +6 -2
  155. package/dist/canvas/useLayerStack.js.map +1 -1
  156. package/dist/canvas/useSceneAdapter.js +5 -3
  157. package/dist/canvas/useSceneAdapter.js.map +1 -1
  158. package/dist/canvas/useViewAim.js +121 -0
  159. package/dist/canvas/useViewAim.js.map +1 -0
  160. package/dist/canvas/useViewNesting.js +3 -2
  161. package/dist/canvas/useViewNesting.js.map +1 -1
  162. package/dist/canvas/useViewRegistrySurface.js +4 -2
  163. package/dist/canvas/useViewRegistrySurface.js.map +1 -1
  164. package/dist/canvas/viewMarkers.js +63 -0
  165. package/dist/canvas/viewMarkers.js.map +1 -0
  166. package/dist/canvas/viewRegistry.js +27 -1
  167. package/dist/canvas/viewRegistry.js.map +1 -1
  168. package/dist/clipboard.d.ts +2 -2
  169. package/dist/{clone-C3e0OTuF.d.ts → clone-qb2e1F5p.d.ts} +2 -2
  170. package/dist/clone.d.ts +3 -3
  171. package/dist/core/isPlainObject.js +2 -2
  172. package/dist/core/layers/layerGroup.js.map +1 -1
  173. package/dist/core/pages/pageEdits.js +2 -3
  174. package/dist/core/pages/pageEdits.js.map +1 -1
  175. package/dist/core/paint/meshKindLazy.js +59 -2
  176. package/dist/core/paint/meshKindLazy.js.map +1 -1
  177. package/dist/core/scene/NodeRouting.js.map +1 -1
  178. package/dist/core/scene/arrivalWindow.js +1 -1
  179. package/dist/core/scene/arrivalWindow.js.map +1 -1
  180. package/dist/core/scene/clipNothing.js +10 -0
  181. package/dist/core/scene/clipNothing.js.map +1 -0
  182. package/dist/core/scene/declaredFieldSetters.js +44 -0
  183. package/dist/core/scene/declaredFieldSetters.js.map +1 -0
  184. package/dist/core/scene/depMemo.js +14 -3
  185. package/dist/core/scene/depMemo.js.map +1 -1
  186. package/dist/core/scene/dependents.js +9 -1
  187. package/dist/core/scene/dependents.js.map +1 -1
  188. package/dist/core/scene/derivedParts.js +10 -11
  189. package/dist/core/scene/derivedParts.js.map +1 -1
  190. package/dist/core/scene/documentOf.js +43 -0
  191. package/dist/core/scene/documentOf.js.map +1 -0
  192. package/dist/core/scene/effectivePose.js +44 -15
  193. package/dist/core/scene/effectivePose.js.map +1 -1
  194. package/dist/core/scene/fieldEdits.js +17 -14
  195. package/dist/core/scene/fieldEdits.js.map +1 -1
  196. package/dist/core/scene/fontReads.js +37 -0
  197. package/dist/core/scene/fontReads.js.map +1 -0
  198. package/dist/core/scene/historyForm.js +4 -4
  199. package/dist/core/scene/historyForm.js.map +1 -1
  200. package/dist/core/scene/inheritedPaint.js +97 -0
  201. package/dist/core/scene/inheritedPaint.js.map +1 -0
  202. package/dist/core/scene/kitOps.js +3 -23
  203. package/dist/core/scene/kitOps.js.map +1 -1
  204. package/dist/core/scene/kitRegistry.js +5 -0
  205. package/dist/core/scene/kitRegistry.js.map +1 -1
  206. package/dist/core/scene/layoutPass.js +1 -2
  207. package/dist/core/scene/layoutPass.js.map +1 -1
  208. package/dist/core/scene/nodeAsShown.js +72 -0
  209. package/dist/core/scene/nodeAsShown.js.map +1 -0
  210. package/dist/core/scene/nodeFnFields.js +2 -10
  211. package/dist/core/scene/nodeFnFields.js.map +1 -1
  212. package/dist/core/scene/nodeMemo.js +21 -8
  213. package/dist/core/scene/nodeMemo.js.map +1 -1
  214. package/dist/core/scene/nodeStore.js +2 -4
  215. package/dist/core/scene/nodeStore.js.map +1 -1
  216. package/dist/core/scene/pathEffects/blend.js.map +1 -1
  217. package/dist/core/scene/pathEffects/derive.js +10 -4
  218. package/dist/core/scene/pathEffects/derive.js.map +1 -1
  219. package/dist/core/scene/pathEffects/effects.js +3 -1
  220. package/dist/core/scene/pathEffects/effects.js.map +1 -1
  221. package/dist/core/scene/pathEffects/foundHidden.js +35 -0
  222. package/dist/core/scene/pathEffects/foundHidden.js.map +1 -0
  223. package/dist/core/scene/pathEffects/textAlong.js +95 -0
  224. package/dist/core/scene/pathEffects/textAlong.js.map +1 -0
  225. package/dist/core/scene/plyStacks.js +89 -0
  226. package/dist/core/scene/plyStacks.js.map +1 -0
  227. package/dist/core/scene/poseHold.js +42 -24
  228. package/dist/core/scene/poseHold.js.map +1 -1
  229. package/dist/core/scene/poseOverrides.js +35 -38
  230. package/dist/core/scene/poseOverrides.js.map +1 -1
  231. package/dist/core/scene/posePlies.js +59 -0
  232. package/dist/core/scene/posePlies.js.map +1 -0
  233. package/dist/core/scene/reconcileSpecs.js +6 -15
  234. package/dist/core/scene/reconcileSpecs.js.map +1 -1
  235. package/dist/core/scene/renderOrder.js +10 -4
  236. package/dist/core/scene/renderOrder.js.map +1 -1
  237. package/dist/core/scene/sameValue.js +17 -0
  238. package/dist/core/scene/sameValue.js.map +1 -0
  239. package/dist/core/scene/scene.js +3 -3
  240. package/dist/core/scene/scene.js.map +1 -1
  241. package/dist/core/scene/sceneEdits.js +8 -0
  242. package/dist/core/scene/sceneEdits.js.map +1 -1
  243. package/dist/core/scene/sceneState.js +1 -2
  244. package/dist/core/scene/sceneState.js.map +1 -1
  245. package/dist/core/scene/serialization.js +45 -9
  246. package/dist/core/scene/serialization.js.map +1 -1
  247. package/dist/core/scene/staleReplay.js +5 -2
  248. package/dist/core/scene/staleReplay.js.map +1 -1
  249. package/dist/core/scene/statedFields.js +4 -2
  250. package/dist/core/scene/statedFields.js.map +1 -1
  251. package/dist/core/scene/treeEdits.js +2 -4
  252. package/dist/core/scene/treeEdits.js.map +1 -1
  253. package/dist/core/scene/treeOrder.js +3 -1
  254. package/dist/core/scene/treeOrder.js.map +1 -1
  255. package/dist/core/scene/treeReads.js +1 -2
  256. package/dist/core/scene/treeReads.js.map +1 -1
  257. package/dist/core/scene/treeShape.js +13 -0
  258. package/dist/core/scene/treeShape.js.map +1 -0
  259. package/dist/core/scene/types.js.map +1 -1
  260. package/dist/core/scene/useScene.js +1 -1
  261. package/dist/core/scene/useScene.js.map +1 -1
  262. package/dist/{cornerHandles-BHuoMs1o.d.ts → cornerHandles-CNCb7aTY.d.ts} +3 -2
  263. package/dist/{depSchema-BpZNOv7q.d.ts → depSchema-B1RS-IJx.d.ts} +39 -18
  264. package/dist/devtools/handlers.js +1 -1
  265. package/dist/devtools/handlers.js.map +1 -1
  266. package/dist/devtools/protocol.js.map +1 -1
  267. package/dist/devtools/registry.js +4 -4
  268. package/dist/devtools/registry.js.map +1 -1
  269. package/dist/devtools-protocol.d.ts +4 -3
  270. package/dist/devtools.d.ts +3 -2
  271. package/dist/features/measure/measureContribution.js +4 -2
  272. package/dist/features/measure/measureContribution.js.map +1 -1
  273. package/dist/features/measure/measureHit.js +5 -3
  274. package/dist/features/measure/measureHit.js.map +1 -1
  275. package/dist/features/measure/measurement.js +1 -1
  276. package/dist/features/measure/measurement.js.map +1 -1
  277. package/dist/features/modes/modeLayers.js +14 -7
  278. package/dist/features/modes/modeLayers.js.map +1 -1
  279. package/dist/features/modes/modeTriggers.js +10 -6
  280. package/dist/features/modes/modeTriggers.js.map +1 -1
  281. package/dist/features/pathEffects/effectSources.js +69 -0
  282. package/dist/features/pathEffects/effectSources.js.map +1 -0
  283. package/dist/features/pathEffects/readSource.js +18 -2
  284. package/dist/features/pathEffects/readSource.js.map +1 -1
  285. package/dist/features/pathEffects/textItems.js +53 -0
  286. package/dist/features/pathEffects/textItems.js.map +1 -0
  287. package/dist/features/pathEffects/useEffectSourcesLayer.js +53 -0
  288. package/dist/features/pathEffects/useEffectSourcesLayer.js.map +1 -0
  289. package/dist/features/paths/editablePath.js +1 -1
  290. package/dist/features/paths/editablePath.js.map +1 -1
  291. package/dist/features/paths/pathEditState.js +5 -2
  292. package/dist/features/paths/pathEditState.js.map +1 -1
  293. package/dist/features/paths/pathSnap/sceneIndex.js +1 -1
  294. package/dist/features/paths/pathSnap/sceneIndex.js.map +1 -1
  295. package/dist/features/selection/enteredGroupFrame.js +65 -0
  296. package/dist/features/selection/enteredGroupFrame.js.map +1 -0
  297. package/dist/features/selection/enteredGroupScope.js +44 -0
  298. package/dist/features/selection/enteredGroupScope.js.map +1 -0
  299. package/dist/features/text/sceneTextDrafts.js +64 -0
  300. package/dist/features/text/sceneTextDrafts.js.map +1 -0
  301. package/dist/features/text/textHitAt.js +34 -3
  302. package/dist/features/text/textHitAt.js.map +1 -1
  303. package/dist/features/text/useSceneTextEdit.js +23 -19
  304. package/dist/features/text/useSceneTextEdit.js.map +1 -1
  305. package/dist/features/viewports/aim.js +105 -0
  306. package/dist/features/viewports/aim.js.map +1 -0
  307. package/dist/features/viewports/viewMarker.js +82 -0
  308. package/dist/features/viewports/viewMarker.js.map +1 -0
  309. package/dist/features/viewports/viewMarkerDrag.js +55 -0
  310. package/dist/features/viewports/viewMarkerDrag.js.map +1 -0
  311. package/dist/index.d.ts +709 -258
  312. package/dist/index.js +33 -12
  313. package/dist/interactions/actions/admitsChild.js +11 -0
  314. package/dist/interactions/actions/admitsChild.js.map +1 -0
  315. package/dist/interactions/actions/booleans/booleans.js +2 -1
  316. package/dist/interactions/actions/booleans/booleans.js.map +1 -1
  317. package/dist/interactions/actions/booleans/operands.js +6 -5
  318. package/dist/interactions/actions/booleans/operands.js.map +1 -1
  319. package/dist/interactions/actions/clipboard/action.js +27 -17
  320. package/dist/interactions/actions/clipboard/action.js.map +1 -1
  321. package/dist/interactions/actions/defaults/anchorEditing.js +44 -23
  322. package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
  323. package/dist/interactions/actions/defaults/anchorJoin.js +12 -6
  324. package/dist/interactions/actions/defaults/anchorJoin.js.map +1 -1
  325. package/dist/interactions/actions/defaults/bendSegment.js +7 -4
  326. package/dist/interactions/actions/defaults/bendSegment.js.map +1 -1
  327. package/dist/interactions/actions/defaults/cancelGesture.js +11 -8
  328. package/dist/interactions/actions/defaults/cancelGesture.js.map +1 -1
  329. package/dist/interactions/actions/defaults/contextMenu.js +33 -11
  330. package/dist/interactions/actions/defaults/contextMenu.js.map +1 -1
  331. package/dist/interactions/actions/defaults/createPaintAction.js +5 -2
  332. package/dist/interactions/actions/defaults/createPaintAction.js.map +1 -1
  333. package/dist/interactions/actions/defaults/delete.js +28 -14
  334. package/dist/interactions/actions/defaults/delete.js.map +1 -1
  335. package/dist/interactions/actions/defaults/duplicate.js +11 -8
  336. package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
  337. package/dist/interactions/actions/defaults/enterGroup.js +88 -0
  338. package/dist/interactions/actions/defaults/enterGroup.js.map +1 -0
  339. package/dist/interactions/actions/defaults/enterPathEdit.js +20 -10
  340. package/dist/interactions/actions/defaults/enterPathEdit.js.map +1 -1
  341. package/dist/interactions/actions/defaults/enterTextEdit.js +1 -1
  342. package/dist/interactions/actions/defaults/enterTextEdit.js.map +1 -1
  343. package/dist/interactions/actions/defaults/escape.js +11 -8
  344. package/dist/interactions/actions/defaults/escape.js.map +1 -1
  345. package/dist/interactions/actions/defaults/exitPathEdit.js +7 -4
  346. package/dist/interactions/actions/defaults/exitPathEdit.js.map +1 -1
  347. package/dist/interactions/actions/defaults/group.js +26 -20
  348. package/dist/interactions/actions/defaults/group.js.map +1 -1
  349. package/dist/interactions/actions/defaults/ingest.js +10 -4
  350. package/dist/interactions/actions/defaults/ingest.js.map +1 -1
  351. package/dist/interactions/actions/defaults/insertPathAnchor.js +7 -4
  352. package/dist/interactions/actions/defaults/insertPathAnchor.js.map +1 -1
  353. package/dist/interactions/actions/defaults/nudge.js +8 -2
  354. package/dist/interactions/actions/defaults/nudge.js.map +1 -1
  355. package/dist/interactions/actions/defaults/pathEditTools.js +11 -8
  356. package/dist/interactions/actions/defaults/pathEditTools.js.map +1 -1
  357. package/dist/interactions/actions/defaults/pinchZoom.js +7 -4
  358. package/dist/interactions/actions/defaults/pinchZoom.js.map +1 -1
  359. package/dist/interactions/actions/defaults/reorder.js +24 -6
  360. package/dist/interactions/actions/defaults/reorder.js.map +1 -1
  361. package/dist/interactions/actions/defaults/selectAll.js +15 -8
  362. package/dist/interactions/actions/defaults/selectAll.js.map +1 -1
  363. package/dist/interactions/actions/defaults/selectDeep.js +35 -0
  364. package/dist/interactions/actions/defaults/selectDeep.js.map +1 -0
  365. package/dist/interactions/actions/defaults/setFill.js +7 -3
  366. package/dist/interactions/actions/defaults/setFill.js.map +1 -1
  367. package/dist/interactions/actions/defaults/setFillOpacity.js +1 -1
  368. package/dist/interactions/actions/defaults/setStroke.js +12 -11
  369. package/dist/interactions/actions/defaults/setStroke.js.map +1 -1
  370. package/dist/interactions/actions/defaults/setStrokeOpacity.js +1 -1
  371. package/dist/interactions/actions/defaults/toolActivate.js +7 -1
  372. package/dist/interactions/actions/defaults/toolActivate.js.map +1 -1
  373. package/dist/interactions/actions/defaults/toolResetToDefault.js +7 -4
  374. package/dist/interactions/actions/defaults/toolResetToDefault.js.map +1 -1
  375. package/dist/interactions/actions/defaults/undoRedo.js +19 -13
  376. package/dist/interactions/actions/defaults/undoRedo.js.map +1 -1
  377. package/dist/interactions/actions/defaults/viewportWheelPan.js +16 -7
  378. package/dist/interactions/actions/defaults/viewportWheelPan.js.map +1 -1
  379. package/dist/interactions/actions/defaults/viewportZoom.js +24 -6
  380. package/dist/interactions/actions/defaults/viewportZoom.js.map +1 -1
  381. package/dist/interactions/actions/drawnBoxes.js +2 -1
  382. package/dist/interactions/actions/drawnBoxes.js.map +1 -1
  383. package/dist/interactions/actions/edit-anchors/action.js +7 -4
  384. package/dist/interactions/actions/edit-anchors/action.js.map +1 -1
  385. package/dist/interactions/actions/flip/action.js +8 -2
  386. package/dist/interactions/actions/flip/action.js.map +1 -1
  387. package/dist/interactions/actions/insert/action.js +1 -0
  388. package/dist/interactions/actions/insert/action.js.map +1 -1
  389. package/dist/interactions/actions/insert/parent.js +7 -5
  390. package/dist/interactions/actions/insert/parent.js.map +1 -1
  391. package/dist/interactions/actions/insert/placeAction.js +3 -2
  392. package/dist/interactions/actions/insert/placeAction.js.map +1 -1
  393. package/dist/interactions/actions/insert/repositionAction.js +2 -1
  394. package/dist/interactions/actions/insert/repositionAction.js.map +1 -1
  395. package/dist/interactions/actions/insert/session.js +2 -2
  396. package/dist/interactions/actions/insert/session.js.map +1 -1
  397. package/dist/interactions/actions/move/action.js +7 -4
  398. package/dist/interactions/actions/move/action.js.map +1 -1
  399. package/dist/interactions/actions/move/commit/reparent.js +1 -1
  400. package/dist/interactions/actions/move/commit/reparent.js.map +1 -1
  401. package/dist/interactions/actions/move/gestureAdapter.js +6 -7
  402. package/dist/interactions/actions/move/gestureAdapter.js.map +1 -1
  403. package/dist/interactions/actions/move/layout/exits.js +1 -1
  404. package/dist/interactions/actions/move/layout/exits.js.map +1 -1
  405. package/dist/interactions/actions/move/session.js +4 -4
  406. package/dist/interactions/actions/move/session.js.map +1 -1
  407. package/dist/interactions/actions/outlines/createOutlinesAction.js +8 -5
  408. package/dist/interactions/actions/outlines/createOutlinesAction.js.map +1 -1
  409. package/dist/interactions/actions/outlines/outlineStroke.js +5 -3
  410. package/dist/interactions/actions/outlines/outlineStroke.js.map +1 -1
  411. package/dist/interactions/actions/pathCommands/cutJoin.js +8 -5
  412. package/dist/interactions/actions/pathCommands/cutJoin.js.map +1 -1
  413. package/dist/interactions/actions/pathCommands/pathNodes.js +14 -5
  414. package/dist/interactions/actions/pathCommands/pathNodes.js.map +1 -1
  415. package/dist/interactions/actions/resize/cornerHandles.js +2 -1
  416. package/dist/interactions/actions/resize/cornerHandles.js.map +1 -1
  417. package/dist/interactions/actions/selectionSubtree.js +12 -7
  418. package/dist/interactions/actions/selectionSubtree.js.map +1 -1
  419. package/dist/interactions/actions/transform-again/action.js +8 -5
  420. package/dist/interactions/actions/transform-again/action.js.map +1 -1
  421. package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js +14 -2
  422. package/dist/interactions/gestures/thresholdDrag/thresholdDrag.js.map +1 -1
  423. package/dist/json.d.ts +4 -0
  424. package/dist/json.js +3 -0
  425. package/dist/kitSettingsSchema.js +8 -1
  426. package/dist/kitSettingsSchema.js.map +1 -1
  427. package/dist/{math-CrJULaZI.d.ts → math-C_elShP_.d.ts} +7 -1
  428. package/dist/math.d.ts +1 -1
  429. package/dist/math.js +2 -1
  430. package/dist/move.d.ts +4 -4
  431. package/dist/{options-DBxEryYq.d.ts → options-ClEm3SEl.d.ts} +1 -1
  432. package/dist/resize.d.ts +4 -4
  433. package/dist/test-seams.d.ts +6 -6
  434. package/dist/tools/builtin/ellipse/useEllipseTool.js +1 -0
  435. package/dist/tools/builtin/ellipse/useEllipseTool.js.map +1 -1
  436. package/dist/tools/builtin/eyedropper/useEyedropperTool.js +2 -1
  437. package/dist/tools/builtin/eyedropper/useEyedropperTool.js.map +1 -1
  438. package/dist/tools/builtin/hand/dragPanContribution.js +4 -1
  439. package/dist/tools/builtin/hand/dragPanContribution.js.map +1 -1
  440. package/dist/tools/builtin/hand/useHandTool.js +7 -4
  441. package/dist/tools/builtin/hand/useHandTool.js.map +1 -1
  442. package/dist/tools/builtin/image/useImageTool.js +1 -0
  443. package/dist/tools/builtin/image/useImageTool.js.map +1 -1
  444. package/dist/tools/builtin/lasso/useLassoTool.js +1 -0
  445. package/dist/tools/builtin/lasso/useLassoTool.js.map +1 -1
  446. package/dist/tools/builtin/line/useLineTool.js +1 -0
  447. package/dist/tools/builtin/line/useLineTool.js.map +1 -1
  448. package/dist/tools/builtin/measure/useMeasureTool.js +12 -5
  449. package/dist/tools/builtin/measure/useMeasureTool.js.map +1 -1
  450. package/dist/tools/builtin/pen/usePenTool.js +14 -6
  451. package/dist/tools/builtin/pen/usePenTool.js.map +1 -1
  452. package/dist/tools/builtin/pencil/usePencilTool.js +1 -0
  453. package/dist/tools/builtin/pencil/usePencilTool.js.map +1 -1
  454. package/dist/tools/builtin/polygon/usePolygonTool.js +13 -4
  455. package/dist/tools/builtin/polygon/usePolygonTool.js.map +1 -1
  456. package/dist/tools/builtin/pressTarget.js +22 -0
  457. package/dist/tools/builtin/pressTarget.js.map +1 -0
  458. package/dist/tools/builtin/rect/useRectTool.js +1 -0
  459. package/dist/tools/builtin/rect/useRectTool.js.map +1 -1
  460. package/dist/tools/builtin/select/adapterPointQuery.js +6 -5
  461. package/dist/tools/builtin/select/adapterPointQuery.js.map +1 -1
  462. package/dist/tools/builtin/select/collapseDeferred.js +40 -0
  463. package/dist/tools/builtin/select/collapseDeferred.js.map +1 -0
  464. package/dist/tools/builtin/select/groupEntry.js +82 -0
  465. package/dist/tools/builtin/select/groupEntry.js.map +1 -0
  466. package/dist/tools/builtin/select/pressDecision.js +32 -0
  467. package/dist/tools/builtin/select/pressDecision.js.map +1 -0
  468. package/dist/tools/builtin/select/selectionContributions.js +22 -13
  469. package/dist/tools/builtin/select/selectionContributions.js.map +1 -1
  470. package/dist/tools/builtin/select/useSelectTool.js +74 -55
  471. package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
  472. package/dist/tools/builtin/slice/useSliceTool.js +22 -8
  473. package/dist/tools/builtin/slice/useSliceTool.js.map +1 -1
  474. package/dist/tools/builtin/star/useStarTool.js +13 -4
  475. package/dist/tools/builtin/star/useStarTool.js.map +1 -1
  476. package/dist/tools/builtin/sticky/stickyNode.js +4 -1
  477. package/dist/tools/builtin/sticky/stickyNode.js.map +1 -1
  478. package/dist/tools/builtin/sticky/useStickyTool.js +10 -5
  479. package/dist/tools/builtin/sticky/useStickyTool.js.map +1 -1
  480. package/dist/tools/builtin/text/useTextTool.js +3 -1
  481. package/dist/tools/builtin/text/useTextTool.js.map +1 -1
  482. package/dist/{types-7puAuVD2.d.ts → types-49uYJN3j.d.ts} +1 -1
  483. package/dist/{types-CniVctmk.d.ts → types-BEdRIhNc.d.ts} +272 -109
  484. package/dist/{types-oFeQ5VjV.d.ts → types-p2alu-R1.d.ts} +1 -1
  485. package/dist/util/paint.js +1 -3
  486. package/dist/util/paint.js.map +1 -1
  487. package/dist/version.js +1 -1
  488. package/package.json +29 -25
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useViewAim.js","names":[],"sources":["../../src/canvas/useViewAim.ts"],"sourcesContent":["/**\n * A `<CanvasView>`'s camera and its aim, whichever of the two the view holds.\n * A camera-held view answers its aim from its camera; an aim-held one derives\n * its camera from the aim, through the same thunk a derived `view` uses.\n */\nimport { useCallback } from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport type { Dims } from 'core/layers/render';\nimport { normalizeView, type View } from 'core/viewport/view';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport { viewportWorldRect } from 'core/viewport/viewportWorldRect';\nimport { isRectAim, rectAim, viewForAim, type ViewAim } from 'features/viewports/aim';\nimport { useCanvasView, type CanvasViewState } from './useCanvasView';\nimport type { CanvasViewProps, ViewRect } from './canvasViewProps';\nimport { sizeOf, type useViewNesting } from './useViewNesting';\nimport { IDENTITY_VIEW, UNMEASURED_DIMS, type ViewRegistry } from './viewRegistry';\n\n/** `held` carried by the camera move that turns the world rect `from` into `to`: panned and scaled\n * with it, so the aim keeps its own shape rather than taking the frame's. */\nfunction carried(held: Bounds, from: Bounds, to: Bounds): Bounds {\n const kx = from.width > 0 ? to.width / from.width : 1;\n const ky = from.height > 0 ? to.height / from.height : 1;\n return {\n x: to.x + (held.x - from.x) * kx,\n y: to.y + (held.y - from.y) * ky,\n width: held.width * kx,\n height: held.height * ky,\n };\n}\n\ntype Nesting = ReturnType<typeof useViewNesting>;\n\nexport function useViewAim(args: {\n props: Pick<CanvasViewProps, 'view' | 'defaultView' | 'onViewChange' | 'viewBounds' | 'aim' | 'onAimChange'>;\n id: string;\n registry: ViewRegistry | null;\n derivedAt: Nesting['derivedAt'];\n rectAt: Nesting['rectAt'];\n rectNow: () => ViewRect;\n}): CanvasViewState & {\n held: 'camera' | 'aim';\n aim(): ViewAim;\n setAim(aim: ViewAim): boolean;\n /** The camera for a frame the caller names. A derived camera is asked for that frame; any other is\n * the same whatever the surface is doing. */\n cameraAt(outer: View, dims: Dims): View;\n} {\n const { props, id, registry, derivedAt, rectAt, rectNow } = args;\n const { view, defaultView, onViewChange, viewBounds, aim: aimProp, onAimChange } = props;\n const held = aimProp !== undefined ? 'aim' : 'camera';\n const live = useLatest({ view, onViewChange, aimProp, onAimChange });\n\n /** The rect an aim-held view holds, for a surface frame. */\n const heldAt = useCallback((outer: View, dims: Dims): Bounds | undefined => {\n const a = live.current.aimProp;\n return typeof a === 'function' ? derivedAt(a, outer, dims) : a;\n }, [live, derivedAt]);\n\n /** The camera derived for a surface frame, or `undefined` for a camera the view keeps. */\n const derivedCamera = useCallback((outer: View, dims: Dims): View | undefined => {\n const rect = heldAt(outer, dims);\n if (rect) return viewForAim(rect, sizeOf(rectAt(outer, dims)), 'contain');\n const v = live.current.view;\n return typeof v === 'function' ? normalizeView(derivedAt(v, outer, dims)) : undefined;\n }, [live, heldAt, rectAt, derivedAt]);\n\n const surfaceFrame = useCallback((): [View, Dims] => {\n const surface = registry?.surface();\n return [surface?.view() ?? IDENTITY_VIEW, surface?.dims() ?? UNMEASURED_DIMS];\n }, [registry]);\n\n /** A camera write on an aim-held view, reported as the held rect it carries. */\n const reportWrite = useCallback((next: View): void => {\n const frame = surfaceFrame();\n const held = heldAt(...frame);\n const current = derivedCamera(...frame);\n if (!held || !current) return;\n const size = sizeOf(rectAt(...frame));\n live.current.onAimChange?.(carried(held, viewportWorldRect(current, size), viewportWorldRect(next, size)));\n }, [live, surfaceFrame, heldAt, derivedCamera, rectAt]);\n\n const state = useCanvasView({\n view: held === 'aim' || typeof view === 'function' ? () => derivedCamera(...surfaceFrame())! : view,\n defaultView,\n onViewChange: held === 'aim'\n ? onAimChange && reportWrite\n : onViewChange,\n viewBounds,\n dims: () => sizeOf(rectNow()),\n requestRedraw: () => registry?.surface()?.requestRedraw(),\n controlledBy: held === 'aim' ? 'aim' : 'view',\n });\n const { getView, setViewFromInput } = state;\n\n const aim = useCallback((): ViewAim => {\n const rect = heldAt(...surfaceFrame());\n return rectAim(id, rect ?? viewportWorldRect(getView(), sizeOf(rectNow())));\n }, [id, heldAt, surfaceFrame, getView, rectNow]);\n\n const setAim = useCallback((next: ViewAim): boolean => {\n if (!isRectAim(next)) return false;\n const { view: v, onViewChange: onView, aimProp: a, onAimChange: report } = live.current;\n if (a === undefined) {\n if (v !== undefined && !onView) return false;\n setViewFromInput(viewForAim(next.extent, sizeOf(rectNow()), 'exact'));\n return true;\n }\n report?.(next.extent);\n return report !== undefined;\n }, [live, setViewFromInput, rectNow]);\n\n const cameraAt = useCallback(\n (outer: View, dims: Dims): View => derivedCamera(outer, dims) ?? getView(),\n [derivedCamera, getView],\n );\n\n return { ...state, held, aim, setAim, cameraAt };\n}\n"],"mappings":"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"}
@@ -13,6 +13,7 @@ var NO_RECT = {
13
13
  w: 0,
14
14
  h: 0
15
15
  };
16
+ /** A view rect's size, as the `Dims` its camera looks through. */
16
17
  var sizeOf = (rect) => ({
17
18
  width: rect.w,
18
19
  height: rect.h
@@ -60,7 +61,7 @@ function useViewNesting(registry, live) {
60
61
  y: r.y + frame.origin.y
61
62
  };
62
63
  }, [live, frameIn]);
63
- /** A derived camera, for a surface frame: asked of the frame it is measured in. */
64
+ /** A derived camera or aim, for a surface frame: asked of the frame it is measured in. */
64
65
  const derivedAt = useCallback((derive, outer, dims) => {
65
66
  const frame = frameIn(outer, dims);
66
67
  return frame ? derive(frame.view, frame.dims) : derive(outer, dims);
@@ -100,5 +101,5 @@ function useViewNesting(registry, live) {
100
101
  }
101
102
 
102
103
  //#endregion
103
- export { NO_RECT, useViewNesting };
104
+ export { NO_RECT, sizeOf, useViewNesting };
104
105
  //# sourceMappingURL=useViewNesting.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useViewNesting.js","names":[],"sources":["../../src/canvas/useViewNesting.ts"],"sourcesContent":["/**\n * How a `<CanvasView>` sits in the view it is nested in: the frame its\n * `bounds` and derived camera are measured against, and the layers it may\n * paint. Without a `parent` the frame is the surface's.\n */\nimport { useCallback, useRef, useState } from 'react';\nimport type { Dims, RenderLayer } from 'core/layers/render';\nimport type { View } from 'core/viewport/view';\nimport type { CanvasViewProps, ViewRect } from './canvasViewProps';\nimport { createSceneLayerGate, viewPaintedLayers } from './sceneLayerPaint';\nimport type { ViewRegistration, ViewRegistry } from './viewRegistry';\n\nexport const NO_RECT: ViewRect = { x: 0, y: 0, w: 0, h: 0 };\n\n/** What a view's `bounds` and `view` are measured against: the surface, or\n * the view it is nested in. `origin` and `clip` are in canvas CSS px. */\nexport interface OuterFrame { view: View; dims: Dims; origin: { x: number; y: number }; clip?: ViewRect }\n\n/** The props the nesting reads, as of the last committed render. */\nexport type ViewNestingInputs = Pick<CanvasViewProps, 'parent' | 'bounds' | 'layers' | 'layerVisibility' | 'layerOrder'>;\n\nconst sizeOf = (rect: ViewRect): Dims => ({ width: rect.w, height: rect.h });\n\nexport function useViewNesting(\n registry: ViewRegistry | null,\n live: { readonly current: ViewNestingInputs },\n) {\n // Set while this view asks its parent for a frame, so a parent chain that\n // loops back finds no frame rather than recursing.\n const measuring = useRef(false);\n /** The frame this view is measured in, for a surface frame; `null` while\n * its parent is missing. */\n const frameIn = useCallback((outer: View, dims: Dims): OuterFrame | null => {\n const p = live.current.parent;\n if (p === undefined) return { view: outer, dims, origin: { x: 0, y: 0 } };\n const reg = registry?.get(p);\n if (!reg || measuring.current) return null;\n measuring.current = true;\n try {\n const r = reg.layer.resolvable(outer, dims);\n return {\n view: r.view, dims: sizeOf(r.rect), origin: { x: r.rect.x, y: r.rect.y }, clip: r.clip ?? r.rect,\n };\n } finally {\n measuring.current = false;\n }\n }, [live, registry]);\n\n const rectAt = useCallback((outer: View, dims: Dims): ViewRect => {\n const frame = frameIn(outer, dims);\n if (!frame) return NO_RECT;\n const b = live.current.bounds;\n const r = typeof b === 'function' ? b(frame.view, frame.dims) : b;\n return { ...r, x: r.x + frame.origin.x, y: r.y + frame.origin.y };\n }, [live, frameIn]);\n\n /** A derived camera, for a surface frame: asked of the frame it is measured in. */\n const derivedAt = useCallback((derive: (outer: View, dims: Dims) => View, outer: View, dims: Dims): View => {\n const frame = frameIn(outer, dims);\n return frame ? derive(frame.view, frame.dims) : derive(outer, dims);\n }, [frameIn]);\n\n /** The view this one is nested in, while it is registered. */\n const parentReg = useCallback((): ViewRegistration | undefined => {\n const p = live.current.parent;\n return p === undefined ? undefined : registry?.get(p);\n }, [live, registry]);\n\n /** The layers this view draws, in the order it draws them: of the surface's,\n * or of what the view it is nested in draws. */\n const drawnLayers = useCallback((): readonly RenderLayer<unknown>[] => {\n if (live.current.parent !== undefined) return viewPaintedLayers(parentReg()?.drawnLayers() ?? [], live.current);\n return viewPaintedLayers(registry?.surface()?.layers() ?? [], live.current);\n }, [registry, live, parentReg]);\n\n const [sceneLayerGate] = useState(createSceneLayerGate);\n /** What this view's picks, marquees and select-alls ask of a scene layer:\n * painted here, and in every view this one is nested in. */\n const layerIsPainted = useCallback((layerId: string): boolean => {\n if (!sceneLayerGate(registry?.surface()?.layers() ?? [], live.current)(layerId)) return false;\n if (live.current.parent === undefined) return true;\n return parentReg()?.layerIsPainted(layerId) ?? false;\n }, [registry, sceneLayerGate, live, parentReg]);\n return { frameIn, rectAt, derivedAt, drawnLayers, layerIsPainted };\n}\n"],"mappings":";;;;;;;;;AAYA,IAAa,UAAoB;CAAE,GAAG;CAAG,GAAG;CAAG,GAAG;CAAG,GAAG;AAAE;AAS1D,IAAM,UAAU,UAA0B;CAAE,OAAO,KAAK;CAAG,QAAQ,KAAK;AAAE;AAE1E,SAAgB,eACd,UACA,MACA;CAGA,MAAM,YAAY,OAAO,KAAK;;;CAG9B,MAAM,UAAU,aAAa,OAAa,SAAkC;EAC1E,MAAM,IAAI,KAAK,QAAQ;EACvB,IAAI,MAAM,QAAW,OAAO;GAAE,MAAM;GAAO;GAAM,QAAQ;IAAE,GAAG;IAAG,GAAG;GAAE;EAAE;EACxE,MAAM,MAAM,UAAU,IAAI,CAAC;EAC3B,IAAI,CAAC,OAAO,UAAU,SAAS,OAAO;EACtC,UAAU,UAAU;EACpB,IAAI;GACF,MAAM,IAAI,IAAI,MAAM,WAAW,OAAO,IAAI;GAC1C,OAAO;IACL,MAAM,EAAE;IAAM,MAAM,OAAO,EAAE,IAAI;IAAG,QAAQ;KAAE,GAAG,EAAE,KAAK;KAAG,GAAG,EAAE,KAAK;IAAE;IAAG,MAAM,EAAE,QAAQ,EAAE;GAC9F;EACF,UAAU;GACR,UAAU,UAAU;EACtB;CACF,GAAG,CAAC,MAAM,QAAQ,CAAC;CAEnB,MAAM,SAAS,aAAa,OAAa,SAAyB;EAChE,MAAM,QAAQ,QAAQ,OAAO,IAAI;EACjC,IAAI,CAAC,OAAO,OAAO;EACnB,MAAM,IAAI,KAAK,QAAQ;EACvB,MAAM,IAAI,OAAO,MAAM,aAAa,EAAE,MAAM,MAAM,MAAM,IAAI,IAAI;EAChE,OAAO;GAAE,GAAG;GAAG,GAAG,EAAE,IAAI,MAAM,OAAO;GAAG,GAAG,EAAE,IAAI,MAAM,OAAO;EAAE;CAClE,GAAG,CAAC,MAAM,OAAO,CAAC;;CAGlB,MAAM,YAAY,aAAa,QAA2C,OAAa,SAAqB;EAC1G,MAAM,QAAQ,QAAQ,OAAO,IAAI;EACjC,OAAO,QAAQ,OAAO,MAAM,MAAM,MAAM,IAAI,IAAI,OAAO,OAAO,IAAI;CACpE,GAAG,CAAC,OAAO,CAAC;;CAGZ,MAAM,YAAY,kBAAgD;EAChE,MAAM,IAAI,KAAK,QAAQ;EACvB,OAAO,MAAM,SAAY,SAAY,UAAU,IAAI,CAAC;CACtD,GAAG,CAAC,MAAM,QAAQ,CAAC;;;CAInB,MAAM,cAAc,kBAAmD;EACrE,IAAI,KAAK,QAAQ,WAAW,QAAW,OAAO,kBAAkB,UAAU,CAAC,EAAE,YAAY,KAAK,CAAC,GAAG,KAAK,OAAO;EAC9G,OAAO,kBAAkB,UAAU,QAAQ,CAAC,EAAE,OAAO,KAAK,CAAC,GAAG,KAAK,OAAO;CAC5E,GAAG;EAAC;EAAU;EAAM;CAAS,CAAC;CAE9B,MAAM,CAAC,kBAAkB,SAAS,oBAAoB;CAQtD,OAAO;EAAE;EAAS;EAAQ;EAAW;EAAa,gBAL3B,aAAa,YAA6B;GAC/D,IAAI,CAAC,eAAe,UAAU,QAAQ,CAAC,EAAE,OAAO,KAAK,CAAC,GAAG,KAAK,OAAO,CAAC,CAAC,OAAO,GAAG,OAAO;GACxF,IAAI,KAAK,QAAQ,WAAW,QAAW,OAAO;GAC9C,OAAO,UAAU,CAAC,EAAE,eAAe,OAAO,KAAK;EACjD,GAAG;GAAC;GAAU;GAAgB;GAAM;EAAS,CACK;CAAe;AACnE"}
1
+ {"version":3,"file":"useViewNesting.js","names":[],"sources":["../../src/canvas/useViewNesting.ts"],"sourcesContent":["/**\n * How a `<CanvasView>` sits in the view it is nested in: the frame its\n * `bounds` and derived camera are measured against, and the layers it may\n * paint. Without a `parent` the frame is the surface's.\n */\nimport { useCallback, useRef, useState } from 'react';\nimport type { Dims, RenderLayer } from 'core/layers/render';\nimport type { View } from 'core/viewport/view';\nimport type { CanvasViewProps, ViewRect } from './canvasViewProps';\nimport { createSceneLayerGate, viewPaintedLayers } from './sceneLayerPaint';\nimport type { ViewRegistration, ViewRegistry } from './viewRegistry';\n\nexport const NO_RECT: ViewRect = { x: 0, y: 0, w: 0, h: 0 };\n\n/** What a view's `bounds` and `view` are measured against: the surface, or\n * the view it is nested in. `origin` and `clip` are in canvas CSS px. */\nexport interface OuterFrame { view: View; dims: Dims; origin: { x: number; y: number }; clip?: ViewRect }\n\n/** The props the nesting reads, as of the last committed render. */\nexport type ViewNestingInputs = Pick<CanvasViewProps, 'parent' | 'bounds' | 'layers' | 'layerVisibility' | 'layerOrder'>;\n\n/** A view rect's size, as the `Dims` its camera looks through. */\nexport const sizeOf = (rect: ViewRect): Dims => ({ width: rect.w, height: rect.h });\n\nexport function useViewNesting(\n registry: ViewRegistry | null,\n live: { readonly current: ViewNestingInputs },\n) {\n // Set while this view asks its parent for a frame, so a parent chain that\n // loops back finds no frame rather than recursing.\n const measuring = useRef(false);\n /** The frame this view is measured in, for a surface frame; `null` while\n * its parent is missing. */\n const frameIn = useCallback((outer: View, dims: Dims): OuterFrame | null => {\n const p = live.current.parent;\n if (p === undefined) return { view: outer, dims, origin: { x: 0, y: 0 } };\n const reg = registry?.get(p);\n if (!reg || measuring.current) return null;\n measuring.current = true;\n try {\n const r = reg.layer.resolvable(outer, dims);\n return {\n view: r.view, dims: sizeOf(r.rect), origin: { x: r.rect.x, y: r.rect.y }, clip: r.clip ?? r.rect,\n };\n } finally {\n measuring.current = false;\n }\n }, [live, registry]);\n\n const rectAt = useCallback((outer: View, dims: Dims): ViewRect => {\n const frame = frameIn(outer, dims);\n if (!frame) return NO_RECT;\n const b = live.current.bounds;\n const r = typeof b === 'function' ? b(frame.view, frame.dims) : b;\n return { ...r, x: r.x + frame.origin.x, y: r.y + frame.origin.y };\n }, [live, frameIn]);\n\n /** A derived camera or aim, for a surface frame: asked of the frame it is measured in. */\n const derivedAt = useCallback(<T>(derive: (outer: View, dims: Dims) => T, outer: View, dims: Dims): T => {\n const frame = frameIn(outer, dims);\n return frame ? derive(frame.view, frame.dims) : derive(outer, dims);\n }, [frameIn]);\n\n /** The view this one is nested in, while it is registered. */\n const parentReg = useCallback((): ViewRegistration | undefined => {\n const p = live.current.parent;\n return p === undefined ? undefined : registry?.get(p);\n }, [live, registry]);\n\n /** The layers this view draws, in the order it draws them: of the surface's,\n * or of what the view it is nested in draws. */\n const drawnLayers = useCallback((): readonly RenderLayer<unknown>[] => {\n if (live.current.parent !== undefined) return viewPaintedLayers(parentReg()?.drawnLayers() ?? [], live.current);\n return viewPaintedLayers(registry?.surface()?.layers() ?? [], live.current);\n }, [registry, live, parentReg]);\n\n const [sceneLayerGate] = useState(createSceneLayerGate);\n /** What this view's picks, marquees and select-alls ask of a scene layer:\n * painted here, and in every view this one is nested in. */\n const layerIsPainted = useCallback((layerId: string): boolean => {\n if (!sceneLayerGate(registry?.surface()?.layers() ?? [], live.current)(layerId)) return false;\n if (live.current.parent === undefined) return true;\n return parentReg()?.layerIsPainted(layerId) ?? false;\n }, [registry, sceneLayerGate, live, parentReg]);\n return { frameIn, rectAt, derivedAt, drawnLayers, layerIsPainted };\n}\n"],"mappings":";;;;;;;;;AAYA,IAAa,UAAoB;CAAE,GAAG;CAAG,GAAG;CAAG,GAAG;CAAG,GAAG;AAAE;;AAU1D,IAAa,UAAU,UAA0B;CAAE,OAAO,KAAK;CAAG,QAAQ,KAAK;AAAE;AAEjF,SAAgB,eACd,UACA,MACA;CAGA,MAAM,YAAY,OAAO,KAAK;;;CAG9B,MAAM,UAAU,aAAa,OAAa,SAAkC;EAC1E,MAAM,IAAI,KAAK,QAAQ;EACvB,IAAI,MAAM,QAAW,OAAO;GAAE,MAAM;GAAO;GAAM,QAAQ;IAAE,GAAG;IAAG,GAAG;GAAE;EAAE;EACxE,MAAM,MAAM,UAAU,IAAI,CAAC;EAC3B,IAAI,CAAC,OAAO,UAAU,SAAS,OAAO;EACtC,UAAU,UAAU;EACpB,IAAI;GACF,MAAM,IAAI,IAAI,MAAM,WAAW,OAAO,IAAI;GAC1C,OAAO;IACL,MAAM,EAAE;IAAM,MAAM,OAAO,EAAE,IAAI;IAAG,QAAQ;KAAE,GAAG,EAAE,KAAK;KAAG,GAAG,EAAE,KAAK;IAAE;IAAG,MAAM,EAAE,QAAQ,EAAE;GAC9F;EACF,UAAU;GACR,UAAU,UAAU;EACtB;CACF,GAAG,CAAC,MAAM,QAAQ,CAAC;CAEnB,MAAM,SAAS,aAAa,OAAa,SAAyB;EAChE,MAAM,QAAQ,QAAQ,OAAO,IAAI;EACjC,IAAI,CAAC,OAAO,OAAO;EACnB,MAAM,IAAI,KAAK,QAAQ;EACvB,MAAM,IAAI,OAAO,MAAM,aAAa,EAAE,MAAM,MAAM,MAAM,IAAI,IAAI;EAChE,OAAO;GAAE,GAAG;GAAG,GAAG,EAAE,IAAI,MAAM,OAAO;GAAG,GAAG,EAAE,IAAI,MAAM,OAAO;EAAE;CAClE,GAAG,CAAC,MAAM,OAAO,CAAC;;CAGlB,MAAM,YAAY,aAAgB,QAAwC,OAAa,SAAkB;EACvG,MAAM,QAAQ,QAAQ,OAAO,IAAI;EACjC,OAAO,QAAQ,OAAO,MAAM,MAAM,MAAM,IAAI,IAAI,OAAO,OAAO,IAAI;CACpE,GAAG,CAAC,OAAO,CAAC;;CAGZ,MAAM,YAAY,kBAAgD;EAChE,MAAM,IAAI,KAAK,QAAQ;EACvB,OAAO,MAAM,SAAY,SAAY,UAAU,IAAI,CAAC;CACtD,GAAG,CAAC,MAAM,QAAQ,CAAC;;;CAInB,MAAM,cAAc,kBAAmD;EACrE,IAAI,KAAK,QAAQ,WAAW,QAAW,OAAO,kBAAkB,UAAU,CAAC,EAAE,YAAY,KAAK,CAAC,GAAG,KAAK,OAAO;EAC9G,OAAO,kBAAkB,UAAU,QAAQ,CAAC,EAAE,OAAO,KAAK,CAAC,GAAG,KAAK,OAAO;CAC5E,GAAG;EAAC;EAAU;EAAM;CAAS,CAAC;CAE9B,MAAM,CAAC,kBAAkB,SAAS,oBAAoB;CAQtD,OAAO;EAAE;EAAS;EAAQ;EAAW;EAAa,gBAL3B,aAAa,YAA6B;GAC/D,IAAI,CAAC,eAAe,UAAU,QAAQ,CAAC,EAAE,OAAO,KAAK,CAAC,GAAG,KAAK,OAAO,CAAC,CAAC,OAAO,GAAG,OAAO;GACxF,IAAI,KAAK,QAAQ,WAAW,QAAW,OAAO;GAC9C,OAAO,UAAU,CAAC,EAAE,eAAe,OAAO,KAAK;EACjD,GAAG;GAAC;GAAU;GAAgB;GAAM;EAAS,CACK;CAAe;AACnE"}
@@ -10,7 +10,7 @@ import { useLatest } from "@weasel-js/react";
10
10
  function useViewRegistrySurface(viewRegistry, layers, frame, services) {
11
11
  const surfaceLayersRef = useLatest(layers);
12
12
  const { viewRef, dimsRef, helpersRef } = frame;
13
- const { requestRedraw, hitTestExtrasIn, clientToWorld } = services;
13
+ const { requestRedraw, hitTestExtrasIn, clientToWorld, setView } = services;
14
14
  useEffect(() => {
15
15
  return viewRegistry?.attachSurface({
16
16
  view: () => viewRef.current,
@@ -20,6 +20,7 @@ function useViewRegistrySurface(viewRegistry, layers, frame, services) {
20
20
  chromeState: () => helpersRef.current.getChromeState(),
21
21
  hitTestExtras: hitTestExtrasIn,
22
22
  clientToWorld,
23
+ setView,
23
24
  claimsAbove: (x, y) => {
24
25
  const view = viewRef.current;
25
26
  const worldX = x / view.scale.x + view.x;
@@ -35,7 +36,8 @@ function useViewRegistrySurface(viewRegistry, layers, frame, services) {
35
36
  surfaceLayersRef,
36
37
  viewRef,
37
38
  helpersRef,
38
- clientToWorld
39
+ clientToWorld,
40
+ setView
39
41
  ]);
40
42
  }
41
43
 
@@ -1 +1 @@
1
- {"version":3,"file":"useViewRegistrySurface.js","names":[],"sources":["../../src/canvas/useViewRegistrySurface.ts"],"sourcesContent":["import { useEffect } from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport type { View } from 'core/viewport/view';\nimport type { Dims, RenderLayer } from 'core/layers/render';\nimport type { CanvasHelpers } from './useViewHelpers';\nimport type { SurfaceHandle, ViewRegistry } from './viewRegistry';\n\n/** What the canvas reads at draw and hit time that changes outside a render. */\nexport interface SurfaceFrame {\n viewRef: { readonly current: View };\n dimsRef: { readonly current: Dims };\n helpersRef: { readonly current: Pick<CanvasHelpers<unknown>, 'getChromeState'> | null };\n}\n\n/** What the canvas does for a view that asks. */\nexport interface SurfaceServices {\n requestRedraw: () => void;\n hitTestExtrasIn: SurfaceHandle['hitTestExtras'];\n clientToWorld: SurfaceHandle['clientToWorld'];\n}\n\n/**\n * Attach this canvas to the view registry as its surface. The stack a view paints through its\n * own camera is this canvas's `layers`, before the views themselves are folded in — otherwise\n * a view would contain itself. Published by reference and read at draw time.\n */\nexport function useViewRegistrySurface(\n viewRegistry: ViewRegistry | null,\n layers: readonly RenderLayer<unknown>[],\n frame: SurfaceFrame,\n services: SurfaceServices,\n): void {\n const surfaceLayersRef = useLatest<readonly RenderLayer<unknown>[]>(layers);\n const { viewRef, dimsRef, helpersRef } = frame;\n const { requestRedraw, hitTestExtrasIn, clientToWorld } = services;\n useEffect(() => {\n return viewRegistry?.attachSurface({\n view: () => viewRef.current,\n dims: () => dimsRef.current,\n layers: () => surfaceLayersRef.current,\n requestRedraw,\n chromeState: () => helpersRef.current!.getChromeState(),\n hitTestExtras: hitTestExtrasIn,\n clientToWorld,\n claimsAbove: (x, y) => {\n const view = viewRef.current;\n const worldX = x / view.scale.x + view.x;\n const worldY = y / view.scale.y + view.y;\n return hitTestExtrasIn(worldX, worldY, view, dimsRef.current, helpersRef.current) !== null;\n },\n });\n }, [viewRegistry, requestRedraw, hitTestExtrasIn, dimsRef, surfaceLayersRef, viewRef, helpersRef, clientToWorld]);\n}\n"],"mappings":";;;;;;;;;AA0BA,SAAgB,uBACd,cACA,QACA,OACA,UACM;CACN,MAAM,mBAAmB,UAA2C,MAAM;CAC1E,MAAM,EAAE,SAAS,SAAS,eAAe;CACzC,MAAM,EAAE,eAAe,iBAAiB,kBAAkB;CAC1D,gBAAgB;EACd,OAAO,cAAc,cAAc;GACjC,YAAY,QAAQ;GACpB,YAAY,QAAQ;GACpB,cAAc,iBAAiB;GAC/B;GACA,mBAAmB,WAAW,QAAS,eAAe;GACtD,eAAe;GACf;GACA,cAAc,GAAG,MAAM;IACrB,MAAM,OAAO,QAAQ;IACrB,MAAM,SAAS,IAAI,KAAK,MAAM,IAAI,KAAK;IACvC,MAAM,SAAS,IAAI,KAAK,MAAM,IAAI,KAAK;IACvC,OAAO,gBAAgB,QAAQ,QAAQ,MAAM,QAAQ,SAAS,WAAW,OAAO,MAAM;GACxF;EACF,CAAC;CACH,GAAG;EAAC;EAAc;EAAe;EAAiB;EAAS;EAAkB;EAAS;EAAY;CAAa,CAAC;AAClH"}
1
+ {"version":3,"file":"useViewRegistrySurface.js","names":[],"sources":["../../src/canvas/useViewRegistrySurface.ts"],"sourcesContent":["import { useEffect } from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport type { View } from 'core/viewport/view';\nimport type { Dims, RenderLayer } from 'core/layers/render';\nimport type { CanvasHelpers } from './useViewHelpers';\nimport type { SurfaceHandle, ViewRegistry } from './viewRegistry';\n\n/** What the canvas reads at draw and hit time that changes outside a render. */\nexport interface SurfaceFrame {\n viewRef: { readonly current: View };\n dimsRef: { readonly current: Dims };\n helpersRef: { readonly current: Pick<CanvasHelpers<unknown>, 'getChromeState'> | null };\n}\n\n/** What the canvas does for a view that asks. */\nexport interface SurfaceServices {\n requestRedraw: () => void;\n hitTestExtrasIn: SurfaceHandle['hitTestExtras'];\n clientToWorld: SurfaceHandle['clientToWorld'];\n setView: NonNullable<SurfaceHandle['setView']>;\n}\n\n/**\n * Attach this canvas to the view registry as its surface. The stack a view paints through its\n * own camera is this canvas's `layers`, before the views themselves are folded in — otherwise\n * a view would contain itself. Published by reference and read at draw time.\n */\nexport function useViewRegistrySurface(\n viewRegistry: ViewRegistry | null,\n layers: readonly RenderLayer<unknown>[],\n frame: SurfaceFrame,\n services: SurfaceServices,\n): void {\n const surfaceLayersRef = useLatest<readonly RenderLayer<unknown>[]>(layers);\n const { viewRef, dimsRef, helpersRef } = frame;\n const { requestRedraw, hitTestExtrasIn, clientToWorld, setView } = services;\n useEffect(() => {\n return viewRegistry?.attachSurface({\n view: () => viewRef.current,\n dims: () => dimsRef.current,\n layers: () => surfaceLayersRef.current,\n requestRedraw,\n chromeState: () => helpersRef.current!.getChromeState(),\n hitTestExtras: hitTestExtrasIn,\n clientToWorld,\n setView,\n claimsAbove: (x, y) => {\n const view = viewRef.current;\n const worldX = x / view.scale.x + view.x;\n const worldY = y / view.scale.y + view.y;\n return hitTestExtrasIn(worldX, worldY, view, dimsRef.current, helpersRef.current) !== null;\n },\n });\n }, [viewRegistry, requestRedraw, hitTestExtrasIn, dimsRef, surfaceLayersRef, viewRef, helpersRef, clientToWorld, setView]);\n}\n"],"mappings":";;;;;;;;;AA2BA,SAAgB,uBACd,cACA,QACA,OACA,UACM;CACN,MAAM,mBAAmB,UAA2C,MAAM;CAC1E,MAAM,EAAE,SAAS,SAAS,eAAe;CACzC,MAAM,EAAE,eAAe,iBAAiB,eAAe,YAAY;CACnE,gBAAgB;EACd,OAAO,cAAc,cAAc;GACjC,YAAY,QAAQ;GACpB,YAAY,QAAQ;GACpB,cAAc,iBAAiB;GAC/B;GACA,mBAAmB,WAAW,QAAS,eAAe;GACtD,eAAe;GACf;GACA;GACA,cAAc,GAAG,MAAM;IACrB,MAAM,OAAO,QAAQ;IACrB,MAAM,SAAS,IAAI,KAAK,MAAM,IAAI,KAAK;IACvC,MAAM,SAAS,IAAI,KAAK,MAAM,IAAI,KAAK;IACvC,OAAO,gBAAgB,QAAQ,QAAQ,MAAM,QAAQ,SAAS,WAAW,OAAO,MAAM;GACxF;EACF,CAAC;CACH,GAAG;EAAC;EAAc;EAAe;EAAiB;EAAS;EAAkB;EAAS;EAAY;EAAe;CAAO,CAAC;AAC3H"}
@@ -0,0 +1,63 @@
1
+ import { aimSpace2d } from "../features/viewports/aim.js";
2
+ import { createViewMarkerLayer, grabViewMarker } from "../features/viewports/viewMarker.js";
3
+ import { layerAffordance } from "./affordanceAt.js";
4
+
5
+ //#region src/canvas/viewMarkers.ts
6
+ /** @internal */
7
+ function createViewMarkerRegistry(markers, deps) {
8
+ let gate = null;
9
+ const targetSpace = (target) => {
10
+ const surface = deps.surface();
11
+ if (!surface) return null;
12
+ if (target === null) return aimSpace2d(null, surface.view(), surface.dims());
13
+ const v = deps.resolve(target, surface.view(), surface.dims());
14
+ return v ? aimSpace2d(target, v.view, {
15
+ width: v.rect.w,
16
+ height: v.rect.h
17
+ }) : null;
18
+ };
19
+ return {
20
+ registerMarker: (props) => {
21
+ const { id } = props();
22
+ for (const live of markers.keys()) if (live().id === id) {
23
+ console.error(`[weasel] registerMarker: marker "${id}" is already on this surface, so this one is skipped. Marker ids are unique per surface; rename one.`);
24
+ return () => {};
25
+ }
26
+ markers.set(props, createViewMarkerLayer(props, deps.host));
27
+ deps.changed();
28
+ return () => {
29
+ markers.delete(props);
30
+ deps.changed();
31
+ };
32
+ },
33
+ markerLayers: (target) => {
34
+ const out = [];
35
+ for (const [props, layer] of markers) if (props().target === target) out.push(layer);
36
+ return out;
37
+ },
38
+ markerAt: (target, world) => {
39
+ if (!gate?.()) return null;
40
+ const space = targetSpace(target);
41
+ const at = space?.project(world);
42
+ if (!space || !at) return null;
43
+ const live = [...markers.keys()];
44
+ for (let i = live.length - 1; i >= 0; i--) {
45
+ const props = live[i]();
46
+ if (props.target !== target) continue;
47
+ const grab = grabViewMarker(props, deps.host, space, at);
48
+ if (grab) return layerAffordance(`view-marker:${props.id}`, { initialScratch: grab });
49
+ }
50
+ return null;
51
+ },
52
+ gateMarkers: (open) => {
53
+ gate = open;
54
+ return () => {
55
+ if (gate === open) gate = null;
56
+ };
57
+ }
58
+ };
59
+ }
60
+
61
+ //#endregion
62
+ export { createViewMarkerRegistry };
63
+ //# sourceMappingURL=viewMarkers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewMarkers.js","names":[],"sources":["../../src/canvas/viewMarkers.ts"],"sourcesContent":["/**\n * The marker half of a surface's view registry: which markers are live, the\n * layers they draw, and which one a press lands on.\n */\nimport type { Dims, RenderLayer } from 'core/layers/render';\nimport type { AffordanceHit } from '@weasel-js/routing';\nimport { aimSpace2d, type AimSpace } from 'features/viewports/aim';\nimport type { ResolvableView } from 'features/viewports/viewResolver';\nimport type { View } from 'core/viewport/view';\nimport {\n createViewMarkerLayer, grabViewMarker, type ViewMarkerHost, type ViewMarkerProps,\n} from 'features/viewports/viewMarker';\nimport { layerAffordance } from './affordanceAt';\n\n/** @internal Composed into `ViewRegistry`. */\nexport interface ViewMarkerRegistry {\n /** Returns an unregister. `props` is read at every draw. An id already live\n * on the surface logs an error and is skipped, and its unregister does nothing. */\n registerMarker(props: () => ViewMarkerProps): () => void;\n /** The layers of the markers drawn in `target`'s frame, in mount order. */\n markerLayers(target: string | null): readonly RenderLayer<unknown>[];\n /** The topmost interactive marker drawn in `target` under `world` (a point\n * in `target`'s world), as the affordance a press there routes. */\n markerAt(target: string | null, world: { x: number; y: number }): AffordanceHit | null;\n /** Lets `markerAt` answer while `open()` says the surface binds a marker drag;\n * until then it answers `null`, so a press falls through. Returns an undo. */\n gateMarkers(open: () => boolean): () => void;\n}\n\n/** What the marker half reads off the rest of the registry. */\nexport interface ViewMarkerRegistryDeps {\n host: ViewMarkerHost;\n /** The surface's camera and size, or `null` before one attaches. */\n surface(): { view(): View; dims(): Dims } | null;\n /** View `id` resolved against the surface, or `undefined` while there is none. */\n resolve(id: string, outer: View, dims: Dims): ResolvableView | undefined;\n changed(): void;\n}\n\n/** @internal */\nexport function createViewMarkerRegistry(\n markers: Map<() => ViewMarkerProps, RenderLayer<unknown>>,\n deps: ViewMarkerRegistryDeps,\n): ViewMarkerRegistry {\n let gate: (() => boolean) | null = null;\n const targetSpace = (target: string | null): AimSpace | null => {\n const surface = deps.surface();\n if (!surface) return null;\n if (target === null) return aimSpace2d(null, surface.view(), surface.dims());\n const v = deps.resolve(target, surface.view(), surface.dims());\n return v ? aimSpace2d(target, v.view, { width: v.rect.w, height: v.rect.h }) : null;\n };\n return {\n registerMarker: (props) => {\n const { id } = props();\n for (const live of markers.keys()) {\n if (live().id === id) {\n console.error(`[weasel] registerMarker: marker \"${id}\" is already on this surface, so this one is skipped. Marker ids are unique per surface; rename one.`);\n return () => {};\n }\n }\n markers.set(props, createViewMarkerLayer(props, deps.host));\n deps.changed();\n return () => { markers.delete(props); deps.changed(); };\n },\n markerLayers: (target) => {\n const out: RenderLayer<unknown>[] = [];\n for (const [props, layer] of markers) if (props().target === target) out.push(layer);\n return out;\n },\n markerAt: (target, world) => {\n if (!gate?.()) return null;\n const space = targetSpace(target);\n const at = space?.project(world);\n if (!space || !at) return null;\n const live = [...markers.keys()];\n for (let i = live.length - 1; i >= 0; i--) {\n const props = live[i]!();\n if (props.target !== target) continue;\n const grab = grabViewMarker(props, deps.host, space, at);\n if (grab) return layerAffordance(`view-marker:${props.id}`, { initialScratch: grab });\n }\n return null;\n },\n gateMarkers: (open) => {\n gate = open;\n return () => { if (gate === open) gate = null; };\n },\n };\n}\n"],"mappings":";;;;;;AAwCA,SAAgB,yBACd,SACA,MACoB;CACpB,IAAI,OAA+B;CACnC,MAAM,eAAe,WAA2C;EAC9D,MAAM,UAAU,KAAK,QAAQ;EAC7B,IAAI,CAAC,SAAS,OAAO;EACrB,IAAI,WAAW,MAAM,OAAO,WAAW,MAAM,QAAQ,KAAK,GAAG,QAAQ,KAAK,CAAC;EAC3E,MAAM,IAAI,KAAK,QAAQ,QAAQ,QAAQ,KAAK,GAAG,QAAQ,KAAK,CAAC;EAC7D,OAAO,IAAI,WAAW,QAAQ,EAAE,MAAM;GAAE,OAAO,EAAE,KAAK;GAAG,QAAQ,EAAE,KAAK;EAAE,CAAC,IAAI;CACjF;CACA,OAAO;EACL,iBAAiB,UAAU;GACzB,MAAM,EAAE,OAAO,MAAM;GACrB,KAAK,MAAM,QAAQ,QAAQ,KAAK,GAC9B,IAAI,KAAK,CAAC,CAAC,OAAO,IAAI;IACpB,QAAQ,MAAM,oCAAoC,GAAG,qGAAqG;IAC1J,aAAa,CAAC;GAChB;GAEF,QAAQ,IAAI,OAAO,sBAAsB,OAAO,KAAK,IAAI,CAAC;GAC1D,KAAK,QAAQ;GACb,aAAa;IAAE,QAAQ,OAAO,KAAK;IAAG,KAAK,QAAQ;GAAG;EACxD;EACA,eAAe,WAAW;GACxB,MAAM,MAA8B,CAAC;GACrC,KAAK,MAAM,CAAC,OAAO,UAAU,SAAS,IAAI,MAAM,CAAC,CAAC,WAAW,QAAQ,IAAI,KAAK,KAAK;GACnF,OAAO;EACT;EACA,WAAW,QAAQ,UAAU;GAC3B,IAAI,CAAC,OAAO,GAAG,OAAO;GACtB,MAAM,QAAQ,YAAY,MAAM;GAChC,MAAM,KAAK,OAAO,QAAQ,KAAK;GAC/B,IAAI,CAAC,SAAS,CAAC,IAAI,OAAO;GAC1B,MAAM,OAAO,CAAC,GAAG,QAAQ,KAAK,CAAC;GAC/B,KAAK,IAAI,IAAI,KAAK,SAAS,GAAG,KAAK,GAAG,KAAK;IACzC,MAAM,QAAQ,KAAK,EAAE,CAAE;IACvB,IAAI,MAAM,WAAW,QAAQ;IAC7B,MAAM,OAAO,eAAe,OAAO,KAAK,MAAM,OAAO,EAAE;IACvD,IAAI,MAAM,OAAO,gBAAgB,eAAe,MAAM,MAAM,EAAE,gBAAgB,KAAK,CAAC;GACtF;GACA,OAAO;EACT;EACA,cAAc,SAAS;GACrB,OAAO;GACP,aAAa;IAAE,IAAI,SAAS,MAAM,OAAO;GAAM;EACjD;CACF;AACF"}
@@ -1,4 +1,7 @@
1
1
  import { createViewResolver, viewContains } from "../features/viewports/viewResolver.js";
2
+ import { isRectAim, rectAim, viewForAim } from "../features/viewports/aim.js";
3
+ import { viewportWorldRect } from "../core/viewport/viewportWorldRect.js";
4
+ import { createViewMarkerRegistry } from "./viewMarkers.js";
2
5
  import { createEmitter } from "@weasel-js/registry";
3
6
  import { createContext, useContext, useMemo, useRef } from "react";
4
7
  import { jsx } from "react/jsx-runtime";
@@ -40,6 +43,7 @@ function ViewRegistryProvider({ children }) {
40
43
  const seqRef = useRef(0);
41
44
  const versionRef = useRef(0);
42
45
  const surfaceRef = useRef(null);
46
+ const markersRef = useRef(/* @__PURE__ */ new Map());
43
47
  const registry = useMemo(() => {
44
48
  const listeners = createEmitter();
45
49
  const changed = () => {
@@ -59,7 +63,7 @@ function ViewRegistryProvider({ children }) {
59
63
  }
60
64
  return false;
61
65
  };
62
- return {
66
+ const registry = {
63
67
  resolver: createViewResolver({
64
68
  views: () => {
65
69
  const surface = surfaceRef.current;
@@ -108,6 +112,27 @@ function ViewRegistryProvider({ children }) {
108
112
  }
109
113
  };
110
114
  },
115
+ aimOf: (id) => {
116
+ if (id !== null) return get(id)?.aim() ?? null;
117
+ const surface = surfaceRef.current;
118
+ return surface ? rectAim(null, viewportWorldRect(surface.view(), surface.dims())) : null;
119
+ },
120
+ setAim: (id, aim) => {
121
+ if (id !== null) return get(id)?.setAim(aim) ?? false;
122
+ const surface = surfaceRef.current;
123
+ if (!isRectAim(aim) || !surface?.setView) return false;
124
+ surface.setView(viewForAim(aim.extent, surface.dims(), "exact"));
125
+ return true;
126
+ },
127
+ ...createViewMarkerRegistry(markersRef.current, {
128
+ host: {
129
+ aimOf: (id) => registry.aimOf(id),
130
+ setAim: (id, aim) => registry.setAim(id, aim)
131
+ },
132
+ surface: () => surfaceRef.current,
133
+ resolve: (id, outer, dims) => get(id)?.layer.resolvable(outer, dims),
134
+ changed
135
+ }),
111
136
  getVersion: () => versionRef.current,
112
137
  subscribe: listeners.subscribe,
113
138
  surface: () => surfaceRef.current,
@@ -118,6 +143,7 @@ function ViewRegistryProvider({ children }) {
118
143
  };
119
144
  }
120
145
  };
146
+ return registry;
121
147
  }, []);
122
148
  return /* @__PURE__ */ jsx(ViewRegistryContext.Provider, {
123
149
  value: registry,
@@ -1 +1 @@
1
- {"version":3,"file":"viewRegistry.js","names":[],"sources":["../../src/canvas/viewRegistry.tsx"],"sourcesContent":["/**\n * The seam between a canvas surface and the views drawn on it.\n *\n * Traffic runs both ways: the surface attaches a handle onto its frame and its\n * layer stack, and each view registers what it contributes back — the viewport\n * node it paints into and the record the gesture dispatcher routes its events\n * to. Both sides are thunks, read at draw and dispatch time, so neither can\n * serve the other a frame that has already moved.\n */\nimport { createContext, useContext, useMemo, useRef, type ReactNode } from 'react';\nimport type { Dims, RenderLayer } from 'core/layers/render';\nimport type { LayerHit } from 'affordances/types';\nimport type { ChromeState } from 'core/selection/chromeState';\nimport type { View } from 'core/viewport/view';\nimport type { ViewportLayer } from 'features/viewports/viewportLayer';\nimport type { DispatcherViewTarget } from '@weasel-js/routing/internal';\nimport { createViewResolver, viewContains, type ViewResolver } from 'features/viewports/viewResolver';\nimport { createEmitter } from '@weasel-js/registry';\nimport type { ViewAt } from './canvasExtension';\n\n/**\n * What a view needs from the surface hosting it.\n *\n * @internal Attached by the surface, read by `<CanvasView>`.\n */\nexport interface SurfaceHandle {\n /** A client point in the surface's own world — the conversion the surface\n * itself makes, its `clientToWorld` override included. */\n clientToWorld(clientX: number, clientY: number): [number, number];\n /** The outer camera. */\n view(): View;\n /** The canvas's CSS-pixel size. */\n dims(): Dims;\n /** The stack a view paints through its own camera, unless it narrows it. */\n layers(): readonly RenderLayer<unknown>[];\n requestRedraw(): void;\n /** The canvas's own view chrome — its selection, its overlay-aware bounds.\n * What a `<CanvasView>` answers for itself, answered for view zero. */\n chromeState(): ChromeState;\n /** Hit-test the externally registered layers at a world point, against the\n * frame and draw envelope of whichever view is asking. The public\n * `hitTestExtras` is this with the canvas's own frame and envelope. */\n hitTestExtras(\n worldX: number,\n worldY: number,\n view: View,\n dims: Dims,\n data: unknown,\n /** Consider only these layers — the ones the asking view paints. A layer\n * asked on a frame it was not drawn in answers for the wrong pixels. */\n only?: readonly RenderLayer<unknown>[],\n ): { layerId: string; hit: LayerHit } | null;\n /** Whether a registered layer claims a canvas-local point on the surface's\n * own frame. Registered layers paint over every view, so such a point is\n * theirs even inside a view's rect. */\n claimsAbove(x: number, y: number): boolean;\n}\n\n/**\n * A client point in the surface's CSS pixels, from its top-left: the surface's\n * own world point put back through its camera, so a `clientToWorld` override\n * reaches every view declared on it.\n *\n * @internal\n */\nexport function surfaceLocal(\n surface: Pick<SurfaceHandle, 'clientToWorld' | 'view'> | null,\n clientX: number,\n clientY: number,\n): { x: number; y: number } {\n if (!surface) return { x: clientX, y: clientY };\n const [worldX, worldY] = surface.clientToWorld(clientX, clientY);\n const view = surface.view();\n return { x: (worldX - view.x) * view.scale.x, y: (worldY - view.y) * view.scale.y };\n}\n\n/**\n * What one view contributes to the surface hosting it.\n *\n * @internal Written by `<CanvasView>`, read by the surface. Consumers declare\n * a view, not a registration.\n */\nexport interface ViewRegistration {\n id: string;\n /** The view this one is nested in: listed, painted and hit after it, and\n * its `layer` measures itself from that view's rect and camera. */\n parent?: string;\n /**\n * Paint and hit order, low to high. Views declared as props take their array\n * index; a view mounted as a child takes `Infinity`, so it lands after them\n * however React happens to order the mount effects.\n */\n order: number;\n /** The viewport node this view paints into. Its `resolvable` is what routes\n * input here. */\n layer: ViewportLayer<unknown>;\n /** `false` keeps input over the view on the canvas beneath it. */\n interactive: boolean;\n /** `false` leaves `layer` for a host to draw — a HUD window's interior — so\n * the surface does not paint it too. */\n paint: boolean;\n /** Everything about dispatching to this view except its id. */\n target: Omit<DispatcherViewTarget, 'id'>;\n /** Whether a scene layer reaches the screen in this view. For input that\n * reaches a view without a dispatch — hover — so it judges this view's\n * paint rather than the surface's. */\n layerIsPainted(layerId: string): boolean;\n /** The layers this view draws, in order — what a view nested in it paints\n * from. */\n drawnLayers(): readonly RenderLayer<unknown>[];\n}\n\n\n/** @internal */\nexport interface ViewRegistry {\n /** Returns an unregister. */\n register(reg: ViewRegistration): () => void;\n /** Live registrations in paint order, each nested view after the view it\n * is nested in. */\n list(): readonly ViewRegistration[];\n /** The live registration with `id`, if any. */\n get(id: string): ViewRegistration | undefined;\n /** The innermost view painted under a canvas-local point, whether or not it\n * takes input, passing over `except` and the views nested in it. */\n viewAt(at: { x: number; y: number }, opts?: { except?: string }): ViewAt;\n /** Bumped whenever the list changes. Pair with {@link ViewRegistry.subscribe}\n * for `useSyncExternalStore`. */\n getVersion(): number;\n subscribe(fn: () => void): () => void;\n /** Which view a client point belongs to, sticky for a captured pointer.\n * One per surface: every caller that routes input — the dispatcher, pinch,\n * hover — must agree about where a point landed. */\n readonly resolver: ViewResolver;\n /** The hosting surface, or `null` before one attaches. */\n surface(): SurfaceHandle | null;\n /** Returns a detach, which leaves a handle attached since alone. */\n attachSurface(handle: SurfaceHandle): () => void;\n}\n\nconst ViewRegistryContext = createContext<ViewRegistry | null>(null);\n\n/** Mounts the registry for one surface. `<SceneCanvas>` mounts one; a view\n * declaring itself as a child must be inside it. */\nexport function ViewRegistryProvider({ children }: { children: ReactNode }) {\n const entriesRef = useRef(new Map<ViewRegistration, number>());\n const seqRef = useRef(0);\n const versionRef = useRef(0);\n const surfaceRef = useRef<SurfaceHandle | null>(null);\n\n const registry = useMemo<ViewRegistry>(() => {\n const listeners = createEmitter();\n const changed = (): void => {\n versionRef.current++;\n listeners.emit();\n };\n const ordered = (): ViewRegistration[] => nestedOrder([...entriesRef.current.keys()].sort((a, b) => (\n a.order - b.order || entriesRef.current.get(a)! - entriesRef.current.get(b)!\n )));\n const get = (id: string): ViewRegistration | undefined => {\n for (const r of entriesRef.current.keys()) if (r.id === id) return r;\n return undefined;\n };\n /** Whether `r` is `id` or nested in it, however deep. */\n const within = (r: ViewRegistration, id: string): boolean => {\n const seen = new Set<ViewRegistration>();\n for (let at: ViewRegistration | undefined = r; at && !seen.has(at); at = at.parent === undefined ? undefined : get(at.parent)) {\n if (at.id === id) return true;\n seen.add(at);\n }\n return false;\n };\n const resolver = createViewResolver({\n views: () => {\n const surface = surfaceRef.current;\n const view = surface?.view() ?? IDENTITY_VIEW;\n const dims = surface?.dims() ?? UNMEASURED_DIMS;\n return ordered().filter((r) => r.interactive).map((r) => r.layer.resolvable(view, dims));\n },\n root: () => surfaceRef.current?.view() ?? IDENTITY_VIEW,\n toLocal: (clientX, clientY) => surfaceLocal(surfaceRef.current, clientX, clientY),\n occluded: (x, y) => surfaceRef.current?.claimsAbove(x, y) ?? false,\n });\n return {\n resolver,\n register: (reg) => {\n entriesRef.current.set(reg, seqRef.current++);\n changed();\n return () => { entriesRef.current.delete(reg); changed(); };\n },\n list: ordered,\n get,\n viewAt: ({ x, y }, opts) => {\n const surface = surfaceRef.current;\n const view = surface?.view() ?? IDENTITY_VIEW;\n const dims = surface?.dims() ?? UNMEASURED_DIMS;\n const except = opts?.except;\n const list = ordered();\n for (let i = list.length - 1; i >= 0; i--) {\n const r = list[i]!;\n if (except !== undefined && within(r, except)) continue;\n const v = r.layer.resolvable(view, dims);\n if (viewContains(v, x, y)) return { id: v.id, view: v.view, rect: v.rect };\n }\n return { id: null, view, rect: { x: 0, y: 0, w: dims.width, h: dims.height } };\n },\n getVersion: () => versionRef.current,\n subscribe: listeners.subscribe,\n surface: () => surfaceRef.current,\n attachSurface: (handle) => {\n surfaceRef.current = handle;\n return () => { if (surfaceRef.current === handle) surfaceRef.current = null; };\n },\n };\n }, []);\n\n return <ViewRegistryContext.Provider value={registry}>{children}</ViewRegistryContext.Provider>;\n}\n\n/** The surface's view registry, or `null` outside one. */\nexport function useOptionalViewRegistry(): ViewRegistry | null {\n return useContext(ViewRegistryContext);\n}\n\n/** Each view followed by the views nested in it, in the order given. A view\n * whose parent is not registered keeps its place; one caught in a cycle\n * goes last. */\nfunction nestedOrder(sorted: readonly ViewRegistration[]): ViewRegistration[] {\n const ids = new Set(sorted.map((r) => r.id));\n const out: ViewRegistration[] = [];\n const placed = new Set<ViewRegistration>();\n const place = (r: ViewRegistration): void => {\n if (placed.has(r)) return;\n placed.add(r);\n out.push(r);\n for (const child of sorted) if (child.parent === r.id) place(child);\n };\n for (const r of sorted) if (r.parent === undefined || !ids.has(r.parent)) place(r);\n for (const r of sorted) place(r);\n return out;\n}\n\n/** The `Dims` a surface reports before it has measured itself. */\nexport const UNMEASURED_DIMS: Dims = { width: 0, height: 0 };\n\n/** The camera a view resolves against before its surface attaches. */\nexport const IDENTITY_VIEW: View = { x: 0, y: 0, scale: { x: 1, y: 1 } };\n"],"mappings":"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"}
1
+ {"version":3,"file":"viewRegistry.js","names":[],"sources":["../../src/canvas/viewRegistry.tsx"],"sourcesContent":["/**\n * The seam between a canvas surface and the views drawn on it.\n *\n * Traffic runs both ways: the surface attaches a handle onto its frame and its\n * layer stack, and each view registers what it contributes back — the viewport\n * node it paints into and the record the gesture dispatcher routes its events\n * to. Both sides are thunks, read at draw and dispatch time, so neither can\n * serve the other a frame that has already moved.\n */\nimport { createContext, useContext, useMemo, useRef, type ReactNode } from 'react';\nimport type { Dims, RenderLayer } from 'core/layers/render';\nimport type { LayerHit } from 'affordances/types';\nimport type { ChromeState } from 'core/selection/chromeState';\nimport type { View } from 'core/viewport/view';\nimport type { ViewportLayer } from 'features/viewports/viewportLayer';\nimport type { DispatcherViewTarget } from '@weasel-js/routing/internal';\nimport { createViewResolver, viewContains, type ViewResolver } from 'features/viewports/viewResolver';\nimport { createEmitter } from '@weasel-js/registry';\nimport type { ViewAt } from './canvasExtension';\nimport { isRectAim, rectAim, viewForAim, type ViewAim } from 'features/viewports/aim';\nimport { viewportWorldRect } from 'core/viewport/viewportWorldRect';\nimport type { ViewMarkerProps } from 'features/viewports/viewMarker';\nimport { createViewMarkerRegistry, type ViewMarkerRegistry } from './viewMarkers';\n\n/**\n * What a view needs from the surface hosting it.\n *\n * @internal Attached by the surface, read by `<CanvasView>`.\n */\nexport interface SurfaceHandle {\n /** A client point in the surface's own world — the conversion the surface\n * itself makes, its `clientToWorld` override included. */\n clientToWorld(clientX: number, clientY: number): [number, number];\n /** The outer camera. */\n view(): View;\n /** The canvas's CSS-pixel size. */\n dims(): Dims;\n /** The stack a view paints through its own camera, unless it narrows it. */\n layers(): readonly RenderLayer<unknown>[];\n requestRedraw(): void;\n /** The canvas's own view chrome — its selection, its overlay-aware bounds.\n * What a `<CanvasView>` answers for itself, answered for view zero. */\n chromeState(): ChromeState;\n /** Hit-test the externally registered layers at a world point, against the\n * frame and draw envelope of whichever view is asking. The public\n * `hitTestExtras` is this with the canvas's own frame and envelope. */\n hitTestExtras(\n worldX: number,\n worldY: number,\n view: View,\n dims: Dims,\n data: unknown,\n /** Consider only these layers — the ones the asking view paints. A layer\n * asked on a frame it was not drawn in answers for the wrong pixels. */\n only?: readonly RenderLayer<unknown>[],\n ): { layerId: string; hit: LayerHit } | null;\n /** Whether a registered layer claims a canvas-local point on the surface's\n * own frame. Registered layers paint over every view, so such a point is\n * theirs even inside a view's rect. */\n claimsAbove(x: number, y: number): boolean;\n /** The root camera's input write: clamped, forwarded when controlled, and ending a glide. */\n setView?(next: View): void;\n}\n\n/**\n * A client point in the surface's CSS pixels, from its top-left: the surface's\n * own world point put back through its camera, so a `clientToWorld` override\n * reaches every view declared on it.\n *\n * @internal\n */\nexport function surfaceLocal(\n surface: Pick<SurfaceHandle, 'clientToWorld' | 'view'> | null,\n clientX: number,\n clientY: number,\n): { x: number; y: number } {\n if (!surface) return { x: clientX, y: clientY };\n const [worldX, worldY] = surface.clientToWorld(clientX, clientY);\n const view = surface.view();\n return { x: (worldX - view.x) * view.scale.x, y: (worldY - view.y) * view.scale.y };\n}\n\n/**\n * What one view contributes to the surface hosting it.\n *\n * @internal Written by `<CanvasView>`, read by the surface. Consumers declare\n * a view, not a registration.\n */\nexport interface ViewRegistration {\n id: string;\n /** The view this one is nested in: listed, painted and hit after it, and\n * its `layer` measures itself from that view's rect and camera. */\n parent?: string;\n /**\n * Paint and hit order, low to high. Views declared as props take their array\n * index; a view mounted as a child takes `Infinity`, so it lands after them\n * however React happens to order the mount effects.\n */\n order: number;\n /** The viewport node this view paints into. Its `resolvable` is what routes\n * input here. */\n layer: ViewportLayer<unknown>;\n /** `false` keeps input over the view on the canvas beneath it. */\n interactive: boolean;\n /** `false` leaves `layer` for a host to draw — a HUD window's interior — so\n * the surface does not paint it too. */\n paint: boolean;\n /** Everything about dispatching to this view except its id. */\n target: Omit<DispatcherViewTarget, 'id'>;\n /** Whether a scene layer reaches the screen in this view. For input that\n * reaches a view without a dispatch — hover — so it judges this view's\n * paint rather than the surface's. */\n layerIsPainted(layerId: string): boolean;\n /** The layers this view draws, in order — what a view nested in it paints\n * from. */\n drawnLayers(): readonly RenderLayer<unknown>[];\n /** Whether the view keeps its camera or its aim; the other is derived. */\n held: 'camera' | 'aim';\n /** What the view shows: the aim it holds, or its camera's. */\n aim(): ViewAim;\n /** `false` for an aim the view cannot read, or one with nowhere to go. */\n setAim(aim: ViewAim): boolean;\n}\n\n\n/** @internal */\nexport interface ViewRegistry extends ViewMarkerRegistry {\n /** Returns an unregister. */\n register(reg: ViewRegistration): () => void;\n /** Live registrations in paint order, each nested view after the view it\n * is nested in. */\n list(): readonly ViewRegistration[];\n /** The live registration with `id`, if any. */\n get(id: string): ViewRegistration | undefined;\n /** The innermost view painted under a canvas-local point, whether or not it\n * takes input, passing over `except` and the views nested in it. */\n viewAt(at: { x: number; y: number }, opts?: { except?: string }): ViewAt;\n /** What view `id` shows (`null`: the surface's root), or `null` while there is no such view. */\n aimOf(id: string | null): ViewAim | null;\n /** Point view `id` at `aim`; `false` when it cannot. */\n setAim(id: string | null, aim: ViewAim): boolean;\n /** Bumped whenever the list changes. Pair with {@link ViewRegistry.subscribe}\n * for `useSyncExternalStore`. */\n getVersion(): number;\n subscribe(fn: () => void): () => void;\n /** Which view a client point belongs to, sticky for a captured pointer.\n * One per surface: every caller that routes input — the dispatcher, pinch,\n * hover — must agree about where a point landed. */\n readonly resolver: ViewResolver;\n /** The hosting surface, or `null` before one attaches. */\n surface(): SurfaceHandle | null;\n /** Returns a detach, which leaves a handle attached since alone. */\n attachSurface(handle: SurfaceHandle): () => void;\n}\n\nconst ViewRegistryContext = createContext<ViewRegistry | null>(null);\n\n/** Mounts the registry for one surface. `<SceneCanvas>` mounts one; a view\n * declaring itself as a child must be inside it. */\nexport function ViewRegistryProvider({ children }: { children: ReactNode }) {\n const entriesRef = useRef(new Map<ViewRegistration, number>());\n const seqRef = useRef(0);\n const versionRef = useRef(0);\n const surfaceRef = useRef<SurfaceHandle | null>(null);\n const markersRef = useRef(new Map<() => ViewMarkerProps, RenderLayer<unknown>>());\n\n const registry = useMemo<ViewRegistry>(() => {\n const listeners = createEmitter();\n const changed = (): void => {\n versionRef.current++;\n listeners.emit();\n };\n const ordered = (): ViewRegistration[] => nestedOrder([...entriesRef.current.keys()].sort((a, b) => (\n a.order - b.order || entriesRef.current.get(a)! - entriesRef.current.get(b)!\n )));\n const get = (id: string): ViewRegistration | undefined => {\n for (const r of entriesRef.current.keys()) if (r.id === id) return r;\n return undefined;\n };\n /** Whether `r` is `id` or nested in it, however deep. */\n const within = (r: ViewRegistration, id: string): boolean => {\n const seen = new Set<ViewRegistration>();\n for (let at: ViewRegistration | undefined = r; at && !seen.has(at); at = at.parent === undefined ? undefined : get(at.parent)) {\n if (at.id === id) return true;\n seen.add(at);\n }\n return false;\n };\n const resolver = createViewResolver({\n views: () => {\n const surface = surfaceRef.current;\n const view = surface?.view() ?? IDENTITY_VIEW;\n const dims = surface?.dims() ?? UNMEASURED_DIMS;\n return ordered().filter((r) => r.interactive).map((r) => r.layer.resolvable(view, dims));\n },\n root: () => surfaceRef.current?.view() ?? IDENTITY_VIEW,\n toLocal: (clientX, clientY) => surfaceLocal(surfaceRef.current, clientX, clientY),\n occluded: (x, y) => surfaceRef.current?.claimsAbove(x, y) ?? false,\n });\n const registry: ViewRegistry = {\n resolver,\n register: (reg) => {\n entriesRef.current.set(reg, seqRef.current++);\n changed();\n return () => { entriesRef.current.delete(reg); changed(); };\n },\n list: ordered,\n get,\n viewAt: ({ x, y }, opts) => {\n const surface = surfaceRef.current;\n const view = surface?.view() ?? IDENTITY_VIEW;\n const dims = surface?.dims() ?? UNMEASURED_DIMS;\n const except = opts?.except;\n const list = ordered();\n for (let i = list.length - 1; i >= 0; i--) {\n const r = list[i]!;\n if (except !== undefined && within(r, except)) continue;\n const v = r.layer.resolvable(view, dims);\n if (viewContains(v, x, y)) return { id: v.id, view: v.view, rect: v.rect };\n }\n return { id: null, view, rect: { x: 0, y: 0, w: dims.width, h: dims.height } };\n },\n aimOf: (id) => {\n if (id !== null) return get(id)?.aim() ?? null;\n const surface = surfaceRef.current;\n return surface ? rectAim(null, viewportWorldRect(surface.view(), surface.dims())) : null;\n },\n setAim: (id, aim) => {\n if (id !== null) return get(id)?.setAim(aim) ?? false;\n const surface = surfaceRef.current;\n if (!isRectAim(aim) || !surface?.setView) return false;\n surface.setView(viewForAim(aim.extent, surface.dims(), 'exact'));\n return true;\n },\n ...createViewMarkerRegistry(markersRef.current, {\n host: { aimOf: (id) => registry.aimOf(id), setAim: (id, aim) => registry.setAim(id, aim) },\n surface: () => surfaceRef.current,\n resolve: (id, outer, dims) => get(id)?.layer.resolvable(outer, dims),\n changed,\n }),\n getVersion: () => versionRef.current,\n subscribe: listeners.subscribe,\n surface: () => surfaceRef.current,\n attachSurface: (handle) => {\n surfaceRef.current = handle;\n return () => { if (surfaceRef.current === handle) surfaceRef.current = null; };\n },\n };\n return registry;\n }, []);\n\n return <ViewRegistryContext.Provider value={registry}>{children}</ViewRegistryContext.Provider>;\n}\n\n/** The surface's view registry, or `null` outside one. */\nexport function useOptionalViewRegistry(): ViewRegistry | null {\n return useContext(ViewRegistryContext);\n}\n\n/** Each view followed by the views nested in it, in the order given. A view\n * whose parent is not registered keeps its place; one caught in a cycle\n * goes last. */\nfunction nestedOrder(sorted: readonly ViewRegistration[]): ViewRegistration[] {\n const ids = new Set(sorted.map((r) => r.id));\n const out: ViewRegistration[] = [];\n const placed = new Set<ViewRegistration>();\n const place = (r: ViewRegistration): void => {\n if (placed.has(r)) return;\n placed.add(r);\n out.push(r);\n for (const child of sorted) if (child.parent === r.id) place(child);\n };\n for (const r of sorted) if (r.parent === undefined || !ids.has(r.parent)) place(r);\n for (const r of sorted) place(r);\n return out;\n}\n\n/** The `Dims` a surface reports before it has measured itself. */\nexport const UNMEASURED_DIMS: Dims = { width: 0, height: 0 };\n\n/** The camera a view resolves against before its surface attaches. */\nexport const IDENTITY_VIEW: View = { x: 0, y: 0, scale: { x: 1, y: 1 } };\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { NodeId } from '@weasel-js/routing';
2
- import { P as PasteOmission, C as ClipboardSnapshot, I as InsertAdapter } from './types-oFeQ5VjV.js';
3
- import './types-7puAuVD2.js';
2
+ import { P as PasteOmission, C as ClipboardSnapshot, I as InsertAdapter } from './types-p2alu-R1.js';
3
+ import './types-49uYJN3j.js';
4
4
  import '@weasel-js/geom';
5
5
  import '@weasel-js/history';
6
6
  import './poseDescriptor-DN2yfnq_.js';
@@ -1,6 +1,6 @@
1
- import { a as PlaneMap } from './types-7puAuVD2.js';
1
+ import { a as PlaneMap } from './types-49uYJN3j.js';
2
2
  import { Op } from '@weasel-js/history';
3
- import { S as SnapTarget, M as MoveAdapter } from './types-oFeQ5VjV.js';
3
+ import { S as SnapTarget, M as MoveAdapter } from './types-p2alu-R1.js';
4
4
  import { FramedBox, View } from '@weasel-js/geom';
5
5
  import { KitSettingsStore } from '@weasel-js/react';
6
6
  import { ModifierState, ResizeAnchor } from '@weasel-js/routing';
package/dist/clone.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- export { C as CloneBehavior, d as CloneBehaviorResult } from './clone-C3e0OTuF.js';
2
- import './types-7puAuVD2.js';
1
+ export { C as CloneBehavior, d as CloneBehaviorResult } from './clone-qb2e1F5p.js';
2
+ import './types-49uYJN3j.js';
3
3
  import '@weasel-js/geom';
4
4
  import '@weasel-js/history';
5
- import './types-oFeQ5VjV.js';
5
+ import './types-p2alu-R1.js';
6
6
  import './poseDescriptor-DN2yfnq_.js';
7
7
  import '@weasel-js/animator';
8
8
  import '@weasel-js/react';
@@ -1,3 +1,3 @@
1
- import { isPlainObject, isPlainPrototype } from "@weasel-js/registry";
1
+ import { isPlainObject as isPlainObject$1, isPlainPrototype } from "@weasel-js/registry";
2
2
 
3
- export { isPlainObject, isPlainPrototype };
3
+ export { isPlainObject$1 as isPlainObject, isPlainPrototype };
@@ -1 +1 @@
1
- {"version":3,"file":"layerGroup.js","names":[],"sources":["../../../src/core/layers/layerGroup.ts"],"sourcesContent":["import type { BlendFactors, CompositeOp, DrawCommand, Effect } from '@weasel-js/renderer';\nimport type { View } from 'core/viewport/view';\nimport type { Dims } from './render';\n\n/**\n * Several consecutive layers composited as one.\n *\n * A layer's own `effects` run over that layer alone, which is the wrong\n * picture whenever a pass reads neighboring pixels: `blur(A over B)` is not\n * `blur(A) over blur(B)`. A group draws its members into one buffer, runs one\n * chain over it, and composites the result back once.\n *\n * Membership is by `RenderLayer.id` — the same names `layerOrder` and\n * `layerVisibility` use, not the `layers` map's slot keys.\n *\n * Only *consecutive* members share a buffer. A group whose members are\n * separated in the render order is drawn as one bracket per run, with a\n * warning.\n *\n * This is a render-stack bracket, not a scene `ContainerNode`.\n */\nexport interface LayerGroup {\n /** Names the group in warnings; not a layer id and never drawn. */\n id: string;\n /**\n * Member layer ids. Order here is ignored — the render order decides.\n *\n * A name claims that layer and every layer under it in the `:` namespace, so\n * `'scene'` covers the `scene:<layerId>` layers a scene with declared layers\n * draws as. The separator is required: `'scene'` does not reach `scenery`.\n * A name matching no layer being drawn is warned about.\n */\n layers: readonly string[];\n /** Passes over the group's combined pixels, in order. A thunk is re-read on\n * every frame, so an animating radius costs no React render. */\n effects?: readonly Effect[] | ((view: View, dims: Dims) => readonly Effect[]);\n /** Opacity applied to the group's composited result, not to each member. */\n alpha?: number;\n /** 4×5 color matrix (row-major, 20 numbers) applied to the composited\n * result. See `GroupDrawCommand.colorMatrix`. */\n colorMatrix?: number[];\n /** How the composited result joins the layers under it. Default\n * `source-over`. See `GroupDrawCommand.composite`. */\n composite?: CompositeOp | BlendFactors;\n}\n\n/**\n * Layer id → the group that owns it. First claim wins across groups, with a\n * warning for the second; the same group naming a layer twice — `['scene',\n * 'scene:tiles']` — passes quietly.\n */\nexport function groupMembership(\n groups: readonly LayerGroup[] | undefined,\n layerIds: Iterable<string>,\n): Map<string, LayerGroup> | null {\n if (!groups || groups.length === 0) return null;\n const stack = [...layerIds];\n const byLayer = new Map<string, LayerGroup>();\n for (const group of groups) {\n for (const name of group.layers) {\n let matched = false;\n for (const id of stack) {\n if (id !== name && !id.startsWith(`${name}:`)) continue;\n matched = true;\n const claimed = byLayer.get(id);\n if (claimed === group) continue;\n if (claimed) {\n console.warn(\n `[weasel] layer \"${id}\" is claimed by layer groups \"${claimed.id}\" and ` +\n `\"${group.id}\"; it stays in \"${claimed.id}\".`,\n );\n continue;\n }\n byLayer.set(id, group);\n }\n if (!matched) {\n console.warn(\n `[weasel] layer group \"${group.id}\" names \"${name}\", which matches no layer ` +\n 'being drawn, so it groups nothing. Check the id against the layer stack — a ' +\n 'scene with declared layers draws as \"scene:<layerId>\", which \"scene\" covers.',\n );\n }\n }\n }\n return byLayer;\n}\n\n/**\n * Put one run of a group's commands under its shared compositing.\n *\n * No transform: every member already carries its own space wrap, so a second\n * one here would apply the view twice to world layers and once to screen ones.\n * A group that composites plainly gets no wrapper at all.\n */\nexport function wrapGroup(\n group: LayerGroup,\n children: DrawCommand[],\n view: View,\n dims: Dims,\n): DrawCommand[] {\n const declared = typeof group.effects === 'function'\n ? group.effects(view, dims)\n : group.effects;\n const effects = declared && declared.length > 0 ? declared : undefined;\n if (!effects && group.alpha === undefined && group.colorMatrix === undefined\n && group.composite === undefined) return children;\n const fades = group.alpha !== undefined && group.alpha < 1;\n return [{\n kind: 'group',\n ...(effects ? { effects } : {}),\n ...(!effects && (fades || group.colorMatrix !== undefined) ? { isolate: true } : {}),\n ...(group.alpha !== undefined ? { alpha: group.alpha } : {}),\n ...(group.colorMatrix !== undefined ? { colorMatrix: group.colorMatrix } : {}),\n ...(group.composite !== undefined ? { composite: group.composite } : {}),\n children,\n }];\n}\n"],"mappings":";;;;;;AAmDA,SAAgB,gBACd,QACA,UACgC;CAChC,IAAI,CAAC,UAAU,OAAO,WAAW,GAAG,OAAO;CAC3C,MAAM,QAAQ,CAAC,GAAG,QAAQ;CAC1B,MAAM,0BAAU,IAAI,IAAwB;CAC5C,KAAK,MAAM,SAAS,QAClB,KAAK,MAAM,QAAQ,MAAM,QAAQ;EAC/B,IAAI,UAAU;EACd,KAAK,MAAM,MAAM,OAAO;GACtB,IAAI,OAAO,QAAQ,CAAC,GAAG,WAAW,GAAG,KAAK,EAAE,GAAG;GAC/C,UAAU;GACV,MAAM,UAAU,QAAQ,IAAI,EAAE;GAC9B,IAAI,YAAY,OAAO;GACvB,IAAI,SAAS;IACX,QAAQ,KACN,mBAAmB,GAAG,gCAAgC,QAAQ,GAAG,SAC7D,MAAM,GAAG,kBAAkB,QAAQ,GAAG,GAC5C;IACA;GACF;GACA,QAAQ,IAAI,IAAI,KAAK;EACvB;EACA,IAAI,CAAC,SACH,QAAQ,KACN,yBAAyB,MAAM,GAAG,WAAW,KAAK,mLAGpD;CAEJ;CAEF,OAAO;AACT;;;;;;;;AASA,SAAgB,UACd,OACA,UACA,MACA,MACe;CACf,MAAM,WAAW,OAAO,MAAM,YAAY,aACtC,MAAM,QAAQ,MAAM,IAAI,IACxB,MAAM;CACV,MAAM,UAAU,YAAY,SAAS,SAAS,IAAI,WAAW;CAC7D,IAAI,CAAC,WAAW,MAAM,UAAU,UAAa,MAAM,gBAAgB,UAC5D,MAAM,cAAc,QAAW,OAAO;CAC7C,MAAM,QAAQ,MAAM,UAAU,UAAa,MAAM,QAAQ;CACzD,OAAO,CAAC;EACN,MAAM;EACN,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;EAC7B,GAAI,CAAC,YAAY,SAAS,MAAM,gBAAgB,UAAa,EAAE,SAAS,KAAK,IAAI,CAAC;EAClF,GAAI,MAAM,UAAU,SAAY,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;EAC1D,GAAI,MAAM,gBAAgB,SAAY,EAAE,aAAa,MAAM,YAAY,IAAI,CAAC;EAC5E,GAAI,MAAM,cAAc,SAAY,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;EACtE;CACF,CAAC;AACH"}
1
+ {"version":3,"file":"layerGroup.js","names":[],"sources":["../../../src/core/layers/layerGroup.ts"],"sourcesContent":["import type { BlendFactors, CompositeOp, DrawCommand, Effect } from '@weasel-js/renderer';\nimport type { View } from 'core/viewport/view';\nimport type { Dims } from './render';\n\n/**\n * Several consecutive layers composited as one.\n *\n * A layer's own `effects` run over that layer alone, which is the wrong\n * picture whenever a pass reads neighboring pixels: `blur(A over B)` is not\n * `blur(A) over blur(B)`. A group draws its members into one buffer, runs one\n * chain over it, and composites the result back once.\n *\n * Membership is by `RenderLayer.id` — the same names `layerOrder` and\n * `layerVisibility` use, not the `layers` map's slot keys.\n *\n * Only *consecutive* members share a buffer. A group whose members are\n * separated in the render order is drawn as one bracket per run, with a\n * warning.\n *\n * This is a render-stack bracket, not a scene `GroupNode`.\n */\nexport interface LayerGroup {\n /** Names the group in warnings; not a layer id and never drawn. */\n id: string;\n /**\n * Member layer ids. Order here is ignored — the render order decides.\n *\n * A name claims that layer and every layer under it in the `:` namespace, so\n * `'scene'` covers the `scene:<layerId>` layers a scene with declared layers\n * draws as. The separator is required: `'scene'` does not reach `scenery`.\n * A name matching no layer being drawn is warned about.\n */\n layers: readonly string[];\n /** Passes over the group's combined pixels, in order. A thunk is re-read on\n * every frame, so an animating radius costs no React render. */\n effects?: readonly Effect[] | ((view: View, dims: Dims) => readonly Effect[]);\n /** Opacity applied to the group's composited result, not to each member. */\n alpha?: number;\n /** 4×5 color matrix (row-major, 20 numbers) applied to the composited\n * result. See `GroupDrawCommand.colorMatrix`. */\n colorMatrix?: number[];\n /** How the composited result joins the layers under it. Default\n * `source-over`. See `GroupDrawCommand.composite`. */\n composite?: CompositeOp | BlendFactors;\n}\n\n/**\n * Layer id → the group that owns it. First claim wins across groups, with a\n * warning for the second; the same group naming a layer twice — `['scene',\n * 'scene:tiles']` — passes quietly.\n */\nexport function groupMembership(\n groups: readonly LayerGroup[] | undefined,\n layerIds: Iterable<string>,\n): Map<string, LayerGroup> | null {\n if (!groups || groups.length === 0) return null;\n const stack = [...layerIds];\n const byLayer = new Map<string, LayerGroup>();\n for (const group of groups) {\n for (const name of group.layers) {\n let matched = false;\n for (const id of stack) {\n if (id !== name && !id.startsWith(`${name}:`)) continue;\n matched = true;\n const claimed = byLayer.get(id);\n if (claimed === group) continue;\n if (claimed) {\n console.warn(\n `[weasel] layer \"${id}\" is claimed by layer groups \"${claimed.id}\" and ` +\n `\"${group.id}\"; it stays in \"${claimed.id}\".`,\n );\n continue;\n }\n byLayer.set(id, group);\n }\n if (!matched) {\n console.warn(\n `[weasel] layer group \"${group.id}\" names \"${name}\", which matches no layer ` +\n 'being drawn, so it groups nothing. Check the id against the layer stack — a ' +\n 'scene with declared layers draws as \"scene:<layerId>\", which \"scene\" covers.',\n );\n }\n }\n }\n return byLayer;\n}\n\n/**\n * Put one run of a group's commands under its shared compositing.\n *\n * No transform: every member already carries its own space wrap, so a second\n * one here would apply the view twice to world layers and once to screen ones.\n * A group that composites plainly gets no wrapper at all.\n */\nexport function wrapGroup(\n group: LayerGroup,\n children: DrawCommand[],\n view: View,\n dims: Dims,\n): DrawCommand[] {\n const declared = typeof group.effects === 'function'\n ? group.effects(view, dims)\n : group.effects;\n const effects = declared && declared.length > 0 ? declared : undefined;\n if (!effects && group.alpha === undefined && group.colorMatrix === undefined\n && group.composite === undefined) return children;\n const fades = group.alpha !== undefined && group.alpha < 1;\n return [{\n kind: 'group',\n ...(effects ? { effects } : {}),\n ...(!effects && (fades || group.colorMatrix !== undefined) ? { isolate: true } : {}),\n ...(group.alpha !== undefined ? { alpha: group.alpha } : {}),\n ...(group.colorMatrix !== undefined ? { colorMatrix: group.colorMatrix } : {}),\n ...(group.composite !== undefined ? { composite: group.composite } : {}),\n children,\n }];\n}\n"],"mappings":";;;;;;AAmDA,SAAgB,gBACd,QACA,UACgC;CAChC,IAAI,CAAC,UAAU,OAAO,WAAW,GAAG,OAAO;CAC3C,MAAM,QAAQ,CAAC,GAAG,QAAQ;CAC1B,MAAM,0BAAU,IAAI,IAAwB;CAC5C,KAAK,MAAM,SAAS,QAClB,KAAK,MAAM,QAAQ,MAAM,QAAQ;EAC/B,IAAI,UAAU;EACd,KAAK,MAAM,MAAM,OAAO;GACtB,IAAI,OAAO,QAAQ,CAAC,GAAG,WAAW,GAAG,KAAK,EAAE,GAAG;GAC/C,UAAU;GACV,MAAM,UAAU,QAAQ,IAAI,EAAE;GAC9B,IAAI,YAAY,OAAO;GACvB,IAAI,SAAS;IACX,QAAQ,KACN,mBAAmB,GAAG,gCAAgC,QAAQ,GAAG,SAC7D,MAAM,GAAG,kBAAkB,QAAQ,GAAG,GAC5C;IACA;GACF;GACA,QAAQ,IAAI,IAAI,KAAK;EACvB;EACA,IAAI,CAAC,SACH,QAAQ,KACN,yBAAyB,MAAM,GAAG,WAAW,KAAK,mLAGpD;CAEJ;CAEF,OAAO;AACT;;;;;;;;AASA,SAAgB,UACd,OACA,UACA,MACA,MACe;CACf,MAAM,WAAW,OAAO,MAAM,YAAY,aACtC,MAAM,QAAQ,MAAM,IAAI,IACxB,MAAM;CACV,MAAM,UAAU,YAAY,SAAS,SAAS,IAAI,WAAW;CAC7D,IAAI,CAAC,WAAW,MAAM,UAAU,UAAa,MAAM,gBAAgB,UAC5D,MAAM,cAAc,QAAW,OAAO;CAC7C,MAAM,QAAQ,MAAM,UAAU,UAAa,MAAM,QAAQ;CACzD,OAAO,CAAC;EACN,MAAM;EACN,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;EAC7B,GAAI,CAAC,YAAY,SAAS,MAAM,gBAAgB,UAAa,EAAE,SAAS,KAAK,IAAI,CAAC;EAClF,GAAI,MAAM,UAAU,SAAY,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;EAC1D,GAAI,MAAM,gBAAgB,SAAY,EAAE,aAAa,MAAM,YAAY,IAAI,CAAC;EAC5E,GAAI,MAAM,cAAc,SAAY,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;EACtE;CACF,CAAC;AACH"}
@@ -1,4 +1,4 @@
1
- import { effectivePose } from "../scene/effectivePose.js";
1
+ import { effectivePose, isEnvelope } from "../scene/effectivePose.js";
2
2
  import { layoutPages, pageOfBounds } from "./pages.js";
3
3
  import { RECT_POSE_DESCRIPTOR, poseDescriptorForNode, translatePoseViaDescriptor } from "../geometry/poseDescriptor.js";
4
4
  import { carriedDescendants, sceneCarrySource } from "../scene/carryDescendants.js";
@@ -51,10 +51,9 @@ function pageMembers(scene, placed, descriptor = RECT_POSE_DESCRIPTOR) {
51
51
  }
52
52
  return out;
53
53
  }
54
- /** Whether a node's pose derives from its children, so moving them moves it. */
55
54
  function derivesPose(scene, id) {
56
55
  const node = scene.get(id);
57
- return node?.dependsOn === "children" && node.derivePose !== void 0;
56
+ return node !== void 0 && isEnvelope(node);
58
57
  }
59
58
  /** The stored poses that move root `id` and what it carries by `dx`, `dy`: the
60
59
  * root, unless its pose derives from its children (then each child moves in
@@ -1 +1 @@
1
- {"version":3,"file":"pageEdits.js","names":[],"sources":["../../../src/core/pages/pageEdits.ts"],"sourcesContent":["/**\n * A document's pages as a scene document value, and the edits that change\n * them. Each edit moves the nodes on a page by however far the page itself\n * moved, so what is on a page stays where it is on that page.\n */\nimport {\n RECT_POSE_DESCRIPTOR, poseDescriptorForNode, translatePoseViaDescriptor, type PoseDescriptor,\n} from '../geometry/poseDescriptor';\nimport { carriedDescendants, sceneCarrySource } from '../scene/carryDescendants';\nimport { effectivePose } from '../scene/effectivePose';\nimport type { NodeId, Scene } from '../scene/types';\nimport { layoutPages, pageOfBounds, type Page, type PageLayoutOptions, type PlacedPage } from './pages';\n\n/** The document value under {@link PAGES_DOC_KEY}: the pages in order, and how they are laid out. */\nexport interface PagesDoc {\n readonly pages: readonly Page[];\n readonly layout?: PageLayoutOptions;\n}\n\n/** The scene document value key a page list lives under (`scene.doc`). */\nexport const PAGES_DOC_KEY = 'core:pages';\n\n/** Options every page edit takes. */\nexport interface PageEditOptions {\n /** The undo entry's label. */\n label?: string;\n /** How node poses are measured and moved. Default: poses are rects. */\n descriptor?: PoseDescriptor<unknown>;\n}\n\ntype AnyScene = Scene<unknown, string, unknown>;\nconst asAny = <TData, TLayer extends string, TPose>(scene: Scene<TData, TLayer, TPose>) =>\n scene as unknown as AnyScene;\n\n/** The scene's pages doc, or `null` when it has none. */\nexport function pagesOf<TData, TLayer extends string, TPose>(scene: Scene<TData, TLayer, TPose>): PagesDoc | null {\n return scene.doc.get<PagesDoc>(PAGES_DOC_KEY) ?? null;\n}\n\n/** The scene's pages, placed in world space; empty when it has no pages doc. */\nexport function placedPagesOf<TData, TLayer extends string, TPose>(scene: Scene<TData, TLayer, TPose>): PlacedPage[] {\n const doc = pagesOf(scene);\n return doc ? layoutPages(doc.pages, doc.layout) : [];\n}\n\n/** An id none of `pages` has: `page-1`, `page-2`, and so on. */\nexport function nextPageId(pages: readonly Page[]): string {\n const taken = new Set(pages.map((p) => p.id));\n let n = 1;\n while (taken.has(`page-${n}`)) n++;\n return `page-${n}`;\n}\n\n/** Whether `id` draws from other nodes and goes wherever they go. */\nfunction followsOthers(scene: AnyScene, id: NodeId): boolean {\n const deps = scene.get(id)?.dependsOn;\n return Array.isArray(deps) && deps.length > 0;\n}\n\n/**\n * The root nodes on each placed page, by page id: each root belongs to the\n * page its bounds overlap most (`pageOfBounds`). A root in the gaps belongs to\n * none, and one that derives from other nodes is left out, since it follows\n * them.\n */\nexport function pageMembers<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n placed: readonly PlacedPage[],\n descriptor: PoseDescriptor<unknown> = RECT_POSE_DESCRIPTOR as PoseDescriptor<unknown>,\n): Map<string, NodeId[]> {\n const s = asAny(scene);\n const out = new Map<string, NodeId[]>(placed.map((p) => [p.id, []]));\n for (const id of s.roots) {\n const node = s.get(id);\n if (!node || followsOthers(s, id)) continue;\n const bounds = poseDescriptorForNode(descriptor, node).getBounds(effectivePose(s, node));\n const page = pageOfBounds(placed, bounds);\n if (page) out.get(page.id)!.push(id);\n }\n return out;\n}\n\n/** Whether a node's pose derives from its children, so moving them moves it. */\nfunction derivesPose(scene: AnyScene, id: NodeId): boolean {\n const node = scene.get(id);\n return node?.dependsOn === 'children' && node.derivePose !== undefined;\n}\n\n/** The stored poses that move root `id` and what it carries by `dx`, `dy`: the\n * root, unless its pose derives from its children (then each child moves in\n * its place), and the descendants it carries, less whatever derives its pose. */\nfunction pageMoveWrites(\n scene: AnyScene,\n id: NodeId,\n dx: number,\n dy: number,\n descriptor: PoseDescriptor<unknown>,\n out: { id: NodeId; pose: unknown }[],\n): void {\n const node = scene.get(id);\n if (!node || followsOthers(scene, id)) return;\n if (derivesPose(scene, id)) {\n for (const child of scene.childrenOf(id)) pageMoveWrites(scene, child, dx, dy, descriptor, out);\n return;\n }\n const move = (pose: unknown, at: NodeId) => translatePoseViaDescriptor(pose, dx, dy, poseDescriptorForNode(descriptor, scene.get(at)!));\n out.push({ id, pose: move(node.pose, id) });\n const carried = carriedDescendants(sceneCarrySource(scene), id, (world, at) => (\n followsOthers(scene, at) || derivesPose(scene, at) ? null : move(world, at)\n ));\n for (const [at, pose] of carried) out.push({ id: at, pose });\n}\n\n/**\n * Replace the scene's pages doc with `next`, moving each page's members by\n * the change in their page's place, all as one undo entry labeled `label`.\n * Members of a page `next` drops are left where they are; `removePage` deletes\n * them first.\n */\nexport function setPages<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n next: PagesDoc,\n label: string,\n options: Pick<PageEditOptions, 'descriptor'> = {},\n): void {\n const s = asAny(scene);\n const descriptor = options.descriptor ?? (RECT_POSE_DESCRIPTOR as PoseDescriptor<unknown>);\n const before = placedPagesOf(s);\n const after = new Map(layoutPages(next.pages, next.layout).map((p) => [p.id, p]));\n const members = pageMembers(s, before, descriptor);\n const writes: { id: NodeId; pose: unknown }[] = [];\n for (const page of before) {\n const to = after.get(page.id);\n if (!to) continue;\n const dx = to.x - page.x;\n const dy = to.y - page.y;\n if (dx === 0 && dy === 0) continue;\n for (const root of members.get(page.id) ?? []) pageMoveWrites(s, root, dx, dy, descriptor, writes);\n }\n s.batch(label, () => {\n for (const { id, pose } of writes) s.setPose(id, pose);\n s.doc.set(PAGES_DOC_KEY, next);\n });\n}\n\nfunction edit<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n fallback: string,\n options: PageEditOptions,\n change: (pages: Page[]) => Page[],\n): void {\n const doc = pagesOf(scene) ?? { pages: [] };\n setPages(scene, { ...doc, pages: change([...doc.pages]) }, options.label ?? fallback, options);\n}\n\n/** Insert `page` at `index`, by default after the last page. */\nexport function addPage<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n page: Page,\n options: PageEditOptions & { index?: number } = {},\n): void {\n edit(scene, 'Add page', options, (pages) => {\n pages.splice(options.index ?? pages.length, 0, page);\n return pages;\n });\n}\n\n/** Delete page `id`, what is on it, and every node that derives from that, as one entry. */\nexport function removePage<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n id: string,\n options: PageEditOptions = {},\n): void {\n const label = options.label ?? 'Delete page';\n const members = pageMembers(scene, placedPagesOf(scene), options.descriptor).get(id) ?? [];\n scene.batch(label, () => {\n if (members.length > 0) scene.removeMany(members);\n edit(scene, label, { ...options, label }, (pages) => pages.filter((p) => p.id !== id));\n });\n}\n\n/** Move page `id` to `index` in the page order. */\nexport function movePage<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n id: string,\n index: number,\n options: PageEditOptions = {},\n): void {\n edit(scene, 'Move page', options, (pages) => {\n const at = pages.findIndex((p) => p.id === id);\n if (at < 0) return pages;\n const [page] = pages.splice(at, 1);\n pages.splice(Math.max(0, Math.min(index, pages.length)), 0, page!);\n return pages;\n });\n}\n\n/** Give page `id` a new size. Its members keep their places on it; the pages after it shift. */\nexport function resizePage<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n id: string,\n size: { readonly width: number; readonly height: number },\n options: PageEditOptions = {},\n): void {\n edit(scene, 'Resize page', options, (pages) =>\n pages.map((p) => (p.id === id ? { ...p, width: size.width, height: size.height } : p)));\n}\n\n/** Rename page `id`; an empty name removes it. */\nexport function renamePage<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n id: string,\n name: string,\n options: PageEditOptions = {},\n): void {\n edit(scene, 'Rename page', options, (pages) => pages.map((p) => {\n if (p.id !== id) return p;\n const { name: _old, ...rest } = p;\n return name === '' ? rest : { ...rest, name };\n }));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"pageEdits.js","names":[],"sources":["../../../src/core/pages/pageEdits.ts"],"sourcesContent":["/**\n * A document's pages as a scene document value, and the edits that change\n * them. Each edit moves the nodes on a page by however far the page itself\n * moved, so what is on a page stays where it is on that page.\n */\nimport {\n RECT_POSE_DESCRIPTOR, poseDescriptorForNode, translatePoseViaDescriptor, type PoseDescriptor,\n} from '../geometry/poseDescriptor';\nimport { carriedDescendants, sceneCarrySource } from '../scene/carryDescendants';\nimport { effectivePose, isEnvelope } from '../scene/effectivePose';\nimport type { NodeId, Scene } from '../scene/types';\nimport { layoutPages, pageOfBounds, type Page, type PageLayoutOptions, type PlacedPage } from './pages';\n\n/** The document value under {@link PAGES_DOC_KEY}: the pages in order, and how they are laid out. */\nexport interface PagesDoc {\n readonly pages: readonly Page[];\n readonly layout?: PageLayoutOptions;\n}\n\n/** The scene document value key a page list lives under (`scene.doc`). */\nexport const PAGES_DOC_KEY = 'core:pages';\n\n/** Options every page edit takes. */\nexport interface PageEditOptions {\n /** The undo entry's label. */\n label?: string;\n /** How node poses are measured and moved. Default: poses are rects. */\n descriptor?: PoseDescriptor<unknown>;\n}\n\ntype AnyScene = Scene<unknown, string, unknown>;\nconst asAny = <TData, TLayer extends string, TPose>(scene: Scene<TData, TLayer, TPose>) =>\n scene as unknown as AnyScene;\n\n/** The scene's pages doc, or `null` when it has none. */\nexport function pagesOf<TData, TLayer extends string, TPose>(scene: Scene<TData, TLayer, TPose>): PagesDoc | null {\n return scene.doc.get<PagesDoc>(PAGES_DOC_KEY) ?? null;\n}\n\n/** The scene's pages, placed in world space; empty when it has no pages doc. */\nexport function placedPagesOf<TData, TLayer extends string, TPose>(scene: Scene<TData, TLayer, TPose>): PlacedPage[] {\n const doc = pagesOf(scene);\n return doc ? layoutPages(doc.pages, doc.layout) : [];\n}\n\n/** An id none of `pages` has: `page-1`, `page-2`, and so on. */\nexport function nextPageId(pages: readonly Page[]): string {\n const taken = new Set(pages.map((p) => p.id));\n let n = 1;\n while (taken.has(`page-${n}`)) n++;\n return `page-${n}`;\n}\n\n/** Whether `id` draws from other nodes and goes wherever they go. */\nfunction followsOthers(scene: AnyScene, id: NodeId): boolean {\n const deps = scene.get(id)?.dependsOn;\n return Array.isArray(deps) && deps.length > 0;\n}\n\n/**\n * The root nodes on each placed page, by page id: each root belongs to the\n * page its bounds overlap most (`pageOfBounds`). A root in the gaps belongs to\n * none, and one that derives from other nodes is left out, since it follows\n * them.\n */\nexport function pageMembers<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n placed: readonly PlacedPage[],\n descriptor: PoseDescriptor<unknown> = RECT_POSE_DESCRIPTOR as PoseDescriptor<unknown>,\n): Map<string, NodeId[]> {\n const s = asAny(scene);\n const out = new Map<string, NodeId[]>(placed.map((p) => [p.id, []]));\n for (const id of s.roots) {\n const node = s.get(id);\n if (!node || followsOthers(s, id)) continue;\n const bounds = poseDescriptorForNode(descriptor, node).getBounds(effectivePose(s, node));\n const page = pageOfBounds(placed, bounds);\n if (page) out.get(page.id)!.push(id);\n }\n return out;\n}\n\nfunction derivesPose(scene: AnyScene, id: NodeId): boolean {\n const node = scene.get(id);\n return node !== undefined && isEnvelope(node);\n}\n\n/** The stored poses that move root `id` and what it carries by `dx`, `dy`: the\n * root, unless its pose derives from its children (then each child moves in\n * its place), and the descendants it carries, less whatever derives its pose. */\nfunction pageMoveWrites(\n scene: AnyScene,\n id: NodeId,\n dx: number,\n dy: number,\n descriptor: PoseDescriptor<unknown>,\n out: { id: NodeId; pose: unknown }[],\n): void {\n const node = scene.get(id);\n if (!node || followsOthers(scene, id)) return;\n if (derivesPose(scene, id)) {\n for (const child of scene.childrenOf(id)) pageMoveWrites(scene, child, dx, dy, descriptor, out);\n return;\n }\n const move = (pose: unknown, at: NodeId) => translatePoseViaDescriptor(pose, dx, dy, poseDescriptorForNode(descriptor, scene.get(at)!));\n out.push({ id, pose: move(node.pose, id) });\n const carried = carriedDescendants(sceneCarrySource(scene), id, (world, at) => (\n followsOthers(scene, at) || derivesPose(scene, at) ? null : move(world, at)\n ));\n for (const [at, pose] of carried) out.push({ id: at, pose });\n}\n\n/**\n * Replace the scene's pages doc with `next`, moving each page's members by\n * the change in their page's place, all as one undo entry labeled `label`.\n * Members of a page `next` drops are left where they are; `removePage` deletes\n * them first.\n */\nexport function setPages<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n next: PagesDoc,\n label: string,\n options: Pick<PageEditOptions, 'descriptor'> = {},\n): void {\n const s = asAny(scene);\n const descriptor = options.descriptor ?? (RECT_POSE_DESCRIPTOR as PoseDescriptor<unknown>);\n const before = placedPagesOf(s);\n const after = new Map(layoutPages(next.pages, next.layout).map((p) => [p.id, p]));\n const members = pageMembers(s, before, descriptor);\n const writes: { id: NodeId; pose: unknown }[] = [];\n for (const page of before) {\n const to = after.get(page.id);\n if (!to) continue;\n const dx = to.x - page.x;\n const dy = to.y - page.y;\n if (dx === 0 && dy === 0) continue;\n for (const root of members.get(page.id) ?? []) pageMoveWrites(s, root, dx, dy, descriptor, writes);\n }\n s.batch(label, () => {\n for (const { id, pose } of writes) s.setPose(id, pose);\n s.doc.set(PAGES_DOC_KEY, next);\n });\n}\n\nfunction edit<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n fallback: string,\n options: PageEditOptions,\n change: (pages: Page[]) => Page[],\n): void {\n const doc = pagesOf(scene) ?? { pages: [] };\n setPages(scene, { ...doc, pages: change([...doc.pages]) }, options.label ?? fallback, options);\n}\n\n/** Insert `page` at `index`, by default after the last page. */\nexport function addPage<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n page: Page,\n options: PageEditOptions & { index?: number } = {},\n): void {\n edit(scene, 'Add page', options, (pages) => {\n pages.splice(options.index ?? pages.length, 0, page);\n return pages;\n });\n}\n\n/** Delete page `id`, what is on it, and every node that derives from that, as one entry. */\nexport function removePage<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n id: string,\n options: PageEditOptions = {},\n): void {\n const label = options.label ?? 'Delete page';\n const members = pageMembers(scene, placedPagesOf(scene), options.descriptor).get(id) ?? [];\n scene.batch(label, () => {\n if (members.length > 0) scene.removeMany(members);\n edit(scene, label, { ...options, label }, (pages) => pages.filter((p) => p.id !== id));\n });\n}\n\n/** Move page `id` to `index` in the page order. */\nexport function movePage<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n id: string,\n index: number,\n options: PageEditOptions = {},\n): void {\n edit(scene, 'Move page', options, (pages) => {\n const at = pages.findIndex((p) => p.id === id);\n if (at < 0) return pages;\n const [page] = pages.splice(at, 1);\n pages.splice(Math.max(0, Math.min(index, pages.length)), 0, page!);\n return pages;\n });\n}\n\n/** Give page `id` a new size. Its members keep their places on it; the pages after it shift. */\nexport function resizePage<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n id: string,\n size: { readonly width: number; readonly height: number },\n options: PageEditOptions = {},\n): void {\n edit(scene, 'Resize page', options, (pages) =>\n pages.map((p) => (p.id === id ? { ...p, width: size.width, height: size.height } : p)));\n}\n\n/** Rename page `id`; an empty name removes it. */\nexport function renamePage<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n id: string,\n name: string,\n options: PageEditOptions = {},\n): void {\n edit(scene, 'Rename page', options, (pages) => pages.map((p) => {\n if (p.id !== id) return p;\n const { name: _old, ...rest } = p;\n return name === '' ? rest : { ...rest, name };\n }));\n}\n"],"mappings":"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"}
@@ -202,6 +202,12 @@ function lerpColorArray(from, to, t, space = "rgb") {
202
202
  }
203
203
  return out;
204
204
  }
205
+ /** Linear-light channel to sRGB encoding (0..1). Unclamped. */
206
+ function linearToSrgb(c) {
207
+ const a = Math.abs(c);
208
+ const v = a <= .0031308 ? a * 12.92 : 1.055 * Math.pow(a, 1 / 2.4) - .055;
209
+ return c < 0 ? -v : v;
210
+ }
205
211
  /** HSL to sRGB. Hue in degrees, saturation, lightness and output all 0..1. */
206
212
  function hslToSrgb(h, s, l) {
207
213
  if (s === 0) return [
@@ -240,8 +246,9 @@ function hueToChannel(p, q, t) {
240
246
  * unitless in the modern syntax); same alpha rules and separator rules
241
247
  * as `rgb()`
242
248
  * - The 148 CSS named colors plus `transparent`
249
+ * - `color(srgb …)` and `color(srgb-linear …)`; `oklab()` and `oklch()`, clamped into sRGB
243
250
  *
244
- * Not supported: `oklch()`, `lab()`, `color()`, `currentColor`, system
251
+ * Not supported: `lab()`, `lch()`, other `color()` spaces, `currentColor`, system
245
252
  * colors. Throws on anything unrecognized.
246
253
  *
247
254
  * Results are cached by input-string identity. Callers MUST treat the
@@ -271,6 +278,14 @@ function parseColorUncached(input) {
271
278
  const parsed = parseHslFunction(lower);
272
279
  if (parsed) return parsed;
273
280
  }
281
+ if (lower.startsWith("color(")) {
282
+ const parsed = parseColorFunction(lower);
283
+ if (parsed) return parsed;
284
+ }
285
+ if (lower.startsWith("ok")) {
286
+ const parsed = parseOkFunction(lower);
287
+ if (parsed) return parsed;
288
+ }
274
289
  throw new Error(`parseColor: unrecognized color "${input}"`);
275
290
  }
276
291
  const RESOLVE_COLOR_CACHE_CAP = 1024;
@@ -399,7 +414,49 @@ function parseHslFunction(s) {
399
414
  parseAlpha(channels.alpha)
400
415
  ];
401
416
  }
402
- /** Parse a hue token: `<number>`, `<number>deg`, `<number>rad`, `<number>grad`, `<number>turn`. Returns degrees 0..360 (modulo). */
417
+ /** `color(srgb …)` or `color(srgb-linear …)`, channels as numbers 0..1 or percents. */
418
+ function parseColorFunction(s) {
419
+ const body = unwrapFn(s, "color");
420
+ const space = body?.match(/^(srgb-linear|srgb)\s+/);
421
+ if (!body || !space || body.includes(",")) return null;
422
+ const channels = splitChannels(body.slice(space[0].length));
423
+ if (!channels) return null;
424
+ const rgb = channels.rgb.map((tok) => parseChannel(tok, 1));
425
+ if (rgb.some((c) => c === null)) return null;
426
+ const [r, g, b] = space[1] === "srgb" ? rgb : rgb.map((c) => linearToSrgb(c));
427
+ return [
428
+ clamp01(r),
429
+ clamp01(g),
430
+ clamp01(b),
431
+ parseAlpha(channels.alpha)
432
+ ];
433
+ }
434
+ /** `oklab(L a b)` or `oklch(L C h)`, clamped into sRGB. L is 0..1 or a percent; a, b and C are numbers. */
435
+ function parseOkFunction(s) {
436
+ const lch = unwrapFn(s, "oklch");
437
+ const body = lch ?? unwrapFn(s, "oklab");
438
+ if (body === null || body.includes(",")) return null;
439
+ const channels = splitChannels(body);
440
+ if (!channels) return null;
441
+ const L = parsePercentOrNumber(channels.rgb[0]);
442
+ const second = Number(channels.rgb[1]);
443
+ if (L === null || !Number.isFinite(second)) return null;
444
+ let A = second;
445
+ let B = Number(channels.rgb[2]);
446
+ if (lch !== null) {
447
+ const h = parseHue(channels.rgb[2]);
448
+ if (h === null) return null;
449
+ [, A, B] = oklchToOklab(L, second, h * (Math.PI / 180));
450
+ } else if (!Number.isFinite(B)) return null;
451
+ const [r, g, b] = oklabToLinearSrgb(L, A, B).map((c) => clamp01(linearToSrgb(c)));
452
+ return [
453
+ r,
454
+ g,
455
+ b,
456
+ parseAlpha(channels.alpha)
457
+ ];
458
+ }
459
+ /** Parse a hue token:`<number>`, `<number>deg`, `<number>rad`, `<number>grad`, `<number>turn`. Returns degrees 0..360 (modulo). */
403
460
  function parseHue(tok) {
404
461
  let n;
405
462
  if (tok.endsWith("deg")) n = Number(tok.slice(0, -3));