refactor(editor): delete curving + endpoint reshaping flags

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>
This commit is contained in:
Wassim SAMAD
2026-06-23 12:18:50 -04:00
co-authored by Claude Opus 4.8
parent 2f2c3ef8f9
commit cfc1fb1672
20 changed files with 263 additions and 262 deletions
+4 -2
View File
@@ -20,7 +20,7 @@ import {
markToolCancelConsumed,
snapScalarToGrid,
triggerSFX,
useEditor,
useInteractionScope,
} from '@pascal-app/editor'
import { useViewer } from '@pascal-app/viewer'
import { useCallback, useEffect, useRef, useState } from 'react'
@@ -51,7 +51,9 @@ export const CurveFenceTool: React.FC<{ node: FenceNode }> = ({ node }) => {
])
const exitCurveMode = useCallback(() => {
useEditor.getState().setCurvingFence(null)
useInteractionScope
.getState()
.endIf((scope) => scope.kind === 'reshaping' && scope.reshape === 'curve')
}, [])
useEffect(() => {
@@ -15,11 +15,10 @@ import {
getAngleToSegmentReference,
getSegmentAngleReferenceAtPoint,
MeasurementPill,
type MovingFenceEndpoint,
triggerSFX,
useAlignmentGuides,
useDragAction,
useEditor,
useInteractionScope,
} from '@pascal-app/editor'
import { useViewer } from '@pascal-app/viewer'
import { Html } from '@react-three/drei'
@@ -40,10 +39,14 @@ import { moveFenceEndpointDragAction } from './actions/move-endpoint'
* - Angle label between this segment and any neighbour segment sharing
* the dragged endpoint — same legacy treatment.
*
* Mounted by the legacy ToolManager via the `move-endpoint` affordance
* key. `target.fence` + `target.endpoint` come from the editor store
* (`useEditor.movingFenceEndpoint`).
* Mounted by ToolManager via the `move-endpoint` affordance key. ToolManager
* reconstructs this `target` from the reshaped node + the scope's endpoint.
*/
export type MovingFenceEndpoint = {
fence: FenceNode
endpoint: 'start' | 'end'
}
type SegmentLike = {
id: string
start: FencePlanPoint
@@ -104,7 +107,9 @@ export const MoveFenceEndpointTool: React.FC<{ target: MovingFenceEndpoint }> =
const exitMoveMode = (committed: boolean) => {
if (committed) triggerSFX('sfx:item-place')
useViewer.getState().setSelection({ selectedIds: [fenceId] })
useEditor.getState().setMovingFenceEndpoint(null)
useInteractionScope
.getState()
.endIf((scope) => scope.kind === 'reshaping' && scope.reshape === 'endpoint')
}
useDragAction({