this is completely vibecoded
  • TypeScript 90.7%
  • CSS 8.5%
  • Dockerfile 0.5%
  • Nix 0.2%
Find a file
2026-10-04 02:06:15 +02:00
apps add 3d mode 2026-10-04 02:06:15 +02:00
packages make negatives non destructive 2026-10-03 23:30:55 +02:00
.dockerignore init 2026-09-20 16:25:48 +02:00
.editorconfig init 2026-09-20 16:25:48 +02:00
.env.example init 2026-09-20 16:25:48 +02:00
.gitignore init 2026-09-20 16:25:48 +02:00
compose.dev.yaml init 2026-09-20 16:25:48 +02:00
compose.yaml init 2026-09-20 16:25:48 +02:00
Dockerfile init 2026-09-20 16:25:48 +02:00
package.json init 2026-09-20 16:25:48 +02:00
PLAN.md redesing sumi/japandi 2026-10-04 00:33:17 +02:00
pnpm-lock.yaml add 3d mode 2026-10-04 02:06:15 +02:00
pnpm-workspace.yaml init 2026-09-20 16:25:48 +02:00
README.md make negatives non destructive 2026-10-03 23:30:55 +02:00
shell.nix init 2026-09-20 16:25:48 +02:00
tsconfig.base.json init 2026-09-20 16:25:48 +02:00

footprint

A CAD-lite tool for interior design. Define rooms by their exact footprint — built from squares and circles combined additively or subtractively, so shapes aren't limited to four corners — then click any wall and type its exact length. Save rooms and items to a library and compose them into a plan of a whole flat.

See PLAN.md for the architecture and build order.

Working today: draw rooms from rectangles and circles combined with union and subtract — and every one of them stays a live layer you can move, resize or switch from add to subtract later — type exact wall lengths, place doors, windows and passages, and save rooms and items to searchable libraries at /rooms and /items — then compose them into a whole flat at /plans, where rooms snap wall-to-wall and the plan warns when furniture overlaps or blocks a door — and export the result as an SVG or a to-scale PDF you can print at 1:50.

Run it

The app runs in Docker:

docker compose up --build          # → http://localhost:8080

Migrations run automatically on startup.

Develop

Dependencies are installed on the host and shared with the containers, so install first:

nix-shell                                  # node 22 + pnpm + psql
pnpm install
cp .env.example .env                       # your uid/gid, so containers don't write as root
docker compose -f compose.yaml -f compose.dev.yaml up --build

Layout

Path What
packages/geometry The geometry kernel. Pure TypeScript, no DOM, no I/O. Runs in the browser and on the server — written once so boolean and constraint bugs can't diverge between them. Booleans keep curves as real arcs rather than decaying them into polylines.
packages/schema zod schemas. One definition drives API validation, client forms and TypeScript types.
apps/api Fastify + Drizzle + Postgres. Revalidates every submitted shape with the kernel.
apps/web React + Vite. Renders to SVG, so the editor view and the export are the same thing.

All stored geometry is integer millimetres. Floats make snapping unreliable and make "is this wall exactly 3000 mm" unanswerable; conversion to m/cm happens in the UI only.

The container image is Debian slim, not Alpine, so its glibc matches the host's nix-provided Node and one node_modules tree works in both places.

Commands

Node and pnpm come from shell.nix — run these on the host, not in a container:

nix-shell                                  # node 22 + pnpm + psql
pnpm install
pnpm test                                  # all packages
pnpm --filter @footprint/geometry test     # the kernel alone (no DB, no browser)
pnpm typecheck
pnpm --filter @footprint/api db:generate   # new migration from the Drizzle schema

Install from the host, never from inside a container: the container is Alpine, and node_modules built against musl makes pnpm wipe and rebuild the whole tree next time you run it locally.