feat: complete house plan maker application

Full-featured house/apartment floor plan editor with:

- Turborepo monorepo (React/Vite client, Fastify/Prisma server, shared Zod schemas)
- 2D room editor with walls, doors, windows, furniture, electrical elements
- 3D room preview with Three.js (auto-hide nearest walls, bird's eye default)
- Wall projection views with interactive drag (elevation, position)
- Apartment floor plan view with room positioning
- Copy/paste, alignment tools, measurement tool, annotations
- Item-attached annotations with leader lines (visible on projections)
- Door open direction (LEFT/RIGHT/INWARD/OUTWARD) with swing arc
- Floor type textures (wood, tile, concrete, laminate, herringbone)
- Wall color picker for 3D view
- Furniture: bed, desk, wardrobe, sofa, table, chair, shelf, nightstand, dresser, bookcase, TV (with stand toggle), AC unit
- Furniture elevation support (wall-mounted items)
- Auto-save with dirty state tracking, batch save API
- Rotation-aware collision detection (SAT/OBB) with 3D elevation check
- Rotation-aware hit testing
- i18n (English/Russian) with locale-aware number formatting
- Dark mode with system preference detection
- Undo/redo, keyboard shortcuts, scale bar
- PDF/PNG/JSON export and JSON import
- Focus trap modal, toast notifications, tooltips
- Responsive layout with overlay palettes
This commit is contained in:
2026-04-05 22:34:03 +03:00
parent b84807bbdb
commit af8b9fe00f
188 changed files with 35795 additions and 0 deletions
@@ -0,0 +1,102 @@
-- CreateTable
CREATE TABLE "Apartment" (
"id" TEXT NOT NULL PRIMARY KEY,
"name" TEXT NOT NULL,
"address" TEXT,
"totalArea" REAL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL
);
-- CreateTable
CREATE TABLE "Room" (
"id" TEXT NOT NULL PRIMARY KEY,
"apartmentId" TEXT NOT NULL,
"name" TEXT NOT NULL,
"shape" TEXT NOT NULL DEFAULT '[]',
"width" REAL,
"height" REAL,
"wallHeight" REAL NOT NULL DEFAULT 2.7,
"plinthHeight" REAL NOT NULL DEFAULT 0.06,
"plinthThickness" REAL NOT NULL DEFAULT 0.01,
"order" INTEGER NOT NULL DEFAULT 0,
"posX" REAL NOT NULL DEFAULT 0,
"posY" REAL NOT NULL DEFAULT 0,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "Room_apartmentId_fkey" FOREIGN KEY ("apartmentId") REFERENCES "Apartment" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "Wall" (
"id" TEXT NOT NULL PRIMARY KEY,
"roomId" TEXT NOT NULL,
"startX" REAL NOT NULL,
"startY" REAL NOT NULL,
"endX" REAL NOT NULL,
"endY" REAL NOT NULL,
"thickness" REAL NOT NULL DEFAULT 0.1,
"direction" TEXT NOT NULL DEFAULT 'OTHER',
CONSTRAINT "Wall_roomId_fkey" FOREIGN KEY ("roomId") REFERENCES "Room" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "WallOpening" (
"id" TEXT NOT NULL PRIMARY KEY,
"roomId" TEXT NOT NULL,
"wallId" TEXT NOT NULL,
"type" TEXT NOT NULL,
"positionAlongWall" REAL NOT NULL,
"width" REAL NOT NULL,
"height" REAL NOT NULL,
"elevationFromFloor" REAL NOT NULL DEFAULT 0,
CONSTRAINT "WallOpening_roomId_fkey" FOREIGN KEY ("roomId") REFERENCES "Room" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "WallOpening_wallId_fkey" FOREIGN KEY ("wallId") REFERENCES "Wall" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "ElectricalItem" (
"id" TEXT NOT NULL PRIMARY KEY,
"roomId" TEXT NOT NULL,
"type" TEXT NOT NULL,
"x" REAL NOT NULL,
"y" REAL NOT NULL,
"wallId" TEXT,
"elevationFromFloor" REAL,
"rotation" REAL NOT NULL DEFAULT 0,
"metadata" TEXT,
CONSTRAINT "ElectricalItem_roomId_fkey" FOREIGN KEY ("roomId") REFERENCES "Room" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "FurnitureItem" (
"id" TEXT NOT NULL PRIMARY KEY,
"roomId" TEXT NOT NULL,
"type" TEXT NOT NULL,
"x" REAL NOT NULL,
"y" REAL NOT NULL,
"width" REAL NOT NULL,
"depth" REAL NOT NULL,
"height" REAL NOT NULL,
"rotation" REAL NOT NULL DEFAULT 0,
"label" TEXT,
CONSTRAINT "FurnitureItem_roomId_fkey" FOREIGN KEY ("roomId") REFERENCES "Room" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateIndex
CREATE INDEX "Room_apartmentId_idx" ON "Room"("apartmentId");
-- CreateIndex
CREATE INDEX "Wall_roomId_idx" ON "Wall"("roomId");
-- CreateIndex
CREATE INDEX "WallOpening_roomId_idx" ON "WallOpening"("roomId");
-- CreateIndex
CREATE INDEX "WallOpening_wallId_idx" ON "WallOpening"("wallId");
-- CreateIndex
CREATE INDEX "ElectricalItem_roomId_idx" ON "ElectricalItem"("roomId");
-- CreateIndex
CREATE INDEX "FurnitureItem_roomId_idx" ON "FurnitureItem"("roomId");
@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (e.g., Git)
provider = "sqlite"
+108
View File
@@ -0,0 +1,108 @@
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
model Apartment {
id String @id @default(cuid())
name String
address String?
totalArea Float?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
rooms Room[]
}
model Room {
id String @id @default(cuid())
apartmentId String
name String
shape String @default("[]") // JSON array of {x, y} points
width Float?
height Float?
wallHeight Float @default(2.7)
plinthHeight Float @default(0.06)
plinthThickness Float @default(0.01)
order Int @default(0)
posX Float @default(0)
posY Float @default(0)
floorType String @default("CONCRETE")
wallColor String @default("#f5f0eb")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
apartment Apartment @relation(fields: [apartmentId], references: [id], onDelete: Cascade)
walls Wall[]
openings WallOpening[]
electricalItems ElectricalItem[]
furnitureItems FurnitureItem[]
@@index([apartmentId])
}
model Wall {
id String @id @default(cuid())
roomId String
startX Float
startY Float
endX Float
endY Float
thickness Float @default(0.1)
direction String @default("OTHER") // NORTH, SOUTH, EAST, WEST, OTHER
room Room @relation(fields: [roomId], references: [id], onDelete: Cascade)
openings WallOpening[]
@@index([roomId])
}
model WallOpening {
id String @id @default(cuid())
roomId String
wallId String
type String // DOOR, WINDOW
positionAlongWall Float
width Float
height Float
elevationFromFloor Float @default(0)
openDirection String @default("LEFT") // LEFT, RIGHT, INWARD, OUTWARD
room Room @relation(fields: [roomId], references: [id], onDelete: Cascade)
wall Wall @relation(fields: [wallId], references: [id], onDelete: Cascade)
@@index([roomId])
@@index([wallId])
}
model ElectricalItem {
id String @id @default(cuid())
roomId String
type String // OUTLET, SWITCH, JUNCTION_BOX, LIGHT_CEILING, LIGHT_WALL, CABLE_ROUTE
x Float
y Float
wallId String?
elevationFromFloor Float?
rotation Float @default(0)
metadata String? // JSON
room Room @relation(fields: [roomId], references: [id], onDelete: Cascade)
@@index([roomId])
}
model FurnitureItem {
id String @id @default(cuid())
roomId String
type String // BED, DESK, WARDROBE, SOFA, TABLE, CHAIR, SHELF, NIGHTSTAND, DRESSER, BOOKCASE, OTHER
x Float
y Float
width Float
depth Float
height Float
rotation Float @default(0)
elevationFromFloor Float @default(0)
label String?
room Room @relation(fields: [roomId], references: [id], onDelete: Cascade)
@@index([roomId])
}