second commit

This commit is contained in:
2026-10-17 12:30:46 -04:00
parent 56046a63fc
commit abc3800db2
14 changed files with 533 additions and 136 deletions
+22 -13
View File
@@ -6,17 +6,20 @@ A web tool for venues (events, banquets, conferences) to lay out furniture in a
The ONLY feature right now is drag-and-drop layout:
- Left palette with two source tiles: chair and table.
- Large rectangular room on the right (fixed 10m x 8m, drawn to scale, 1m = 60px, faint 0.5m grid).
- Click (no drag) a palette tile to open a settings menu for that furniture type:
- Available: how many of that type the venue has (number stepper, min = number already placed).
- Tables only: Seats per table (number stepper, min 1).
- Palette tiles show a badge with remaining count (available minus placed).
- Drag a tile into the room to create an item. The tile stays in the palette. If remaining is 0, the tile is greyed out and can't be dragged.
- Distinguish click from drag with a small movement threshold (about 5px).
- Left sidebar (palette) with three source tiles: chair, table, screen.
- Resizable by dragging its right edge (default 264px, min 152px, max 600px). Tiles live in an auto-fill grid: they stretch in width only (fixed 84px height) until another column fits.
- Large rectangular room in the center (fixed 10m x 8m). It is scaled to fill the available space (`pxPerM` computed via `fitScale` and passed down; a ResizeObserver in `App.tsx` recomputes it). A "1 m" scale bar sits under the room. No grid lines.
- Click (no drag) a palette tile to open its settings menu. The menu takes over the sidebar (replaces the tiles); close with Escape or the "Back" button:
- Available: how many of that type the venue has. Min = number already placed.
- Tables only: Seats per table (min 1).
- Both are iOS-timer-style scroll wheels (`components/Wheel.tsx`): drag 1:1, momentum fling, press to stop mid-roll, spring settle for slow releases, mouse wheel / trackpad, tap a row, arrow keys. Max 999.
- Palette tiles show a badge with remaining count (available minus placed). Defaults: 20 chairs, 6 tables, 8 seats per table.
- Drag a tile into the room to create an item. The tile stays in the palette. If remaining is 0, the tile is greyed out and can't be dragged (click still opens the menu).
- Distinguish click from drag with a small movement threshold (5px).
- Screens (2 x 0.2 m default, 2 available by default) are the only resizable type: click a placed screen to select it, then drag its left/right end handles to change its length only (top-down view, so thickness stays 0.2 m; min length 0.5 m, stays inside the room; red + snap back to the old size if it would overlap). `RESIZABLE` and `resizeRect` live in `geometry.ts`.
- Drag existing items to move them. Items are clamped inside the room walls.
- No overlap allowed. Invalid position = red outline while dragging; on drop, snap back to last valid position (or cancel the add if new).
- Snap to the 0.5m grid on drop.
- No overlap allowed (touching edges is fine). Invalid position = red outline while dragging, green when valid; on drop, snap back to last valid position (or cancel the add if new).
- NO grid snapping: items land exactly where dropped (snapping was removed on request).
Do not build anything beyond this unless asked. Do not design in a way that blocks the roadmap below.
@@ -29,7 +32,7 @@ Do not build anything beyond this unless asked. Do not design in a way that bloc
## Data model
```ts
type FurnitureType = "chair" | "table";
type FurnitureType = "chair" | "table" | "screen";
interface FurnitureItem {
id: string;
@@ -55,16 +58,22 @@ interface FurnitureTypeConfig {
Seats per table is a type-level setting for now (applies to all tables). Per-table overrides come later. Chairs and table seats are independent for now.
Default sizes: chair 0.5 x 0.5 m, table 1.8 x 0.8 m.
Default sizes: chair 0.5 x 0.5 m, table 1.8 x 0.8 m, screen 2 x 0.2 m.
## Conventions
- Store everything in meters. Convert to pixels only at render time (one `PX_PER_M` constant).
- Store everything in meters. Convert to pixels only at render time using the `pxPerM` scale (dynamic, computed by `fitScale` in `geometry.ts`; there is no fixed px-per-meter constant).
- Collision = axis-aligned bounding boxes. Keep collision and clamping logic in pure functions (no React or Konva imports) so the optimizer can reuse them.
- Keep geometry/rules logic separate from rendering components.
- Small, typed, readable code. No new dependencies without a good reason.
- Keep the UI minimal: light grey app background, room centered in the remaining space.
## File layout
- `src/types.ts`, `src/geometry.ts` (pure: clamp, overlap, validity, fitScale, default sizes), `src/inventory.ts` (pure: placedCount, remaining), `src/store.ts` (reducer: items, room, configs).
- `src/App.tsx`: owns the store, palette drag, room scale.
- `src/components/`: `Palette` (sidebar, tiles, resize), `TypeMenu`, `Wheel`, `Room` (Konva), `FurnitureShape`, `ScaleBar`.
## Roadmap (not built yet, don't block)
Venue-focused features planned for later phases: