From be013d26e52807a5962d8acbdd472cb5976f47d7 Mon Sep 17 00:00:00 2001 From: wass08 Date: Wed, 18 Feb 2026 13:14:55 +0900 Subject: [PATCH] window panel --- .../components/tools/window/window-tool.tsx | 4 +- .../components/ui/panels/window-panel.tsx | 216 ++++++++++++++---- .../components/ui/primitives/number-input.tsx | 10 +- packages/core/src/schema/nodes/window.ts | 3 +- .../core/src/systems/window/window-system.tsx | 34 +-- 5 files changed, 198 insertions(+), 69 deletions(-) diff --git a/apps/editor/components/tools/window/window-tool.tsx b/apps/editor/components/tools/window/window-tool.tsx index 10c7663b..694d0d71 100644 --- a/apps/editor/components/tools/window/window-tool.tsx +++ b/apps/editor/components/tools/window/window-tool.tsx @@ -280,13 +280,15 @@ export const WindowTool: React.FC = () => { frameDepth: draft.frameDepth, columnRatios: draft.columnRatios, rowRatios: draft.rowRatios, - dividerThickness: draft.dividerThickness, + columnDividerThickness: draft.columnDividerThickness, + rowDividerThickness: draft.rowDividerThickness, sill: draft.sill, sillDepth: draft.sillDepth, sillThickness: draft.sillThickness, }) useScene.getState().createNode(node, event.node.id as AnyNodeId) + useViewer.getState().setSelection({ selectedIds: [node.id] }) useScene.temporal.getState().pause() event.stopPropagation() diff --git a/apps/editor/components/ui/panels/window-panel.tsx b/apps/editor/components/ui/panels/window-panel.tsx index 2e40e633..da51ee5d 100644 --- a/apps/editor/components/ui/panels/window-panel.tsx +++ b/apps/editor/components/ui/panels/window-panel.tsx @@ -2,7 +2,7 @@ import { type AnyNode, type AnyNodeId, type WindowNode, useScene } from '@pascal-app/core' import { useViewer } from '@pascal-app/viewer' -import { X } from 'lucide-react' +import { FlipHorizontal2, X } from 'lucide-react' import Image from 'next/image' import { useCallback } from 'react' import { NumberInput } from '@/components/ui/primitives/number-input' @@ -23,7 +23,6 @@ export function WindowPanel() { (updates: Partial) => { if (!selectedId) return updateNode(selectedId as AnyNode['id'], updates) - // Mark dirty so window-system regenerates geometry useScene.getState().dirtyNodes.add(selectedId as AnyNodeId) }, [selectedId, updateNode], @@ -33,13 +32,53 @@ export function WindowPanel() { setSelection({ selectedIds: [] }) }, [setSelection]) + const handleFlip = useCallback(() => { + if (!node) return + handleUpdate({ + side: node.side === 'front' ? 'back' : 'front', + rotation: [node.rotation[0], node.rotation[1] + Math.PI, node.rotation[2]], + }) + }, [node, handleUpdate]) + if (!node || node.type !== 'window' || selectedIds.length !== 1) return null - const columns = node.columnRatios.length - const rows = node.rowRatios.length + const numCols = node.columnRatios.length + const numRows = node.rowRatios.length + + // Normalized ratios (always sum to 1 for display) + const colSum = node.columnRatios.reduce((a, b) => a + b, 0) + const rowSum = node.rowRatios.reduce((a, b) => a + b, 0) + const normCols = node.columnRatios.map(r => r / colSum) + const normRows = node.rowRatios.map(r => r / rowSum) + + const setColumnRatio = (index: number, newVal: number) => { + const clamped = Math.max(0.05, Math.min(0.95, newVal)) + const neighborIdx = index < numCols - 1 ? index + 1 : index - 1 + const delta = clamped - normCols[index]! + const neighborVal = Math.max(0.05, normCols[neighborIdx]! - delta) + const newRatios = normCols.map((v, i) => { + if (i === index) return clamped + if (i === neighborIdx) return neighborVal + return v + }) + handleUpdate({ columnRatios: newRatios }) + } + + const setRowRatio = (index: number, newVal: number) => { + const clamped = Math.max(0.05, Math.min(0.95, newVal)) + const neighborIdx = index < numRows - 1 ? index + 1 : index - 1 + const delta = clamped - normRows[index]! + const neighborVal = Math.max(0.05, normRows[neighborIdx]! - delta) + const newRatios = normRows.map((v, i) => { + if (i === index) return clamped + if (i === neighborIdx) return neighborVal + return v + }) + handleUpdate({ rowRatios: newRatios }) + } return ( -
+
{/* Header */}
@@ -59,6 +98,36 @@ export function WindowPanel() { {/* Content */}
+ + {/* Position */} +
+ +
+ handleUpdate({ position: [v, node.position[1], node.position[2]] })} + precision={2} + /> + handleUpdate({ position: [node.position[0], v, node.position[2]] })} + precision={2} + /> +
+ +
+ {/* Dimensions */}