Implements plans/editor-placement-interaction-overhaul.md: an authoritative interaction-scope state machine plus the catalogued placement/interaction fixes, and split-view floor-plan performance. - Interaction-scope spine (lib/interaction/* + store/use-interaction-scope), driven from central useEditor setters; overlay scoping (zone labels, context badges, floating action menu) reads resolveOverlayPolicy. - Bug tracks A/B/D/E/F/G/H: handle/cutout raycast, footprint validity, auto-slab loop, ceiling hosting, B-key tool desync, 2D drop offset, per-frame jank. - Snapping modes (grid/lines/angles/off) + contextual HUD chips; modifier model (Shift=cycle, Alt=free place, Ctrl=grid step). - Item move now tracks the cursor 1:1 (was a laggy per-frame lerp); handle rig hides during a whole-node move; rotate gizmo advertises Shift=free rotation in the HUD and hides the move cross while rotating. - Floor-plan perf: pause live reactivity while in 3D-only view; per-node geometry cache so only changed nodes rebuild on a drag; hoist wall miters to a once-per-pass ctx.levelData (O(N^2) -> O(N) on wall/opening drags). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
39 lines
1.4 KiB
TypeScript
39 lines
1.4 KiB
TypeScript
import type { ToolHint } from '@pascal-app/core'
|
|
import { ContextualHelperPanel } from './contextual-helper-panel'
|
|
|
|
/**
|
|
* Generic helper panel rendered from `def.toolHints` data. Matches the
|
|
* visual styling of the hand-written `<WallHelper>` / `<ItemHelper>` /
|
|
* etc. so registry-driven kinds get a consistent look without each kind
|
|
* writing its own component.
|
|
*
|
|
* Drops the need for per-kind helper files entirely — kinds declare
|
|
* their hints as static data in their `NodeDefinition`.
|
|
*/
|
|
export function RegisteredToolHelper({
|
|
hints,
|
|
shiftPressed = false,
|
|
}: {
|
|
hints: ToolHint[]
|
|
shiftPressed?: boolean
|
|
}) {
|
|
if (hints.length === 0) return null
|
|
return (
|
|
<ContextualHelperPanel
|
|
showSnapping
|
|
hints={hints.map((hint) => {
|
|
// Shift is a per-kind bypass for item / opening / zone / duct placement
|
|
// ("Free place", "Free angle", …) — those hints flip to a bypassed
|
|
// state while held. For wall / fence, Shift now cycles the snapping
|
|
// mode (no hold-to-bypass), so it must NOT show the bypass treatment.
|
|
const isBypassHint = hint.key === 'Shift' && hint.label !== 'Cycle snapping mode'
|
|
return {
|
|
keys: [hint.key],
|
|
label: shiftPressed && isBypassHint ? 'Guided constraints bypassed' : hint.label,
|
|
active: shiftPressed && isBypassHint,
|
|
}
|
|
})}
|
|
/>
|
|
)
|
|
}
|