From 0e778ab5262a688521b270efbce0b1589f84c498 Mon Sep 17 00:00:00 2001 From: Wassim SAMAD Date: Thu, 25 Jun 2026 14:56:24 -0400 Subject: [PATCH] fix(editor): commit slab locally in 2D-only view (3D tool can't) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Slab is committed by its 3D registry tool, which accumulates the grid:click vertices the 2D panel emits and commits on close. That path is dead in 2D-only view — the 3D canvas is display:none, so the tool never commits and the slab is never created (split/3D work because the 3D side is live). Mirror the zone pattern: the panel commits the slab itself on both close paths (double-click + click-first-vertex), gated to viewMode==='2d' so split/3D keep their single-owner tool commit (no double-create). Co-Authored-By: Claude Opus 4.8 --- .../src/components/editor/floorplan-panel.tsx | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/packages/editor/src/components/editor/floorplan-panel.tsx b/packages/editor/src/components/editor/floorplan-panel.tsx index 96f08c8c..93b09090 100644 --- a/packages/editor/src/components/editor/floorplan-panel.tsx +++ b/packages/editor/src/components/editor/floorplan-panel.tsx @@ -33,6 +33,7 @@ import { type RoofSegmentNode, type SiteNode, type SlabNode, + SlabNode as SlabNodeSchema, type SpawnNode, type StairNode, StairNode as StairNodeSchema, @@ -7579,6 +7580,33 @@ export function FloorplanPanel({ [levelId, setSelection], ) + // Slab / ceiling are normally committed by their 3D registry tools (which + // accumulate the same `grid:click` vertices the panel emits). That path is + // dead in 2D-only view — the 3D canvas is `display:none`, so the tool never + // commits. These local committers (mirroring `createZoneOnCurrentLevel`, and + // the tools' own `commitSlab/CeilingDrawing`) are called ONLY in 2D-only view + // so split / 3D keep their single-owner tool commit (no double-create). + const createSlabOnCurrentLevel = useCallback( + (points: WallPlanPoint[]) => { + if (!levelId) { + return null + } + const { createNode, nodes } = useScene.getState() + const slabCount = Object.values(nodes).filter((node) => node.type === 'slab').length + const defaults = useEditor.getState().toolDefaults.slab ?? {} + const slab = SlabNodeSchema.parse({ + ...defaults, + name: `Slab ${slabCount + 1}`, + polygon: points.map(([x, z]) => [x, z] as [number, number]), + }) + createNode(slab, levelId) + sfxEmitter.emit('sfx:structure-build') + setSelection({ selectedIds: [slab.id] }) + return slab.id + }, + [levelId, setSelection], + ) + useEffect(() => { if (!isStairBuildActive) { useStairBuildPreview.getState().reset() @@ -9097,6 +9125,10 @@ export function FloorplanPanel({ const firstPoint = slabDraftPoints[0] if (firstPoint && slabDraftPoints.length >= 3 && isPointNearPlanPoint(point, firstPoint)) { + // 2D-only view: the 3D tool can't commit, so close the polygon here. + if (useEditor.getState().viewMode === '2d') { + createSlabOnCurrentLevel(slabDraftPoints) + } clearDraft() return } @@ -9104,7 +9136,7 @@ export function FloorplanPanel({ setSlabDraftPoints((currentPoints) => [...currentPoints, point]) setCursorPoint(point) }, - [clearDraft, slabDraftPoints], + [clearDraft, createSlabOnCurrentLevel, slabDraftPoints], ) const handleSlabPlacementConfirm = useCallback( (point?: WallPlanPoint) => { @@ -9127,9 +9159,13 @@ export function FloorplanPanel({ return } + // 2D-only view: the 3D tool can't commit, so create the slab here. + if (useEditor.getState().viewMode === '2d') { + createSlabOnCurrentLevel(nextPoints) + } clearDraft() }, - [clearDraft, slabDraftPoints], + [clearDraft, createSlabOnCurrentLevel, slabDraftPoints], ) const handleCeilingPlacementPoint = useCallback( (point: WallPlanPoint) => {