diff --git a/CLAUDE.md b/CLAUDE.md index a0ebe69..9118f17 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -15,7 +15,7 @@ Layout and editing UI. No optimizer yet. **Editing (Phase 2)** - Click an item to select it (blue outline); click empty floor to deselect (Escape/Done too). Dragging still moves. Selecting opens the item panel in the sidebar (replaces tiles / type menu). -- Item panel: Rotate 90 button + free angle wheel (0-359, wraps); Lock/Unlock; Delete (also Delete/Backspace key, ignored while typing in an input); "Seats at this table" wheel for tables (per-item override, "Reset to default" link when overridden); Size wheels (0.3-6 m, 0.1 steps; screens show only Length). +- Item panel (chairs have no rotation controls; every other type does): Rotate 90 button + free angle wheel (0-359, wraps); Lock/Unlock; Delete (also Delete/Backspace key, ignored while typing in an input); "Seats at this table" wheel for tables (per-item override, "Reset to default" link when overridden); Size wheels (0.3-6 m, 0.1 steps; screens show only Length). - Rotation turns around the item's center and collision/wall clamping use the rotated bounding box (`boundsOf`). A rotate/size change is pushed back inside the walls, then rejected if it would overlap (`tryUpdate`). - Locked items can't be dragged or rotated and show a padlock. (Size, seats and delete are still allowed on locked items.) - Delete frees inventory automatically (remaining is derived from `items`). @@ -54,6 +54,7 @@ interface FurnitureTypeConfig { id: FurnitureType; name: string; shape: Shape; defaultWidth: number; defaultHeight: number; // meters isTable: boolean; + rotatable: boolean; // chairs: false (no rotation controls) available: number; // inventory count seatsPerUnit?: number; // type-level seats } diff --git a/src/App.tsx b/src/App.tsx index 6cf3f7c..554f1b6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -162,6 +162,7 @@ export default function App() { defaultWidth: d.width, defaultHeight: d.height, isTable: d.isTable, + rotatable: true, available: d.available, seatsPerUnit: d.seats, }, diff --git a/src/components/ItemPanel.tsx b/src/components/ItemPanel.tsx index 76133ed..7705f11 100644 --- a/src/components/ItemPanel.tsx +++ b/src/components/ItemPanel.tsx @@ -46,26 +46,28 @@ export function ItemPanel({ item, config, onPatch, onDelete, onClose }: Props) { {item.locked ? " 🔒" : ""} -
-
Rotation
- `${v}°`} - onChange={(v) => patch({ rotation: v })} - /> - -
+ {config.rotatable && ( +
+
Rotation
+ `${v}°`} + onChange={(v) => patch({ rotation: v })} + /> + +
+ )} {config.isTable && (
diff --git a/src/store.ts b/src/store.ts index 9974fb1..effac36 100644 --- a/src/store.ts +++ b/src/store.ts @@ -19,9 +19,9 @@ export type Action = | { kind: "addConfig"; config: FurnitureTypeConfig }; const BUILT_IN: FurnitureTypeConfig[] = [ - { id: "chair", name: "Chair", shape: "chair", defaultWidth: 0.5, defaultHeight: 0.5, isTable: false, available: 20 }, - { id: "table", name: "Table", shape: "table", defaultWidth: 1.8, defaultHeight: 0.8, isTable: true, available: 6, seatsPerUnit: 8 }, - { id: "screen", name: "Screen", shape: "screen", defaultWidth: 2, defaultHeight: 0.2, isTable: false, available: 2 }, + { id: "chair", name: "Chair", shape: "chair", defaultWidth: 0.5, defaultHeight: 0.5, isTable: false, rotatable: false, available: 20 }, + { id: "table", name: "Table", shape: "table", defaultWidth: 1.8, defaultHeight: 0.8, isTable: true, rotatable: true, available: 6, seatsPerUnit: 8 }, + { id: "screen", name: "Screen", shape: "screen", defaultWidth: 2, defaultHeight: 0.2, isTable: false, rotatable: true, available: 2 }, ]; export const initialState: LayoutState = { diff --git a/src/types.ts b/src/types.ts index 79583bf..cf1e434 100644 --- a/src/types.ts +++ b/src/types.ts @@ -36,6 +36,7 @@ export interface FurnitureTypeConfig { defaultWidth: number; // meters defaultHeight: number; isTable: boolean; + rotatable: boolean; // false = no rotation controls (chairs) available: number; // inventory count seatsPerUnit?: number; // type-level seats (tables, couches...) }