← back to Quadrille Showroom
README.md
83 lines
# The Quadrille House — Endless Wings Wallcovering Showroom
A 3D "Endless Wings" showroom (Three.js) where you walk past hinged sample-book
**wings**, each showing a wallcovering tiled onto its panel, and a **Peruse
Collection** button auto-tours the whole catalog endlessly.
Built by fusing two existing local codebases:
- the **Endless Wings engine** from `~/Projects/dw-showroom` (50-wing chrome rail,
WASD walk, fan-cascade book-flip, lazy textures), and
- the **Quadrille House data** from `~/Projects/quadrille-house-site/data/house.json`,
filtered to the **883 wallcoverings** (878 China Seas + 5 Grasscloth — China Seas
is the wallpaper arm of the Quadrille umbrella).
Standalone, behind basic-auth (`admin / DWSecure2024!`), port **7690**.
## Run
```sh
npm install
node scripts/build-showroom-data.js # build data/showroom-products.json from house.json
node server.js # http://127.0.0.1:7690 (basic-auth)
```
## What's in it
- **Endless wings** — 50 live wings at a time (perf-bounded); `‹ Prev` / `Next ›`
+ per-brand jump page through all 883. Sort (Curated / Newest / Title / SKU /
Book) + density slider (20–80 wings).
- **Peruse Collection** (`P`, or the gold button) — camera flies ~4 ft to each
wing, opens it like a sample book, dwells 3 s, advances, and **pages window→window,
wrapping last→first = truly endless.** `Space` stops and inspects; `Esc` / click /
WASD interrupt.
- **Real wallpaper tiling** — every texture loads through the same-origin image
proxy (CORS-safe + readable canvas) and runs the **FullDesign seam gate** (JS port
of `skills/fulldesign`, verified at parity with the Python reference). Seamless →
`RepeatWrapping`; not seamless → contain-fit + a red "general wall image" flag bar.
- **View on Designer Wallcoverings** CTA per wing (live products link out; held SKUs
show "Request a Memo Sample"). Room thumbnail in the detail panel.
## Image-model pipeline — `scripts/gen-assets.js`
Design rule: **don't let AI re-generate the real product.** Splitting by where
invention is legitimate:
| Layer | Method | Cost | Why |
|---|---|---|---|
| **Tiles** | local mirror book-match (`img_tools.py`) | **$0** | makes the *genuine* China Seas pattern tile seamlessly — no AI hallucination of the product |
| **Rooms** | Gemini 2.5 Flash Image | ~$0.003/img | a backdrop *setting* is invented; the real swatch is passed as reference so the wall reads true |
```sh
node scripts/gen-assets.js # dry-run: classify + cost preview ($0)
node scripts/gen-assets.js --tiles # build all ~552 $0 seamless tiles
node scripts/gen-assets.js --rooms --execute --limit 25 --ceiling 0.10 # paid sample
```
Per-call + running **$ totals** are always printed (hard rule). `--rooms` never
spends without `--execute` and stops at `--ceiling`. The server prefers a generated
tile/room per-SKU at boot (`data/gen-tiles/<sku>.png`, `data/gen-rooms/<sku>.jpg` —
gitignored, rebuildable). Measured: ~63% of swatches fail the seam gate (~552 tiles,
$0); 519 lack a room shot (full Gemini run ≈ **$1.56**).
## Deploy
`.deploy.conf` targets pm2 `quadrille-showroom` on Kamatera `:7690`. Deploy is
Steve-gated (`/deploy`).
## Hosting decision — DTD verdict (2026-06-26)
Run through `/officer-yolo` (panel decides · vp-dw-commerce owns · gates hold).
**Verdict: C — keep LOCAL on Mac2 `:7690` for now. Do not deploy / no DNS change.**
Vote 2–1 (Codex C, Qwen C, Claude A-dissent). Rationale: it's a basic-auth-gated
internal/sales tool, not a public storefront — no confirmed remote-access need yet,
and a new always-on Kamatera service adds pm2-fleet fragility + attack surface for no
gain. **Escalation rule:** when remote sharing becomes concrete, deploy via **Option A**
— Cloudflare-proxied `quadrille-showroom.designerwallcoverings.com` behind basic-auth
(origin IP hidden, CF absorbs scans, basic-auth = 2nd gate). **Never Option B** (raw
`IP:7690` open port — exposes the Node origin, violates Cloudflare-first + minimize-
attack-surface). That deploy is a GATED action (DNS + Kamatera) — draft to
`pending-approval/` for Steve when the need arises; never auto-run.
Run locally: `node server.js` → http://127.0.0.1:7690 (admin / DWSecure2024!).