@weasel-js/core 1.11.0 → 1.11.1
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 +427 -0
- package/README.md +1 -1
- package/dist/animation/index.js +3 -3
- package/dist/animation/rig/bindRig.js +1 -1
- package/dist/animation/rig/rigNode.js.map +1 -1
- package/dist/barrel/canvas.js +7 -3
- package/dist/barrel/interactions.js +5 -4
- package/dist/barrel/paths.js +3 -3
- package/dist/barrel/renderer.js +3 -2
- package/dist/barrel/routing.js +6 -11
- package/dist/barrel/scene.js +8 -5
- package/dist/barrel/viewport.js +4 -3
- package/dist/canvas/Canvas.js +3 -1
- package/dist/canvas/Canvas.js.map +1 -1
- package/dist/canvas/SceneCanvas/SceneCanvasInput.js +17 -3
- package/dist/canvas/SceneCanvas/SceneCanvasInput.js.map +1 -1
- package/dist/canvas/SceneCanvas/builtinToolSets.js +30 -0
- package/dist/canvas/SceneCanvas/builtinToolSets.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/coordTrace.js +5 -16
- package/dist/canvas/SceneCanvas/coordTrace.js.map +1 -1
- package/dist/canvas/SceneCanvas/defaultNodeProperties.js +5 -0
- package/dist/canvas/SceneCanvas/defaultNodeProperties.js.map +1 -1
- package/dist/canvas/SceneCanvas/features.js +45 -5
- package/dist/canvas/SceneCanvas/features.js.map +1 -1
- package/dist/canvas/SceneCanvas/props.js +1 -11
- package/dist/canvas/SceneCanvas/props.js.map +1 -1
- package/dist/canvas/SceneCanvas/toolRegistry.js +8 -4
- package/dist/canvas/SceneCanvas/toolRegistry.js.map +1 -1
- package/dist/canvas/SceneCanvas/useActionsPropResolver.js +1 -11
- package/dist/canvas/SceneCanvas/useActionsPropResolver.js.map +1 -1
- package/dist/canvas/SceneCanvas/useBuiltinShapeTools.js.map +1 -1
- package/dist/canvas/SceneCanvas/useBuiltinTools.js +2 -9
- package/dist/canvas/SceneCanvas/useBuiltinTools.js.map +1 -1
- package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js +93 -0
- package/dist/canvas/SceneCanvas/useCanvasDevtoolsRoot.js.map +1 -0
- package/dist/canvas/SceneCanvas/useCanvasSelection.js +7 -2
- package/dist/canvas/SceneCanvas/useCanvasSelection.js.map +1 -1
- package/dist/canvas/SceneCanvas/useCanvasTools.js +8 -8
- package/dist/canvas/SceneCanvas/useCanvasTools.js.map +1 -1
- package/dist/canvas/SceneCanvas/useNodeKinds.js +1 -1
- package/dist/canvas/SceneCanvas/useNodeKinds.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js +13 -2
- package/dist/canvas/SceneCanvas/useSceneCanvasLayers.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneCanvasTools.js +1 -3
- package/dist/canvas/SceneCanvas/useSceneCanvasTools.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneChrome.js +6 -7
- package/dist/canvas/SceneCanvas/useSceneChrome.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js +5 -3
- package/dist/canvas/SceneCanvas/useSceneGestureDispatcher.js.map +1 -1
- package/dist/canvas/SceneCanvas/useScenePicking.js +2 -3
- package/dist/canvas/SceneCanvas/useScenePicking.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneSelectTool.js +2 -1
- package/dist/canvas/SceneCanvas/useSceneSelectTool.js.map +1 -1
- package/dist/canvas/SceneCanvas/useSceneStandardActions.js +3 -4
- package/dist/canvas/SceneCanvas/useSceneStandardActions.js.map +1 -1
- package/dist/canvas/SceneCanvas.js +16 -14
- package/dist/canvas/SceneCanvas.js.map +1 -1
- package/dist/canvas/SurfaceCanvas.js +5 -22
- package/dist/canvas/SurfaceCanvas.js.map +1 -1
- package/dist/canvas/canvasRenderer.js +9 -5
- package/dist/canvas/canvasRenderer.js.map +1 -1
- package/dist/canvas/contextAttributes.js +14 -0
- package/dist/canvas/contextAttributes.js.map +1 -0
- package/dist/canvas/deps/editAnchors.js +9 -113
- package/dist/canvas/deps/editAnchors.js.map +1 -1
- package/dist/canvas/deps/insert.js +4 -2
- package/dist/canvas/deps/insert.js.map +1 -1
- package/dist/canvas/deps/pathEditCommits.js +193 -0
- package/dist/canvas/deps/pathEditCommits.js.map +1 -0
- package/dist/canvas/deps/snap.js +12 -9
- package/dist/canvas/deps/snap.js.map +1 -1
- package/dist/canvas/deps/transformFollowers.js +20 -0
- package/dist/canvas/deps/transformFollowers.js.map +1 -0
- package/dist/canvas/derivedPath.js +20 -1
- package/dist/canvas/derivedPath.js.map +1 -1
- package/dist/canvas/drawnBounds.js +29 -3
- package/dist/canvas/drawnBounds.js.map +1 -1
- package/dist/canvas/nodeShapes/derivedPainter.js +9 -1
- package/dist/canvas/nodeShapes/derivedPainter.js.map +1 -1
- package/dist/canvas/nodeShapes/pathPainter.js +3 -2
- package/dist/canvas/nodeShapes/pathPainter.js.map +1 -1
- package/dist/canvas/nodeShapes/shapePainter.js +1 -1
- package/dist/canvas/overlayRoleStrokes.js +12 -0
- package/dist/canvas/overlayRoleStrokes.js.map +1 -1
- package/dist/canvas/pathOverlay.js +27 -7
- package/dist/canvas/pathOverlay.js.map +1 -1
- package/dist/canvas/renderDebugSnapshot.js +1 -0
- package/dist/canvas/renderDebugSnapshot.js.map +1 -1
- package/dist/canvas/renderSceneToPixels.js +6 -5
- package/dist/canvas/renderSceneToPixels.js.map +1 -1
- package/dist/canvas/sceneAdapter.js.map +1 -1
- package/dist/canvas/useCanvasPaint.js +2 -1
- package/dist/canvas/useCanvasPaint.js.map +1 -1
- package/dist/canvas/useInFlightBounds.js +52 -0
- package/dist/canvas/useInFlightBounds.js.map +1 -0
- package/dist/canvas/useViewHelpers.js +7 -9
- package/dist/canvas/useViewHelpers.js.map +1 -1
- package/dist/clipboard.d.ts +3 -2
- package/dist/{clone-DZDrfIlU.d.ts → clone-C3e0OTuF.d.ts} +8 -21
- package/dist/clone.d.ts +4 -3
- package/dist/core/geometry/autoPoseDescriptor.js +1 -1
- package/dist/core/ops/setDependsOn.js +37 -0
- package/dist/core/ops/setDependsOn.js.map +1 -0
- package/dist/core/ops/writtenBy.js +53 -0
- package/dist/core/ops/writtenBy.js.map +1 -0
- package/dist/core/pages/fitViewToPage.js +12 -1
- package/dist/core/pages/fitViewToPage.js.map +1 -1
- package/dist/core/pages/pageEdits.js +164 -0
- package/dist/core/pages/pageEdits.js.map +1 -0
- package/dist/core/pages/pages.js +30 -1
- package/dist/core/pages/pages.js.map +1 -1
- package/dist/core/scene/NodeRouting.js +8 -3
- package/dist/core/scene/NodeRouting.js.map +1 -1
- package/dist/core/scene/arrivalWindow.js.map +1 -1
- package/dist/core/scene/carryDescendants.js +51 -0
- package/dist/core/scene/carryDescendants.js.map +1 -0
- package/dist/core/scene/docValues.js +57 -0
- package/dist/core/scene/docValues.js.map +1 -0
- package/dist/core/scene/editPass.js +111 -0
- package/dist/core/scene/editPass.js.map +1 -0
- package/dist/core/scene/effectivePose.js +7 -1
- package/dist/core/scene/effectivePose.js.map +1 -1
- package/dist/core/scene/fieldEdits.js +4 -1
- package/dist/core/scene/fieldEdits.js.map +1 -1
- package/dist/core/scene/historyFacade.js +1 -0
- package/dist/core/scene/historyFacade.js.map +1 -1
- package/dist/core/scene/layoutPass.js +28 -3
- package/dist/core/scene/layoutPass.js.map +1 -1
- package/dist/core/scene/recorder.js +28 -19
- package/dist/core/scene/recorder.js.map +1 -1
- package/dist/core/scene/scene.js +15 -9
- package/dist/core/scene/scene.js.map +1 -1
- package/dist/core/scene/sceneChanges.js +13 -0
- package/dist/core/scene/sceneChanges.js.map +1 -0
- package/dist/core/scene/sceneEdits.js +8 -1
- package/dist/core/scene/sceneEdits.js.map +1 -1
- package/dist/core/scene/sceneMutators.js +1 -0
- package/dist/core/scene/sceneMutators.js.map +1 -1
- package/dist/core/scene/sceneRecording.js +20 -3
- package/dist/core/scene/sceneRecording.js.map +1 -1
- package/dist/core/scene/sceneSelection.js +4 -1
- package/dist/core/scene/sceneSelection.js.map +1 -1
- package/dist/core/scene/sceneState.js +11 -5
- package/dist/core/scene/sceneState.js.map +1 -1
- package/dist/core/scene/serialization.js +5 -3
- package/dist/core/scene/serialization.js.map +1 -1
- package/dist/core/scene/types.js.map +1 -1
- package/dist/core/scene/unitScopes.js +19 -1
- package/dist/core/scene/unitScopes.js.map +1 -1
- package/dist/core/selection/usePublishSceneSelection.js +6 -12
- package/dist/core/selection/usePublishSceneSelection.js.map +1 -1
- package/dist/core/shapeKinds.js +25 -11
- package/dist/core/shapeKinds.js.map +1 -1
- package/dist/{cornerHandles-Camw_yeu.d.ts → cornerHandles-BHuoMs1o.d.ts} +1 -1
- package/dist/debug/createDebugOverlayLayer.js +16 -111
- package/dist/debug/createDebugOverlayLayer.js.map +1 -1
- package/dist/debug/createDebugSink.js +2 -1
- package/dist/debug/createDebugSink.js.map +1 -1
- package/dist/debug/defaultTheme.js +4 -2
- package/dist/debug/defaultTheme.js.map +1 -1
- package/dist/debug/features.js +17 -0
- package/dist/debug/features.js.map +1 -0
- package/dist/debug/overlayPanels.js +117 -0
- package/dist/debug/overlayPanels.js.map +1 -0
- package/dist/debug/parseDebugFlags.js +4 -13
- package/dist/debug/parseDebugFlags.js.map +1 -1
- package/dist/{depSchema-Cgfu_G9s.d.ts → depSchema-BpZNOv7q.d.ts} +114 -892
- package/dist/devtools/handlers.js +112 -0
- package/dist/devtools/handlers.js.map +1 -0
- package/dist/devtools/history.js +71 -0
- package/dist/devtools/history.js.map +1 -0
- package/dist/devtools/hook.js +74 -0
- package/dist/devtools/hook.js.map +1 -0
- package/dist/devtools/label.js +25 -0
- package/dist/devtools/label.js.map +1 -0
- package/dist/devtools/notices.js +58 -0
- package/dist/devtools/notices.js.map +1 -0
- package/dist/devtools/protocol.js +7 -0
- package/dist/devtools/protocol.js.map +1 -0
- package/dist/devtools/registry.js +90 -0
- package/dist/devtools/registry.js.map +1 -0
- package/dist/devtools/root.js +80 -0
- package/dist/devtools/root.js.map +1 -0
- package/dist/devtools/serialize.js +86 -0
- package/dist/devtools/serialize.js.map +1 -0
- package/dist/devtools/session.js +111 -0
- package/dist/devtools/session.js.map +1 -0
- package/dist/devtools/transport.js +67 -0
- package/dist/devtools/transport.js.map +1 -0
- package/dist/devtools/useDevtoolsRoot.js +81 -0
- package/dist/devtools/useDevtoolsRoot.js.map +1 -0
- package/dist/devtools-protocol.d.ts +435 -0
- package/dist/devtools-protocol.js +7 -0
- package/dist/devtools.d.ts +8 -0
- package/dist/devtools.js +12 -0
- package/dist/devtools.js.map +1 -0
- package/dist/features/chrome-caps/useHoverTracking.js +16 -10
- package/dist/features/chrome-caps/useHoverTracking.js.map +1 -1
- package/dist/features/ingestion/contentHandlers.js +25 -2
- package/dist/features/ingestion/contentHandlers.js.map +1 -1
- package/dist/features/measure/measureCommands.js +7 -5
- package/dist/features/measure/measureCommands.js.map +1 -1
- package/dist/features/paths/index.js +2 -2
- package/dist/features/paths/pathEditExtension.js.map +1 -1
- package/dist/features/paths/pathSnap/pathSnapSource.js +5 -5
- package/dist/features/paths/pathSnap/pathSnapSource.js.map +1 -1
- package/dist/features/paths/pathSnap/sceneIndex.js +10 -3
- package/dist/features/paths/pathSnap/sceneIndex.js.map +1 -1
- package/dist/features/selection/selectionPose.js +4 -1
- package/dist/features/selection/selectionPose.js.map +1 -1
- package/dist/features/snap/gather.js +9 -1
- package/dist/features/snap/gather.js.map +1 -1
- package/dist/features/snap/lazySession.js +17 -0
- package/dist/features/snap/lazySession.js.map +1 -0
- package/dist/features/snap/marks.js.map +1 -1
- package/dist/features/snap/probes2d.js +27 -19
- package/dist/features/snap/probes2d.js.map +1 -1
- package/dist/features/snap/readoutLayer.js.map +1 -1
- package/dist/features/snap/roles.js +2 -1
- package/dist/features/snap/roles.js.map +1 -1
- package/dist/features/snap/rotationSource.js +25 -8
- package/dist/features/snap/rotationSource.js.map +1 -1
- package/dist/features/snap/sceneSession2d.js +6 -4
- package/dist/features/snap/sceneSession2d.js.map +1 -1
- package/dist/features/snap/session2d.js +37 -23
- package/dist/features/snap/session2d.js.map +1 -1
- package/dist/features/snap/spanSnap.js +5 -2
- package/dist/features/snap/spanSnap.js.map +1 -1
- package/dist/icons.table.js +1 -1
- package/dist/icons.table.js.map +1 -1
- package/dist/icons.table.json +0 -10
- package/dist/index.d.ts +701 -696
- package/dist/index.js +35 -29
- package/dist/interactions/actions/align/action.js +6 -4
- package/dist/interactions/actions/align/action.js.map +1 -1
- package/dist/interactions/actions/align/align.js.map +1 -1
- package/dist/interactions/actions/booleans/action.js +7 -1
- package/dist/interactions/actions/booleans/action.js.map +1 -1
- package/dist/interactions/actions/clone/action.js +1 -1
- package/dist/interactions/actions/clone/behaviors.js +4 -4
- package/dist/interactions/actions/clone/behaviors.js.map +1 -1
- package/dist/interactions/actions/defaults/bendSegment.js +1 -1
- package/dist/interactions/actions/defaults/bendSegment.js.map +1 -1
- package/dist/interactions/actions/defaults/createPaintAction.js +1 -1
- package/dist/interactions/actions/defaults/delete.js +48 -39
- package/dist/interactions/actions/defaults/delete.js.map +1 -1
- package/dist/interactions/actions/defaults/duplicate.js +1 -1
- package/dist/interactions/actions/defaults/group.js +2 -2
- package/dist/interactions/actions/defaults/ingest.js +7 -5
- package/dist/interactions/actions/defaults/ingest.js.map +1 -1
- package/dist/interactions/actions/defaults/nudge.js +13 -38
- package/dist/interactions/actions/defaults/nudge.js.map +1 -1
- package/dist/interactions/actions/distribute/action.js +6 -4
- package/dist/interactions/actions/distribute/action.js.map +1 -1
- package/dist/interactions/actions/drawnBoxes.js +2 -1
- package/dist/interactions/actions/drawnBoxes.js.map +1 -1
- package/dist/interactions/actions/edit-anchors/action.js +3 -6
- package/dist/interactions/actions/edit-anchors/action.js.map +1 -1
- package/dist/interactions/actions/edit-anchors/preview.js +41 -8
- package/dist/interactions/actions/edit-anchors/preview.js.map +1 -1
- package/dist/interactions/actions/flip/action.js +6 -4
- package/dist/interactions/actions/flip/action.js.map +1 -1
- package/dist/interactions/actions/gestureLifecycle.js +5 -2
- package/dist/interactions/actions/gestureLifecycle.js.map +1 -1
- package/dist/interactions/actions/insert/action.js +1 -2
- package/dist/interactions/actions/insert/action.js.map +1 -1
- package/dist/interactions/actions/insert/commit.js +1 -1
- package/dist/interactions/actions/insert/parent.js +11 -0
- package/dist/interactions/actions/insert/parent.js.map +1 -0
- package/dist/interactions/actions/insert/placeAction.js +7 -2
- package/dist/interactions/actions/insert/placeAction.js.map +1 -1
- package/dist/interactions/actions/insert/session.js +5 -1
- package/dist/interactions/actions/insert/session.js.map +1 -1
- package/dist/interactions/actions/move/action.js +8 -4
- package/dist/interactions/actions/move/action.js.map +1 -1
- package/dist/interactions/actions/move/behaviors/layoutDrop.js +0 -1
- package/dist/interactions/actions/move/behaviors/layoutDrop.js.map +1 -1
- package/dist/interactions/actions/move/behaviors/snapToContainer.js +1 -1
- package/dist/interactions/actions/move/capture.js +1 -1
- package/dist/interactions/actions/move/commit/index.js +0 -2
- package/dist/interactions/actions/move/commit/index.js.map +1 -1
- package/dist/interactions/actions/move/commit/layoutDrop.js +2 -2
- package/dist/interactions/actions/move/commit/layoutRelease.js +2 -2
- package/dist/interactions/actions/move/commit/reparent.js +11 -5
- package/dist/interactions/actions/move/commit/reparent.js.map +1 -1
- package/dist/interactions/actions/move/frame.js +4 -10
- package/dist/interactions/actions/move/frame.js.map +1 -1
- package/dist/interactions/actions/move/landedDescendants.js +20 -17
- package/dist/interactions/actions/move/landedDescendants.js.map +1 -1
- package/dist/interactions/actions/move/layout/container.js +2 -1
- package/dist/interactions/actions/move/layout/container.js.map +1 -1
- package/dist/interactions/actions/move/layout/dragged.js +1 -1
- package/dist/interactions/actions/move/layout/dropPass.js +18 -1
- package/dist/interactions/actions/move/layout/dropPass.js.map +1 -1
- package/dist/interactions/actions/move/layout/exits.js +5 -5
- package/dist/interactions/actions/move/layout/exits.js.map +1 -1
- package/dist/interactions/actions/move/session.js +14 -3
- package/dist/interactions/actions/move/session.js.map +1 -1
- package/dist/interactions/actions/move/translate.js +8 -37
- package/dist/interactions/actions/move/translate.js.map +1 -1
- package/dist/interactions/actions/moveNodes.js +87 -0
- package/dist/interactions/actions/moveNodes.js.map +1 -0
- package/dist/interactions/actions/planeInput.js.map +1 -1
- package/dist/interactions/actions/resize/action.js +83 -72
- package/dist/interactions/actions/resize/action.js.map +1 -1
- package/dist/interactions/actions/resize/commit.js +2 -1
- package/dist/interactions/actions/resize/commit.js.map +1 -1
- package/dist/interactions/actions/resize/session.js +2 -0
- package/dist/interactions/actions/resize/session.js.map +1 -1
- package/dist/interactions/actions/resolvePreviews.js +3 -3
- package/dist/interactions/actions/resolvePreviews.js.map +1 -1
- package/dist/interactions/actions/rotate/action.js +7 -4
- package/dist/interactions/actions/rotate/action.js.map +1 -1
- package/dist/interactions/actions/rotate/commit.js +2 -1
- package/dist/interactions/actions/rotate/commit.js.map +1 -1
- package/dist/interactions/actions/rotate/session.js +2 -0
- package/dist/interactions/actions/rotate/session.js.map +1 -1
- package/dist/interactions/actions/selectionSubtree.js +13 -2
- package/dist/interactions/actions/selectionSubtree.js.map +1 -1
- package/dist/interactions/actions/testUtils.js.map +1 -1
- package/dist/interactions/actions/transform-again/action.js +5 -6
- package/dist/interactions/actions/transform-again/action.js.map +1 -1
- package/dist/interactions/actions/transformFinish.js +106 -0
- package/dist/interactions/actions/transformFinish.js.map +1 -0
- package/dist/kitSettingsSchema.js +0 -4
- package/dist/kitSettingsSchema.js.map +1 -1
- package/dist/move.d.ts +5 -4
- package/dist/{options-p9GZMXbY.d.ts → options-DBxEryYq.d.ts} +1 -1
- package/dist/resize.d.ts +4 -3
- package/dist/routing.d.ts +1 -1
- package/dist/routing.js +2 -2
- package/dist/test-hook/createTestHook.js +1 -1
- package/dist/test-hook/createTestHook.js.map +1 -1
- package/dist/test-hook/useTestHook.js +2 -1
- package/dist/test-hook/useTestHook.js.map +1 -1
- package/dist/test-seams.d.ts +9 -5
- package/dist/tools/builtin/pen/penHandle.js +3 -5
- package/dist/tools/builtin/pen/penHandle.js.map +1 -1
- package/dist/tools/index.js +26 -0
- package/dist/{types-CHVXGfYm.d.ts → types-7puAuVD2.d.ts} +3 -262
- package/dist/types-BN9hbdMI.d.ts +184 -0
- package/dist/types-CniVctmk.d.ts +949 -0
- package/dist/types-oFeQ5VjV.d.ts +264 -0
- package/dist/version.js +1 -1
- package/package.json +35 -25
- package/dist/canvas/SceneCanvas/useBuiltinRigTools.js +0 -65
- package/dist/canvas/SceneCanvas/useBuiltinRigTools.js.map +0 -1
- package/dist/tools/builtin/bone/boneActions.js +0 -156
- package/dist/tools/builtin/bone/boneActions.js.map +0 -1
- package/dist/tools/builtin/bone/boneOverlay.js +0 -71
- package/dist/tools/builtin/bone/boneOverlay.js.map +0 -1
- package/dist/tools/builtin/bone/placedRig.js +0 -28
- package/dist/tools/builtin/bone/placedRig.js.map +0 -1
- package/dist/tools/builtin/bone/rigEditState.js +0 -30
- package/dist/tools/builtin/bone/rigEditState.js.map +0 -1
- package/dist/tools/builtin/bone/useBoneTool.js +0 -125
- package/dist/tools/builtin/bone/useBoneTool.js.map +0 -1
- package/dist/tools/builtin/weight/useWeightTool.js +0 -101
- package/dist/tools/builtin/weight/useWeightTool.js.map +0 -1
- package/dist/tools/builtin/weight/weightActions.js +0 -140
- package/dist/tools/builtin/weight/weightActions.js.map +0 -1
- package/dist/tools/builtin/weight/weightOverlay.js +0 -78
- package/dist/tools/builtin/weight/weightOverlay.js.map +0 -1
|
@@ -17,7 +17,12 @@ import { useMemo } from "react";
|
|
|
17
17
|
* caller's own api is not wrapped and still writes.
|
|
18
18
|
*/
|
|
19
19
|
function useCanvasSelection(args) {
|
|
20
|
-
const { scene,
|
|
20
|
+
const { scene, selectable, kindOf } = args;
|
|
21
|
+
const { selection: supplied, selectionOptions: options, describeKind } = args.props;
|
|
22
|
+
const describe = useMemo(() => describeKind ?? (kindOf && ((node) => {
|
|
23
|
+
const kind = kindOf(node);
|
|
24
|
+
return kind === "unknown" ? void 0 : kind;
|
|
25
|
+
})), [describeKind, kindOf]);
|
|
21
26
|
const derivedOptions = useMemo(() => ({
|
|
22
27
|
scene,
|
|
23
28
|
...options ?? {}
|
|
@@ -25,7 +30,7 @@ function useCanvasSelection(args) {
|
|
|
25
30
|
const internal = useSelection(derivedOptions);
|
|
26
31
|
const base = supplied ?? internal;
|
|
27
32
|
const selection = selectable ? base : readOnlySelection(base);
|
|
28
|
-
usePublishSceneSelection(scene, selection.current,
|
|
33
|
+
usePublishSceneSelection(scene, selection.current, describe);
|
|
29
34
|
return selection;
|
|
30
35
|
}
|
|
31
36
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCanvasSelection.js","names":[],"sources":["../../../src/canvas/SceneCanvas/useCanvasSelection.ts"],"sourcesContent":["import { useMemo } from 'react';\nimport type { Node, NodeId, Scene } from 'core/scene/types';\nimport { readOnlySelection } from 'core/selection/readOnlySelection';\nimport { usePublishSceneSelection } from 'core/selection/usePublishSceneSelection';\nimport { useSelection, type SelectionApi, type UseSelectionOptions } from 'core/selection/useSelection';\n\n/**\n * The canvas's selection, published to any surrounding selection context.\n *\n * A caller-supplied api wins, and the one built here is then unused. The\n * built one is bound to the scene, so the selection an edit was made under\n * rides on the scene's history entries and undo can put it back.\n *\n * Everything the kit writes selection through (the `selection` dep, the\n * adapter's `setSelection`, ops committed through either) reads the returned\n * api, so on an unselectable canvas every write path here is closed. The\n * caller's own api is not wrapped and still writes.\n */\nexport function useCanvasSelection<TData, TLayer extends string, TPose>(args: {\n scene: Scene<TData, TLayer, TPose>;\n
|
|
1
|
+
{"version":3,"file":"useCanvasSelection.js","names":[],"sources":["../../../src/canvas/SceneCanvas/useCanvasSelection.ts"],"sourcesContent":["import { useMemo } from 'react';\nimport type { Node, NodeId, Scene } from 'core/scene/types';\nimport { readOnlySelection } from 'core/selection/readOnlySelection';\nimport { usePublishSceneSelection } from 'core/selection/usePublishSceneSelection';\nimport { useSelection, type SelectionApi, type UseSelectionOptions } from 'core/selection/useSelection';\nimport type { SceneCanvasProps } from './props';\n\n/**\n * The canvas's selection, published to any surrounding selection context.\n *\n * A caller-supplied api wins, and the one built here is then unused. The\n * built one is bound to the scene, so the selection an edit was made under\n * rides on the scene's history entries and undo can put it back.\n *\n * Everything the kit writes selection through (the `selection` dep, the\n * adapter's `setSelection`, ops committed through either) reads the returned\n * api, so on an unselectable canvas every write path here is closed. The\n * caller's own api is not wrapped and still writes.\n */\nexport function useCanvasSelection<TData, TLayer extends string, TPose>(args: {\n scene: Scene<TData, TLayer, TPose>;\n props: Pick<SceneCanvasProps<TData, TLayer, TPose>, 'selection' | 'selectionOptions' | 'describeKind'>;\n selectable: boolean;\n /** The canvas's routing kind, published for each node unless `describeKind` is given. */\n kindOf: ((node: { kind: string; data: TData }) => string) | undefined;\n}): SelectionApi {\n const { scene, selectable, kindOf } = args;\n const { selection: supplied, selectionOptions: options, describeKind } = args.props;\n const describe = useMemo(() => describeKind ?? (kindOf && ((node: Node<TData, TLayer, TPose>) => {\n const kind = kindOf(node);\n return kind === 'unknown' ? undefined : kind;\n })), [describeKind, kindOf]);\n const derivedOptions = useMemo<UseSelectionOptions>(\n () => ({ scene, ...(options ?? {}) }),\n [scene, options],\n );\n const internal = useSelection(derivedOptions);\n const base = supplied ?? internal;\n const selection = selectable ? base : readOnlySelection(base);\n usePublishSceneSelection(scene, selection.current as readonly NodeId[], describe);\n return selection;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAmBA,SAAgB,mBAAwD,MAMvD;CACf,MAAM,EAAE,OAAO,YAAY,WAAW;CACtC,MAAM,EAAE,WAAW,UAAU,kBAAkB,SAAS,iBAAiB,KAAK;CAC9E,MAAM,WAAW,cAAc,iBAAiB,YAAY,SAAqC;EAC/F,MAAM,OAAO,OAAO,IAAI;EACxB,OAAO,SAAS,YAAY,SAAY;CAC1C,KAAK,CAAC,cAAc,MAAM,CAAC;CAC3B,MAAM,iBAAiB,eACd;EAAE;EAAO,GAAI,WAAW,CAAC;CAAG,IACnC,CAAC,OAAO,OAAO,CACjB;CACA,MAAM,WAAW,aAAa,cAAc;CAC5C,MAAM,OAAO,YAAY;CACzB,MAAM,YAAY,aAAa,OAAO,kBAAkB,IAAI;CAC5D,yBAAyB,OAAO,UAAU,SAA8B,QAAQ;CAChF,OAAO;AACT"}
|
|
@@ -2,7 +2,7 @@ import { createLongPressFeedbackContribution } from "../../features/longPress/lo
|
|
|
2
2
|
import { useTools } from "../../tools/overlayBinding.js";
|
|
3
3
|
import { presetActions, presetAmbient, presetContributions, resolveFeatures } from "./features.js";
|
|
4
4
|
import { useBuiltinTools } from "./useBuiltinTools.js";
|
|
5
|
-
import {
|
|
5
|
+
import { useEffect, useMemo } from "react";
|
|
6
6
|
import { useLatest } from "@weasel-js/react";
|
|
7
7
|
|
|
8
8
|
//#region src/canvas/SceneCanvas/useCanvasTools.ts
|
|
@@ -21,12 +21,16 @@ var entryKey = (e) => {
|
|
|
21
21
|
};
|
|
22
22
|
/** The presets a canvas runs with. Keyed on the entries, not the array, so
|
|
23
23
|
* an inline `features={['draw', pathTools]}` does not re-derive every render. */
|
|
24
|
-
function useEnabledFeatures(features, named, hasViewport) {
|
|
24
|
+
function useEnabledFeatures(features, named, hasViewport, selectRotates) {
|
|
25
25
|
const featuresKey = (features ?? []).map(entryKey).join("|");
|
|
26
|
-
return useMemo(() => resolveFeatures(features, named, {
|
|
26
|
+
return useMemo(() => resolveFeatures(features, named, {
|
|
27
|
+
viewport: hasViewport,
|
|
28
|
+
selectRotates
|
|
29
|
+
}), [
|
|
27
30
|
featuresKey,
|
|
28
31
|
named,
|
|
29
|
-
hasViewport
|
|
32
|
+
hasViewport,
|
|
33
|
+
selectRotates
|
|
30
34
|
]);
|
|
31
35
|
}
|
|
32
36
|
/**
|
|
@@ -37,12 +41,8 @@ function useEnabledFeatures(features, named, hasViewport) {
|
|
|
37
41
|
function useCanvasTools(args) {
|
|
38
42
|
const { select, enabled, tools: toolsProp, defaultTools, toolOptions, inertia, ambient, initialActiveTool, modes, onToolsCreated } = args;
|
|
39
43
|
const takeover = toolsProp && isToolsApi(toolsProp) ? toolsProp : null;
|
|
40
|
-
const selectionRef = useLatest(args.selection);
|
|
41
|
-
const selected = useCallback(() => selectionRef.current.get(), [selectionRef]);
|
|
42
44
|
const registry = useBuiltinTools({
|
|
43
45
|
select,
|
|
44
|
-
scene: args.scene,
|
|
45
|
-
selection: selected,
|
|
46
46
|
enabled,
|
|
47
47
|
defaultTools,
|
|
48
48
|
patch: toolsProp && !isToolsApi(toolsProp) ? toolsProp : null,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCanvasTools.js","names":[],"sources":["../../../src/canvas/SceneCanvas/useCanvasTools.ts"],"sourcesContent":["import {
|
|
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 ?? []), ...(pressFeedback ? [pressFeedback] : [])],\n [enabled, ambient, pressFeedback],\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":";;;;;;;;;;AAuBA,SAAS,WAAW,OAAiD;CACnE,OAAO,OAAQ,MAAmB,cAAc;AAClD;AAEA,IAAM,gCAAgB,IAAI,QAAwB;AAClD,IAAI,aAAa;AACjB,IAAM,YAAY,MAA4B;CAC5C,IAAI,OAAO,MAAM,UAAU,OAAO;CAClC,IAAI,IAAI,cAAc,IAAI,CAAC;CAC3B,IAAI,MAAM,QAAW,cAAc,IAAI,GAAI,IAAI,YAAa;CAC5D,OAAO,IAAI;AACb;;;AAIA,SAAgB,mBACd,UACA,OACA,aACA,eACgB;CAChB,MAAM,eAAe,YAAY,CAAC,EAAA,CAAG,IAAI,QAAQ,CAAC,CAAC,KAAK,GAAG;CAC3D,OAAO,cACC,gBAAgB,UAAU,OAAO;EAAE,UAAU;EAAa;CAAc,CAAC,GAE/E;EAAC;EAAa;EAAO;EAAa;CAAa,CACjD;AACF;;;;;;AAOA,SAAgB,eAAe,MAqB7B;CACA,MAAM,EAAE,QAAQ,SAAS,OAAO,WAAW,cAAc,aAAa,SAAS,SAAS,mBAAmB,OAAO,mBAAmB;CAErI,MAAM,WAAW,aAAa,WAAW,SAAS,IAAI,YAAY;CAClE,MAAM,WAAW,gBAAgB;EAC/B;EACA;EACA;EACA,OAAO,aAAa,CAAC,WAAW,SAAS,IAAI,YAAY;EACzD;EACA;CACF,CAAC;CAED,MAAM,uBAAuB,cAAc,oCAAoC,GAAG,CAAC,CAAC;CACpF,MAAM,cAAc,KAAK,iBAAiB;CAC1C,MAAM,gBAAgB,gBAAgB,OAAO,uBAAuB,eAAe;CACnF,MAAM,gBAAgB,cACd;EAAC,GAAG,oBAAoB,OAAO;EAAG,GAAI,WAAW,CAAC;EAAI,GAAI,gBAAgB,CAAC,aAAa,IAAI,CAAC;CAAE,GACrG;EAAC;EAAS;EAAS;CAAa,CAClC;CAEA,MAAM,WAAW,SAAS;EACxB,QAAQ,sBAAsB,YAAY,WAAW,WAAW;EAChE;EACA,GAAI,cAAc,SAAS,EAAE,SAAS,cAAc,IAAI,CAAC;EACzD,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;CAC3B,CAAC;CACD,MAAM,QAAQ,YAAY;CAE1B,MAAM,oBAAoB,UAAU,cAAc;CAClD,gBAAgB;EAAE,kBAAkB,UAAU,KAAK;CAAG,GAAG,CAAC,OAAO,iBAAiB,CAAC;CAEnF,OAAO;EAAE;EAAO;EAAU;CAAS;AACrC;;;;;;;;;AAUA,SAAgB,mBAAmB,MAO8B;CAC/D,MAAM,EAAE,SAAS,MAAM,OAAO,MAAM,WAAW;CAC/C,MAAM,gBAAgB,cACd,cAAc,SAAS;EAAE;EAAM;CAAO,CAAC,GAC7C;EAAC;EAAS;EAAM;CAAM,CACxB;CAgBA,OAAO;EAAE;EAAe,iBAfA,cAAc;GACpC,MAAM,MAAM,cAAc,OAAO;GACjC,MAAM,OAAO,IAAI,IAAI,IAAI,KAAK,MAAM,EAAE,EAAE,CAAC;GACzC,MAAM,wBAAQ,IAAI,IAAY;GAC9B,KAAK,MAAM,SAAS;IAAC,GAAG,OAAO,OAAO,MAAM,QAAQ;IAAG,GAAG,MAAM;IAAS,GAAG;GAAa,GACvF,KAAK,MAAM,KAAK,MAAM,YAAY,CAAC,GAAG,MAAM,IAAI,EAAE,QAAQ;GAE5D,KAAK,MAAM,KAAK,MACd,IAAI,MAAM,IAAI,EAAE,EAAE,KAAK,CAAC,KAAK,IAAI,EAAE,EAAE,GAAG;IACtC,KAAK,IAAI,EAAE,EAAE;IACb,IAAI,KAAK,CAAC;GACZ;GAEF,OAAO;EACT,GAAG;GAAC;GAAS;GAAM;GAAO;EAAa,CACf;CAAgB;AAC1C"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createNodeRouting } from "../../core/scene/NodeRouting.js";
|
|
2
1
|
import { inferredNodeRouting } from "./defaultNodeRouting.js";
|
|
2
|
+
import { createNodeRouting } from "../../core/scene/NodeRouting.js";
|
|
3
3
|
import { useMemo } from "react";
|
|
4
4
|
|
|
5
5
|
//#region src/canvas/SceneCanvas/useNodeKinds.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useNodeKinds.js","names":[],"sources":["../../../src/canvas/SceneCanvas/useNodeKinds.ts"],"sourcesContent":["import { useMemo } from 'react';\nimport { createNodeRouting, type NodeRoutingEntry } from 'core/scene/NodeRouting';\nimport type { NodeId, Scene } from 'core/scene/types';\nimport { inferredNodeRouting } from './defaultNodeRouting';\n\n/**\n * The canvas's routing-trait classifier, by node data and by node id, so\n * `target: 'kind:text'` on a binding speaks the vocabulary of `Hit.kind`.\n * Unset `routing` falls back to `inferredNodeRouting`. An explicit empty list\n * opts out: both are undefined, every hit is `kind: 'unknown'`, and no\n * `kind:` target form can match.\n */\nexport function useNodeKinds<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n routing: readonly NodeRoutingEntry[] | undefined,\n):
|
|
1
|
+
{"version":3,"file":"useNodeKinds.js","names":[],"sources":["../../../src/canvas/SceneCanvas/useNodeKinds.ts"],"sourcesContent":["import { useMemo } from 'react';\nimport { createNodeRouting, type NodeRoutingEntry } from 'core/scene/NodeRouting';\nimport type { NodeId, Scene } from 'core/scene/types';\nimport { inferredNodeRouting } from './defaultNodeRouting';\n\nexport interface NodeKinds<TData> {\n kindOf: ((node: { kind: string; data: TData }) => string) | undefined;\n kindOfNode: ((id: string) => string | undefined) | undefined;\n}\n\n/**\n * The canvas's routing-trait classifier, by node data and by node id, so\n * `target: 'kind:text'` on a binding speaks the vocabulary of `Hit.kind`.\n * Unset `routing` falls back to `inferredNodeRouting`. An explicit empty list\n * opts out: both are undefined, every hit is `kind: 'unknown'`, and no\n * `kind:` target form can match.\n */\nexport function useNodeKinds<TData, TLayer extends string, TPose>(\n scene: Scene<TData, TLayer, TPose>,\n routing: readonly NodeRoutingEntry[] | undefined,\n): NodeKinds<TData> {\n const kindOf = useMemo(() => {\n const effective = routing === undefined ? inferredNodeRouting : routing;\n if (effective.length === 0) return undefined;\n const registry = createNodeRouting();\n for (const k of effective) registry.register(k);\n return (node: { kind: string; data: TData }) => registry.classify(node.data, node);\n }, [routing]);\n\n const kindOfNode = useMemo(() => {\n if (!kindOf) return undefined;\n return (id: string): string | undefined => {\n const node = scene.get(id as NodeId);\n return node ? kindOf(node) : undefined;\n };\n }, [kindOf, scene]);\n\n return { kindOf, kindOfNode };\n}\n"],"mappings":";;;;;;;;;;;;AAiBA,SAAgB,aACd,OACA,SACkB;CAClB,MAAM,SAAS,cAAc;EAC3B,MAAM,YAAY,YAAY,SAAY,sBAAsB;EAChE,IAAI,UAAU,WAAW,GAAG,OAAO;EACnC,MAAM,WAAW,kBAAkB;EACnC,KAAK,MAAM,KAAK,WAAW,SAAS,SAAS,CAAC;EAC9C,QAAQ,SAAwC,SAAS,SAAS,KAAK,MAAM,IAAI;CACnF,GAAG,CAAC,OAAO,CAAC;CAUZ,OAAO;EAAE;EAAQ,YARE,cAAc;GAC/B,IAAI,CAAC,QAAQ,OAAO;GACpB,QAAQ,OAAmC;IACzC,MAAM,OAAO,MAAM,IAAI,EAAY;IACnC,OAAO,OAAO,OAAO,IAAI,IAAI;GAC/B;EACF,GAAG,CAAC,QAAQ,KAAK,CAEA;CAAW;AAC9B"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { targetSizesPx } from "../../core/device/targets.js";
|
|
2
|
+
import { drawnBoundsScene } from "../derivedPath.js";
|
|
2
3
|
import { createSnapOutcomeStore } from "../../features/snap/outcomeStore.js";
|
|
3
4
|
import { builtLayersOf, isCustomEntry } from "../layerSlots.js";
|
|
4
5
|
import { useTargetBasePx } from "../../core/device/useTargetBasePx.js";
|
|
@@ -60,7 +61,17 @@ function useSceneCanvasLayers(args) {
|
|
|
60
61
|
const chromeAffordancesRef = useRef([]);
|
|
61
62
|
const slopsLayer = useMemo(() => createSlopsDebugLayer({ getAffordances: () => chromeAffordancesRef.current }), []);
|
|
62
63
|
const [snapStore] = useState(() => createSnapOutcomeStore());
|
|
63
|
-
const
|
|
64
|
+
const { color, gapColor, lineWidth } = args.snapReadoutStyle ?? {};
|
|
65
|
+
const snapReadoutLayer = useMemo(() => createSnapReadoutLayer(snapStore, {
|
|
66
|
+
...color !== void 0 ? { color } : {},
|
|
67
|
+
...gapColor !== void 0 ? { gapColor } : {},
|
|
68
|
+
...lineWidth !== void 0 ? { lineWidth } : {}
|
|
69
|
+
}), [
|
|
70
|
+
snapStore,
|
|
71
|
+
color,
|
|
72
|
+
gapColor,
|
|
73
|
+
lineWidth
|
|
74
|
+
]);
|
|
64
75
|
const overlaySlot = merged.pathEditingOverlay;
|
|
65
76
|
const overlayConfig = overlaySlot && !isCustomEntry(overlaySlot) ? overlaySlot : void 0;
|
|
66
77
|
const overlayAnchor = overlaySlot === null ? "dispatcherOverlay" : "pathEditingOverlay";
|
|
@@ -68,7 +79,7 @@ function useSceneCanvasLayers(args) {
|
|
|
68
79
|
nodeOf: (id) => scene.get(id),
|
|
69
80
|
planeOfNode: args.planeOfNode
|
|
70
81
|
}, overlayConfig);
|
|
71
|
-
const { layer: selectionOverlayLayer, rotationBadge } = useSelectionOverlayLayer(merged.selectionOverlay, pathEditState.getEditingIds, poseDescriptor, args.boundsIncludeStroke ? scene : void 0, resizeHandles);
|
|
82
|
+
const { layer: selectionOverlayLayer, rotationBadge } = useSelectionOverlayLayer(merged.selectionOverlay, pathEditState.getEditingIds, poseDescriptor, args.boundsIncludeStroke ? drawnBoundsScene(scene) : void 0, resizeHandles);
|
|
72
83
|
return {
|
|
73
84
|
layers: useMemo(() => ({
|
|
74
85
|
...builtLayersOf(merged),
|
|
@@ -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 type { DrawnBoundsScene } from '../drawnBounds';\nimport { createSnapOutcomeStore, type SnapOutcomeStore } from 'features/snap/outcomeStore';\nimport { createSnapReadoutLayer, type SnapReadoutStyle } from 'features/snap/readoutLayer';\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 const snapReadoutLayer = useMemo(() => createSnapReadoutLayer(snapStore, args.snapReadoutStyle), [snapStore, args.snapReadoutStyle]);\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 ? (scene as unknown as DrawnBoundsScene) : 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":";;;;;;;;;;;;;;;;;;;;;;;;AAsCA,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;CAC3D,MAAM,mBAAmB,cAAc,uBAAuB,WAAW,KAAK,gBAAgB,GAAG,CAAC,WAAW,KAAK,gBAAgB,CAAC;CACnI,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,sBAAuB,QAAwC,QACpE,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 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"}
|
|
@@ -3,11 +3,9 @@ import { useCanvasTools, useFeatureBindings } from "./useCanvasTools.js";
|
|
|
3
3
|
//#region src/canvas/SceneCanvas/useSceneCanvasTools.ts
|
|
4
4
|
/** The canvas's tools, and the contributions and standard actions its presets bring. */
|
|
5
5
|
function useSceneCanvasTools(args) {
|
|
6
|
-
const { props, select,
|
|
6
|
+
const { props, select, enabled } = args;
|
|
7
7
|
const { tools, internal, takeover } = useCanvasTools({
|
|
8
8
|
select,
|
|
9
|
-
scene,
|
|
10
|
-
selection,
|
|
11
9
|
enabled,
|
|
12
10
|
tools: props.tools,
|
|
13
11
|
defaultTools: props.defaultTools,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSceneCanvasTools.js","names":[],"sources":["../../../src/canvas/SceneCanvas/useSceneCanvasTools.ts"],"sourcesContent":["import type {
|
|
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"}
|
|
@@ -16,7 +16,7 @@ import { useLatest } from "@weasel-js/react";
|
|
|
16
16
|
* pointer to capture, so whichever view is under the cursor owns the point.
|
|
17
17
|
*/
|
|
18
18
|
function useSceneHover(args) {
|
|
19
|
-
const { canvasApiRef, viewRef, nodeAtPointRef, enabled } = args;
|
|
19
|
+
const { canvasApiRef, viewRef, nodeAtPointRef, enabled, onChange } = args;
|
|
20
20
|
const surfaceViewRegistry = useOptionalViewRegistry();
|
|
21
21
|
return useHoverTracking({
|
|
22
22
|
nodeAtClientPoint: (clientX, clientY) => {
|
|
@@ -32,7 +32,8 @@ function useSceneHover(args) {
|
|
|
32
32
|
} : view);
|
|
33
33
|
return hit ? { id: hit.id } : null;
|
|
34
34
|
},
|
|
35
|
-
enabled
|
|
35
|
+
enabled,
|
|
36
|
+
...onChange ? { onChange } : {}
|
|
36
37
|
});
|
|
37
38
|
}
|
|
38
39
|
/**
|
|
@@ -42,17 +43,16 @@ function useSceneHover(args) {
|
|
|
42
43
|
* An unselectable canvas hides the marquee and lasso by default: every
|
|
43
44
|
* selection write no-ops there, so the select tool's empty-drag binding still
|
|
44
45
|
* claims the gesture, keeping it from falling through to an ambient drag such
|
|
45
|
-
* as insert, but paints nothing.
|
|
46
|
+
* as insert, but paints nothing.
|
|
46
47
|
*/
|
|
47
48
|
function useChromeRules(args) {
|
|
48
|
-
const { enabled, selectable,
|
|
49
|
+
const { enabled, selectable, chromeVisibility } = args;
|
|
49
50
|
return useMemo(() => {
|
|
50
51
|
const defaults = presetChromeRules(enabled, PRESET_OWNED_CHROME);
|
|
51
52
|
if (!selectable) {
|
|
52
53
|
defaults["action.marquee"] = never;
|
|
53
54
|
defaults["action.lasso"] = never;
|
|
54
55
|
}
|
|
55
|
-
if (rotateOff) defaults["selection.rotation-handle"] = never;
|
|
56
56
|
return {
|
|
57
57
|
...defaults,
|
|
58
58
|
...chromeVisibility
|
|
@@ -60,7 +60,6 @@ function useChromeRules(args) {
|
|
|
60
60
|
}, [
|
|
61
61
|
chromeVisibility,
|
|
62
62
|
selectable,
|
|
63
|
-
rotateOff,
|
|
64
63
|
enabled
|
|
65
64
|
]);
|
|
66
65
|
}
|
|
@@ -93,7 +92,7 @@ function useSceneCanvasChrome(args) {
|
|
|
93
92
|
const { getHover, canvasRef: trackHoverOn } = useSceneHover({
|
|
94
93
|
...args.hover,
|
|
95
94
|
viewRef,
|
|
96
|
-
enabled: rules.chromeVisibility !== void 0
|
|
95
|
+
enabled: rules.chromeVisibility !== void 0 || args.hover.onChange !== void 0
|
|
97
96
|
});
|
|
98
97
|
return {
|
|
99
98
|
...useSurfaceChromeCaps({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSceneChrome.js","names":[],"sources":["../../../src/canvas/SceneCanvas/useSceneChrome.ts"],"sourcesContent":["import { useCallback, useMemo, type RefObject } from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport type { Dispatcher, RuleCtx } from '@weasel-js/routing';\nimport type { NodeId } from 'core/scene/types';\nimport type { View } from 'core/viewport/view';\nimport { never, useHoverTracking, type HoverTracking, type VisibilityRules } from 'features/chrome-caps';\nimport {\n useChromeCaps,\n type ChromeCaps,\n type ChromeCapsSource,\n type ViewRuleInputs,\n} from 'features/chrome-caps/useChromeCaps';\nimport type { CanvasExtensionApi } from '../canvasExtension';\nimport { useOptionalViewRegistry } from '../viewRegistry';\nimport { presetChromeRules, type EnabledPresets } from './features';\nimport { PRESET_OWNED_CHROME } from './presets/chrome';\nimport type { NodeAtPoint } from './useNodeAtPoint';\nimport { worldPointAt } from './worldPointAt';\n\n/**\n * The node under the pointer, for `RuleCtx.hover`. One lookup, not a\n * client-to-world thunk beside a picker: the point and the camera its\n * tolerance converts against have to come from the same view. Hover has no\n * pointer to capture, so whichever view is under the cursor owns the point.\n */\nexport function useSceneHover(args: {\n canvasApiRef: RefObject<Pick<CanvasExtensionApi, 'clientToWorld'> | null>;\n /** The surface's own camera, for a point no declared view claims. */\n viewRef: { readonly current: View };\n nodeAtPointRef: { readonly current: NodeAtPoint | undefined };\n enabled: boolean;\n}): HoverTracking {\n const { canvasApiRef, viewRef, nodeAtPointRef, enabled } = args;\n const surfaceViewRegistry = useOptionalViewRegistry();\n return useHoverTracking({\n nodeAtClientPoint: (clientX, clientY) => {\n const api = canvasApiRef.current;\n if (!api) return null;\n const target = surfaceViewRegistry?.resolver.at(null, clientX, clientY);\n const view = target?.view ?? viewRef.current;\n const [x, y] = worldPointAt(api, target, clientX, clientY);\n // A view judges its own layers; the surface's gate is the picker's default.\n const reg = target?.id != null\n ? surfaceViewRegistry?.list().find((r) => r.id === target.id)\n : undefined;\n const hit = nodeAtPointRef.current?.(\n x, y, reg ? { scale: view.scale, layerIsPainted: reg.layerIsPainted } : view,\n );\n return hit ? { id: hit.id as NodeId } : null;\n },\n enabled,\n });\n}\n\n/**\n * The surface's chrome-visibility rules: the presets' defaults under the\n * consumer's `chromeVisibility`, which wins.\n *\n * An unselectable canvas hides the marquee and lasso by default: every\n * selection write no-ops there, so the select tool's empty-drag binding still\n * claims the gesture, keeping it from falling through to an ambient drag such\n * as insert, but paints nothing
|
|
1
|
+
{"version":3,"file":"useSceneChrome.js","names":[],"sources":["../../../src/canvas/SceneCanvas/useSceneChrome.ts"],"sourcesContent":["import { useCallback, useMemo, type RefObject } from 'react';\nimport { useLatest } from '@weasel-js/react';\nimport type { Dispatcher, RuleCtx } from '@weasel-js/routing';\nimport type { NodeId } from 'core/scene/types';\nimport type { View } from 'core/viewport/view';\nimport { never, useHoverTracking, type HoverTracking, type VisibilityRules } from 'features/chrome-caps';\nimport {\n useChromeCaps,\n type ChromeCaps,\n type ChromeCapsSource,\n type ViewRuleInputs,\n} from 'features/chrome-caps/useChromeCaps';\nimport type { CanvasExtensionApi } from '../canvasExtension';\nimport { useOptionalViewRegistry } from '../viewRegistry';\nimport { presetChromeRules, type EnabledPresets } from './features';\nimport { PRESET_OWNED_CHROME } from './presets/chrome';\nimport type { NodeAtPoint } from './useNodeAtPoint';\nimport { worldPointAt } from './worldPointAt';\n\n/**\n * The node under the pointer, for `RuleCtx.hover`. One lookup, not a\n * client-to-world thunk beside a picker: the point and the camera its\n * tolerance converts against have to come from the same view. Hover has no\n * pointer to capture, so whichever view is under the cursor owns the point.\n */\nexport function useSceneHover(args: {\n canvasApiRef: RefObject<Pick<CanvasExtensionApi, 'clientToWorld'> | null>;\n /** The surface's own camera, for a point no declared view claims. */\n viewRef: { readonly current: View };\n nodeAtPointRef: { readonly current: NodeAtPoint | undefined };\n enabled: boolean;\n onChange?: (id: NodeId | null) => void;\n}): HoverTracking {\n const { canvasApiRef, viewRef, nodeAtPointRef, enabled, onChange } = args;\n const surfaceViewRegistry = useOptionalViewRegistry();\n return useHoverTracking({\n nodeAtClientPoint: (clientX, clientY) => {\n const api = canvasApiRef.current;\n if (!api) return null;\n const target = surfaceViewRegistry?.resolver.at(null, clientX, clientY);\n const view = target?.view ?? viewRef.current;\n const [x, y] = worldPointAt(api, target, clientX, clientY);\n // A view judges its own layers; the surface's gate is the picker's default.\n const reg = target?.id != null\n ? surfaceViewRegistry?.list().find((r) => r.id === target.id)\n : undefined;\n const hit = nodeAtPointRef.current?.(\n x, y, reg ? { scale: view.scale, layerIsPainted: reg.layerIsPainted } : view,\n );\n return hit ? { id: hit.id as NodeId } : null;\n },\n enabled,\n ...(onChange ? { onChange } : {}),\n });\n}\n\n/**\n * The surface's chrome-visibility rules: the presets' defaults under the\n * consumer's `chromeVisibility`, which wins.\n *\n * An unselectable canvas hides the marquee and lasso by default: every\n * selection write no-ops there, so the select tool's empty-drag binding still\n * claims the gesture, keeping it from falling through to an ambient drag such\n * as insert, but paints nothing.\n */\nexport function useChromeRules(args: {\n enabled: EnabledPresets;\n selectable: boolean;\n chromeVisibility: VisibilityRules | undefined;\n}): VisibilityRules {\n const { enabled, selectable, chromeVisibility } = args;\n return useMemo(() => {\n const defaults = presetChromeRules(enabled, PRESET_OWNED_CHROME);\n if (!selectable) {\n defaults['action.marquee'] = never;\n defaults['action.lasso'] = never;\n }\n return { ...defaults, ...chromeVisibility };\n }, [chromeVisibility, selectable, enabled]);\n}\n\n/** The chrome-caps answers for the surface's own view. */\nexport function useSurfaceChromeCaps(args: {\n source: ChromeCapsSource;\n /** The selection as rendered. `SelectionApi.current` can be a live getter\n * that runs ahead of the commit the painted chrome was built from. */\n selection: readonly NodeId[];\n viewRef: { readonly current: View };\n dispatcher: Pick<Dispatcher, 'getActiveAction'>;\n}): {\n chromeCaps: ChromeCaps;\n getIsVisible: () => (id: string) => boolean;\n getRuleCtx: () => RuleCtx | undefined;\n} {\n const { viewRef, dispatcher } = args;\n const chromeCaps = useChromeCaps(args.source);\n const committedSelectionRef = useLatest(args.selection);\n const currentRuleInputs = useCallback((): ViewRuleInputs => ({\n selection: committedSelectionRef.current,\n view: viewRef.current,\n action: dispatcher.getActiveAction(),\n }), [dispatcher, committedSelectionRef, viewRef]);\n const getIsVisible = useCallback(\n (): ((id: string) => boolean) => chromeCaps.isVisible(currentRuleInputs()),\n [chromeCaps, currentRuleInputs],\n );\n const getRuleCtx = useCallback(\n () => chromeCaps.ruleCtx(currentRuleInputs()),\n [chromeCaps, currentRuleInputs],\n );\n return { chromeCaps, getIsVisible, getRuleCtx };\n}\n\n/**\n * The surface's chrome: hover tracking, the visibility rules, and the chrome-caps answers they\n * feed. `source` is the chrome-caps source minus the parts built here.\n */\nexport function useSceneCanvasChrome(args: {\n source: Omit<ChromeCapsSource, 'rules' | 'getHover'>;\n rules: Parameters<typeof useChromeRules>[0];\n hover: Omit<Parameters<typeof useSceneHover>[0], 'enabled' | 'viewRef'>;\n selection: readonly NodeId[];\n viewRef: { readonly current: View };\n dispatcher: Pick<Dispatcher, 'getActiveAction'>;\n}) {\n const { rules, viewRef } = args;\n const { getHover, canvasRef: trackHoverOn } = useSceneHover({\n ...args.hover, viewRef, enabled: rules.chromeVisibility !== undefined || args.hover.onChange !== undefined,\n });\n const chromeRules = useChromeRules(rules);\n const caps = useSurfaceChromeCaps({\n source: { rules: chromeRules, getHover, ...args.source },\n selection: args.selection,\n viewRef,\n dispatcher: args.dispatcher,\n });\n return { ...caps, trackHoverOn };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAyBA,SAAgB,cAAc,MAOZ;CAChB,MAAM,EAAE,cAAc,SAAS,gBAAgB,SAAS,aAAa;CACrE,MAAM,sBAAsB,wBAAwB;CACpD,OAAO,iBAAiB;EACtB,oBAAoB,SAAS,YAAY;GACvC,MAAM,MAAM,aAAa;GACzB,IAAI,CAAC,KAAK,OAAO;GACjB,MAAM,SAAS,qBAAqB,SAAS,GAAG,MAAM,SAAS,OAAO;GACtE,MAAM,OAAO,QAAQ,QAAQ,QAAQ;GACrC,MAAM,CAAC,GAAG,KAAK,aAAa,KAAK,QAAQ,SAAS,OAAO;GAEzD,MAAM,MAAM,QAAQ,MAAM,OACtB,qBAAqB,KAAK,CAAC,CAAC,MAAM,MAAM,EAAE,OAAO,OAAO,EAAE,IAC1D;GACJ,MAAM,MAAM,eAAe,UACzB,GAAG,GAAG,MAAM;IAAE,OAAO,KAAK;IAAO,gBAAgB,IAAI;GAAe,IAAI,IAC1E;GACA,OAAO,MAAM,EAAE,IAAI,IAAI,GAAa,IAAI;EAC1C;EACA;EACA,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;CACjC,CAAC;AACH;;;;;;;;;;AAWA,SAAgB,eAAe,MAIX;CAClB,MAAM,EAAE,SAAS,YAAY,qBAAqB;CAClD,OAAO,cAAc;EACnB,MAAM,WAAW,kBAAkB,SAAS,mBAAmB;EAC/D,IAAI,CAAC,YAAY;GACf,SAAS,oBAAoB;GAC7B,SAAS,kBAAkB;EAC7B;EACA,OAAO;GAAE,GAAG;GAAU,GAAG;EAAiB;CAC5C,GAAG;EAAC;EAAkB;EAAY;CAAO,CAAC;AAC5C;;AAGA,SAAgB,qBAAqB,MAWnC;CACA,MAAM,EAAE,SAAS,eAAe;CAChC,MAAM,aAAa,cAAc,KAAK,MAAM;CAC5C,MAAM,wBAAwB,UAAU,KAAK,SAAS;CACtD,MAAM,oBAAoB,mBAAmC;EAC3D,WAAW,sBAAsB;EACjC,MAAM,QAAQ;EACd,QAAQ,WAAW,gBAAgB;CACrC,IAAI;EAAC;EAAY;EAAuB;CAAO,CAAC;CAShD,OAAO;EAAE;EAAY,cARA,kBACc,WAAW,UAAU,kBAAkB,CAAC,GACzE,CAAC,YAAY,iBAAiB,CAMX;EAAc,YAJhB,kBACX,WAAW,QAAQ,kBAAkB,CAAC,GAC5C,CAAC,YAAY,iBAAiB,CAEG;CAAW;AAChD;;;;;AAMA,SAAgB,qBAAqB,MAOlC;CACD,MAAM,EAAE,OAAO,YAAY;CAC3B,MAAM,EAAE,UAAU,WAAW,iBAAiB,cAAc;EAC1D,GAAG,KAAK;EAAO;EAAS,SAAS,MAAM,qBAAqB,UAAa,KAAK,MAAM,aAAa;CACnG,CAAC;CAQD,OAAO;EAAE,GANI,qBAAqB;GAChC,QAAQ;IAAE,OAFQ,eAAe,KAEhB;IAAa;IAAU,GAAG,KAAK;GAAO;GACvD,WAAW,KAAK;GAChB;GACA,YAAY,KAAK;EACnB,CACY;EAAM;CAAa;AACjC"}
|
|
@@ -86,7 +86,7 @@ function useClassifyTarget(inputs, kindOfNodeRef) {
|
|
|
86
86
|
}
|
|
87
87
|
/** Runs this canvas's gesture dispatcher over its tools, contributions, and chrome. */
|
|
88
88
|
function useSceneGestureDispatcher(inputs) {
|
|
89
|
-
const { canvasRef, canvasApiRef, tools, contributions, enabled, keyboard, claimKeys, viewRef, kindOfNode, dispatcher, getRuleCtx, onDoubleClick, onClick, longPress: { duration: longPressDuration, haptics: longPressHaptics } = {} } = inputs;
|
|
89
|
+
const { canvasRef, canvasApiRef, tools, contributions, enabled, keyboard, claimKeys, viewRef, kindOfNode, dispatcher, traces, getRuleCtx, onDoubleClick, onClick, longPress: { duration: longPressDuration, haptics: longPressHaptics } = {} } = inputs;
|
|
90
90
|
const registry = useActionsRegistry();
|
|
91
91
|
const depRegistry = useDepRegistry();
|
|
92
92
|
const viewRegistry = useOptionalViewRegistry();
|
|
@@ -111,12 +111,13 @@ function useSceneGestureDispatcher(inputs) {
|
|
|
111
111
|
x,
|
|
112
112
|
y
|
|
113
113
|
};
|
|
114
|
-
traceConversion(clientX, clientY, api ? canvasRef.current : null, viewRef?.current, world);
|
|
114
|
+
traceConversion(traces.coords, clientX, clientY, api ? canvasRef.current : null, viewRef?.current, world);
|
|
115
115
|
return world;
|
|
116
116
|
}, [
|
|
117
117
|
canvasRef,
|
|
118
118
|
canvasApiRef,
|
|
119
|
-
viewRef
|
|
119
|
+
viewRef,
|
|
120
|
+
traces.coords
|
|
120
121
|
]);
|
|
121
122
|
const requestRedraw = useCallback(() => {
|
|
122
123
|
canvasApiRef.current?.requestRedraw?.();
|
|
@@ -144,6 +145,7 @@ function useSceneGestureDispatcher(inputs) {
|
|
|
144
145
|
affordanceAt: affordanceWithLayers,
|
|
145
146
|
classifyTarget,
|
|
146
147
|
dispatcher,
|
|
148
|
+
trace: traces.dispatch,
|
|
147
149
|
clientToWorld,
|
|
148
150
|
requestRedraw,
|
|
149
151
|
paintedCursor,
|
|
@@ -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 } 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 } 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 /** 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, 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(clientX, clientY, api ? canvasRef.current : null, viewRef?.current, world);\n return world;\n }, [canvasRef, canvasApiRef, viewRef]);\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 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, 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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"}
|
|
@@ -5,7 +5,6 @@ import { composeAlphaFor } from "../sceneSlotWiring.js";
|
|
|
5
5
|
import { doubleClickTarget } from "../doubleClickTarget.js";
|
|
6
6
|
import { useSceneAdapter } from "../useSceneAdapter.js";
|
|
7
7
|
import { useNodeAtPoint } from "./useNodeAtPoint.js";
|
|
8
|
-
import { useNodeKinds } from "./useNodeKinds.js";
|
|
9
8
|
import { useSceneSelectTool } from "./useSceneSelectTool.js";
|
|
10
9
|
import { useMemo } from "react";
|
|
11
10
|
import { useLatest } from "@weasel-js/react";
|
|
@@ -17,8 +16,8 @@ import { useLatest } from "@weasel-js/react";
|
|
|
17
16
|
*/
|
|
18
17
|
function useScenePicking(args) {
|
|
19
18
|
const { scene, selection, descriptor, viewRef } = args;
|
|
20
|
-
const { geometry,
|
|
21
|
-
const { kindOf, kindOfNode } =
|
|
19
|
+
const { geometry, alphaFor, layerVisibility, layerOrder, isPointerInteractive } = args.props;
|
|
20
|
+
const { kindOf, kindOfNode } = args.kinds;
|
|
22
21
|
const composedAlphaFor = useMemo(() => composeAlphaFor(scene, alphaFor), [scene, alphaFor]);
|
|
23
22
|
const surfaceViewRegistry = useOptionalViewRegistry();
|
|
24
23
|
const viewLayerGate = useViewLayerGate(layerVisibility, layerOrder, () => surfaceViewRegistry?.surface()?.layers() ?? []);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useScenePicking.js","names":[],"sources":["../../../src/canvas/SceneCanvas/useScenePicking.ts"],"sourcesContent":["import { useMemo, type RefObject } from 'react';\nimport { asNodeId, type Scene } from 'core/scene/types';\nimport { useLatest } from '@weasel-js/react';\nimport { doubleClickTarget } from '../doubleClickTarget';\nimport type { SelectionApi } from 'core/selection/useSelection';\nimport type { View } from 'core/viewport/view';\nimport type { PoseDescriptor } from 'interactions/actions/resize/geometry';\nimport { composeAlphaFor } from '../sceneSlotWiring';\nimport { useSceneAdapter } from '../useSceneAdapter';\nimport type { SceneCanvasProps } from './props';\nimport { useNodeAtPoint } from './useNodeAtPoint';\nimport {
|
|
1
|
+
{"version":3,"file":"useScenePicking.js","names":[],"sources":["../../../src/canvas/SceneCanvas/useScenePicking.ts"],"sourcesContent":["import { useMemo, type RefObject } from 'react';\nimport { asNodeId, type Scene } from 'core/scene/types';\nimport { useLatest } from '@weasel-js/react';\nimport { doubleClickTarget } from '../doubleClickTarget';\nimport type { SelectionApi } from 'core/selection/useSelection';\nimport type { View } from 'core/viewport/view';\nimport type { PoseDescriptor } from 'interactions/actions/resize/geometry';\nimport { composeAlphaFor } from '../sceneSlotWiring';\nimport { useSceneAdapter } from '../useSceneAdapter';\nimport type { SceneCanvasProps } from './props';\nimport { useNodeAtPoint } from './useNodeAtPoint';\nimport type { NodeKinds } from './useNodeKinds';\nimport { useSceneSelectTool, type UseSceneSelectToolArgs } from './useSceneSelectTool';\nimport { useOptionalViewRegistry } from '../viewRegistry';\nimport { useViewLayerGate } from '../useViewLayerGate';\n\ntype PickingProps<TData, TLayer extends string, TPose> = Pick<\n SceneCanvasProps<TData, TLayer, TPose>,\n 'geometry' | 'alphaFor' | 'layerVisibility' | 'layerOrder' | 'isPointerInteractive' | 'isolatedGroup'\n>;\n\n/**\n * The canvas's adapter and everything that answers \"what is under this point\": the select\n * tool's pickers and bounds, the node kinds, and the paint gates picking shares with the painter.\n */\nexport function useScenePicking<TData, TLayer extends string, TPose>(args: {\n scene: Scene<TData, TLayer, TPose>;\n selection: SelectionApi;\n descriptor: PoseDescriptor<TPose>;\n props: PickingProps<TData, TLayer, TPose>;\n viewRef: RefObject<View>;\n selectTool: UseSceneSelectToolArgs<TData, TLayer, TPose>['selectTool'];\n kinds: NodeKinds<TData>;\n}) {\n const { scene, selection, descriptor, viewRef } = args;\n const { geometry, alphaFor, layerVisibility, layerOrder, isPointerInteractive } = args.props;\n const { kindOf, kindOfNode } = args.kinds;\n\n // The painted alpha of a node in this view: the caller's `alphaFor` times any per-node\n // override alpha. The pick path reads the same number the painter does, or a node faded\n // to nothing stays clickable.\n const composedAlphaFor = useMemo(\n () => composeAlphaFor(scene, alphaFor),\n [scene, alphaFor],\n );\n const surfaceViewRegistry = useOptionalViewRegistry();\n const viewLayerGate = useViewLayerGate(\n layerVisibility, layerOrder, () => surfaceViewRegistry?.surface()?.layers() ?? [],\n );\n\n const adapter = useSceneAdapter(scene, {\n // `adapterMethods` is memoized; `selection` itself is a fresh object each render.\n selection: selection.adapterMethods,\n poseDescriptor: descriptor,\n });\n\n const selectTool = useSceneSelectTool({\n scene,\n adapter,\n poseDescriptor: descriptor,\n geometry,\n // The pick tolerance is declared in screen pixels; this is what converts it.\n getView: () => viewRef.current,\n // Only when the consumer actually fades something. Without a prop the\n // composed function is just the override lookup, and the source resolves\n // that once per walk instead of once per candidate.\n ...(alphaFor ? { alphaOf: composedAlphaFor } : {}),\n ...(viewLayerGate ? { layerIsPainted: viewLayerGate } : {}),\n ...(isPointerInteractive ? { isPointerInteractive } : {}),\n selectTool: args.selectTool,\n });\n\n const getNodeAtPoint = useNodeAtPoint({\n pickBest: selectTool.pickBest, nodes: adapter, kindOf,\n });\n\n const isolatedGroup = useLatest(args.props.isolatedGroup);\n const doubleClickNames = useMemo(() => kindOfNode && ((hitId: string) => doubleClickTarget(hitId, {\n parentOf: (id) => scene.get(asNodeId(id))?.parent ?? null,\n kindOf: kindOfNode,\n scope: isolatedGroup.current?.() ?? null,\n })), [scene, kindOfNode, isolatedGroup]);\n\n return {\n ...selectTool, adapter, kindOfNode, doubleClickNames, getNodeAtPoint, viewLayerGate,\n /** The composed paint alpha, only when the consumer fades anything. */\n alphaOf: alphaFor ? composedAlphaFor : undefined,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAyBA,SAAgB,gBAAqD,MAQlE;CACD,MAAM,EAAE,OAAO,WAAW,YAAY,YAAY;CAClD,MAAM,EAAE,UAAU,UAAU,iBAAiB,YAAY,yBAAyB,KAAK;CACvF,MAAM,EAAE,QAAQ,eAAe,KAAK;CAKpC,MAAM,mBAAmB,cACjB,gBAAgB,OAAO,QAAQ,GACrC,CAAC,OAAO,QAAQ,CAClB;CACA,MAAM,sBAAsB,wBAAwB;CACpD,MAAM,gBAAgB,iBACpB,iBAAiB,kBAAkB,qBAAqB,QAAQ,CAAC,EAAE,OAAO,KAAK,CAAC,CAClF;CAEA,MAAM,UAAU,gBAAgB,OAAO;EAErC,WAAW,UAAU;EACrB,gBAAgB;CAClB,CAAC;CAED,MAAM,aAAa,mBAAmB;EACpC;EACA;EACA,gBAAgB;EAChB;EAEA,eAAe,QAAQ;EAIvB,GAAI,WAAW,EAAE,SAAS,iBAAiB,IAAI,CAAC;EAChD,GAAI,gBAAgB,EAAE,gBAAgB,cAAc,IAAI,CAAC;EACzD,GAAI,uBAAuB,EAAE,qBAAqB,IAAI,CAAC;EACvD,YAAY,KAAK;CACnB,CAAC;CAED,MAAM,iBAAiB,eAAe;EACpC,UAAU,WAAW;EAAU,OAAO;EAAS;CACjD,CAAC;CAED,MAAM,gBAAgB,UAAU,KAAK,MAAM,aAAa;CACxD,MAAM,mBAAmB,cAAc,gBAAgB,UAAkB,kBAAkB,OAAO;EAChG,WAAW,OAAO,MAAM,IAAI,SAAS,EAAE,CAAC,CAAC,EAAE,UAAU;EACrD,QAAQ;EACR,OAAO,cAAc,UAAU,KAAK;CACtC,CAAC,IAAI;EAAC;EAAO;EAAY;CAAa,CAAC;CAEvC,OAAO;EACL,GAAG;EAAY;EAAS;EAAY;EAAkB;EAAgB;;EAEtE,SAAS,WAAW,mBAAmB;CACzC;AACF"}
|
|
@@ -2,6 +2,7 @@ import { rectFromPlane } from "../../core/viewport/parallax.js";
|
|
|
2
2
|
import { AUTO_POSE_DESCRIPTOR } from "../../core/geometry/autoPoseDescriptor.js";
|
|
3
3
|
import { drawnBoundsOf } from "../drawnBounds.js";
|
|
4
4
|
import { asNodeId } from "../../core/scene/types.js";
|
|
5
|
+
import { drawnBoundsScene } from "../derivedPath.js";
|
|
5
6
|
import { pickWalk, scenePickSource, scenePlaneOf } from "../pickWalk.js";
|
|
6
7
|
import { pickTopMostHit } from "../../tools/builtin/pickTopMostHit.js";
|
|
7
8
|
import { useSelectTool } from "../../tools/builtin/select/useSelectTool.js";
|
|
@@ -36,7 +37,7 @@ function cameraOf(v) {
|
|
|
36
37
|
}
|
|
37
38
|
/** `id` drawn at `pose`, in world, as the chrome boxes it. */
|
|
38
39
|
function chromeBox(scene, adapter, descriptor, includeStroke, id, pose, scale) {
|
|
39
|
-
return drawnBoundsOf(scene, asNodeId(id), pose, {
|
|
40
|
+
return drawnBoundsOf(drawnBoundsScene(scene), asNodeId(id), pose, {
|
|
40
41
|
descriptor,
|
|
41
42
|
worldPoseOf: (m) => adapter.getWorldPose(m),
|
|
42
43
|
includeStroke,
|