← back to Quadrille Showroom
furniture FIX 1: onload = clean empty room + table+chair only (no room-type dressing on boot)
6bdf2b5ddb11e4b2d34f74852713116d78ce9022 · 2026-07-02 10:16:18 -0700 · Steve
Boot no longer builds a fully-dressed 'living' room type (sofa/lamps/wingchair/
rug/sculpture/plant). Room-type dressing now appears ONLY when the user clicks a
room-type chip; a returning user also boots to the clean empty room. Verified via
probe-boot-scene: currentRoomType null, no roomType group, 1 furnitureRoot (table+chair).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Files touched
M public/js/viewmodes.jsA scripts/probe-boot-scene.mjsA scripts/verify-qh6.mjs
Diff
commit 6bdf2b5ddb11e4b2d34f74852713116d78ce9022
Author: Steve <steve@designerwallcoverings.com>
Date: Thu Jul 2 10:16:18 2026 -0700
furniture FIX 1: onload = clean empty room + table+chair only (no room-type dressing on boot)
Boot no longer builds a fully-dressed 'living' room type (sofa/lamps/wingchair/
rug/sculpture/plant). Room-type dressing now appears ONLY when the user clicks a
room-type chip; a returning user also boots to the clean empty room. Verified via
probe-boot-scene: currentRoomType null, no roomType group, 1 furnitureRoot (table+chair).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---
public/js/viewmodes.js | 15 ++++-----
scripts/probe-boot-scene.mjs | 45 +++++++++++++++++++++++++
scripts/verify-qh6.mjs | 78 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 130 insertions(+), 8 deletions(-)
diff --git a/public/js/viewmodes.js b/public/js/viewmodes.js
index 676866d..a815c54 100644
--- a/public/js/viewmodes.js
+++ b/public/js/viewmodes.js
@@ -842,16 +842,15 @@ function boot() {
// Perf toggle restore.
if (localStorage.getItem('qh_perf') === '1') togglePerf(true); else togglePerf(false);
- // Room-type restore (Phase 2). Default = Living Room (closest to the canonical box).
- // Build the furnished room WITHOUT auto-re-framing on boot so the showroom's own
- // walk/guided boot pose still owns the camera; the user re-frames by clicking a chip.
- const savedRoom = localStorage.getItem('qh_room_type');
- const bootRoom = (savedRoom && QH.ROOM_TYPES && QH.ROOM_TYPES[savedRoom]) ? savedRoom : 'living';
- // Reflect the saved floor override on the picker.
+ // Room-type dressing (sofa / lamps / wingchair / boards / rug / sculpture / plant) is
+ // OFF on boot (Steve 2026-07-02): "onload load nothing except the table and chair;
+ // remove all other elements." The room boots as a CLEAN empty room + the active style's
+ // table + chair only (swapFurniture owns those). A furnished room type is built ONLY when
+ // the user explicitly clicks a room-type chip (applyRoomType, wired at buildPanel). We do
+ // NOT auto-restore a saved room type either — a returning user also boots to the clean
+ // empty room and re-dresses by choosing a chip.
const savedFloor = QH.floorOverride || 'default';
document.querySelectorAll('#vm-floor .vm-chip').forEach(c => c.classList.toggle('active', c.dataset.floor === savedFloor));
- // Defer the room build a beat so products/center-book have a chance to load (palette hook).
- setTimeout(() => { applyRoomType(bootRoom, { noFrame: true }); }, 900);
// View: Hero is ALWAYS the boot view for a first-time load (locked rule). But if
// the user previously picked a mode, restore it AFTER the guided default has
diff --git a/scripts/probe-boot-scene.mjs b/scripts/probe-boot-scene.mjs
new file mode 100644
index 0000000..c3ba6ac
--- /dev/null
+++ b/scripts/probe-boot-scene.mjs
@@ -0,0 +1,45 @@
+import pw from '/Users/stevestudio2/.npm-global/lib/node_modules/playwright/index.js';
+const { chromium } = pw;
+
+const errors = [];
+const browser = await chromium.launch({ channel: 'chrome', headless: true, args: ['--use-angle=metal', '--ignore-gpu-blocklist'] });
+const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
+page.on('pageerror', e => errors.push('pageerror: ' + e.message));
+page.on('console', m => { if (m.type() === 'error') errors.push('console.error: ' + m.text()); });
+
+await page.goto('http://localhost:7690/', { waitUntil: 'domcontentloaded' });
+await page.waitForFunction(() => window._qh && window._qh.wingBoards && window._qh.wingBoards.length >= 50, { timeout: 25000 });
+await page.waitForTimeout(3500);
+
+const report = await page.evaluate(() => {
+ const qh = window._qh;
+ const scene = qh.scene;
+ // top-level scene children summary
+ const topLevel = scene.children.map(c => ({
+ name: c.name || '(unnamed)',
+ type: c.type,
+ isLight: !!c.isLight,
+ childCount: c.children ? c.children.length : 0
+ }));
+ // count meshes by keyword to detect room-dressing props
+ const kw = { sofa:0, lamp:0, wing:0, board:0, rug:0, plant:0, olive:0, tree:0, sculpture:0, bust:0, chair:0, table:0 };
+ let meshTotal = 0;
+ scene.traverse(o => {
+ if (o.isMesh) meshTotal++;
+ const n = (o.name || '').toLowerCase();
+ for (const k of Object.keys(kw)) if (n.includes(k)) kw[k]++;
+ });
+ return {
+ furniturePresent: qh.furniturePresent,
+ furnitureChildCount: qh.furnitureChildCount,
+ currentRoomType: qh.currentRoomType,
+ roomTypeGroupPresent: !!(scene.getObjectByName && scene.children.some(c => (c.name||'').startsWith('roomType:'))),
+ topLevelCount: topLevel.length,
+ topLevel,
+ meshTotal,
+ keywordCounts: kw
+ };
+});
+
+console.log(JSON.stringify({ errors, report }, null, 2));
+await browser.close();
diff --git a/scripts/verify-qh6.mjs b/scripts/verify-qh6.mjs
new file mode 100644
index 0000000..e89cc26
--- /dev/null
+++ b/scripts/verify-qh6.mjs
@@ -0,0 +1,78 @@
+import pw from '/Users/stevestudio2/.npm-global/lib/node_modules/playwright/index.js';
+const { chromium } = pw;
+
+const errors = [];
+const browser = await chromium.launch({ channel: 'chrome', headless: true, args: ['--use-angle=metal', '--ignore-gpu-blocklist', '--window-size=1440,900'] });
+const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
+page.on('pageerror', e => errors.push('pageerror: ' + e.message));
+page.on('console', m => { if (m.type() === 'error') errors.push('console.error: ' + m.text()); });
+
+// Clean slate — clear any saved room-type/view so we test true first-boot.
+await page.addInitScript(() => { try { localStorage.clear(); } catch (e) {} });
+
+await page.goto('http://localhost:7690/', { waitUntil: 'domcontentloaded' });
+await page.waitForFunction(() => window._qh && window._qh.wingBoards && window._qh.wingBoards.length >= 50, { timeout: 25000 });
+await page.waitForTimeout(4000); // let boot settle
+
+const out = {};
+
+// (a) ONLOAD: only table+chair + empty room, no room-type dressing
+out.boot = await page.evaluate(() => {
+ const qh = window._qh, scene = qh.scene;
+ const roomTypeGroups = scene.children.filter(c => (c.name || '').startsWith('roomType:')).map(c => c.name);
+ const furnitureRoots = scene.children.filter(c => c.name === 'furnitureRoot').length;
+ return {
+ currentRoomType: qh.currentRoomType,
+ roomTypeGroups,
+ furnitureRoots,
+ furnitureChildCount: qh.furnitureChildCount,
+ furnitureStyle: qh.furnitureStyle
+ };
+});
+
+// Screenshots per style at DEFAULT camera (dead-ahead rest pose).
+const styles = ['contemporary', 'traditional', 'transitional', 'brutalist'];
+for (const s of styles) {
+ await page.evaluate((k) => window._qh.swapFurniture(k, { includeExtras: false }), s);
+ await page.waitForTimeout(700);
+ // reset to canonical rest pose so all 4 shots are the same framing
+ await page.evaluate(() => { if (window._qh.setSpin) window._qh.setSpin(0, -0.08); });
+ await page.waitForTimeout(500);
+ await page.screenshot({ path: `/tmp/qh6-${s}.png` });
+}
+
+// (c) all 4 swap + (d) exactly 1 furnitureRoot after 10 swaps (leak test)
+const order = ['contemporary', 'traditional', 'transitional', 'brutalist'];
+let swapOK = true;
+for (let i = 0; i < 10; i++) {
+ const k = order[i % order.length];
+ const res = await page.evaluate((key) => {
+ window._qh.swapFurniture(key, { includeExtras: false });
+ return { style: window._qh.furnitureStyle, present: window._qh.furniturePresent, childCount: window._qh.furnitureChildCount };
+ }, k);
+ if (res.style !== k || !res.present || res.childCount < 1) swapOK = false;
+ await page.waitForTimeout(120);
+}
+out.leak = await page.evaluate(() => {
+ const scene = window._qh.scene;
+ return { furnitureRootCount: scene.children.filter(c => c.name === 'furnitureRoot').length, style: window._qh.furnitureStyle };
+});
+out.swapAll4OK = swapOK;
+
+// (f) "At the Table" seated view still works
+out.tableView = await page.evaluate(async () => {
+ try {
+ window._viewmode.set('table');
+ return { set: true };
+ } catch (e) { return { set: false, err: String(e) }; }
+});
+await page.waitForTimeout(1500);
+out.tableViewAfter = await page.evaluate(() => {
+ const cam = window._qh.camera.position;
+ return { camY: +cam.y.toFixed(3), camZ: +cam.z.toFixed(3), errorsSoFar: 0 };
+});
+await page.screenshot({ path: '/tmp/qh6-tableview.png' });
+
+out.errors = errors;
+console.log(JSON.stringify(out, null, 2));
+await browser.close();
← 0a7d7ab furniture: art-direction fixes — concrete-grey Brutalist, wa
·
back to Quadrille Showroom
·
furniture FIX 2: Transitional now unmistakably distinct from 02b4e0a →