@weasel-js/core 1.11.1 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +184 -0
- package/dist/WeaselProvider.js +1 -1
- package/dist/affordances/pathAnchors.js +10 -6
- package/dist/affordances/pathAnchors.js.map +1 -1
- package/dist/barrel/canvas.js +3 -1
- package/dist/barrel/paint.js +4 -4
- package/dist/barrel/paths.js +8 -4
- package/dist/barrel/renderer.js +3 -2
- package/dist/barrel/routing.js +4 -4
- package/dist/barrel/scene.js +2 -1
- package/dist/barrel/text.js +2 -2
- package/dist/barrel/viewport.js +6 -2
- package/dist/canvas/Canvas.js +2 -1
- package/dist/canvas/Canvas.js.map +1 -1
- package/dist/canvas/CanvasView.js +23 -34
- package/dist/canvas/CanvasView.js.map +1 -1
- package/dist/canvas/ModalityHud.js +1 -1
- package/dist/canvas/NodeShape.js +4 -1
- package/dist/canvas/NodeShape.js.map +1 -1
- package/dist/canvas/SceneCanvas/ActionsProviderIfRoot.js +1 -1
- package/dist/canvas/SceneCanvas/DepRegistryProviderIfRoot.js +1 -1
- package/dist/canvas/SceneCanvas/PointerProviderIfRoot.js +1 -1
- package/dist/canvas/SceneCanvas/SurfaceDeclarations.js +23 -0
- package/dist/canvas/SceneCanvas/SurfaceDeclarations.js.map +1 -0
- package/dist/canvas/SceneCanvas/canvasPassThrough.js +1 -0
- package/dist/canvas/SceneCanvas/canvasPassThrough.js.map +1 -1
- package/dist/canvas/SceneCanvas/props.js.map +1 -1
- package/dist/canvas/SceneCanvas/scenePointQuery.js +7 -3
- package/dist/canvas/SceneCanvas/scenePointQuery.js.map +1 -1
- package/dist/canvas/SceneCanvas/useActionTunings.js +6 -1
- package/dist/canvas/SceneCanvas/useActionTunings.js.map +1 -1
- package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js +1 -0
- package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js.map +1 -1
- package/dist/canvas/SceneCanvas/useCanvasTools.js +7 -2
- package/dist/canvas/SceneCanvas/useCanvasTools.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneCanvasHandle.js +9 -3
- package/dist/canvas/SceneCanvas/useSceneCanvasHandle.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js +10 -2
- package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneCanvasTools.js +1 -0
- package/dist/canvas/SceneCanvas/useSceneCanvasTools.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js +19 -4
- package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneStandardActions.js +2 -1
- package/dist/canvas/SceneCanvas/useSceneStandardActions.js.map +1 -1
- package/dist/canvas/SceneCanvas.js +11 -10
- package/dist/canvas/SceneCanvas.js.map +1 -1
- package/dist/canvas/ViewMarker.js +35 -0
- package/dist/canvas/ViewMarker.js.map +1 -0
- package/dist/canvas/defaultDrawOne.js +2 -0
- package/dist/canvas/defaultDrawOne.js.map +1 -1
- package/dist/canvas/deps/booleans.js +2 -1
- package/dist/canvas/deps/booleans.js.map +1 -1
- package/dist/canvas/deps/hitTestArea.js +2 -2
- package/dist/canvas/deps/hitTestArea.js.map +1 -1
- package/dist/canvas/deps/insert.js +6 -2
- package/dist/canvas/deps/insert.js.map +1 -1
- package/dist/canvas/deps/pathEditCommits.js +3 -7
- package/dist/canvas/deps/pathEditCommits.js.map +1 -1
- package/dist/canvas/drawnBounds.js +11 -3
- package/dist/canvas/drawnBounds.js.map +1 -1
- package/dist/canvas/inheritedPaints.js +44 -0
- package/dist/canvas/inheritedPaints.js.map +1 -0
- package/dist/canvas/nodeShapes/derivedPainter.js +15 -16
- package/dist/canvas/nodeShapes/derivedPainter.js.map +1 -1
- package/dist/canvas/nodeShapes/nodePaint.js +13 -34
- package/dist/canvas/nodeShapes/nodePaint.js.map +1 -1
- package/dist/canvas/nodeShapes/pathPainter.js +9 -8
- package/dist/canvas/nodeShapes/pathPainter.js.map +1 -1
- package/dist/canvas/nodeShapes/rectFallbackPainter.js +4 -5
- package/dist/canvas/nodeShapes/rectFallbackPainter.js.map +1 -1
- package/dist/canvas/nodeShapes/shapePainter.js +11 -10
- package/dist/canvas/nodeShapes/shapePainter.js.map +1 -1
- package/dist/canvas/nodeShapes/textPainter.js +13 -12
- package/dist/canvas/nodeShapes/textPainter.js.map +1 -1
- package/dist/canvas/pickReach.js +3 -3
- package/dist/canvas/pickReach.js.map +1 -1
- package/dist/canvas/pickWalk.js +3 -1
- package/dist/canvas/pickWalk.js.map +1 -1
- package/dist/canvas/renderSceneToPixels.js +1 -1
- package/dist/canvas/renderSceneToPixels.js.map +1 -1
- package/dist/canvas/sceneSlotWiring.js +2 -1
- package/dist/canvas/sceneSlotWiring.js.map +1 -1
- package/dist/canvas/sceneViewRender.js +2 -1
- package/dist/canvas/sceneViewRender.js.map +1 -1
- package/dist/canvas/surfaceContribution.js +12 -8
- package/dist/canvas/surfaceContribution.js.map +1 -1
- package/dist/canvas/useCanvasView.js +4 -2
- package/dist/canvas/useCanvasView.js.map +1 -1
- package/dist/canvas/useLayerStack.js +6 -2
- package/dist/canvas/useLayerStack.js.map +1 -1
- package/dist/canvas/useViewAim.js +121 -0
- package/dist/canvas/useViewAim.js.map +1 -0
- package/dist/canvas/useViewNesting.js +3 -2
- package/dist/canvas/useViewNesting.js.map +1 -1
- package/dist/canvas/useViewRegistrySurface.js +4 -2
- package/dist/canvas/useViewRegistrySurface.js.map +1 -1
- package/dist/canvas/viewMarkers.js +63 -0
- package/dist/canvas/viewMarkers.js.map +1 -0
- package/dist/canvas/viewRegistry.js +27 -1
- package/dist/canvas/viewRegistry.js.map +1 -1
- package/dist/core/pages/pageEdits.js +2 -3
- package/dist/core/pages/pageEdits.js.map +1 -1
- package/dist/core/paint/meshKindLazy.js +59 -2
- package/dist/core/paint/meshKindLazy.js.map +1 -1
- package/dist/core/scene/clipNothing.js +13 -0
- package/dist/core/scene/clipNothing.js.map +1 -0
- package/dist/core/scene/depMemo.js +11 -3
- package/dist/core/scene/depMemo.js.map +1 -1
- package/dist/core/scene/dependents.js +7 -1
- package/dist/core/scene/dependents.js.map +1 -1
- package/dist/core/scene/derivedParts.js +10 -11
- package/dist/core/scene/derivedParts.js.map +1 -1
- package/dist/core/scene/effectivePose.js +7 -2
- package/dist/core/scene/effectivePose.js.map +1 -1
- package/dist/core/scene/fontReads.js +37 -0
- package/dist/core/scene/fontReads.js.map +1 -0
- package/dist/core/scene/inheritedPaint.js +75 -0
- package/dist/core/scene/inheritedPaint.js.map +1 -0
- package/dist/core/scene/kitRegistry.js +5 -0
- package/dist/core/scene/kitRegistry.js.map +1 -1
- package/dist/core/scene/nodeMemo.js +15 -7
- package/dist/core/scene/nodeMemo.js.map +1 -1
- package/dist/core/scene/pathEffects/blend.js.map +1 -1
- package/dist/core/scene/pathEffects/derive.js +7 -2
- package/dist/core/scene/pathEffects/derive.js.map +1 -1
- package/dist/core/scene/pathEffects/effects.js +3 -1
- package/dist/core/scene/pathEffects/effects.js.map +1 -1
- package/dist/core/scene/pathEffects/textAlong.js +95 -0
- package/dist/core/scene/pathEffects/textAlong.js.map +1 -0
- package/dist/{cornerHandles-BHuoMs1o.d.ts → cornerHandles-p8fDIAKt.d.ts} +2 -1
- package/dist/{depSchema-BpZNOv7q.d.ts → depSchema-mG19aEdD.d.ts} +61 -13
- package/dist/devtools/registry.js +4 -4
- package/dist/devtools/registry.js.map +1 -1
- package/dist/features/measure/measureContribution.js +4 -2
- package/dist/features/measure/measureContribution.js.map +1 -1
- package/dist/features/modes/modeTriggers.js +7 -3
- package/dist/features/modes/modeTriggers.js.map +1 -1
- package/dist/features/pathEffects/effectSources.js +60 -0
- package/dist/features/pathEffects/effectSources.js.map +1 -0
- package/dist/features/pathEffects/readSource.js +17 -1
- package/dist/features/pathEffects/readSource.js.map +1 -1
- package/dist/features/pathEffects/textItems.js +53 -0
- package/dist/features/pathEffects/textItems.js.map +1 -0
- package/dist/features/pathEffects/useEffectSourcesLayer.js +57 -0
- package/dist/features/pathEffects/useEffectSourcesLayer.js.map +1 -0
- package/dist/features/paths/pathEditState.js +5 -2
- package/dist/features/paths/pathEditState.js.map +1 -1
- package/dist/features/text/sceneTextDrafts.js +65 -0
- package/dist/features/text/sceneTextDrafts.js.map +1 -0
- package/dist/features/text/textHitAt.js +31 -2
- package/dist/features/text/textHitAt.js.map +1 -1
- package/dist/features/text/useSceneTextEdit.js +22 -18
- package/dist/features/text/useSceneTextEdit.js.map +1 -1
- package/dist/features/viewports/aim.js +105 -0
- package/dist/features/viewports/aim.js.map +1 -0
- package/dist/features/viewports/viewMarker.js +82 -0
- package/dist/features/viewports/viewMarker.js.map +1 -0
- package/dist/features/viewports/viewMarkerDrag.js +55 -0
- package/dist/features/viewports/viewMarkerDrag.js.map +1 -0
- package/dist/index.d.ts +500 -201
- package/dist/index.js +23 -12
- package/dist/interactions/actions/booleans/booleans.js +2 -1
- package/dist/interactions/actions/booleans/booleans.js.map +1 -1
- package/dist/interactions/actions/booleans/operands.js +6 -5
- package/dist/interactions/actions/booleans/operands.js.map +1 -1
- package/dist/interactions/actions/clipboard/action.js +27 -17
- package/dist/interactions/actions/clipboard/action.js.map +1 -1
- package/dist/interactions/actions/defaults/anchorEditing.js +44 -23
- package/dist/interactions/actions/defaults/anchorEditing.js.map +1 -1
- package/dist/interactions/actions/defaults/anchorJoin.js +12 -6
- package/dist/interactions/actions/defaults/anchorJoin.js.map +1 -1
- package/dist/interactions/actions/defaults/bendSegment.js +7 -4
- package/dist/interactions/actions/defaults/bendSegment.js.map +1 -1
- package/dist/interactions/actions/defaults/cancelGesture.js +11 -8
- package/dist/interactions/actions/defaults/cancelGesture.js.map +1 -1
- package/dist/interactions/actions/defaults/contextMenu.js +17 -8
- package/dist/interactions/actions/defaults/contextMenu.js.map +1 -1
- package/dist/interactions/actions/defaults/createPaintAction.js +1 -1
- package/dist/interactions/actions/defaults/createPaintAction.js.map +1 -1
- package/dist/interactions/actions/defaults/delete.js +28 -14
- package/dist/interactions/actions/defaults/delete.js.map +1 -1
- package/dist/interactions/actions/defaults/duplicate.js +11 -8
- package/dist/interactions/actions/defaults/duplicate.js.map +1 -1
- package/dist/interactions/actions/defaults/enterPathEdit.js +19 -9
- package/dist/interactions/actions/defaults/enterPathEdit.js.map +1 -1
- package/dist/interactions/actions/defaults/escape.js +11 -8
- package/dist/interactions/actions/defaults/escape.js.map +1 -1
- package/dist/interactions/actions/defaults/exitPathEdit.js +7 -4
- package/dist/interactions/actions/defaults/exitPathEdit.js.map +1 -1
- package/dist/interactions/actions/defaults/group.js +19 -13
- package/dist/interactions/actions/defaults/group.js.map +1 -1
- package/dist/interactions/actions/defaults/ingest.js +10 -4
- package/dist/interactions/actions/defaults/ingest.js.map +1 -1
- package/dist/interactions/actions/defaults/insertPathAnchor.js +7 -4
- package/dist/interactions/actions/defaults/insertPathAnchor.js.map +1 -1
- package/dist/interactions/actions/defaults/nudge.js +8 -2
- package/dist/interactions/actions/defaults/nudge.js.map +1 -1
- package/dist/interactions/actions/defaults/pathEditTools.js +11 -8
- package/dist/interactions/actions/defaults/pathEditTools.js.map +1 -1
- package/dist/interactions/actions/defaults/pinchZoom.js +7 -4
- package/dist/interactions/actions/defaults/pinchZoom.js.map +1 -1
- package/dist/interactions/actions/defaults/reorder.js +24 -6
- package/dist/interactions/actions/defaults/reorder.js.map +1 -1
- package/dist/interactions/actions/defaults/selectAll.js +8 -5
- package/dist/interactions/actions/defaults/selectAll.js.map +1 -1
- package/dist/interactions/actions/defaults/setFill.js +7 -3
- package/dist/interactions/actions/defaults/setFill.js.map +1 -1
- package/dist/interactions/actions/defaults/setFillOpacity.js +1 -1
- package/dist/interactions/actions/defaults/setStroke.js +12 -11
- package/dist/interactions/actions/defaults/setStroke.js.map +1 -1
- package/dist/interactions/actions/defaults/setStrokeOpacity.js +1 -1
- package/dist/interactions/actions/defaults/toolActivate.js +7 -1
- package/dist/interactions/actions/defaults/toolActivate.js.map +1 -1
- package/dist/interactions/actions/defaults/toolResetToDefault.js +7 -4
- package/dist/interactions/actions/defaults/toolResetToDefault.js.map +1 -1
- package/dist/interactions/actions/defaults/undoRedo.js +19 -13
- package/dist/interactions/actions/defaults/undoRedo.js.map +1 -1
- package/dist/interactions/actions/defaults/viewportWheelPan.js +16 -7
- package/dist/interactions/actions/defaults/viewportWheelPan.js.map +1 -1
- package/dist/interactions/actions/defaults/viewportZoom.js +24 -6
- package/dist/interactions/actions/defaults/viewportZoom.js.map +1 -1
- package/dist/interactions/actions/edit-anchors/action.js +7 -4
- package/dist/interactions/actions/edit-anchors/action.js.map +1 -1
- package/dist/interactions/actions/flip/action.js +8 -2
- package/dist/interactions/actions/flip/action.js.map +1 -1
- package/dist/interactions/actions/insert/repositionAction.js +2 -1
- package/dist/interactions/actions/insert/repositionAction.js.map +1 -1
- package/dist/interactions/actions/move/action.js +7 -4
- package/dist/interactions/actions/move/action.js.map +1 -1
- package/dist/interactions/actions/outlines/createOutlinesAction.js +8 -5
- package/dist/interactions/actions/outlines/createOutlinesAction.js.map +1 -1
- package/dist/interactions/actions/outlines/outlineStroke.js +5 -3
- package/dist/interactions/actions/outlines/outlineStroke.js.map +1 -1
- package/dist/interactions/actions/pathCommands/cutJoin.js +8 -5
- package/dist/interactions/actions/pathCommands/cutJoin.js.map +1 -1
- package/dist/interactions/actions/resize/cornerHandles.js +2 -1
- package/dist/interactions/actions/resize/cornerHandles.js.map +1 -1
- package/dist/interactions/actions/selectionSubtree.js +12 -7
- package/dist/interactions/actions/selectionSubtree.js.map +1 -1
- package/dist/interactions/actions/transform-again/action.js +8 -5
- package/dist/interactions/actions/transform-again/action.js.map +1 -1
- package/dist/json.d.ts +4 -0
- package/dist/json.js +3 -0
- package/dist/kitSettingsSchema.js +8 -1
- package/dist/kitSettingsSchema.js.map +1 -1
- package/dist/{math-CrJULaZI.d.ts → math-B56zxVs5.d.ts} +10 -1
- package/dist/math.d.ts +1 -1
- package/dist/math.js +2 -1
- package/dist/resize.d.ts +1 -1
- package/dist/test-seams.d.ts +2 -2
- package/dist/tools/builtin/ellipse/useEllipseTool.js +1 -0
- package/dist/tools/builtin/ellipse/useEllipseTool.js.map +1 -1
- package/dist/tools/builtin/eyedropper/useEyedropperTool.js +2 -1
- package/dist/tools/builtin/eyedropper/useEyedropperTool.js.map +1 -1
- package/dist/tools/builtin/hand/dragPanContribution.js +4 -1
- package/dist/tools/builtin/hand/dragPanContribution.js.map +1 -1
- package/dist/tools/builtin/hand/useHandTool.js +7 -4
- package/dist/tools/builtin/hand/useHandTool.js.map +1 -1
- package/dist/tools/builtin/image/useImageTool.js +1 -0
- package/dist/tools/builtin/image/useImageTool.js.map +1 -1
- package/dist/tools/builtin/lasso/useLassoTool.js +1 -0
- package/dist/tools/builtin/lasso/useLassoTool.js.map +1 -1
- package/dist/tools/builtin/line/useLineTool.js +1 -0
- package/dist/tools/builtin/line/useLineTool.js.map +1 -1
- package/dist/tools/builtin/measure/useMeasureTool.js +12 -5
- package/dist/tools/builtin/measure/useMeasureTool.js.map +1 -1
- package/dist/tools/builtin/pen/usePenTool.js +14 -6
- package/dist/tools/builtin/pen/usePenTool.js.map +1 -1
- package/dist/tools/builtin/pencil/usePencilTool.js +1 -0
- package/dist/tools/builtin/pencil/usePencilTool.js.map +1 -1
- package/dist/tools/builtin/polygon/usePolygonTool.js +13 -4
- package/dist/tools/builtin/polygon/usePolygonTool.js.map +1 -1
- package/dist/tools/builtin/rect/useRectTool.js +1 -0
- package/dist/tools/builtin/rect/useRectTool.js.map +1 -1
- package/dist/tools/builtin/select/adapterPointQuery.js +3 -2
- package/dist/tools/builtin/select/adapterPointQuery.js.map +1 -1
- package/dist/tools/builtin/select/pressDecision.js +32 -0
- package/dist/tools/builtin/select/pressDecision.js.map +1 -0
- package/dist/tools/builtin/select/selectionContributions.js +22 -13
- package/dist/tools/builtin/select/selectionContributions.js.map +1 -1
- package/dist/tools/builtin/select/useSelectTool.js +13 -10
- package/dist/tools/builtin/select/useSelectTool.js.map +1 -1
- package/dist/tools/builtin/slice/useSliceTool.js +22 -8
- package/dist/tools/builtin/slice/useSliceTool.js.map +1 -1
- package/dist/tools/builtin/star/useStarTool.js +13 -4
- package/dist/tools/builtin/star/useStarTool.js.map +1 -1
- package/dist/tools/builtin/sticky/stickyNode.js +4 -1
- package/dist/tools/builtin/sticky/stickyNode.js.map +1 -1
- package/dist/tools/builtin/sticky/useStickyTool.js +10 -5
- package/dist/tools/builtin/sticky/useStickyTool.js.map +1 -1
- package/dist/tools/builtin/text/useTextTool.js +3 -1
- package/dist/tools/builtin/text/useTextTool.js.map +1 -1
- package/dist/util/paint.js +1 -3
- package/dist/util/paint.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +28 -24
|
@@ -15,8 +15,11 @@ function scenePointQuery(d, shapePicking, pickTolerancePx) {
|
|
|
15
15
|
scale: s
|
|
16
16
|
};
|
|
17
17
|
return {
|
|
18
|
-
hits: (n, pose, derived) => {
|
|
19
|
-
const ink = shapePicking ? findShapeInk(n, pose, {
|
|
18
|
+
hits: (n, pose, derived, inherited) => {
|
|
19
|
+
const ink = shapePicking ? findShapeInk(n, pose, {
|
|
20
|
+
scale,
|
|
21
|
+
inherited
|
|
22
|
+
}) : null;
|
|
20
23
|
const slop = {
|
|
21
24
|
...tolerance,
|
|
22
25
|
px: tolerance.px + (ink?.outsetPx ?? 0),
|
|
@@ -26,7 +29,8 @@ function scenePointQuery(d, shapePicking, pickTolerancePx) {
|
|
|
26
29
|
return !shapePicking || shapeCoversPoint(n, pose, px, py, {
|
|
27
30
|
tolerance,
|
|
28
31
|
scale,
|
|
29
|
-
derivedPath: derived
|
|
32
|
+
derivedPath: derived,
|
|
33
|
+
inherited
|
|
30
34
|
});
|
|
31
35
|
},
|
|
32
36
|
clipAdmits: (clip) => pathContainsPoint(clip, px, py),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scenePointQuery.js","names":[],"sources":["../../../src/canvas/SceneCanvas/scenePointQuery.ts"],"sourcesContent":["import { applyToPoint, meanScale, pathContainsPoint } from '@weasel-js/geom';\nimport type { PickQuery } from 'canvas/pickWalk';\nimport { findShapeInk, shapeCoversPoint } from 'canvas/NodeShape';\nimport { toPlane } from 'core/viewport/parallax';\nimport type { PoseDescriptor } from 'interactions/actions/resize/geometry';\nimport { poseContains, poseContainsRotated } from './poseGeometry';\n\n/** The point query `<SceneCanvas>`'s default pick asks: a world point with\n * `pickTolerancePx` of screen slop, refined to the painter's ink when\n * `shapePicking` is on. */\nexport function scenePointQuery<TPose>(\n d: PoseDescriptor<unknown>,\n shapePicking: boolean,\n pickTolerancePx: number,\n): (px: number, py: number, s: { x: number; y: number }) => PickQuery<TPose> {\n // `s` makes the slop a screen distance, so the grab zone stays the same\n // apparent thickness at any zoom and on every axis. A `{ px }` stroke's\n // reach is on screen too, and joins the slop there.\n const pointQuery = (px: number, py: number, s: { x: number; y: number }): PickQuery<TPose> => {\n const scale = meanScale(s);\n const tolerance = { px: pickTolerancePx, scale: s };\n return {\n hits: (n, pose, derived) => {\n // The pre-filter has to be at least as generous as the refinement\n // that follows it, or it rejects points the refinement would have\n // claimed. A stroke reaches past the pose box by `outset` — a whole\n // stroke width for an outer align — on top of the pointer slop.\n const ink = shapePicking ? findShapeInk(n as never, pose, { scale }) : null;\n // A derived node's pose is a placeholder — typically zero-sized at\n // the origin — so its own box rejects every point the path covers.\n // The path is the region to test instead, and `poseContains` already\n // reads a path-like pose as one.\n const slop = { ...tolerance, px: tolerance.px + (ink?.outsetPx ?? 0), reach: ink?.outset ?? 0 };\n const admitted = derived\n ? poseContains(derived as never, px, py, slop, d as PoseDescriptor<unknown>)\n : poseContainsRotated(pose, px, py, slop, d as PoseDescriptor<unknown>);\n if (!admitted) return false;\n // `shapeCoversPoint` narrows the rect to the ink the painter actually\n // lays down (and answers `true` for painters that have no silhouette,\n // so nothing becomes unpickable).\n return !shapePicking\n || shapeCoversPoint(n as never, pose, px, py, {\n tolerance, scale, derivedPath: derived,\n });\n },\n clipAdmits: (clip) => pathContainsPoint(clip, px, py),\n inPlane: (m) => {\n const p = toPlane(m, { x: px, y: py });\n return pointQuery(p.x, p.y, { x: s.x / m.scale.x, y: s.y / m.scale.y });\n },\n inCopy: (m) => {\n const [x, y] = applyToPoint(m, px, py);\n const k = Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2]));\n return pointQuery(x, y, { x: s.x / k, y: s.y / k });\n },\n };\n };\n return pointQuery;\n}\n"],"mappings":";;;;;;;;;AAUA,SAAgB,gBACd,GACA,cACA,iBAC2E;CAI3E,MAAM,cAAc,IAAY,IAAY,MAAkD;EAC5F,MAAM,QAAQ,UAAU,CAAC;EACzB,MAAM,YAAY;GAAE,IAAI;GAAiB,OAAO;EAAE;EAClD,OAAO;GACL,OAAO,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"scenePointQuery.js","names":[],"sources":["../../../src/canvas/SceneCanvas/scenePointQuery.ts"],"sourcesContent":["import { applyToPoint, meanScale, pathContainsPoint } from '@weasel-js/geom';\nimport type { PickQuery } from 'canvas/pickWalk';\nimport { findShapeInk, shapeCoversPoint } from 'canvas/NodeShape';\nimport { toPlane } from 'core/viewport/parallax';\nimport type { PoseDescriptor } from 'interactions/actions/resize/geometry';\nimport { poseContains, poseContainsRotated } from './poseGeometry';\n\n/** The point query `<SceneCanvas>`'s default pick asks: a world point with\n * `pickTolerancePx` of screen slop, refined to the painter's ink when\n * `shapePicking` is on. */\nexport function scenePointQuery<TPose>(\n d: PoseDescriptor<unknown>,\n shapePicking: boolean,\n pickTolerancePx: number,\n): (px: number, py: number, s: { x: number; y: number }) => PickQuery<TPose> {\n // `s` makes the slop a screen distance, so the grab zone stays the same\n // apparent thickness at any zoom and on every axis. A `{ px }` stroke's\n // reach is on screen too, and joins the slop there.\n const pointQuery = (px: number, py: number, s: { x: number; y: number }): PickQuery<TPose> => {\n const scale = meanScale(s);\n const tolerance = { px: pickTolerancePx, scale: s };\n return {\n hits: (n, pose, derived, inherited) => {\n // The pre-filter has to be at least as generous as the refinement\n // that follows it, or it rejects points the refinement would have\n // claimed. A stroke reaches past the pose box by `outset` — a whole\n // stroke width for an outer align — on top of the pointer slop.\n const ink = shapePicking ? findShapeInk(n as never, pose, { scale, inherited }) : null;\n // A derived node's pose is a placeholder — typically zero-sized at\n // the origin — so its own box rejects every point the path covers.\n // The path is the region to test instead, and `poseContains` already\n // reads a path-like pose as one.\n const slop = { ...tolerance, px: tolerance.px + (ink?.outsetPx ?? 0), reach: ink?.outset ?? 0 };\n const admitted = derived\n ? poseContains(derived as never, px, py, slop, d as PoseDescriptor<unknown>)\n : poseContainsRotated(pose, px, py, slop, d as PoseDescriptor<unknown>);\n if (!admitted) return false;\n // `shapeCoversPoint` narrows the rect to the ink the painter actually\n // lays down (and answers `true` for painters that have no silhouette,\n // so nothing becomes unpickable).\n return !shapePicking\n || shapeCoversPoint(n as never, pose, px, py, {\n tolerance, scale, derivedPath: derived, inherited,\n });\n },\n clipAdmits: (clip) => pathContainsPoint(clip, px, py),\n inPlane: (m) => {\n const p = toPlane(m, { x: px, y: py });\n return pointQuery(p.x, p.y, { x: s.x / m.scale.x, y: s.y / m.scale.y });\n },\n inCopy: (m) => {\n const [x, y] = applyToPoint(m, px, py);\n const k = Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2]));\n return pointQuery(x, y, { x: s.x / k, y: s.y / k });\n },\n };\n };\n return pointQuery;\n}\n"],"mappings":";;;;;;;;;AAUA,SAAgB,gBACd,GACA,cACA,iBAC2E;CAI3E,MAAM,cAAc,IAAY,IAAY,MAAkD;EAC5F,MAAM,QAAQ,UAAU,CAAC;EACzB,MAAM,YAAY;GAAE,IAAI;GAAiB,OAAO;EAAE;EAClD,OAAO;GACL,OAAO,GAAG,MAAM,SAAS,cAAc;IAKrC,MAAM,MAAM,eAAe,aAAa,GAAY,MAAM;KAAE;KAAO;IAAU,CAAC,IAAI;IAKlF,MAAM,OAAO;KAAE,GAAG;KAAW,IAAI,UAAU,MAAM,KAAK,YAAY;KAAI,OAAO,KAAK,UAAU;IAAE;IAI9F,IAAI,EAHa,UACb,aAAa,SAAkB,IAAI,IAAI,MAAM,CAA4B,IACzE,oBAAoB,MAAM,IAAI,IAAI,MAAM,CAA4B,IACzD,OAAO;IAItB,OAAO,CAAC,gBACH,iBAAiB,GAAY,MAAM,IAAI,IAAI;KAC5C;KAAW;KAAO,aAAa;KAAS;IAC1C,CAAC;GACL;GACA,aAAa,SAAS,kBAAkB,MAAM,IAAI,EAAE;GACpD,UAAU,MAAM;IACd,MAAM,IAAI,QAAQ,GAAG;KAAE,GAAG;KAAI,GAAG;IAAG,CAAC;IACrC,OAAO,WAAW,EAAE,GAAG,EAAE,GAAG;KAAE,GAAG,EAAE,IAAI,EAAE,MAAM;KAAG,GAAG,EAAE,IAAI,EAAE,MAAM;IAAE,CAAC;GACxE;GACA,SAAS,MAAM;IACb,MAAM,CAAC,GAAG,KAAK,aAAa,GAAG,IAAI,EAAE;IACrC,MAAM,IAAI,KAAK,KAAK,KAAK,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC;IACvD,OAAO,WAAW,GAAG,GAAG;KAAE,GAAG,EAAE,IAAI;KAAG,GAAG,EAAE,IAAI;IAAE,CAAC;GACpD;EACF;CACF;CACA,OAAO;AACT"}
|
|
@@ -14,7 +14,12 @@ function useActionTunings(tune) {
|
|
|
14
14
|
};
|
|
15
15
|
}, [reg, tune]);
|
|
16
16
|
}
|
|
17
|
+
/** Hold `overrides` on the registry in scope for as long as the object is the same one. */
|
|
18
|
+
function useBindingOverrides(overrides) {
|
|
19
|
+
const reg = useActionsRegistry();
|
|
20
|
+
useEffect(() => reg && overrides ? reg.overrideBindings(overrides) : void 0, [reg, overrides]);
|
|
21
|
+
}
|
|
17
22
|
|
|
18
23
|
//#endregion
|
|
19
|
-
export { useActionTunings };
|
|
24
|
+
export { useActionTunings, useBindingOverrides };
|
|
20
25
|
//# sourceMappingURL=useActionTunings.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useActionTunings.js","names":[],"sources":["../../../src/canvas/SceneCanvas/useActionTunings.ts"],"sourcesContent":["import { useEffect } from 'react';\nimport type { ActionTuning, ActionTunings } from '@weasel-js/routing';\nimport { useActionsRegistry } from '@weasel-js/routing/react';\n\n/** Hold `tune`'s tunings on the registry in scope for as long as the object\n * is the same one. */\nexport function useActionTunings(tune: ActionTunings | undefined): void {\n const reg = useActionsRegistry();\n useEffect(() => {\n if (!reg || !tune) return;\n const releases = Object.entries(tune).flatMap(([id, t]) => (t ? [reg.tune(id, t as ActionTuning)] : []));\n return () => { for (const release of releases) release(); };\n }, [reg, tune]);\n}\n"],"mappings":";;;;;;AAMA,SAAgB,iBAAiB,MAAuC;CACtE,MAAM,MAAM,mBAAmB;CAC/B,gBAAgB;EACd,IAAI,CAAC,OAAO,CAAC,MAAM;EACnB,MAAM,WAAW,OAAO,QAAQ,IAAI,CAAC,CAAC,SAAS,CAAC,IAAI,OAAQ,IAAI,CAAC,IAAI,KAAK,IAAI,CAAiB,CAAC,IAAI,CAAC,CAAE;EACvG,aAAa;GAAE,KAAK,MAAM,WAAW,UAAU,QAAQ;EAAG;CAC5D,GAAG,CAAC,KAAK,IAAI,CAAC;AAChB"}
|
|
1
|
+
{"version":3,"file":"useActionTunings.js","names":[],"sources":["../../../src/canvas/SceneCanvas/useActionTunings.ts"],"sourcesContent":["import { useEffect } from 'react';\nimport type { ActionTuning, ActionTunings, BindingOverrides } from '@weasel-js/routing';\nimport { useActionsRegistry } from '@weasel-js/routing/react';\n\n/** Hold `tune`'s tunings on the registry in scope for as long as the object\n * is the same one. */\nexport function useActionTunings(tune: ActionTunings | undefined): void {\n const reg = useActionsRegistry();\n useEffect(() => {\n if (!reg || !tune) return;\n const releases = Object.entries(tune).flatMap(([id, t]) => (t ? [reg.tune(id, t as ActionTuning)] : []));\n return () => { for (const release of releases) release(); };\n }, [reg, tune]);\n}\n\n/** Hold `overrides` on the registry in scope for as long as the object is the same one. */\nexport function useBindingOverrides(overrides: BindingOverrides | undefined): void {\n const reg = useActionsRegistry();\n useEffect(() => (reg && overrides ? reg.overrideBindings(overrides) : undefined), [reg, overrides]);\n}\n"],"mappings":";;;;;;AAMA,SAAgB,iBAAiB,MAAuC;CACtE,MAAM,MAAM,mBAAmB;CAC/B,gBAAgB;EACd,IAAI,CAAC,OAAO,CAAC,MAAM;EACnB,MAAM,WAAW,OAAO,QAAQ,IAAI,CAAC,CAAC,SAAS,CAAC,IAAI,OAAQ,IAAI,CAAC,IAAI,KAAK,IAAI,CAAiB,CAAC,IAAI,CAAC,CAAE;EACvG,aAAa;GAAE,KAAK,MAAM,WAAW,UAAU,QAAQ;EAAG;CAC5D,GAAG,CAAC,KAAK,IAAI,CAAC;AAChB;;AAGA,SAAgB,oBAAoB,WAA+C;CACjF,MAAM,MAAM,mBAAmB;CAC/B,gBAAiB,OAAO,YAAY,IAAI,iBAAiB,SAAS,IAAI,QAAY,CAAC,KAAK,SAAS,CAAC;AACpG"}
|
|
@@ -51,6 +51,7 @@ function useCanvasDevtoolsRoot(c) {
|
|
|
51
51
|
contributions: c.contributions,
|
|
52
52
|
actions,
|
|
53
53
|
deps,
|
|
54
|
+
...actions ? { overrides: actions.bindingOverrides() } : {},
|
|
54
55
|
routingKinds: (c.routing ?? inferredNodeRouting).map((k) => k.name)
|
|
55
56
|
}),
|
|
56
57
|
subscribeRegistry(listener) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCanvasDevtoolsRoot.js","names":[],"sources":["../../../src/canvas/SceneCanvas/useCanvasDevtoolsRoot.ts"],"sourcesContent":["import { useEffect, useState, type MutableRefObject, type RefObject } from 'react';\nimport {\n createDispatchTrace, createTraceRing, type Contribution, type DepRegistry, type DispatchTrace, type TraceRing,\n} from '@weasel-js/routing';\nimport { useActionsRegistry, useDepRegistry } from '@weasel-js/routing/react';\nimport type { NodeRoutingEntry } from 'core/scene/NodeRouting';\nimport type { NodeId, Scene } from 'core/scene/types';\nimport type { SelectionApi } from 'core/selection/useSelection';\nimport type { DebugConfig } from '../../debug/types';\nimport { parseDebugFlags } from '../../debug/parseDebugFlags';\nimport { recordHistory } from '../../devtools/history';\nimport type { DevtoolsHistoryRecord } from '../../devtools/protocol';\nimport { useDevtoolsLabel } from '../../devtools/label';\nimport { describeRegistry } from '../../devtools/registry';\nimport type { DevtoolsRoot } from '../../devtools/root';\nimport { useDevtoolsRoot, type DevtoolsOverride } from '../../devtools/useDevtoolsRoot';\nimport type { ToolsApi } from '../../tools/overlayBinding';\nimport type { CanvasExtensionApi } from '../canvasExtension';\nimport type { CanvasViewState } from '../useCanvasView';\nimport type { CoordTraceEntry } from './coordTrace';\nimport { inferredNodeRouting } from './defaultNodeRouting';\n\n/** A canvas's trace buffers: its dispatchers' decisions, its `clientToWorld` conversions, and its scene's history steps. */\nexport interface CanvasTraces {\n dispatch: DispatchTrace;\n coords: TraceRing<CoordTraceEntry>;\n history: TraceRing<DevtoolsHistoryRecord>;\n}\n\n/** What `<SceneCanvas>` hands its input wiring for devtools: the `debug` in effect and the override's setter. */\nexport interface CanvasDevtools {\n debug: DebugConfig | false | undefined;\n setOverride(override: DevtoolsOverride | null): void;\n}\n\nfunction resolved(debug: DebugConfig | false | undefined): DebugConfig | null {\n if (debug === false) return null;\n if (debug !== undefined) return debug;\n return typeof window === 'undefined' ? null : parseDebugFlags(window.location.search);\n}\n\n/** What `get-registry` reports of `deps`, as one comparable string; null for a registry that cannot list itself. */\nfunction depListingKey(deps: DepRegistry): string | null {\n return deps.list?.().map((d) => `${d.name}:${d.depth}:${d.stacked}`).join('\\u0000') ?? null;\n}\n\n/** This canvas's trace buffers and its devtools root, read from the latest render. */\nexport function useCanvasDevtoolsRoot(c: {\n scene: Scene<unknown, string, unknown>;\n selection: SelectionApi;\n viewState: CanvasViewState;\n tools: ToolsApi;\n /** The entries the canvas's `features` presets brought beside its tools. */\n contributions: readonly Contribution<unknown>[];\n routing: readonly NodeRoutingEntry[] | undefined;\n element: MutableRefObject<HTMLElement | null>;\n api: RefObject<CanvasExtensionApi | null>;\n devtools: CanvasDevtools;\n}): { root: DevtoolsRoot; traces: CanvasTraces } {\n const [traces] = useState<CanvasTraces>(() => ({\n dispatch: createDispatchTrace(),\n coords: createTraceRing<CoordTraceEntry>(),\n history: createTraceRing<DevtoolsHistoryRecord>(),\n }));\n const actions = useActionsRegistry();\n const deps = useDepRegistry();\n const framed = useDevtoolsLabel();\n const root = useDevtoolsRoot({\n label: () => {\n const el = c.element.current;\n return el?.getAttribute('aria-label') || el?.id || framed\n || el?.parentElement?.closest('[aria-label]')?.getAttribute('aria-label') || '';\n },\n scene: () => c.scene,\n selection: () => c.selection.current,\n select: (ids) => c.selection.set([...ids] as NodeId[]),\n view: () => c.viewState.getView(),\n activeTool: () => c.tools.active ?? null,\n tools: () => Object.keys(c.tools.registry),\n setTool: (id) => c.tools.setActive(id),\n trace: traces.dispatch,\n coords: traces.coords,\n frame: () => c.api.current?.getDebug()?.snapshot().frame ?? null,\n overlays: () => resolved(c.devtools.debug),\n setOverride: (o) => c.devtools.setOverride(o),\n subscribeView: c.viewState.subscribeView,\n registry: () => describeRegistry({\n tools: c.tools, contributions: c.contributions, actions, deps,\n routingKinds: (c.routing ?? inferredNodeRouting).map((k) => k.name),\n }),\n subscribeRegistry(listener) {\n // The dep registry also notifies when a source's value changes, as every selection does; only the listing is ours.\n let listed = depListingKey(deps);\n const onDeps = (): void => {\n const next = depListingKey(deps);\n if (next !== null && next === listed) return;\n listed = next;\n listener();\n };\n const offs = [actions?.subscribe(listener), deps.subscribe?.(onDeps)];\n return () => { for (const off of offs) off?.(); };\n },\n history: traces.history,\n }, { selection: c.selection.current, tool: c.tools.active ?? null });\n useEffect(() => recordHistory(c.scene.history, traces.history), [c.scene, traces.history]);\n const { registry, ambient } = c.tools;\n useEffect(() => { root.notify('registry'); }, [root, registry, ambient, c.contributions, c.routing]);\n return { root, traces };\n}\n"],"mappings":";;;;;;;;;;;AAmCA,SAAS,SAAS,OAA4D;CAC5E,IAAI,UAAU,OAAO,OAAO;CAC5B,IAAI,UAAU,QAAW,OAAO;CAChC,OAAO,OAAO,WAAW,cAAc,OAAO,gBAAgB,OAAO,SAAS,MAAM;AACtF;;AAGA,SAAS,cAAc,MAAkC;CACvD,OAAO,KAAK,OAAO,CAAC,CAAC,KAAK,MAAM,GAAG,EAAE,KAAK,GAAG,EAAE,MAAM,GAAG,EAAE,SAAS,CAAC,CAAC,KAAK,IAAQ,KAAK;AACzF;;AAGA,SAAgB,sBAAsB,GAWW;CAC/C,MAAM,CAAC,UAAU,gBAA8B;EAC7C,UAAU,oBAAoB;EAC9B,QAAQ,gBAAiC;EACzC,SAAS,gBAAuC;CAClD,EAAE;CACF,MAAM,UAAU,mBAAmB;CACnC,MAAM,OAAO,eAAe;CAC5B,MAAM,SAAS,iBAAiB;CAChC,MAAM,OAAO,gBAAgB;EAC3B,aAAa;GACX,MAAM,KAAK,EAAE,QAAQ;GACrB,OAAO,IAAI,aAAa,YAAY,KAAK,IAAI,MAAM,UAC9C,IAAI,eAAe,QAAQ,cAAc,CAAC,EAAE,aAAa,YAAY,KAAK;EACjF;EACA,aAAa,EAAE;EACf,iBAAiB,EAAE,UAAU;EAC7B,SAAS,QAAQ,EAAE,UAAU,IAAI,CAAC,GAAG,GAAG,CAAa;EACrD,YAAY,EAAE,UAAU,QAAQ;EAChC,kBAAkB,EAAE,MAAM,UAAU;EACpC,aAAa,OAAO,KAAK,EAAE,MAAM,QAAQ;EACzC,UAAU,OAAO,EAAE,MAAM,UAAU,EAAE;EACrC,OAAO,OAAO;EACd,QAAQ,OAAO;EACf,aAAa,EAAE,IAAI,SAAS,SAAS,CAAC,EAAE,SAAS,CAAC,CAAC,SAAS;EAC5D,gBAAgB,SAAS,EAAE,SAAS,KAAK;EACzC,cAAc,MAAM,EAAE,SAAS,YAAY,CAAC;EAC5C,eAAe,EAAE,UAAU;EAC3B,gBAAgB,iBAAiB;GAC/B,OAAO,EAAE;GAAO,eAAe,EAAE;GAAe;GAAS;GACzD,eAAe,EAAE,WAAW,oBAAA,CAAqB,KAAK,MAAM,EAAE,IAAI;EACpE,CAAC;EACD,kBAAkB,UAAU;GAE1B,IAAI,SAAS,cAAc,IAAI;GAC/B,MAAM,eAAqB;IACzB,MAAM,OAAO,cAAc,IAAI;IAC/B,IAAI,SAAS,QAAQ,SAAS,QAAQ;IACtC,SAAS;IACT,SAAS;GACX;GACA,MAAM,OAAO,CAAC,SAAS,UAAU,QAAQ,GAAG,KAAK,YAAY,MAAM,CAAC;GACpE,aAAa;IAAE,KAAK,MAAM,OAAO,MAAM,MAAM;GAAG;EAClD;EACA,SAAS,OAAO;CAClB,GAAG;EAAE,WAAW,EAAE,UAAU;EAAS,MAAM,EAAE,MAAM,UAAU;CAAK,CAAC;CACnE,gBAAgB,cAAc,EAAE,MAAM,SAAS,OAAO,OAAO,GAAG,CAAC,EAAE,OAAO,OAAO,OAAO,CAAC;CACzF,MAAM,EAAE,UAAU,YAAY,EAAE;CAChC,gBAAgB;EAAE,KAAK,OAAO,UAAU;CAAG,GAAG;EAAC;EAAM;EAAU;EAAS,EAAE;EAAe,EAAE;CAAO,CAAC;CACnG,OAAO;EAAE;EAAM;CAAO;AACxB"}
|
|
1
|
+
{"version":3,"file":"useCanvasDevtoolsRoot.js","names":[],"sources":["../../../src/canvas/SceneCanvas/useCanvasDevtoolsRoot.ts"],"sourcesContent":["import { useEffect, useState, type MutableRefObject, type RefObject } from 'react';\nimport {\n createDispatchTrace, createTraceRing, type Contribution, type DepRegistry, type DispatchTrace, type TraceRing,\n} from '@weasel-js/routing';\nimport { useActionsRegistry, useDepRegistry } from '@weasel-js/routing/react';\nimport type { NodeRoutingEntry } from 'core/scene/NodeRouting';\nimport type { NodeId, Scene } from 'core/scene/types';\nimport type { SelectionApi } from 'core/selection/useSelection';\nimport type { DebugConfig } from '../../debug/types';\nimport { parseDebugFlags } from '../../debug/parseDebugFlags';\nimport { recordHistory } from '../../devtools/history';\nimport type { DevtoolsHistoryRecord } from '../../devtools/protocol';\nimport { useDevtoolsLabel } from '../../devtools/label';\nimport { describeRegistry } from '../../devtools/registry';\nimport type { DevtoolsRoot } from '../../devtools/root';\nimport { useDevtoolsRoot, type DevtoolsOverride } from '../../devtools/useDevtoolsRoot';\nimport type { ToolsApi } from '../../tools/overlayBinding';\nimport type { CanvasExtensionApi } from '../canvasExtension';\nimport type { CanvasViewState } from '../useCanvasView';\nimport type { CoordTraceEntry } from './coordTrace';\nimport { inferredNodeRouting } from './defaultNodeRouting';\n\n/** A canvas's trace buffers: its dispatchers' decisions, its `clientToWorld` conversions, and its scene's history steps. */\nexport interface CanvasTraces {\n dispatch: DispatchTrace;\n coords: TraceRing<CoordTraceEntry>;\n history: TraceRing<DevtoolsHistoryRecord>;\n}\n\n/** What `<SceneCanvas>` hands its input wiring for devtools: the `debug` in effect and the override's setter. */\nexport interface CanvasDevtools {\n debug: DebugConfig | false | undefined;\n setOverride(override: DevtoolsOverride | null): void;\n}\n\nfunction resolved(debug: DebugConfig | false | undefined): DebugConfig | null {\n if (debug === false) return null;\n if (debug !== undefined) return debug;\n return typeof window === 'undefined' ? null : parseDebugFlags(window.location.search);\n}\n\n/** What `get-registry` reports of `deps`, as one comparable string; null for a registry that cannot list itself. */\nfunction depListingKey(deps: DepRegistry): string | null {\n return deps.list?.().map((d) => `${d.name}:${d.depth}:${d.stacked}`).join('\\u0000') ?? null;\n}\n\n/** This canvas's trace buffers and its devtools root, read from the latest render. */\nexport function useCanvasDevtoolsRoot(c: {\n scene: Scene<unknown, string, unknown>;\n selection: SelectionApi;\n viewState: CanvasViewState;\n tools: ToolsApi;\n /** The entries the canvas's `features` presets brought beside its tools. */\n contributions: readonly Contribution<unknown>[];\n routing: readonly NodeRoutingEntry[] | undefined;\n element: MutableRefObject<HTMLElement | null>;\n api: RefObject<CanvasExtensionApi | null>;\n devtools: CanvasDevtools;\n}): { root: DevtoolsRoot; traces: CanvasTraces } {\n const [traces] = useState<CanvasTraces>(() => ({\n dispatch: createDispatchTrace(),\n coords: createTraceRing<CoordTraceEntry>(),\n history: createTraceRing<DevtoolsHistoryRecord>(),\n }));\n const actions = useActionsRegistry();\n const deps = useDepRegistry();\n const framed = useDevtoolsLabel();\n const root = useDevtoolsRoot({\n label: () => {\n const el = c.element.current;\n return el?.getAttribute('aria-label') || el?.id || framed\n || el?.parentElement?.closest('[aria-label]')?.getAttribute('aria-label') || '';\n },\n scene: () => c.scene,\n selection: () => c.selection.current,\n select: (ids) => c.selection.set([...ids] as NodeId[]),\n view: () => c.viewState.getView(),\n activeTool: () => c.tools.active ?? null,\n tools: () => Object.keys(c.tools.registry),\n setTool: (id) => c.tools.setActive(id),\n trace: traces.dispatch,\n coords: traces.coords,\n frame: () => c.api.current?.getDebug()?.snapshot().frame ?? null,\n overlays: () => resolved(c.devtools.debug),\n setOverride: (o) => c.devtools.setOverride(o),\n subscribeView: c.viewState.subscribeView,\n registry: () => describeRegistry({\n tools: c.tools, contributions: c.contributions, actions, deps,\n ...(actions ? { overrides: actions.bindingOverrides() } : {}),\n routingKinds: (c.routing ?? inferredNodeRouting).map((k) => k.name),\n }),\n subscribeRegistry(listener) {\n // The dep registry also notifies when a source's value changes, as every selection does; only the listing is ours.\n let listed = depListingKey(deps);\n const onDeps = (): void => {\n const next = depListingKey(deps);\n if (next !== null && next === listed) return;\n listed = next;\n listener();\n };\n const offs = [actions?.subscribe(listener), deps.subscribe?.(onDeps)];\n return () => { for (const off of offs) off?.(); };\n },\n history: traces.history,\n }, { selection: c.selection.current, tool: c.tools.active ?? null });\n useEffect(() => recordHistory(c.scene.history, traces.history), [c.scene, traces.history]);\n const { registry, ambient } = c.tools;\n useEffect(() => { root.notify('registry'); }, [root, registry, ambient, c.contributions, c.routing]);\n return { root, traces };\n}\n"],"mappings":";;;;;;;;;;;AAmCA,SAAS,SAAS,OAA4D;CAC5E,IAAI,UAAU,OAAO,OAAO;CAC5B,IAAI,UAAU,QAAW,OAAO;CAChC,OAAO,OAAO,WAAW,cAAc,OAAO,gBAAgB,OAAO,SAAS,MAAM;AACtF;;AAGA,SAAS,cAAc,MAAkC;CACvD,OAAO,KAAK,OAAO,CAAC,CAAC,KAAK,MAAM,GAAG,EAAE,KAAK,GAAG,EAAE,MAAM,GAAG,EAAE,SAAS,CAAC,CAAC,KAAK,IAAQ,KAAK;AACzF;;AAGA,SAAgB,sBAAsB,GAWW;CAC/C,MAAM,CAAC,UAAU,gBAA8B;EAC7C,UAAU,oBAAoB;EAC9B,QAAQ,gBAAiC;EACzC,SAAS,gBAAuC;CAClD,EAAE;CACF,MAAM,UAAU,mBAAmB;CACnC,MAAM,OAAO,eAAe;CAC5B,MAAM,SAAS,iBAAiB;CAChC,MAAM,OAAO,gBAAgB;EAC3B,aAAa;GACX,MAAM,KAAK,EAAE,QAAQ;GACrB,OAAO,IAAI,aAAa,YAAY,KAAK,IAAI,MAAM,UAC9C,IAAI,eAAe,QAAQ,cAAc,CAAC,EAAE,aAAa,YAAY,KAAK;EACjF;EACA,aAAa,EAAE;EACf,iBAAiB,EAAE,UAAU;EAC7B,SAAS,QAAQ,EAAE,UAAU,IAAI,CAAC,GAAG,GAAG,CAAa;EACrD,YAAY,EAAE,UAAU,QAAQ;EAChC,kBAAkB,EAAE,MAAM,UAAU;EACpC,aAAa,OAAO,KAAK,EAAE,MAAM,QAAQ;EACzC,UAAU,OAAO,EAAE,MAAM,UAAU,EAAE;EACrC,OAAO,OAAO;EACd,QAAQ,OAAO;EACf,aAAa,EAAE,IAAI,SAAS,SAAS,CAAC,EAAE,SAAS,CAAC,CAAC,SAAS;EAC5D,gBAAgB,SAAS,EAAE,SAAS,KAAK;EACzC,cAAc,MAAM,EAAE,SAAS,YAAY,CAAC;EAC5C,eAAe,EAAE,UAAU;EAC3B,gBAAgB,iBAAiB;GAC/B,OAAO,EAAE;GAAO,eAAe,EAAE;GAAe;GAAS;GACzD,GAAI,UAAU,EAAE,WAAW,QAAQ,iBAAiB,EAAE,IAAI,CAAC;GAC3D,eAAe,EAAE,WAAW,oBAAA,CAAqB,KAAK,MAAM,EAAE,IAAI;EACpE,CAAC;EACD,kBAAkB,UAAU;GAE1B,IAAI,SAAS,cAAc,IAAI;GAC/B,MAAM,eAAqB;IACzB,MAAM,OAAO,cAAc,IAAI;IAC/B,IAAI,SAAS,QAAQ,SAAS,QAAQ;IACtC,SAAS;IACT,SAAS;GACX;GACA,MAAM,OAAO,CAAC,SAAS,UAAU,QAAQ,GAAG,KAAK,YAAY,MAAM,CAAC;GACpE,aAAa;IAAE,KAAK,MAAM,OAAO,MAAM,MAAM;GAAG;EAClD;EACA,SAAS,OAAO;CAClB,GAAG;EAAE,WAAW,EAAE,UAAU;EAAS,MAAM,EAAE,MAAM,UAAU;CAAK,CAAC;CACnE,gBAAgB,cAAc,EAAE,MAAM,SAAS,OAAO,OAAO,GAAG,CAAC,EAAE,OAAO,OAAO,OAAO,CAAC;CACzF,MAAM,EAAE,UAAU,YAAY,EAAE;CAChC,gBAAgB;EAAE,KAAK,OAAO,UAAU;CAAG,GAAG;EAAC;EAAM;EAAU;EAAS,EAAE;EAAe,EAAE;CAAO,CAAC;CACnG,OAAO;EAAE;EAAM;CAAO;AACxB"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createViewMarkerContribution } from "../../features/viewports/viewMarkerDrag.js";
|
|
1
2
|
import { createLongPressFeedbackContribution } from "../../features/longPress/longPressFeedback.js";
|
|
2
3
|
import { useTools } from "../../tools/overlayBinding.js";
|
|
3
4
|
import { presetActions, presetAmbient, presetContributions, resolveFeatures } from "./features.js";
|
|
@@ -52,14 +53,18 @@ function useCanvasTools(args) {
|
|
|
52
53
|
const defaultPressFeedback = useMemo(() => createLongPressFeedbackContribution(), []);
|
|
53
54
|
const feedbackOpt = args.pressFeedback ?? true;
|
|
54
55
|
const pressFeedback = feedbackOpt === true ? defaultPressFeedback : feedbackOpt || null;
|
|
56
|
+
const defaultMarkerDrag = useMemo(() => createViewMarkerContribution(), []);
|
|
57
|
+
const markerDrag = args.actions === null ? null : defaultMarkerDrag;
|
|
55
58
|
const mergedAmbient = useMemo(() => [
|
|
56
59
|
...presetContributions(enabled),
|
|
57
60
|
...ambient ?? [],
|
|
58
|
-
...pressFeedback ? [pressFeedback] : []
|
|
61
|
+
...pressFeedback ? [pressFeedback] : [],
|
|
62
|
+
...markerDrag ? [markerDrag] : []
|
|
59
63
|
], [
|
|
60
64
|
enabled,
|
|
61
65
|
ambient,
|
|
62
|
-
pressFeedback
|
|
66
|
+
pressFeedback,
|
|
67
|
+
markerDrag
|
|
63
68
|
]);
|
|
64
69
|
const internal = useTools({
|
|
65
70
|
active: initialActiveTool ?? ("select" in registry ? "select" : null),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCanvasTools.js","names":[],"sources":["../../../src/canvas/SceneCanvas/useCanvasTools.ts"],"sourcesContent":["import { useEffect, useMemo } from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport type { Action } from '@weasel-js/routing';\nimport { createLongPressFeedbackContribution } from '../../features/longPress/longPressFeedback';\nimport { useTools, type Contribution, type Tool, type ToolsApi } from '../../tools/overlayBinding';\nimport {\n presetActions,\n presetAmbient,\n presetContributions,\n resolveFeatures,\n type EnabledPresets,\n type FeatureBindingOptions,\n type FeatureEntry,\n type NamedPresets,\n} from './features';\nimport type { SceneCanvasProps } from './props';\nimport type { ToolsPatch } from './toolRegistry';\nimport { useBuiltinTools } from './useBuiltinTools';\n\ntype Props = SceneCanvasProps<unknown, string, unknown>;\n\n/** Discriminate the polymorphic `tools` prop: `ToolsApi` has `setActive`\n * / `active` / `registry`; the patch record shape has none of those. */\nfunction isToolsApi(tools: ToolsApi | ToolsPatch): tools is ToolsApi {\n return typeof (tools as ToolsApi).setActive === 'function';\n}\n\nconst presetSerials = new WeakMap<object, number>();\nlet nextSerial = 0;\nconst entryKey = (e: FeatureEntry): string => {\n if (typeof e === 'string') return e;\n let n = presetSerials.get(e);\n if (n === undefined) presetSerials.set(e, (n = nextSerial++));\n return `#${n}`;\n};\n\n/** The presets a canvas runs with. Keyed on the entries, not the array, so\n * an inline `features={['draw', pathTools]}` does not re-derive every render. */\nexport function useEnabledFeatures(\n features: readonly FeatureEntry[] | undefined,\n named: NamedPresets | null,\n hasViewport: boolean,\n selectRotates: boolean,\n): EnabledPresets {\n const featuresKey = (features ?? []).map(entryKey).join('|');\n return useMemo(\n () => resolveFeatures(features, named, { viewport: hasViewport, selectRotates }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [featuresKey, named, hasViewport, selectRotates],\n );\n}\n\n/**\n * The canvas's tools: a caller's own `ToolsApi` when the `tools` prop is one\n * (the takeover form), and otherwise the built-ins the presets and\n * `defaultTools` name, patched by a `tools` record.\n */\nexport function useCanvasTools(args: {\n /** The canvas's select tool, which is built from its scene and pickers. */\n select: Tool;\n enabled: EnabledPresets;\n tools: Props['tools'];\n defaultTools: Props['defaultTools'];\n toolOptions: Props['toolOptions'];\n /** `viewport.inertia`, which is the hand tool's. */\n inertia: NonNullable<Props['viewport']>['inertia'];\n ambient: Props['ambient'];\n /** `longPress.feedback`. Default true, which installs the kit's own. */\n pressFeedback: NonNullable<Props['longPress']>['feedback'];\n initialActiveTool: Props['initialActiveTool'];\n modes: Props['modes'];\n onToolsCreated: Props['onToolsCreated'];\n}): {\n /** What the canvas routes input through: the takeover, or `internal`. */\n tools: ToolsApi;\n /** The canvas's own api, built whether or not a takeover replaces it. */\n internal: ToolsApi;\n takeover: ToolsApi | null;\n} {\n const { select, enabled, tools: toolsProp, defaultTools, toolOptions, inertia, ambient, initialActiveTool, modes, onToolsCreated } = args;\n\n const takeover = toolsProp && isToolsApi(toolsProp) ? toolsProp : null;\n const registry = useBuiltinTools({\n select,\n enabled,\n defaultTools,\n patch: toolsProp && !isToolsApi(toolsProp) ? toolsProp : null,\n toolOptions,\n inertia,\n });\n\n const defaultPressFeedback = useMemo(() => createLongPressFeedbackContribution(), []);\n const feedbackOpt = args.pressFeedback ?? true;\n const pressFeedback = feedbackOpt === true ? defaultPressFeedback : feedbackOpt || null;\n const mergedAmbient = useMemo(\n () => [...presetContributions(enabled), ...(ambient ?? [])
|
|
1
|
+
{"version":3,"file":"useCanvasTools.js","names":[],"sources":["../../../src/canvas/SceneCanvas/useCanvasTools.ts"],"sourcesContent":["import { useEffect, useMemo } from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport type { Action } from '@weasel-js/routing';\nimport { createLongPressFeedbackContribution } from '../../features/longPress/longPressFeedback';\nimport { createViewMarkerContribution } from '../../features/viewports/viewMarkerDrag';\nimport { useTools, type Contribution, type Tool, type ToolsApi } from '../../tools/overlayBinding';\nimport {\n presetActions,\n presetAmbient,\n presetContributions,\n resolveFeatures,\n type EnabledPresets,\n type FeatureBindingOptions,\n type FeatureEntry,\n type NamedPresets,\n} from './features';\nimport type { SceneCanvasProps } from './props';\nimport type { ToolsPatch } from './toolRegistry';\nimport { useBuiltinTools } from './useBuiltinTools';\n\ntype Props = SceneCanvasProps<unknown, string, unknown>;\n\n/** Discriminate the polymorphic `tools` prop: `ToolsApi` has `setActive`\n * / `active` / `registry`; the patch record shape has none of those. */\nfunction isToolsApi(tools: ToolsApi | ToolsPatch): tools is ToolsApi {\n return typeof (tools as ToolsApi).setActive === 'function';\n}\n\nconst presetSerials = new WeakMap<object, number>();\nlet nextSerial = 0;\nconst entryKey = (e: FeatureEntry): string => {\n if (typeof e === 'string') return e;\n let n = presetSerials.get(e);\n if (n === undefined) presetSerials.set(e, (n = nextSerial++));\n return `#${n}`;\n};\n\n/** The presets a canvas runs with. Keyed on the entries, not the array, so\n * an inline `features={['draw', pathTools]}` does not re-derive every render. */\nexport function useEnabledFeatures(\n features: readonly FeatureEntry[] | undefined,\n named: NamedPresets | null,\n hasViewport: boolean,\n selectRotates: boolean,\n): EnabledPresets {\n const featuresKey = (features ?? []).map(entryKey).join('|');\n return useMemo(\n () => resolveFeatures(features, named, { viewport: hasViewport, selectRotates }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [featuresKey, named, hasViewport, selectRotates],\n );\n}\n\n/**\n * The canvas's tools: a caller's own `ToolsApi` when the `tools` prop is one\n * (the takeover form), and otherwise the built-ins the presets and\n * `defaultTools` name, patched by a `tools` record.\n */\nexport function useCanvasTools(args: {\n /** The canvas's select tool, which is built from its scene and pickers. */\n select: Tool;\n enabled: EnabledPresets;\n tools: Props['tools'];\n defaultTools: Props['defaultTools'];\n toolOptions: Props['toolOptions'];\n /** `viewport.inertia`, which is the hand tool's. */\n inertia: NonNullable<Props['viewport']>['inertia'];\n ambient: Props['ambient'];\n /** `null` leaves out the kit's default actions, the marker drag among them. */\n actions: Props['actions'];\n /** `longPress.feedback`. Default true, which installs the kit's own. */\n pressFeedback: NonNullable<Props['longPress']>['feedback'];\n initialActiveTool: Props['initialActiveTool'];\n modes: Props['modes'];\n onToolsCreated: Props['onToolsCreated'];\n}): {\n /** What the canvas routes input through: the takeover, or `internal`. */\n tools: ToolsApi;\n /** The canvas's own api, built whether or not a takeover replaces it. */\n internal: ToolsApi;\n takeover: ToolsApi | null;\n} {\n const { select, enabled, tools: toolsProp, defaultTools, toolOptions, inertia, ambient, initialActiveTool, modes, onToolsCreated } = args;\n\n const takeover = toolsProp && isToolsApi(toolsProp) ? toolsProp : null;\n const registry = useBuiltinTools({\n select,\n enabled,\n defaultTools,\n patch: toolsProp && !isToolsApi(toolsProp) ? toolsProp : null,\n toolOptions,\n inertia,\n });\n\n const defaultPressFeedback = useMemo(() => createLongPressFeedbackContribution(), []);\n const feedbackOpt = args.pressFeedback ?? true;\n const pressFeedback = feedbackOpt === true ? defaultPressFeedback : feedbackOpt || null;\n const defaultMarkerDrag = useMemo(() => createViewMarkerContribution(), []);\n const markerDrag = args.actions === null ? null : defaultMarkerDrag;\n const mergedAmbient = useMemo(\n () => [\n ...presetContributions(enabled), ...(ambient ?? []),\n ...(pressFeedback ? [pressFeedback] : []), ...(markerDrag ? [markerDrag] : []),\n ],\n [enabled, ambient, pressFeedback, markerDrag],\n );\n\n const internal = useTools({\n active: initialActiveTool ?? ('select' in registry ? 'select' : null),\n registry,\n ...(mergedAmbient.length ? { ambient: mergedAmbient } : {}),\n ...(modes ? { modes } : {}),\n });\n const tools = takeover ?? internal;\n\n const onToolsCreatedRef = useLatest(onToolsCreated);\n useEffect(() => { onToolsCreatedRef.current?.(tools); }, [tools, onToolsCreatedRef]);\n\n return { tools, internal, takeover };\n}\n\n/**\n * The presets' ambient bindings, and the standard actions the canvas\n * registers. The bindings are not tools and sit outside `tools`, so they apply\n * under a takeover too. An action is registered when an enabled preset brings\n * it, or when it is in `pool` and a tool or contribution on this canvas binds\n * it: a tool brings the actions it routes to, so `defaultTools={['rect']}`\n * works under any preset.\n */\nexport function useFeatureBindings(args: {\n enabled: EnabledPresets;\n /** Actions registered only when something binds them. */\n pool: readonly Action[];\n tools: Pick<ToolsApi, 'registry' | 'ambient'>;\n move: FeatureBindingOptions['move'];\n rotate: FeatureBindingOptions['rotate'];\n}): { contributions: Contribution[]; standardActions: Action[] } {\n const { enabled, pool, tools, move, rotate } = args;\n const contributions = useMemo<Contribution[]>(\n () => presetAmbient(enabled, { move, rotate }),\n [enabled, move, rotate],\n );\n const standardActions = useMemo(() => {\n const out = presetActions(enabled);\n const have = new Set(out.map((a) => a.id));\n const bound = new Set<string>();\n for (const entry of [...Object.values(tools.registry), ...tools.ambient, ...contributions]) {\n for (const b of entry.bindings ?? []) bound.add(b.actionId);\n }\n for (const a of pool) {\n if (bound.has(a.id) && !have.has(a.id)) {\n have.add(a.id);\n out.push(a);\n }\n }\n return out;\n }, [enabled, pool, tools, contributions]);\n return { contributions, standardActions };\n}\n\n"],"mappings":"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"}
|
|
@@ -26,7 +26,9 @@ function useAddedViews() {
|
|
|
26
26
|
w: 0,
|
|
27
27
|
h: 0
|
|
28
28
|
}
|
|
29
|
-
}, [surfaceViewRegistry])
|
|
29
|
+
}, [surfaceViewRegistry]),
|
|
30
|
+
aimOf: useCallback((id) => surfaceViewRegistry?.aimOf(id) ?? null, [surfaceViewRegistry]),
|
|
31
|
+
setAim: useCallback((id, aim) => surfaceViewRegistry?.setAim(id, aim) ?? false, [surfaceViewRegistry])
|
|
30
32
|
};
|
|
31
33
|
}
|
|
32
34
|
/**
|
|
@@ -35,7 +37,7 @@ function useAddedViews() {
|
|
|
35
37
|
* and keeps the canvas's own copies of the element and the handle current.
|
|
36
38
|
*/
|
|
37
39
|
function useSceneCanvasHandle(args) {
|
|
38
|
-
const { ref, elementRef, apiRef, onElement, ingest, viewAnimation, camera, addView, viewAt } = args;
|
|
40
|
+
const { ref, elementRef, apiRef, onElement, ingest, viewAnimation, camera, addView, viewAt, aimOf, setAim } = args;
|
|
39
41
|
const [ready, setReady] = useState(false);
|
|
40
42
|
return {
|
|
41
43
|
handleRef: useCallback((node) => {
|
|
@@ -49,7 +51,9 @@ function useSceneCanvasHandle(args) {
|
|
|
49
51
|
isViewAnimating: viewAnimation.isAnimating,
|
|
50
52
|
camera,
|
|
51
53
|
addView,
|
|
52
|
-
viewAt
|
|
54
|
+
viewAt,
|
|
55
|
+
aimOf,
|
|
56
|
+
setAim
|
|
53
57
|
} : null;
|
|
54
58
|
apiRef.current = extended;
|
|
55
59
|
setReady(extended !== null);
|
|
@@ -63,6 +67,8 @@ function useSceneCanvasHandle(args) {
|
|
|
63
67
|
camera,
|
|
64
68
|
addView,
|
|
65
69
|
viewAt,
|
|
70
|
+
aimOf,
|
|
71
|
+
setAim,
|
|
66
72
|
onElement
|
|
67
73
|
]),
|
|
68
74
|
ready
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSceneCanvasHandle.js","names":[],"sources":["../../../src/canvas/SceneCanvas/useSceneCanvasHandle.ts"],"sourcesContent":["import { useCallback, useState, type ForwardedRef, type MutableRefObject } from 'react';\nimport { assignRef } from '@weasel-js/react';\nimport type { ViewAnimationApi } from 'core/viewport/useViewAnimation';\nimport type { CanvasViewProps } from '../CanvasView';\nimport type { CanvasExtensionApi, CanvasViewHandle, SceneCanvasApi } from '../canvasExtension';\nimport { IDENTITY_VIEW, useOptionalViewRegistry } from '../viewRegistry';\n\n/** Views added through the handle rather than declared as props or children. */\nexport function useAddedViews(): {\n views: readonly CanvasViewProps[];\n addView: SceneCanvasApi['addView'];\n viewAt: SceneCanvasApi['viewAt'];\n} {\n const surfaceViewRegistry = useOptionalViewRegistry();\n const [views, setViews] = useState<readonly CanvasViewProps[]>([]);\n const addView = useCallback((props: CanvasViewProps): CanvasViewHandle => {\n setViews((list) => [...list.filter((v) => v.id !== props.id), props]);\n return {\n id: props.id,\n draw: (data, outer, dims) =>\n surfaceViewRegistry?.list().find((r) => r.id === props.id)?.layer.draw(data, outer, dims) ?? [],\n remove: () => setViews((list) => list.filter((v) => v !== props)),\n };\n }, [surfaceViewRegistry]);\n const viewAt = useCallback<SceneCanvasApi['viewAt']>((at, opts) => (\n surfaceViewRegistry?.viewAt(at, opts) ?? { id: null, view: IDENTITY_VIEW, rect: { x: 0, y: 0, w: 0, h: 0 } }\n ), [surfaceViewRegistry]);\n return { views, addView, viewAt };\n}\n\n/**\n * The ref callback `<SceneCanvas>` hands `<Canvas>`. It extends the\n * primitive's handle into the `SceneCanvasApi` the consumer's ref receives,\n * and keeps the canvas's own copies of the element and the handle current.\n */\nexport function useSceneCanvasHandle(args: {\n ref: ForwardedRef<SceneCanvasApi>;\n /** Written with the canvas element, for the input listeners. */\n elementRef: MutableRefObject<HTMLElement | null>;\n /** Written with the extended handle, for `requestRedraw` and point conversion. */\n apiRef: MutableRefObject<CanvasExtensionApi | null>;\n /** Told the element whenever it attaches or detaches. */\n onElement: (element: HTMLElement | null) => void;\n ingest: SceneCanvasApi['ingest'];\n viewAnimation: Pick<ViewAnimationApi, 'animate' | 'stop' | 'isAnimating'>;\n camera: SceneCanvasApi['camera'];\n addView: SceneCanvasApi['addView'];\n viewAt: SceneCanvasApi['viewAt'];\n}): {\n handleRef: (node: CanvasExtensionApi | null) => void;\n /** Whether the handle is attached, as state: an effect needing the handle\n * keys off this instead of assuming when React attaches it. */\n ready: boolean;\n} {\n const { ref, elementRef, apiRef, onElement, ingest, viewAnimation, camera, addView, viewAt } = args;\n const [ready, setReady] = useState(false);\n const handleRef = useCallback(\n (node: CanvasExtensionApi | null) => {\n elementRef.current = node?.element ?? null;\n onElement(elementRef.current);\n const extended: SceneCanvasApi | null = node\n ? {\n ...node,\n ingest,\n animateView: viewAnimation.animate,\n stopViewAnimation: viewAnimation.stop,\n isViewAnimating: viewAnimation.isAnimating,\n camera,\n addView,\n viewAt,\n }\n : null;\n apiRef.current = extended;\n setReady(extended !== null);\n assignRef(ref, extended);\n },\n [ref, elementRef, apiRef, ingest, viewAnimation, camera, addView, viewAt, onElement],\n );\n return { handleRef, ready };\n}\n"],"mappings":";;;;;;AAQA,SAAgB,
|
|
1
|
+
{"version":3,"file":"useSceneCanvasHandle.js","names":[],"sources":["../../../src/canvas/SceneCanvas/useSceneCanvasHandle.ts"],"sourcesContent":["import { useCallback, useState, type ForwardedRef, type MutableRefObject } from 'react';\nimport { assignRef } from '@weasel-js/react';\nimport type { ViewAnimationApi } from 'core/viewport/useViewAnimation';\nimport type { CanvasViewProps } from '../CanvasView';\nimport type { CanvasExtensionApi, CanvasViewHandle, SceneCanvasApi } from '../canvasExtension';\nimport { IDENTITY_VIEW, useOptionalViewRegistry } from '../viewRegistry';\n\n/** Views added through the handle rather than declared as props or children. */\nexport function useAddedViews(): {\n views: readonly CanvasViewProps[];\n addView: SceneCanvasApi['addView'];\n viewAt: SceneCanvasApi['viewAt'];\n aimOf: SceneCanvasApi['aimOf'];\n setAim: SceneCanvasApi['setAim'];\n} {\n const surfaceViewRegistry = useOptionalViewRegistry();\n const [views, setViews] = useState<readonly CanvasViewProps[]>([]);\n const addView = useCallback((props: CanvasViewProps): CanvasViewHandle => {\n setViews((list) => [...list.filter((v) => v.id !== props.id), props]);\n return {\n id: props.id,\n draw: (data, outer, dims) =>\n surfaceViewRegistry?.list().find((r) => r.id === props.id)?.layer.draw(data, outer, dims) ?? [],\n remove: () => setViews((list) => list.filter((v) => v !== props)),\n };\n }, [surfaceViewRegistry]);\n const viewAt = useCallback<SceneCanvasApi['viewAt']>((at, opts) => (\n surfaceViewRegistry?.viewAt(at, opts) ?? { id: null, view: IDENTITY_VIEW, rect: { x: 0, y: 0, w: 0, h: 0 } }\n ), [surfaceViewRegistry]);\n const aimOf = useCallback<SceneCanvasApi['aimOf']>(\n (id) => surfaceViewRegistry?.aimOf(id) ?? null, [surfaceViewRegistry]);\n const setAim = useCallback<SceneCanvasApi['setAim']>(\n (id, aim) => surfaceViewRegistry?.setAim(id, aim) ?? false, [surfaceViewRegistry]);\n return { views, addView, viewAt, aimOf, setAim };\n}\n\n/**\n * The ref callback `<SceneCanvas>` hands `<Canvas>`. It extends the\n * primitive's handle into the `SceneCanvasApi` the consumer's ref receives,\n * and keeps the canvas's own copies of the element and the handle current.\n */\nexport function useSceneCanvasHandle(args: {\n ref: ForwardedRef<SceneCanvasApi>;\n /** Written with the canvas element, for the input listeners. */\n elementRef: MutableRefObject<HTMLElement | null>;\n /** Written with the extended handle, for `requestRedraw` and point conversion. */\n apiRef: MutableRefObject<CanvasExtensionApi | null>;\n /** Told the element whenever it attaches or detaches. */\n onElement: (element: HTMLElement | null) => void;\n ingest: SceneCanvasApi['ingest'];\n viewAnimation: Pick<ViewAnimationApi, 'animate' | 'stop' | 'isAnimating'>;\n camera: SceneCanvasApi['camera'];\n addView: SceneCanvasApi['addView'];\n viewAt: SceneCanvasApi['viewAt'];\n aimOf: SceneCanvasApi['aimOf'];\n setAim: SceneCanvasApi['setAim'];\n}): {\n handleRef: (node: CanvasExtensionApi | null) => void;\n /** Whether the handle is attached, as state: an effect needing the handle\n * keys off this instead of assuming when React attaches it. */\n ready: boolean;\n} {\n const { ref, elementRef, apiRef, onElement, ingest, viewAnimation, camera, addView, viewAt, aimOf, setAim } = args;\n const [ready, setReady] = useState(false);\n const handleRef = useCallback(\n (node: CanvasExtensionApi | null) => {\n elementRef.current = node?.element ?? null;\n onElement(elementRef.current);\n const extended: SceneCanvasApi | null = node\n ? {\n ...node,\n ingest,\n animateView: viewAnimation.animate,\n stopViewAnimation: viewAnimation.stop,\n isViewAnimating: viewAnimation.isAnimating,\n camera,\n addView,\n viewAt,\n aimOf,\n setAim,\n }\n : null;\n apiRef.current = extended;\n setReady(extended !== null);\n assignRef(ref, extended);\n },\n [ref, elementRef, apiRef, ingest, viewAnimation, camera, addView, viewAt, aimOf, setAim, onElement],\n );\n return { handleRef, ready };\n}\n"],"mappings":";;;;;;AAQA,SAAgB,gBAMd;CACA,MAAM,sBAAsB,wBAAwB;CACpD,MAAM,CAAC,OAAO,YAAY,SAAqC,CAAC,CAAC;CAiBjE,OAAO;EAAE;EAAO,SAhBA,aAAa,UAA6C;GACxE,UAAU,SAAS,CAAC,GAAG,KAAK,QAAQ,MAAM,EAAE,OAAO,MAAM,EAAE,GAAG,KAAK,CAAC;GACpE,OAAO;IACL,IAAI,MAAM;IACV,OAAO,MAAM,OAAO,SAClB,qBAAqB,KAAK,CAAC,CAAC,MAAM,MAAM,EAAE,OAAO,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC;IAChG,cAAc,UAAU,SAAS,KAAK,QAAQ,MAAM,MAAM,KAAK,CAAC;GAClE;EACF,GAAG,CAAC,mBAAmB,CAQP;EAAS,QAPV,aAAuC,IAAI,SACxD,qBAAqB,OAAO,IAAI,IAAI,KAAK;GAAE,IAAI;GAAM,MAAM;GAAe,MAAM;IAAE,GAAG;IAAG,GAAG;IAAG,GAAG;IAAG,GAAG;GAAE;EAAE,GAC1G,CAAC,mBAAmB,CAKE;EAAQ,OAJnB,aACX,OAAO,qBAAqB,MAAM,EAAE,KAAK,MAAM,CAAC,mBAAmB,CAGrC;EAAO,QAFzB,aACZ,IAAI,QAAQ,qBAAqB,OAAO,IAAI,GAAG,KAAK,OAAO,CAAC,mBAAmB,CAC1C;CAAO;AACjD;;;;;;AAOA,SAAgB,qBAAqB,MAoBnC;CACA,MAAM,EAAE,KAAK,YAAY,QAAQ,WAAW,QAAQ,eAAe,QAAQ,SAAS,QAAQ,OAAO,WAAW;CAC9G,MAAM,CAAC,OAAO,YAAY,SAAS,KAAK;CAyBxC,OAAO;EAAE,WAxBS,aACf,SAAoC;GACnC,WAAW,UAAU,MAAM,WAAW;GACtC,UAAU,WAAW,OAAO;GAC5B,MAAM,WAAkC,OACpC;IACE,GAAG;IACH;IACA,aAAa,cAAc;IAC3B,mBAAmB,cAAc;IACjC,iBAAiB,cAAc;IAC/B;IACA;IACA;IACA;IACA;GACF,IACA;GACJ,OAAO,UAAU;GACjB,SAAS,aAAa,IAAI;GAC1B,UAAU,KAAK,QAAQ;EACzB,GACA;GAAC;GAAK;GAAY;GAAQ;GAAQ;GAAe;GAAQ;GAAS;GAAQ;GAAO;GAAQ;EAAS,CAE3F;EAAW;CAAM;AAC5B"}
|
|
@@ -9,6 +9,7 @@ import { mergeLayersWithDefaults } from "./mergeLayers.js";
|
|
|
9
9
|
import { useDispatcherOverlayLayer } from "./useDispatcherOverlayLayer.js";
|
|
10
10
|
import { usePreviewGhostLayer } from "./usePreviewGhostLayer.js";
|
|
11
11
|
import { useDepartureGhostLayer } from "./useDepartureGhostLayer.js";
|
|
12
|
+
import { useEffectSourcesLayer } from "../../features/pathEffects/useEffectSourcesLayer.js";
|
|
12
13
|
import { usePoseTransition } from "../../animation/poseTransition.js";
|
|
13
14
|
import { useSceneSlotLayers } from "./useSceneSlotLayers.js";
|
|
14
15
|
import { useSelectionOverlayLayer } from "./useSelectionOverlayLayer.js";
|
|
@@ -20,7 +21,8 @@ import { useMemo, useRef, useState } from "react";
|
|
|
20
21
|
* The layer map `<SceneCanvas>` hands `<Canvas>`: the caller's `layers` over
|
|
21
22
|
* the kit defaults, with the scene and selection-overlay slots built, and the
|
|
22
23
|
* canvas's own layers anchored around them. Removed nodes leave just above
|
|
23
|
-
* the scene, the preview ghost above them,
|
|
24
|
+
* the scene, the preview ghost above them, a selected path effect's hidden sources above that, the dispatcher's
|
|
25
|
+
* overlay (marquee, lasso) above those, and the
|
|
24
26
|
* anchor overlay above the selection chrome.
|
|
25
27
|
*/
|
|
26
28
|
function useSceneCanvasLayers(args) {
|
|
@@ -51,6 +53,7 @@ function useSceneCanvasLayers(args) {
|
|
|
51
53
|
sceneSlot,
|
|
52
54
|
departures
|
|
53
55
|
});
|
|
56
|
+
const effectSourcesLayer = useEffectSourcesLayer(scene);
|
|
54
57
|
const dispatcherOverlay = useDispatcherOverlayLayer({ dispatcher });
|
|
55
58
|
const hitRadiusPx = args.selectTool?.handleHitRadius ?? targetSizesPx(targetScale, baseSizes).handle;
|
|
56
59
|
const edges = args.selectTool?.resize?.edgeHandles ?? true;
|
|
@@ -93,9 +96,13 @@ function useSceneCanvasLayers(args) {
|
|
|
93
96
|
layer: previewLayer,
|
|
94
97
|
after: "departureGhost"
|
|
95
98
|
},
|
|
99
|
+
effectSources: {
|
|
100
|
+
layer: effectSourcesLayer,
|
|
101
|
+
after: "previewGhost"
|
|
102
|
+
},
|
|
96
103
|
dispatcherOverlay: {
|
|
97
104
|
layer: dispatcherOverlay,
|
|
98
|
-
after: "
|
|
105
|
+
after: "effectSources"
|
|
99
106
|
},
|
|
100
107
|
pathEditingOverlay: overlaySlot === null || isCustomEntry(overlaySlot) ? overlaySlot : {
|
|
101
108
|
layer: pathEditingOverlayLayer,
|
|
@@ -115,6 +122,7 @@ function useSceneCanvasLayers(args) {
|
|
|
115
122
|
selectionOverlayLayer,
|
|
116
123
|
departureLayer,
|
|
117
124
|
previewLayer,
|
|
125
|
+
effectSourcesLayer,
|
|
118
126
|
dispatcherOverlay,
|
|
119
127
|
pathEditingOverlayLayer,
|
|
120
128
|
overlaySlot,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSceneCanvasLayers.js","names":[],"sources":["../../../src/canvas/SceneCanvas/useSceneCanvasLayers.ts"],"sourcesContent":["import { useMemo, useRef, useState, type MutableRefObject } from 'react';\nimport type { ColorOverrideRegistry } from '@weasel-js/animator';\nimport type { Dispatcher } from '@weasel-js/routing';\nimport type { Affordance } from 'affordances/types';\nimport type { Node, NodeId, Scene } from 'core/scene/types';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport type { PlaneMap } from 'core/viewport/parallax';\nimport type { View } from 'core/viewport/view';\nimport type { PathEditState } from 'features/paths/pathEditState';\nimport { usePathEditOverlayLayer } from 'features/paths/usePathEdit';\nimport type { rotationBadgeOf } from 'features/selection/overlay';\nimport type { PoseDescriptor } from 'interactions/actions/resize/geometry';\nimport type { HierarchicalAdapter } from '../buildSceneTree';\nimport { builtLayersOf, isCustomEntry, type CanvasLayers } from '../layerSlots';\nimport { createSlopsDebugLayer } from '../slopsDebugLayer';\nimport { useTargetBasePx } from 'core/device/useTargetBasePx';\nimport { targetSizesPx } from 'core/device/targets';\nimport type { ResizeHandleLayout } from 'interactions/actions/resize/cornerHandles';\nimport { mergeLayersWithDefaults } from './mergeLayers';\nimport type { SceneCanvasLayers } from './props';\nimport { useDispatcherOverlayLayer } from './useDispatcherOverlayLayer';\nimport { usePreviewGhostLayer } from './usePreviewGhostLayer';\nimport { useDepartureGhostLayer } from './useDepartureGhostLayer';\nimport type { Departures } from '../../animation/departures';\nimport { usePoseTransition, type PoseTransitionSeat } from '../../animation/poseTransition';\nimport { useSceneSlotLayers } from './useSceneSlotLayers';\nimport { useSelectionOverlayLayer } from './useSelectionOverlayLayer';\nimport { drawnBoundsScene } from '../derivedPath';\nimport { createSnapOutcomeStore, type SnapOutcomeStore } from 'features/snap/outcomeStore';\nimport { createSnapReadoutLayer } from 'features/snap/readoutLayer';\nimport type { SnapReadoutStyle } from 'features/snap/types';\n\n/**\n * The layer map `<SceneCanvas>` hands `<Canvas>`: the caller's `layers` over\n * the kit defaults, with the scene and selection-overlay slots built, and the\n * canvas's own layers anchored around them. Removed nodes leave just above\n * the scene, the preview ghost above them, the dispatcher's overlay (marquee, lasso) above the ghost, and the\n * anchor overlay above the selection chrome.\n */\nexport function useSceneCanvasLayers<TData, TLayer extends string, TPose>(args: {\n /** `<SceneCanvas layers>`. Omitted, the result is the full default set. */\n layers: SceneCanvasLayers<Node<TData, TLayer, TPose>, TPose> | undefined;\n /** The device profile's, which sizes the default handles. */\n targetScale: number;\n scene: Scene<TData, TLayer, TPose>;\n adapter: HierarchicalAdapter<Node<TData, TLayer, TPose>, TPose>;\n poseDescriptor: PoseDescriptor<TPose>;\n alphaFor: ((id: string) => number) | undefined;\n colorOverrides: ColorOverrideRegistry | undefined;\n boundsOf: (id: string) => Bounds | null;\n planeOfNode: (id: string, view: View) => PlaneMap | null;\n /** `<SceneCanvas poseTransition>`, whose ghosts of removed nodes this draws. */\n poseTransition: PoseTransitionSeat<TPose> | null | undefined;\n /** Ids an in-flight gesture is ghosting. */\n previewIds: () => Iterable<string> | null;\n dispatcher: Dispatcher;\n /** Whether to mount the snap readout layer. */\n snapReadout: boolean;\n snapReadoutStyle?: SnapReadoutStyle;\n pathEditState: PathEditState;\n /** `debug.slops`: paint the hit regions of the chrome affordances. */\n slops: boolean;\n /** `geometry.boundsIncludeStroke`: whether chrome sits on the stroke. */\n boundsIncludeStroke: boolean;\n /** `selectTool`'s `handleHitRadius` and `resize.edgeHandles`, resolved here into\n * the one {@link ResizeHandleLayout} the overlay paints and the hit-test reads. */\n selectTool?: { handleHitRadius?: number; resize?: { edgeHandles?: boolean } };\n}): {\n layers: CanvasLayers;\n /** Where the selection overlay paints its rotate badge, or null. */\n rotationBadge: ReturnType<typeof rotationBadgeOf>;\n /** Which resize handles exist and their hit radius, for every hit-test of this canvas. */\n resizeHandles: ResizeHandleLayout;\n /** The affordances the gesture mounter hit-tests, which it publishes here\n * so the slops halos are the hit regions rather than a copy of them. */\n chromeAffordancesRef: MutableRefObject<readonly Affordance[]>;\n /** The marks the readout layer draws; the canvas's snap dep writes them. */\n snapStore: SnapOutcomeStore;\n /** The removed nodes the seat is seeing off. */\n departures: Departures<TPose, Node<TData, TLayer, TPose>>;\n} {\n const { scene, adapter, poseDescriptor, alphaFor, dispatcher, pathEditState, slops, targetScale } = args;\n\n const baseSizes = useTargetBasePx();\n const merged = useMemo(\n () => mergeLayersWithDefaults(args.layers, targetScale, baseSizes),\n [args.layers, targetScale, baseSizes],\n );\n\n const { config: sceneSlot, layers: sceneLayers } = useSceneSlotLayers({\n slot: merged.scene,\n scene,\n adapter,\n alphaFor,\n colorOverrides: args.colorOverrides,\n boundsOf: args.boundsOf,\n previewIds: args.previewIds,\n });\n // Takes the scene-wired slot, so a ghosted derived node still resolves its path.\n const previewLayer = usePreviewGhostLayer<TData, TLayer, TPose>({ scene, sceneSlot, dispatcher });\n const departures = usePoseTransition(scene, args.poseTransition);\n const departureLayer = useDepartureGhostLayer({ scene, sceneSlot, departures });\n const dispatcherOverlay = useDispatcherOverlayLayer({ dispatcher });\n\n const hitRadiusPx = args.selectTool?.handleHitRadius ?? targetSizesPx(targetScale, baseSizes).handle;\n const edges = args.selectTool?.resize?.edgeHandles ?? true;\n const resizeHandles = useMemo<ResizeHandleLayout>(() => ({ edges, hitRadiusPx }), [edges, hitRadiusPx]);\n\n const chromeAffordancesRef = useRef<readonly Affordance[]>([]);\n const slopsLayer = useMemo(\n () => createSlopsDebugLayer({ getAffordances: () => chromeAffordancesRef.current }),\n [],\n );\n\n const [snapStore] = useState(() => createSnapOutcomeStore());\n // Keyed on the style's values, so an inline `readoutStyle` literal keeps the layer.\n const { color, gapColor, lineWidth } = args.snapReadoutStyle ?? {};\n const snapReadoutLayer = useMemo(() => createSnapReadoutLayer(snapStore, {\n ...(color !== undefined ? { color } : {}),\n ...(gapColor !== undefined ? { gapColor } : {}),\n ...(lineWidth !== undefined ? { lineWidth } : {}),\n }), [snapStore, color, gapColor, lineWidth]);\n const overlaySlot = merged.pathEditingOverlay;\n const overlayConfig = overlaySlot && !isCustomEntry(overlaySlot) ? overlaySlot : undefined;\n const overlayAnchor = overlaySlot === null ? 'dispatcherOverlay' : 'pathEditingOverlay';\n const pathEditingOverlayLayer = usePathEditOverlayLayer(pathEditState, {\n nodeOf: (id) => scene.get(id as NodeId),\n planeOfNode: args.planeOfNode,\n }, overlayConfig);\n\n const { layer: selectionOverlayLayer, rotationBadge } = useSelectionOverlayLayer<TPose>(\n merged.selectionOverlay,\n pathEditState.getEditingIds,\n poseDescriptor,\n args.boundsIncludeStroke ? drawnBoundsScene(scene) : undefined,\n resizeHandles,\n );\n\n const layers = useMemo<CanvasLayers>(() => ({\n ...builtLayersOf(merged),\n scene: sceneLayers,\n selectionOverlay: selectionOverlayLayer ? { layer: selectionOverlayLayer } : null,\n departureGhost: { layer: departureLayer, after: 'scene' },\n previewGhost: { layer: previewLayer, after: 'departureGhost' },\n dispatcherOverlay: { layer: dispatcherOverlay, after: 'previewGhost' },\n pathEditingOverlay: overlaySlot === null || isCustomEntry(overlaySlot)\n ? overlaySlot\n : { layer: pathEditingOverlayLayer, after: 'selectionOverlay' },\n ...(args.snapReadout ? { snapReadout: { layer: snapReadoutLayer, after: overlayAnchor } } : {}),\n ...(slops ? { slopsDebug: { layer: slopsLayer, after: args.snapReadout ? 'snapReadout' : overlayAnchor } } : {}),\n }), [\n merged, sceneLayers, selectionOverlayLayer, departureLayer, previewLayer, dispatcherOverlay, pathEditingOverlayLayer, overlaySlot, overlayAnchor,\n snapReadoutLayer, args.snapReadout, slops, slopsLayer,\n ]);\n\n return { layers, rotationBadge, resizeHandles, chromeAffordancesRef, snapStore, departures };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAuCA,SAAgB,qBAA0D,MAyCxE;CACA,MAAM,EAAE,OAAO,SAAS,gBAAgB,UAAU,YAAY,eAAe,OAAO,gBAAgB;CAEpG,MAAM,YAAY,gBAAgB;CAClC,MAAM,SAAS,cACP,wBAAwB,KAAK,QAAQ,aAAa,SAAS,GACjE;EAAC,KAAK;EAAQ;EAAa;CAAS,CACtC;CAEA,MAAM,EAAE,QAAQ,WAAW,QAAQ,gBAAgB,mBAAmB;EACpE,MAAM,OAAO;EACb;EACA;EACA;EACA,gBAAgB,KAAK;EACrB,UAAU,KAAK;EACf,YAAY,KAAK;CACnB,CAAC;CAED,MAAM,eAAe,qBAA2C;EAAE;EAAO;EAAW;CAAW,CAAC;CAChG,MAAM,aAAa,kBAAkB,OAAO,KAAK,cAAc;CAC/D,MAAM,iBAAiB,uBAAuB;EAAE;EAAO;EAAW;CAAW,CAAC;CAC9E,MAAM,oBAAoB,0BAA0B,EAAE,WAAW,CAAC;CAElE,MAAM,cAAc,KAAK,YAAY,mBAAmB,cAAc,aAAa,SAAS,CAAC,CAAC;CAC9F,MAAM,QAAQ,KAAK,YAAY,QAAQ,eAAe;CACtD,MAAM,gBAAgB,eAAmC;EAAE;EAAO;CAAY,IAAI,CAAC,OAAO,WAAW,CAAC;CAEtG,MAAM,uBAAuB,OAA8B,CAAC,CAAC;CAC7D,MAAM,aAAa,cACX,sBAAsB,EAAE,sBAAsB,qBAAqB,QAAQ,CAAC,GAClF,CAAC,CACH;CAEA,MAAM,CAAC,aAAa,eAAe,uBAAuB,CAAC;CAE3D,MAAM,EAAE,OAAO,UAAU,cAAc,KAAK,oBAAoB,CAAC;CACjE,MAAM,mBAAmB,cAAc,uBAAuB,WAAW;EACvE,GAAI,UAAU,SAAY,EAAE,MAAM,IAAI,CAAC;EACvC,GAAI,aAAa,SAAY,EAAE,SAAS,IAAI,CAAC;EAC7C,GAAI,cAAc,SAAY,EAAE,UAAU,IAAI,CAAC;CACjD,CAAC,GAAG;EAAC;EAAW;EAAO;EAAU;CAAS,CAAC;CAC3C,MAAM,cAAc,OAAO;CAC3B,MAAM,gBAAgB,eAAe,CAAC,cAAc,WAAW,IAAI,cAAc;CACjF,MAAM,gBAAgB,gBAAgB,OAAO,sBAAsB;CACnE,MAAM,0BAA0B,wBAAwB,eAAe;EACrE,SAAS,OAAO,MAAM,IAAI,EAAY;EACtC,aAAa,KAAK;CACpB,GAAG,aAAa;CAEhB,MAAM,EAAE,OAAO,uBAAuB,kBAAkB,yBACtD,OAAO,kBACP,cAAc,eACd,gBACA,KAAK,sBAAsB,iBAAiB,KAAK,IAAI,QACrD,aACF;CAmBA,OAAO;EAAE,QAjBM,eAA6B;GAC1C,GAAG,cAAc,MAAM;GACvB,OAAO;GACP,kBAAkB,wBAAwB,EAAE,OAAO,sBAAsB,IAAI;GAC7E,gBAAgB;IAAE,OAAO;IAAgB,OAAO;GAAQ;GACxD,cAAc;IAAE,OAAO;IAAc,OAAO;GAAiB;GAC7D,mBAAmB;IAAE,OAAO;IAAmB,OAAO;GAAe;GACrE,oBAAoB,gBAAgB,QAAQ,cAAc,WAAW,IACjE,cACA;IAAE,OAAO;IAAyB,OAAO;GAAmB;GAChE,GAAI,KAAK,cAAc,EAAE,aAAa;IAAE,OAAO;IAAkB,OAAO;GAAc,EAAE,IAAI,CAAC;GAC7F,GAAI,QAAQ,EAAE,YAAY;IAAE,OAAO;IAAY,OAAO,KAAK,cAAc,gBAAgB;GAAc,EAAE,IAAI,CAAC;EAChH,IAAI;GACF;GAAQ;GAAa;GAAuB;GAAgB;GAAc;GAAmB;GAAyB;GAAa;GACnI;GAAkB,KAAK;GAAa;GAAO;EAC7C,CAES;EAAQ;EAAe;EAAe;EAAsB;EAAW;CAAW;AAC7F"}
|
|
1
|
+
{"version":3,"file":"useSceneCanvasLayers.js","names":[],"sources":["../../../src/canvas/SceneCanvas/useSceneCanvasLayers.ts"],"sourcesContent":["import { useMemo, useRef, useState, type MutableRefObject } from 'react';\nimport type { ColorOverrideRegistry } from '@weasel-js/animator';\nimport type { Dispatcher } from '@weasel-js/routing';\nimport type { Affordance } from 'affordances/types';\nimport type { Node, NodeId, Scene } from 'core/scene/types';\nimport type { Bounds } from 'core/viewport/fitViewToBounds';\nimport type { PlaneMap } from 'core/viewport/parallax';\nimport type { View } from 'core/viewport/view';\nimport type { PathEditState } from 'features/paths/pathEditState';\nimport { usePathEditOverlayLayer } from 'features/paths/usePathEdit';\nimport type { rotationBadgeOf } from 'features/selection/overlay';\nimport type { PoseDescriptor } from 'interactions/actions/resize/geometry';\nimport type { HierarchicalAdapter } from '../buildSceneTree';\nimport { builtLayersOf, isCustomEntry, type CanvasLayers } from '../layerSlots';\nimport { createSlopsDebugLayer } from '../slopsDebugLayer';\nimport { useTargetBasePx } from 'core/device/useTargetBasePx';\nimport { targetSizesPx } from 'core/device/targets';\nimport type { ResizeHandleLayout } from 'interactions/actions/resize/cornerHandles';\nimport { mergeLayersWithDefaults } from './mergeLayers';\nimport type { SceneCanvasLayers } from './props';\nimport { useDispatcherOverlayLayer } from './useDispatcherOverlayLayer';\nimport { usePreviewGhostLayer } from './usePreviewGhostLayer';\nimport { useDepartureGhostLayer } from './useDepartureGhostLayer';\nimport { useEffectSourcesLayer } from 'features/pathEffects/useEffectSourcesLayer';\nimport type { Departures } from '../../animation/departures';\nimport { usePoseTransition, type PoseTransitionSeat } from '../../animation/poseTransition';\nimport { useSceneSlotLayers } from './useSceneSlotLayers';\nimport { useSelectionOverlayLayer } from './useSelectionOverlayLayer';\nimport { drawnBoundsScene } from '../derivedPath';\nimport { createSnapOutcomeStore, type SnapOutcomeStore } from 'features/snap/outcomeStore';\nimport { createSnapReadoutLayer } from 'features/snap/readoutLayer';\nimport type { SnapReadoutStyle } from 'features/snap/types';\n\n/**\n * The layer map `<SceneCanvas>` hands `<Canvas>`: the caller's `layers` over\n * the kit defaults, with the scene and selection-overlay slots built, and the\n * canvas's own layers anchored around them. Removed nodes leave just above\n * the scene, the preview ghost above them, a selected path effect's hidden sources above that, the dispatcher's\n * overlay (marquee, lasso) above those, and the\n * anchor overlay above the selection chrome.\n */\nexport function useSceneCanvasLayers<TData, TLayer extends string, TPose>(args: {\n /** `<SceneCanvas layers>`. Omitted, the result is the full default set. */\n layers: SceneCanvasLayers<Node<TData, TLayer, TPose>, TPose> | undefined;\n /** The device profile's, which sizes the default handles. */\n targetScale: number;\n scene: Scene<TData, TLayer, TPose>;\n adapter: HierarchicalAdapter<Node<TData, TLayer, TPose>, TPose>;\n poseDescriptor: PoseDescriptor<TPose>;\n alphaFor: ((id: string) => number) | undefined;\n colorOverrides: ColorOverrideRegistry | undefined;\n boundsOf: (id: string) => Bounds | null;\n planeOfNode: (id: string, view: View) => PlaneMap | null;\n /** `<SceneCanvas poseTransition>`, whose ghosts of removed nodes this draws. */\n poseTransition: PoseTransitionSeat<TPose> | null | undefined;\n /** Ids an in-flight gesture is ghosting. */\n previewIds: () => Iterable<string> | null;\n dispatcher: Dispatcher;\n /** Whether to mount the snap readout layer. */\n snapReadout: boolean;\n snapReadoutStyle?: SnapReadoutStyle;\n pathEditState: PathEditState;\n /** `debug.slops`: paint the hit regions of the chrome affordances. */\n slops: boolean;\n /** `geometry.boundsIncludeStroke`: whether chrome sits on the stroke. */\n boundsIncludeStroke: boolean;\n /** `selectTool`'s `handleHitRadius` and `resize.edgeHandles`, resolved here into\n * the one {@link ResizeHandleLayout} the overlay paints and the hit-test reads. */\n selectTool?: { handleHitRadius?: number; resize?: { edgeHandles?: boolean } };\n}): {\n layers: CanvasLayers;\n /** Where the selection overlay paints its rotate badge, or null. */\n rotationBadge: ReturnType<typeof rotationBadgeOf>;\n /** Which resize handles exist and their hit radius, for every hit-test of this canvas. */\n resizeHandles: ResizeHandleLayout;\n /** The affordances the gesture mounter hit-tests, which it publishes here\n * so the slops halos are the hit regions rather than a copy of them. */\n chromeAffordancesRef: MutableRefObject<readonly Affordance[]>;\n /** The marks the readout layer draws; the canvas's snap dep writes them. */\n snapStore: SnapOutcomeStore;\n /** The removed nodes the seat is seeing off. */\n departures: Departures<TPose, Node<TData, TLayer, TPose>>;\n} {\n const { scene, adapter, poseDescriptor, alphaFor, dispatcher, pathEditState, slops, targetScale } = args;\n\n const baseSizes = useTargetBasePx();\n const merged = useMemo(\n () => mergeLayersWithDefaults(args.layers, targetScale, baseSizes),\n [args.layers, targetScale, baseSizes],\n );\n\n const { config: sceneSlot, layers: sceneLayers } = useSceneSlotLayers({\n slot: merged.scene,\n scene,\n adapter,\n alphaFor,\n colorOverrides: args.colorOverrides,\n boundsOf: args.boundsOf,\n previewIds: args.previewIds,\n });\n // Takes the scene-wired slot, so a ghosted derived node still resolves its path.\n const previewLayer = usePreviewGhostLayer<TData, TLayer, TPose>({ scene, sceneSlot, dispatcher });\n const departures = usePoseTransition(scene, args.poseTransition);\n const departureLayer = useDepartureGhostLayer({ scene, sceneSlot, departures });\n const effectSourcesLayer = useEffectSourcesLayer(scene);\n const dispatcherOverlay = useDispatcherOverlayLayer({ dispatcher });\n\n const hitRadiusPx = args.selectTool?.handleHitRadius ?? targetSizesPx(targetScale, baseSizes).handle;\n const edges = args.selectTool?.resize?.edgeHandles ?? true;\n const resizeHandles = useMemo<ResizeHandleLayout>(() => ({ edges, hitRadiusPx }), [edges, hitRadiusPx]);\n\n const chromeAffordancesRef = useRef<readonly Affordance[]>([]);\n const slopsLayer = useMemo(\n () => createSlopsDebugLayer({ getAffordances: () => chromeAffordancesRef.current }),\n [],\n );\n\n const [snapStore] = useState(() => createSnapOutcomeStore());\n // Keyed on the style's values, so an inline `readoutStyle` literal keeps the layer.\n const { color, gapColor, lineWidth } = args.snapReadoutStyle ?? {};\n const snapReadoutLayer = useMemo(() => createSnapReadoutLayer(snapStore, {\n ...(color !== undefined ? { color } : {}),\n ...(gapColor !== undefined ? { gapColor } : {}),\n ...(lineWidth !== undefined ? { lineWidth } : {}),\n }), [snapStore, color, gapColor, lineWidth]);\n const overlaySlot = merged.pathEditingOverlay;\n const overlayConfig = overlaySlot && !isCustomEntry(overlaySlot) ? overlaySlot : undefined;\n const overlayAnchor = overlaySlot === null ? 'dispatcherOverlay' : 'pathEditingOverlay';\n const pathEditingOverlayLayer = usePathEditOverlayLayer(pathEditState, {\n nodeOf: (id) => scene.get(id as NodeId),\n planeOfNode: args.planeOfNode,\n }, overlayConfig);\n\n const { layer: selectionOverlayLayer, rotationBadge } = useSelectionOverlayLayer<TPose>(\n merged.selectionOverlay,\n pathEditState.getEditingIds,\n poseDescriptor,\n args.boundsIncludeStroke ? drawnBoundsScene(scene) : undefined,\n resizeHandles,\n );\n\n const layers = useMemo<CanvasLayers>(() => ({\n ...builtLayersOf(merged),\n scene: sceneLayers,\n selectionOverlay: selectionOverlayLayer ? { layer: selectionOverlayLayer } : null,\n departureGhost: { layer: departureLayer, after: 'scene' },\n previewGhost: { layer: previewLayer, after: 'departureGhost' },\n effectSources: { layer: effectSourcesLayer, after: 'previewGhost' },\n dispatcherOverlay: { layer: dispatcherOverlay, after: 'effectSources' },\n pathEditingOverlay: overlaySlot === null || isCustomEntry(overlaySlot)\n ? overlaySlot\n : { layer: pathEditingOverlayLayer, after: 'selectionOverlay' },\n ...(args.snapReadout ? { snapReadout: { layer: snapReadoutLayer, after: overlayAnchor } } : {}),\n ...(slops ? { slopsDebug: { layer: slopsLayer, after: args.snapReadout ? 'snapReadout' : overlayAnchor } } : {}),\n }), [\n merged, sceneLayers, selectionOverlayLayer, departureLayer, previewLayer, effectSourcesLayer, dispatcherOverlay, pathEditingOverlayLayer, overlaySlot, overlayAnchor,\n snapReadoutLayer, args.snapReadout, slops, slopsLayer,\n ]);\n\n return { layers, rotationBadge, resizeHandles, chromeAffordancesRef, snapStore, departures };\n}\n"],"mappings":"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"}
|
|
@@ -12,6 +12,7 @@ function useSceneCanvasTools(args) {
|
|
|
12
12
|
toolOptions: props.toolOptions,
|
|
13
13
|
inertia: props.viewport?.inertia,
|
|
14
14
|
ambient: props.ambient,
|
|
15
|
+
actions: props.actions,
|
|
15
16
|
pressFeedback: props.longPress?.feedback,
|
|
16
17
|
initialActiveTool: props.initialActiveTool,
|
|
17
18
|
modes: props.modes,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSceneCanvasTools.js","names":[],"sources":["../../../src/canvas/SceneCanvas/useSceneCanvasTools.ts"],"sourcesContent":["import type { Tool } from '../../tools/overlayBinding';\nimport type { EnabledPresets, FeatureBindingOptions, PresetSource } from './features';\nimport type { SceneCanvasProps } from './props';\nimport { useCanvasTools, useFeatureBindings } from './useCanvasTools';\n\ntype Props = SceneCanvasProps<unknown, string, unknown>;\n\n/** The `<SceneCanvas>` props its tools and preset bindings read. */\nexport type SceneCanvasToolProps = Pick<\n Props,\n | 'tools' | 'defaultTools' | 'toolOptions' | 'viewport' | 'ambient' | 'longPress'\n | 'initialActiveTool' | 'modes' | 'onToolsCreated' | 'selectTool'\n>;\n\n/** The canvas's tools, and the contributions and standard actions its presets bring. */\nexport function useSceneCanvasTools(args: {\n props: SceneCanvasToolProps;\n select: Tool;\n /** The select tool's move options, which the selection's `move` binding takes. */\n move: unknown;\n enabled: EnabledPresets;\n presetSource: PresetSource;\n}) {\n const { props, select, enabled } = args;\n const { tools, internal, takeover } = useCanvasTools({\n select,\n enabled,\n tools: props.tools,\n defaultTools: props.defaultTools,\n toolOptions: props.toolOptions,\n inertia: props.viewport?.inertia,\n ambient: props.ambient,\n pressFeedback: props.longPress?.feedback,\n initialActiveTool: props.initialActiveTool,\n modes: props.modes,\n onToolsCreated: props.onToolsCreated,\n });\n const { contributions, standardActions } = useFeatureBindings({\n enabled, pool: args.presetSource.pool,\n tools,\n move: args.move as FeatureBindingOptions['move'],\n rotate: (props.selectTool?.rotate || undefined) as FeatureBindingOptions['rotate'],\n });\n return { tools, internal, takeover, contributions, standardActions };\n}\n"],"mappings":";;;;AAeA,SAAgB,oBAAoB,MAOjC;CACD,MAAM,EAAE,OAAO,QAAQ,YAAY;CACnC,MAAM,EAAE,OAAO,UAAU,aAAa,eAAe;EACnD;EACA;EACA,OAAO,MAAM;EACb,cAAc,MAAM;EACpB,aAAa,MAAM;EACnB,SAAS,MAAM,UAAU;EACzB,SAAS,MAAM;EACf,eAAe,MAAM,WAAW;EAChC,mBAAmB,MAAM;EACzB,OAAO,MAAM;EACb,gBAAgB,MAAM;CACxB,CAAC;CACD,MAAM,EAAE,eAAe,oBAAoB,mBAAmB;EAC5D;EAAS,MAAM,KAAK,aAAa;EACjC;EACA,MAAM,KAAK;EACX,QAAS,MAAM,YAAY,UAAU;CACvC,CAAC;CACD,OAAO;EAAE;EAAO;EAAU;EAAU;EAAe;CAAgB;AACrE"}
|
|
1
|
+
{"version":3,"file":"useSceneCanvasTools.js","names":[],"sources":["../../../src/canvas/SceneCanvas/useSceneCanvasTools.ts"],"sourcesContent":["import type { Tool } from '../../tools/overlayBinding';\nimport type { EnabledPresets, FeatureBindingOptions, PresetSource } from './features';\nimport type { SceneCanvasProps } from './props';\nimport { useCanvasTools, useFeatureBindings } from './useCanvasTools';\n\ntype Props = SceneCanvasProps<unknown, string, unknown>;\n\n/** The `<SceneCanvas>` props its tools and preset bindings read. */\nexport type SceneCanvasToolProps = Pick<\n Props,\n | 'tools' | 'defaultTools' | 'toolOptions' | 'viewport' | 'ambient' | 'longPress'\n | 'initialActiveTool' | 'modes' | 'onToolsCreated' | 'selectTool' | 'actions'\n>;\n\n/** The canvas's tools, and the contributions and standard actions its presets bring. */\nexport function useSceneCanvasTools(args: {\n props: SceneCanvasToolProps;\n select: Tool;\n /** The select tool's move options, which the selection's `move` binding takes. */\n move: unknown;\n enabled: EnabledPresets;\n presetSource: PresetSource;\n}) {\n const { props, select, enabled } = args;\n const { tools, internal, takeover } = useCanvasTools({\n select,\n enabled,\n tools: props.tools,\n defaultTools: props.defaultTools,\n toolOptions: props.toolOptions,\n inertia: props.viewport?.inertia,\n ambient: props.ambient,\n actions: props.actions,\n pressFeedback: props.longPress?.feedback,\n initialActiveTool: props.initialActiveTool,\n modes: props.modes,\n onToolsCreated: props.onToolsCreated,\n });\n const { contributions, standardActions } = useFeatureBindings({\n enabled, pool: args.presetSource.pool,\n tools,\n move: args.move as FeatureBindingOptions['move'],\n rotate: (props.selectTool?.rotate || undefined) as FeatureBindingOptions['rotate'],\n });\n return { tools, internal, takeover, contributions, standardActions };\n}\n"],"mappings":";;;;AAeA,SAAgB,oBAAoB,MAOjC;CACD,MAAM,EAAE,OAAO,QAAQ,YAAY;CACnC,MAAM,EAAE,OAAO,UAAU,aAAa,eAAe;EACnD;EACA;EACA,OAAO,MAAM;EACb,cAAc,MAAM;EACpB,aAAa,MAAM;EACnB,SAAS,MAAM,UAAU;EACzB,SAAS,MAAM;EACf,SAAS,MAAM;EACf,eAAe,MAAM,WAAW;EAChC,mBAAmB,MAAM;EACzB,OAAO,MAAM;EACb,gBAAgB,MAAM;CACxB,CAAC;CACD,MAAM,EAAE,eAAe,oBAAoB,mBAAmB;EAC5D;EAAS,MAAM,KAAK,aAAa;EACjC;EACA,MAAM,KAAK;EACX,QAAS,MAAM,YAAY,UAAU;CACvC,CAAC;CACD,OAAO;EAAE;EAAO;EAAU;EAAU;EAAe;CAAgB;AACrE"}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { EMPTY_CHROME_STATE } from "../../core/selection/chromeState.js";
|
|
2
|
-
import { useDispatchDepSources } from "../deps/dispatchSources.js";
|
|
3
2
|
import { anchorStateFrom, buildAffordanceAt, buildClassifyTarget, layerAffordance } from "../affordanceAt.js";
|
|
4
|
-
import { pathFromPlane } from "../planeClips.js";
|
|
5
3
|
import { useOptionalViewRegistry } from "../viewRegistry.js";
|
|
4
|
+
import { useDispatchDepSources } from "../deps/dispatchSources.js";
|
|
5
|
+
import { pathFromPlane } from "../planeClips.js";
|
|
6
|
+
import { VIEW_MARKER_ENTRY_ID } from "../../features/viewports/viewMarkerDrag.js";
|
|
6
7
|
import { useKitSettingsDepSource } from "../deps/kitSettings.js";
|
|
7
8
|
import { useChromeAffordances } from "./useChromeAffordances.js";
|
|
8
9
|
import { useSurfaceRouting } from "./useSurfaceRouting.js";
|
|
9
10
|
import { traceConversion } from "./coordTrace.js";
|
|
10
|
-
import { useCallback, useInsertionEffect, useMemo, useState } from "react";
|
|
11
|
+
import { useCallback, useEffect, useInsertionEffect, useMemo, useState } from "react";
|
|
11
12
|
import { useLatest } from "@weasel-js/react";
|
|
12
13
|
import { createLongPressStore } from "@weasel-js/routing";
|
|
13
14
|
import { useActionsRegistry, useDepRegistry, useGestureDispatcher } from "@weasel-js/routing/react";
|
|
@@ -57,9 +58,22 @@ function useAffordanceAt(inputs, depRegistry, viewRegistry) {
|
|
|
57
58
|
return (worldPoint) => {
|
|
58
59
|
const extra = canvasApiRef.current?.hitTestExtras?.(worldPoint.x, worldPoint.y);
|
|
59
60
|
if (extra) return layerAffordance(extra.layerId, extra.hit);
|
|
61
|
+
const marker = viewRegistry?.markerAt(null, worldPoint);
|
|
62
|
+
if (marker) return marker;
|
|
60
63
|
return affordanceAt ? affordanceAt(worldPoint) : null;
|
|
61
64
|
};
|
|
62
|
-
}, [
|
|
65
|
+
}, [
|
|
66
|
+
affordanceAt,
|
|
67
|
+
canvasApiRef,
|
|
68
|
+
viewRegistry
|
|
69
|
+
]);
|
|
70
|
+
}
|
|
71
|
+
/** Markers take presses only while the routed tools carry the marker drag:
|
|
72
|
+
* `actions={null}` and a tools takeover both leave it out. */
|
|
73
|
+
function useMarkerGate(viewRegistry, ambient) {
|
|
74
|
+
const installed = ambient.some((entry) => entry.id === VIEW_MARKER_ENTRY_ID);
|
|
75
|
+
const installedRef = useLatest(installed);
|
|
76
|
+
useEffect(() => viewRegistry?.gateMarkers(() => installedRef.current), [viewRegistry, installedRef]);
|
|
63
77
|
}
|
|
64
78
|
/** Which body a world point lands on, its selection state, and its routing kind. */
|
|
65
79
|
function useClassifyTarget(inputs, kindOfNodeRef) {
|
|
@@ -102,6 +116,7 @@ function useSceneGestureDispatcher(inputs) {
|
|
|
102
116
|
contributions
|
|
103
117
|
]);
|
|
104
118
|
const kindOfNodeRef = useLatest(kindOfNode);
|
|
119
|
+
useMarkerGate(viewRegistry, tools.ambient);
|
|
105
120
|
const affordanceWithLayers = useAffordanceAt(inputs, depRegistry, viewRegistry);
|
|
106
121
|
const classifyTarget = useClassifyTarget(inputs, kindOfNodeRef);
|
|
107
122
|
const clientToWorld = useCallback((clientX, clientY) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSceneGestureDispatcher.js","names":[],"sources":["../../../src/canvas/SceneCanvas/useSceneGestureDispatcher.ts"],"sourcesContent":["import { useLatest } from '@weasel-js/react';\nimport { createLongPressStore, type Dispatcher, type DispatchTrace, type TraceRing } from '@weasel-js/routing';\nimport { useActionsRegistry, useDepRegistry, useGestureDispatcher, type DepRegistry } from '@weasel-js/routing/react';\nimport type { Affordance } from 'affordances/types';\nimport { EMPTY_CHROME_STATE } from 'core/selection/chromeState';\nimport type { View } from 'core/viewport/view';\nimport type { RuleCtx } from 'features/chrome-caps';\nimport type React from 'react';\nimport { useCallback, useInsertionEffect, useMemo, useState } from 'react';\nimport type { Contribution } from '../../tools/overlayBinding';\nimport type { ToolsApi } from '../../tools/overlayBinding';\nimport { useDispatchDepSources } from '../deps/dispatchSources';\nimport { anchorStateFrom, buildAffordanceAt, buildClassifyTarget, layerAffordance } from '../affordanceAt';\nimport { useChromeAffordances } from './useChromeAffordances';\nimport type { CanvasExtensionApi } from '../canvasExtension';\nimport { useKitSettingsDepSource } from '../deps/kitSettings';\nimport { pathFromPlane } from '../planeClips';\nimport { useOptionalViewRegistry, type ViewRegistry } from '../viewRegistry';\nimport { useSurfaceRouting } from './useSurfaceRouting';\nimport { traceConversion, type CoordTraceEntry } from './coordTrace';\nimport type { SceneCanvasLongPress } from './props';\n\n/** What `<SceneCanvas>` hands {@link useSceneGestureDispatcher}. */\nexport interface SceneGestureDispatcherInputs {\n canvasRef: React.RefObject<HTMLElement | null>;\n /** Holds the full `CanvasExtensionApi` so the gesture dispatcher can call\n * `requestRedraw()` between pointer events. */\n canvasApiRef: React.RefObject<CanvasExtensionApi | null>;\n tools: ToolsApi;\n /** Always-live entries the canvas installs beside `tools` — the selection's\n * `move` / `transform` bindings. Ahead of the tools' ambient entries, so a\n * tie at one specificity goes to the kit's. */\n contributions: readonly Contribution[];\n enabled: boolean;\n /** When false, the dispatcher leaves keyboard listeners unattached so\n * keyboard-bound actions never fire. Wired to `enableKeybindings`. */\n keyboard: boolean;\n /** Take the keyboard on mount. Wired to `claimKeys`. */\n claimKeys: boolean;\n selectionRef?: React.RefObject<import('core/selection/useSelection').SelectionApi>;\n boundsOf?: (id: string) => import('core/viewport/fitViewToBounds').Bounds | null;\n /** The parallax plane a node is drawn through, under the surface's camera —\n * so path anchors are hit where the plane draws them. */\n planeOfNode?: (id: string) => import('core/viewport/parallax').PlaneMap | null;\n pickEvery?: (worldX: number, worldY: number) => string[];\n /** Single topmost hit (collapses parent/child via `pickTopMostHit`), used to\n * classify the body under the pointer. Must match the select tool's own\n * resolution so a child node isn't misclassified by its parent's selection.\n * Falls back to `pickEvery`'s last id when absent. */\n pickBest?: (worldX: number, worldY: number) => string | null;\n /** Resolves a hit node id to its routing-trait kind, so the body\n * classification carries `kind` alongside `bodyTarget` and the\n * `kind:<k>` / `kind:<k>:selected` target forms can match. Absent when\n * the consumer opted out of routing (`routing={[]}`). */\n kindOfNode?: (id: string) => string | undefined;\n /** The node a double-click on a hit names (`doubleClickTarget`), for the classification. */\n doubleClickNames?: (hitId: string) => string;\n viewRef?: React.RefObject<View>;\n /** Pre-created dispatcher to pump events into. When omitted,\n * `useGestureDispatcher` creates one internally (legacy path). */\n dispatcher?: Dispatcher;\n /** Where the dispatcher records its decisions and `clientToWorld` its conversions. */\n traces: { dispatch: DispatchTrace; coords: TraceRing<CoordTraceEntry> };\n /** Chrome-caps visibility resolver factory. Threaded into\n * `buildAffordanceAt` so the hit-test pipeline gates corner / rotate /\n * anchor affordances on the same chrome ids the renderer uses. */\n getIsVisibleForCanvas?: () => (id: string) => boolean;\n /** Live RuleCtx factory. Threaded into `useGestureDispatcher` so the\n * dispatcher's eligibility filter sees the same mode/capabilities/selection\n * view of the world that chrome-caps does. */\n getRuleCtx?: () => RuleCtx | undefined;\n /** `DeviceProfile.targetScale`, so the affordance grab zones resolve to the\n * same sizes the chrome paints at. */\n targetScale?: number;\n /** Consumer override for the corner-handle grab radius\n * (`selectTool.handleHitRadius`). Undefined takes the device-scaled size. */\n handleHitRadius?: number;\n /** `selectTool.resize.edgeHandles`: resize handles at the edge midpoints. Default true. */\n edgeHandles?: boolean;\n /** The rotate badge the selection overlay paints, grabbable where drawn. */\n rotationBadge?: { distancePx: number; sizePx: number } | null;\n /** Receives the affordances this mounter hit-tests, for the slops overlay. */\n chromeAffordancesRef?: React.MutableRefObject<readonly Affordance[]>;\n /** Fires on every synthesized double click, in world coords. Backs the\n * `onDoubleClick` prop — see the option's doc on\n * `UseGestureDispatcherOptions` for why it's an observer, not a binding. */\n onDoubleClick?: (world: { x: number; y: number }) => void;\n /** Fires after every synthesized click. Backs the `onClick` prop. */\n onClick?: (world: { x: number; y: number }) => void;\n /** Backs `<SceneCanvas longPress>`. */\n longPress?: SceneCanvasLongPress;\n}\n\ntype AffordanceInputs = Pick<\n SceneGestureDispatcherInputs,\n | 'canvasApiRef' | 'selectionRef' | 'boundsOf' | 'planeOfNode' | 'viewRef' | 'getIsVisibleForCanvas'\n | 'targetScale' | 'handleHitRadius' | 'edgeHandles' | 'rotationBadge' | 'chromeAffordancesRef'\n>;\n\n/** What a press on the canvas grabs: a registered layer's widget, else a piece of the\n * selection chrome. */\nfunction useAffordanceAt(inputs: AffordanceInputs, depRegistry: DepRegistry, viewRegistry: ViewRegistry | null) {\n const {\n canvasApiRef, selectionRef, boundsOf, planeOfNode, viewRef, getIsVisibleForCanvas,\n targetScale, handleHitRadius, edgeHandles, rotationBadge, chromeAffordancesRef,\n } = inputs;\n // `getAnchorState` thunk for `buildAffordanceAt` — reads the live\n // `editAnchors` dep from the registry at call time.\n const depRegistryRef = useLatest(depRegistry);\n const planeOfNodeRef = useLatest(planeOfNode);\n const getAnchorState = useMemo(() => anchorStateFrom(\n () => depRegistryRef.current,\n (id, path) => pathFromPlane(path, planeOfNodeRef.current?.(id) ?? null),\n ), [depRegistryRef, planeOfNodeRef]);\n\n // Build the `affordanceAt` thunk. Takes world coords and delegates to\n // `buildAffordanceAt` for handle hit-testing.\n const affordances = useChromeAffordances({ targetScale, handleHitRadius, edgeHandles, rotationBadge, getAnchorState });\n useInsertionEffect(() => {\n if (chromeAffordancesRef) chromeAffordancesRef.current = affordances;\n });\n const affordanceAt = useMemo(() => {\n if (!selectionRef || !boundsOf || !viewRef) return undefined;\n return buildAffordanceAt({\n // The canvas's own view chrome, built once by its helpers — not a\n // second construction here that has to agree with the painted one.\n getChromeState: () => viewRegistry?.surface()?.chromeState() ?? EMPTY_CHROME_STATE,\n // Radii are declared in screen pixels and converted against this view.\n getView: () => viewRef.current ?? { x: 0, y: 0, scale: { x: 1, y: 1 } },\n affordances,\n // Chrome-caps resolver: keep the affordance hit-test in sync with what\n // the renderer is actually painting. Without this, a click on a (no\n // longer visible) resize handle position still classifies as a resize\n // handle — e.g. an anchor drag in path-edit mode resizes the path's\n // bounding box instead of moving the anchor.\n ...(getIsVisibleForCanvas ? { getIsVisible: () => getIsVisibleForCanvas() } : {}),\n });\n }, [selectionRef, boundsOf, viewRef, affordances, getIsVisibleForCanvas, viewRegistry]);\n\n return useMemo(() => {\n // Note this is NOT gated on `affordanceAt` being built: registered layers\n // produce affordances of their own, and a consumer with no selection\n // chrome (a canvas that is nothing but a HUD, say) still needs those.\n return (worldPoint: { x: number; y: number }) => {\n // Registered layers first: they draw on top of the kit's own chrome, so\n // they get first refusal on the point. A hit becomes an `AffordanceHit`\n // whose kind names the layer, carrying whatever the layer's hit-test\n // resolved — which is how `@weasel-js/hud` routes a press on one of its\n // widgets to its own action instead of the active tool.\n const extra = canvasApiRef.current?.hitTestExtras?.(worldPoint.x, worldPoint.y);\n if (extra) return layerAffordance(extra.layerId, extra.hit);\n return affordanceAt ? affordanceAt(worldPoint) : null;\n };\n }, [affordanceAt, canvasApiRef]);\n}\n\n/** Which body a world point lands on, its selection state, and its routing kind. */\nfunction useClassifyTarget(\n inputs: Pick<SceneGestureDispatcherInputs, 'selectionRef' | 'pickEvery' | 'pickBest' | 'viewRef' | 'doubleClickNames'>,\n kindOfNodeRef: { readonly current: SceneGestureDispatcherInputs['kindOfNode'] },\n) {\n const { selectionRef, pickEvery, pickBest, viewRef } = inputs;\n // Stable refs for the optional thunk inputs so the thunks themselves are\n // stable function identities across renders (no need to pass them as deps).\n const pickEveryRef = useLatest(pickEvery);\n const pickBestRef = useLatest(pickBest);\n const doubleClickNamesRef = useLatest(inputs.doubleClickNames);\n // Build the `classifyTarget` thunk. Takes world coords and delegates to\n // `buildClassifyTarget`.\n return useMemo(() => {\n if (!selectionRef || !pickEvery || !viewRef) return undefined;\n return buildClassifyTarget(\n () => selectionRef.current?.current ?? [],\n // Use the select tool's own topmost-hit resolution (`pickTopMostHit`,\n // which collapses parent/child) so a child body is classified by ITS\n // OWN selection — not its parent's.\n (wx: number, wy: number) => {\n if (pickBestRef.current) return pickBestRef.current(wx, wy);\n const ids = pickEveryRef.current?.(wx, wy) ?? [];\n return ids.length > 0 ? ids[ids.length - 1] : null;\n },\n // Node kind comes from the routing trait — the same classifier that\n // names `Hit.kind` — so `target: 'kind:text'` on a binding speaks the\n // vocabulary the consumer already declared in `routing`, rather than a\n // second one invented for bindings. `routing={[]}` opts out and leaves\n // every `kind:` target unmatchable.\n (id: string) => kindOfNodeRef.current?.(id),\n (id: string) => doubleClickNamesRef.current?.(id) ?? id,\n );\n }, [selectionRef, pickEvery, viewRef, pickBestRef, pickEveryRef, kindOfNodeRef, doubleClickNamesRef]);\n}\n\n/** Runs this canvas's gesture dispatcher over its tools, contributions, and chrome. */\nexport function useSceneGestureDispatcher(inputs: SceneGestureDispatcherInputs): void {\n const {\n canvasRef, canvasApiRef, tools, contributions, enabled, keyboard, claimKeys, viewRef,\n kindOfNode, dispatcher, traces, getRuleCtx, onDoubleClick, onClick,\n longPress: { duration: longPressDuration, haptics: longPressHaptics } = {},\n } = inputs;\n const registry = useActionsRegistry();\n const depRegistry = useDepRegistry();\n const viewRegistry = useOptionalViewRegistry();\n const entriesById = useMemo<ReadonlyMap<string, Contribution>>(() => {\n const m = new Map<string, Contribution>();\n for (const [id, tool] of Object.entries(tools.registry)) {\n m.set(id, tool);\n }\n for (const entry of contributions) m.set(entry.id, entry);\n // Ambient entries too — their bindings assemble at ambient scope, and the\n // dispatcher resolves them through this same map.\n for (const entry of tools.ambient) m.set(entry.id, entry);\n return m;\n }, [tools.registry, tools.ambient, contributions]);\n\n const kindOfNodeRef = useLatest(kindOfNode);\n\n const affordanceWithLayers = useAffordanceAt(inputs, depRegistry, viewRegistry);\n const classifyTarget = useClassifyTarget(inputs, kindOfNodeRef);\n\n // Through the handle, so a `clientToWorld` override on the canvas applies.\n const clientToWorld = useCallback((clientX: number, clientY: number): { x: number; y: number } => {\n const api = canvasApiRef.current;\n const [x, y] = api ? api.clientToWorld(clientX, clientY) : [clientX, clientY];\n const world = { x, y };\n traceConversion(traces.coords, clientX, clientY, api ? canvasRef.current : null, viewRef?.current, world);\n return world;\n }, [canvasRef, canvasApiRef, viewRef, traces.coords]);\n\n // Stable callback that asks the canvas to redraw. Reads via ref so the\n // identity stays stable while the underlying API binds after mount.\n const requestRedraw = useCallback(() => {\n canvasApiRef.current?.requestRedraw?.();\n }, [canvasApiRef]);\n\n // The painted tier's channel, read live for the same reason — the handle it\n // lives on binds after mount.\n const paintedCursor = useCallback(() => canvasApiRef.current?.paintedCursor, [canvasApiRef]);\n const cursorSink = useCallback(() => canvasApiRef.current?.cursorSink, [canvasApiRef]);\n\n // The pending long-press, published as the `longPress` dep for feedback.\n const [longPressState] = useState(createLongPressStore);\n const longPress = useMemo(() => ({\n state: longPressState,\n ...(longPressDuration !== undefined ? { duration: longPressDuration } : {}),\n ...(longPressHaptics !== undefined ? { haptics: longPressHaptics } : {}),\n }), [longPressState, longPressDuration, longPressHaptics]);\n\n const routing = useSurfaceRouting(viewRegistry);\n\n // Hover cursors live in `useGestureDispatcher`'s hover-cursor pump:\n // affordance hits carry `AffordanceHit.cursor` (set by `buildAffordanceAt`),\n // and everything else is predicted via `Dispatcher.resolveOnly` +\n // `Action.cursor`. Nothing SceneCanvas-specific remains here.\n useGestureDispatcher({\n canvasRef,\n actions: registry!,\n entriesById,\n enabled,\n keyboard,\n claimKeys,\n affordanceAt: affordanceWithLayers,\n classifyTarget,\n dispatcher,\n trace: traces.dispatch,\n clientToWorld,\n requestRedraw,\n paintedCursor,\n cursorSink,\n getRuleCtx,\n onClick,\n onDoubleClick,\n longPress,\n ...routing,\n });\n useDispatchDepSources(longPressState, classifyTarget, kindOfNodeRef);\n useKitSettingsDepSource();\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useSceneGestureDispatcher.js","names":[],"sources":["../../../src/canvas/SceneCanvas/useSceneGestureDispatcher.ts"],"sourcesContent":["import { useLatest } from '@weasel-js/react';\nimport { createLongPressStore, type Dispatcher, type DispatchTrace, type TraceRing } from '@weasel-js/routing';\nimport { useActionsRegistry, useDepRegistry, useGestureDispatcher, type DepRegistry } from '@weasel-js/routing/react';\nimport type { Affordance } from 'affordances/types';\nimport { EMPTY_CHROME_STATE } from 'core/selection/chromeState';\nimport type { View } from 'core/viewport/view';\nimport type { RuleCtx } from 'features/chrome-caps';\nimport type React from 'react';\nimport { useCallback, useEffect, useInsertionEffect, useMemo, useState } from 'react';\nimport type { Contribution } from '../../tools/overlayBinding';\nimport type { ToolsApi } from '../../tools/overlayBinding';\nimport { useDispatchDepSources } from '../deps/dispatchSources';\nimport { anchorStateFrom, buildAffordanceAt, buildClassifyTarget, layerAffordance } from '../affordanceAt';\nimport { useChromeAffordances } from './useChromeAffordances';\nimport type { CanvasExtensionApi } from '../canvasExtension';\nimport { useKitSettingsDepSource } from '../deps/kitSettings';\nimport { pathFromPlane } from '../planeClips';\nimport { useOptionalViewRegistry, type ViewRegistry } from '../viewRegistry';\nimport { useSurfaceRouting } from './useSurfaceRouting';\nimport { traceConversion, type CoordTraceEntry } from './coordTrace';\nimport type { SceneCanvasLongPress } from './props';\nimport { VIEW_MARKER_ENTRY_ID } from '../../features/viewports/viewMarkerDrag';\n\n/** What `<SceneCanvas>` hands {@link useSceneGestureDispatcher}. */\nexport interface SceneGestureDispatcherInputs {\n canvasRef: React.RefObject<HTMLElement | null>;\n /** Holds the full `CanvasExtensionApi` so the gesture dispatcher can call\n * `requestRedraw()` between pointer events. */\n canvasApiRef: React.RefObject<CanvasExtensionApi | null>;\n tools: ToolsApi;\n /** Always-live entries the canvas installs beside `tools` — the selection's\n * `move` / `transform` bindings. Ahead of the tools' ambient entries, so a\n * tie at one specificity goes to the kit's. */\n contributions: readonly Contribution[];\n enabled: boolean;\n /** When false, the dispatcher leaves keyboard listeners unattached so\n * keyboard-bound actions never fire. Wired to `enableKeybindings`. */\n keyboard: boolean;\n /** Take the keyboard on mount. Wired to `claimKeys`. */\n claimKeys: boolean;\n selectionRef?: React.RefObject<import('core/selection/useSelection').SelectionApi>;\n boundsOf?: (id: string) => import('core/viewport/fitViewToBounds').Bounds | null;\n /** The parallax plane a node is drawn through, under the surface's camera —\n * so path anchors are hit where the plane draws them. */\n planeOfNode?: (id: string) => import('core/viewport/parallax').PlaneMap | null;\n pickEvery?: (worldX: number, worldY: number) => string[];\n /** Single topmost hit (collapses parent/child via `pickTopMostHit`), used to\n * classify the body under the pointer. Must match the select tool's own\n * resolution so a child node isn't misclassified by its parent's selection.\n * Falls back to `pickEvery`'s last id when absent. */\n pickBest?: (worldX: number, worldY: number) => string | null;\n /** Resolves a hit node id to its routing-trait kind, so the body\n * classification carries `kind` alongside `bodyTarget` and the\n * `kind:<k>` / `kind:<k>:selected` target forms can match. Absent when\n * the consumer opted out of routing (`routing={[]}`). */\n kindOfNode?: (id: string) => string | undefined;\n /** The node a double-click on a hit names (`doubleClickTarget`), for the classification. */\n doubleClickNames?: (hitId: string) => string;\n viewRef?: React.RefObject<View>;\n /** Pre-created dispatcher to pump events into. When omitted,\n * `useGestureDispatcher` creates one internally (legacy path). */\n dispatcher?: Dispatcher;\n /** Where the dispatcher records its decisions and `clientToWorld` its conversions. */\n traces: { dispatch: DispatchTrace; coords: TraceRing<CoordTraceEntry> };\n /** Chrome-caps visibility resolver factory. Threaded into\n * `buildAffordanceAt` so the hit-test pipeline gates corner / rotate /\n * anchor affordances on the same chrome ids the renderer uses. */\n getIsVisibleForCanvas?: () => (id: string) => boolean;\n /** Live RuleCtx factory. Threaded into `useGestureDispatcher` so the\n * dispatcher's eligibility filter sees the same mode/capabilities/selection\n * view of the world that chrome-caps does. */\n getRuleCtx?: () => RuleCtx | undefined;\n /** `DeviceProfile.targetScale`, so the affordance grab zones resolve to the\n * same sizes the chrome paints at. */\n targetScale?: number;\n /** Consumer override for the corner-handle grab radius\n * (`selectTool.handleHitRadius`). Undefined takes the device-scaled size. */\n handleHitRadius?: number;\n /** `selectTool.resize.edgeHandles`: resize handles at the edge midpoints. Default true. */\n edgeHandles?: boolean;\n /** The rotate badge the selection overlay paints, grabbable where drawn. */\n rotationBadge?: { distancePx: number; sizePx: number } | null;\n /** Receives the affordances this mounter hit-tests, for the slops overlay. */\n chromeAffordancesRef?: React.MutableRefObject<readonly Affordance[]>;\n /** Fires on every synthesized double click, in world coords. Backs the\n * `onDoubleClick` prop — see the option's doc on\n * `UseGestureDispatcherOptions` for why it's an observer, not a binding. */\n onDoubleClick?: (world: { x: number; y: number }) => void;\n /** Fires after every synthesized click. Backs the `onClick` prop. */\n onClick?: (world: { x: number; y: number }) => void;\n /** Backs `<SceneCanvas longPress>`. */\n longPress?: SceneCanvasLongPress;\n}\n\ntype AffordanceInputs = Pick<\n SceneGestureDispatcherInputs,\n | 'canvasApiRef' | 'selectionRef' | 'boundsOf' | 'planeOfNode' | 'viewRef' | 'getIsVisibleForCanvas'\n | 'targetScale' | 'handleHitRadius' | 'edgeHandles' | 'rotationBadge' | 'chromeAffordancesRef'\n>;\n\n/** What a press on the canvas grabs: a registered layer's widget, else a piece of the\n * selection chrome. */\nfunction useAffordanceAt(inputs: AffordanceInputs, depRegistry: DepRegistry, viewRegistry: ViewRegistry | null) {\n const {\n canvasApiRef, selectionRef, boundsOf, planeOfNode, viewRef, getIsVisibleForCanvas,\n targetScale, handleHitRadius, edgeHandles, rotationBadge, chromeAffordancesRef,\n } = inputs;\n // `getAnchorState` thunk for `buildAffordanceAt` — reads the live\n // `editAnchors` dep from the registry at call time.\n const depRegistryRef = useLatest(depRegistry);\n const planeOfNodeRef = useLatest(planeOfNode);\n const getAnchorState = useMemo(() => anchorStateFrom(\n () => depRegistryRef.current,\n (id, path) => pathFromPlane(path, planeOfNodeRef.current?.(id) ?? null),\n ), [depRegistryRef, planeOfNodeRef]);\n\n // Build the `affordanceAt` thunk. Takes world coords and delegates to\n // `buildAffordanceAt` for handle hit-testing.\n const affordances = useChromeAffordances({ targetScale, handleHitRadius, edgeHandles, rotationBadge, getAnchorState });\n useInsertionEffect(() => {\n if (chromeAffordancesRef) chromeAffordancesRef.current = affordances;\n });\n const affordanceAt = useMemo(() => {\n if (!selectionRef || !boundsOf || !viewRef) return undefined;\n return buildAffordanceAt({\n // The canvas's own view chrome, built once by its helpers — not a\n // second construction here that has to agree with the painted one.\n getChromeState: () => viewRegistry?.surface()?.chromeState() ?? EMPTY_CHROME_STATE,\n // Radii are declared in screen pixels and converted against this view.\n getView: () => viewRef.current ?? { x: 0, y: 0, scale: { x: 1, y: 1 } },\n affordances,\n // Chrome-caps resolver: keep the affordance hit-test in sync with what\n // the renderer is actually painting. Without this, a click on a (no\n // longer visible) resize handle position still classifies as a resize\n // handle — e.g. an anchor drag in path-edit mode resizes the path's\n // bounding box instead of moving the anchor.\n ...(getIsVisibleForCanvas ? { getIsVisible: () => getIsVisibleForCanvas() } : {}),\n });\n }, [selectionRef, boundsOf, viewRef, affordances, getIsVisibleForCanvas, viewRegistry]);\n\n return useMemo(() => {\n // Note this is NOT gated on `affordanceAt` being built: registered layers\n // produce affordances of their own, and a consumer with no selection\n // chrome (a canvas that is nothing but a HUD, say) still needs those.\n return (worldPoint: { x: number; y: number }) => {\n // Registered layers first: they draw on top of the kit's own chrome, so\n // they get first refusal on the point. A hit becomes an `AffordanceHit`\n // whose kind names the layer, carrying whatever the layer's hit-test\n // resolved — which is how `@weasel-js/hud` routes a press on one of its\n // widgets to its own action instead of the active tool.\n const extra = canvasApiRef.current?.hitTestExtras?.(worldPoint.x, worldPoint.y);\n if (extra) return layerAffordance(extra.layerId, extra.hit);\n const marker = viewRegistry?.markerAt(null, worldPoint);\n if (marker) return marker;\n return affordanceAt ? affordanceAt(worldPoint) : null;\n };\n }, [affordanceAt, canvasApiRef, viewRegistry]);\n}\n\n/** Markers take presses only while the routed tools carry the marker drag:\n * `actions={null}` and a tools takeover both leave it out. */\nfunction useMarkerGate(viewRegistry: ViewRegistry | null, ambient: readonly Contribution[]): void {\n const installed = ambient.some((entry) => entry.id === VIEW_MARKER_ENTRY_ID);\n const installedRef = useLatest(installed);\n useEffect(() => viewRegistry?.gateMarkers(() => installedRef.current), [viewRegistry, installedRef]);\n}\n\n/** Which body a world point lands on, its selection state, and its routing kind. */\nfunction useClassifyTarget(\n inputs: Pick<SceneGestureDispatcherInputs, 'selectionRef' | 'pickEvery' | 'pickBest' | 'viewRef' | 'doubleClickNames'>,\n kindOfNodeRef: { readonly current: SceneGestureDispatcherInputs['kindOfNode'] },\n) {\n const { selectionRef, pickEvery, pickBest, viewRef } = inputs;\n // Stable refs for the optional thunk inputs so the thunks themselves are\n // stable function identities across renders (no need to pass them as deps).\n const pickEveryRef = useLatest(pickEvery);\n const pickBestRef = useLatest(pickBest);\n const doubleClickNamesRef = useLatest(inputs.doubleClickNames);\n // Build the `classifyTarget` thunk. Takes world coords and delegates to\n // `buildClassifyTarget`.\n return useMemo(() => {\n if (!selectionRef || !pickEvery || !viewRef) return undefined;\n return buildClassifyTarget(\n () => selectionRef.current?.current ?? [],\n // Use the select tool's own topmost-hit resolution (`pickTopMostHit`,\n // which collapses parent/child) so a child body is classified by ITS\n // OWN selection — not its parent's.\n (wx: number, wy: number) => {\n if (pickBestRef.current) return pickBestRef.current(wx, wy);\n const ids = pickEveryRef.current?.(wx, wy) ?? [];\n return ids.length > 0 ? ids[ids.length - 1] : null;\n },\n // Node kind comes from the routing trait — the same classifier that\n // names `Hit.kind` — so `target: 'kind:text'` on a binding speaks the\n // vocabulary the consumer already declared in `routing`, rather than a\n // second one invented for bindings. `routing={[]}` opts out and leaves\n // every `kind:` target unmatchable.\n (id: string) => kindOfNodeRef.current?.(id),\n (id: string) => doubleClickNamesRef.current?.(id) ?? id,\n );\n }, [selectionRef, pickEvery, viewRef, pickBestRef, pickEveryRef, kindOfNodeRef, doubleClickNamesRef]);\n}\n\n/** Runs this canvas's gesture dispatcher over its tools, contributions, and chrome. */\nexport function useSceneGestureDispatcher(inputs: SceneGestureDispatcherInputs): void {\n const {\n canvasRef, canvasApiRef, tools, contributions, enabled, keyboard, claimKeys, viewRef,\n kindOfNode, dispatcher, traces, getRuleCtx, onDoubleClick, onClick,\n longPress: { duration: longPressDuration, haptics: longPressHaptics } = {},\n } = inputs;\n const registry = useActionsRegistry();\n const depRegistry = useDepRegistry();\n const viewRegistry = useOptionalViewRegistry();\n const entriesById = useMemo<ReadonlyMap<string, Contribution>>(() => {\n const m = new Map<string, Contribution>();\n for (const [id, tool] of Object.entries(tools.registry)) {\n m.set(id, tool);\n }\n for (const entry of contributions) m.set(entry.id, entry);\n // Ambient entries too — their bindings assemble at ambient scope, and the\n // dispatcher resolves them through this same map.\n for (const entry of tools.ambient) m.set(entry.id, entry);\n return m;\n }, [tools.registry, tools.ambient, contributions]);\n\n const kindOfNodeRef = useLatest(kindOfNode);\n useMarkerGate(viewRegistry, tools.ambient);\n\n const affordanceWithLayers = useAffordanceAt(inputs, depRegistry, viewRegistry);\n const classifyTarget = useClassifyTarget(inputs, kindOfNodeRef);\n\n // Through the handle, so a `clientToWorld` override on the canvas applies.\n const clientToWorld = useCallback((clientX: number, clientY: number): { x: number; y: number } => {\n const api = canvasApiRef.current;\n const [x, y] = api ? api.clientToWorld(clientX, clientY) : [clientX, clientY];\n const world = { x, y };\n traceConversion(traces.coords, clientX, clientY, api ? canvasRef.current : null, viewRef?.current, world);\n return world;\n }, [canvasRef, canvasApiRef, viewRef, traces.coords]);\n\n // Stable callback that asks the canvas to redraw. Reads via ref so the\n // identity stays stable while the underlying API binds after mount.\n const requestRedraw = useCallback(() => {\n canvasApiRef.current?.requestRedraw?.();\n }, [canvasApiRef]);\n\n // The painted tier's channel, read live for the same reason — the handle it\n // lives on binds after mount.\n const paintedCursor = useCallback(() => canvasApiRef.current?.paintedCursor, [canvasApiRef]);\n const cursorSink = useCallback(() => canvasApiRef.current?.cursorSink, [canvasApiRef]);\n\n // The pending long-press, published as the `longPress` dep for feedback.\n const [longPressState] = useState(createLongPressStore);\n const longPress = useMemo(() => ({\n state: longPressState,\n ...(longPressDuration !== undefined ? { duration: longPressDuration } : {}),\n ...(longPressHaptics !== undefined ? { haptics: longPressHaptics } : {}),\n }), [longPressState, longPressDuration, longPressHaptics]);\n\n const routing = useSurfaceRouting(viewRegistry);\n\n // Hover cursors live in `useGestureDispatcher`'s hover-cursor pump:\n // affordance hits carry `AffordanceHit.cursor` (set by `buildAffordanceAt`),\n // and everything else is predicted via `Dispatcher.resolveOnly` +\n // `Action.cursor`. Nothing SceneCanvas-specific remains here.\n useGestureDispatcher({\n canvasRef,\n actions: registry!,\n entriesById,\n enabled,\n keyboard,\n claimKeys,\n affordanceAt: affordanceWithLayers,\n classifyTarget,\n dispatcher,\n trace: traces.dispatch,\n clientToWorld,\n requestRedraw,\n paintedCursor,\n cursorSink,\n getRuleCtx,\n onClick,\n onDoubleClick,\n longPress,\n ...routing,\n });\n useDispatchDepSources(longPressState, classifyTarget, kindOfNodeRef);\n useKitSettingsDepSource();\n}\n"],"mappings":"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"}
|
|
@@ -19,7 +19,7 @@ import { useDispatcherDepSource } from "../deps/dispatcher.js";
|
|
|
19
19
|
import { useLayoutDepSource } from "../deps/layout.js";
|
|
20
20
|
import { useIngestionDepSource } from "../deps/ingestion.js";
|
|
21
21
|
import { useActionsPropResolver } from "./useActionsPropResolver.js";
|
|
22
|
-
import { useActionTunings } from "./useActionTunings.js";
|
|
22
|
+
import { useActionTunings, useBindingOverrides } from "./useActionTunings.js";
|
|
23
23
|
import { useViewportActions, viewportZoomOption } from "./useViewportActions.js";
|
|
24
24
|
import { useEffect, useMemo } from "react";
|
|
25
25
|
import { useActionsRegistry, useDepSource } from "@weasel-js/routing/react";
|
|
@@ -100,6 +100,7 @@ function useSceneStandardActions(inputs) {
|
|
|
100
100
|
useDepSource("boundsIncludeStroke", () => boundsIncludeStroke);
|
|
101
101
|
useActionsPropResolver(props.actions, standardActions.map((a) => a.id).join("|"));
|
|
102
102
|
useActionTunings(tune);
|
|
103
|
+
useBindingOverrides(props.bindingOverrides);
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
//#endregion
|