Migrate `curvingWall`, `curvingFence`, `movingWallEndpoint`, `movingFenceEndpoint` (and their setters) off `useEditor` onto the authoritative interaction scope. The `reshaping` scope variant gains an `endpoint` discriminator; existence checks read `useIsCurveReshape()` / `useEndpointReshape()`, and the few sites that need the node (affordance-tool mounts, wall-vs-fence type checks) read it from `useReshapingNode()` — a frozen drag-start snapshot, mirroring the old flags so the tools' own per-frame writes don't feed back. `MovingWallEndpoint` / `MovingFenceEndpoint` move to the kind-owned tools that consume them. `editor-api` is simpler: endpoint engagement is kind-agnostic, and the `engageMove` reshape clears are gone (the scope is single-owner). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
41 lines
1.9 KiB
TypeScript
41 lines
1.9 KiB
TypeScript
import type { AnyNode, EditorApi } from '@pascal-app/core'
|
|
import useEditor from '../store/use-editor'
|
|
import useInteractionScope from '../store/use-interaction-scope'
|
|
import { endpointReshapeScope } from './interaction/scope'
|
|
|
|
/**
|
|
* Concrete {@link EditorApi} backed by `useEditor` + the interaction scope.
|
|
* Descriptors call into editor state through this interface; the editor owns
|
|
* the actual store wiring so core stays decoupled.
|
|
*
|
|
* `engageMove` no longer clears any in-progress endpoint drag or curve gesture:
|
|
* `setMovingNode` begins the `moving` scope, and the scope is single-owner, so
|
|
* it atomically replaces any prior reshape — there is no separate flag to reset.
|
|
*/
|
|
export function createEditorApi(): EditorApi {
|
|
return {
|
|
engageMove(node: AnyNode) {
|
|
const editor = useEditor.getState()
|
|
editor.setPlacementDragMode(false)
|
|
// `setMovingNode` is typed against a narrower union than `AnyNode`
|
|
// (every concrete kind enumerated). Descriptors pass any node; the
|
|
// cast lets registry-driven move kinds through without forcing a
|
|
// schema-level type widening.
|
|
editor.setMovingNode(node as Parameters<typeof editor.setMovingNode>[0])
|
|
},
|
|
engageMoveDrag(node: AnyNode) {
|
|
const editor = useEditor.getState()
|
|
// Flag drag mode BEFORE mounting the move tool so the coordinator reads
|
|
// it at setup and wires its commit-on-release listener.
|
|
editor.setPlacementDragMode(true)
|
|
editor.setMovingNode(node as Parameters<typeof editor.setMovingNode>[0])
|
|
},
|
|
engageEndpointMove(node: AnyNode, endpoint: 'start' | 'end') {
|
|
// Endpoint reshape is kind-agnostic: the scope carries the node id + which
|
|
// endpoint, and consumers recover the node from the scene. Adding a new
|
|
// endpoint-draggable kind needs no entry here.
|
|
useInteractionScope.getState().begin(endpointReshapeScope(node.id, endpoint))
|
|
},
|
|
}
|
|
}
|