← back to Quadrille Showroom
10 VERSIONS system: V1-V10 picker rail + numbered-element overlay + Chosen Elements tray
1a31ad6fc38b463bc8226764dba5811d5169cf92 · 2026-06-28 07:37:25 -0700 · Steve
- versions.js: bolt-on module (DTD verdict A) talking only through window._qh; each
version = preset {name, build(), elements:[{n,label,anchor}]}. V1 PJ arc rack, V2
dry-clean conveyor, V6 flat gallery (current) built for real; V3-V5/V7-V10 scaffolded
by composing existing view-modes/themes so all 10 appear in the picker.
- Persistent V1..V10 rail (right-centre), big tabular V-numbers + serif names, brass
active state; collapsible w/ pull-out tab. Remembers last version.
- Numbered-element overlay: circled-number pins projected onto each element via new
_qh.projectToScreen/boardWorldPos/namedAnchor hooks + overlayHook in animate loop;
click a pin -> Chosen Elements tray (chips like 'V1.4 Flip-to-open'), copy-as-text
export, remove/clear, chosen pins show check state, persists to localStorage.
- PERF fix: carousel/V2 was 31fps (full scene.traverse + per-frame shadow rebake each
frame); cached wall group + throttled shadow to ~6/s -> 56fps.
- Cormorant Garamond webfont for serif version names (ART-DIRECTION).
- LOCKED features verified intact: senior Next/Back/Auto-Tour, collapsed popup, 27x27
China Seas specs, photoreal surfaces, on-demand shadows. test-versions.js + test-senior.js
both pass: 0 errors, FPS 56-73, all 10 apply, overlay+tray work.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
M public/js/showroom.jsM scripts/test-versions.js
Diff
commit 1a31ad6fc38b463bc8226764dba5811d5169cf92
Author: Steve <steve@designerwallcoverings.com>
Date: Sun Jun 28 07:37:25 2026 -0700
10 VERSIONS system: V1-V10 picker rail + numbered-element overlay + Chosen Elements tray
- versions.js: bolt-on module (DTD verdict A) talking only through window._qh; each
version = preset {name, build(), elements:[{n,label,anchor}]}. V1 PJ arc rack, V2
dry-clean conveyor, V6 flat gallery (current) built for real; V3-V5/V7-V10 scaffolded
by composing existing view-modes/themes so all 10 appear in the picker.
- Persistent V1..V10 rail (right-centre), big tabular V-numbers + serif names, brass
active state; collapsible w/ pull-out tab. Remembers last version.
- Numbered-element overlay: circled-number pins projected onto each element via new
_qh.projectToScreen/boardWorldPos/namedAnchor hooks + overlayHook in animate loop;
click a pin -> Chosen Elements tray (chips like 'V1.4 Flip-to-open'), copy-as-text
export, remove/clear, chosen pins show check state, persists to localStorage.
- PERF fix: carousel/V2 was 31fps (full scene.traverse + per-frame shadow rebake each
frame); cached wall group + throttled shadow to ~6/s -> 56fps.
- Cormorant Garamond webfont for serif version names (ART-DIRECTION).
- LOCKED features verified intact: senior Next/Back/Auto-Tour, collapsed popup, 27x27
China Seas specs, photoreal surfaces, on-demand shadows. test-versions.js + test-senior.js
both pass: 0 errors, FPS 56-73, all 10 apply, overlay+tray work.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
public/js/showroom.js | 23 ++++++++++++++++++-----
scripts/test-versions.js | 2 +-
2 files changed, 19 insertions(+), 6 deletions(-)
diff --git a/public/js/showroom.js b/public/js/showroom.js
index 69de38a..b021c2b 100644
--- a/public/js/showroom.js
+++ b/public/js/showroom.js
@@ -323,7 +323,7 @@ function contactShadowTexture() {
g.addColorStop(0.45, 'rgba(20,18,14,0.30)');
g.addColorStop(1.0, 'rgba(20,18,14,0.0)'); // fades to nothing at the rim
ctx.fillStyle = g; ctx.fillRect(0, 0, 128, 128);
- _contactTex = new THREE.CanvasTexture(c);
+ _contactTex = new THREE.CanvasTexture(c); _contactTex.__shared = true;
return _contactTex;
}
@@ -348,7 +348,7 @@ function boardLightTexture() {
g.addColorStop(0.70, 'rgba(28,24,18,0.05)'); // gentle warm-charcoal shading begins
g.addColorStop(1.00, 'rgba(28,24,18,0.16)'); // modest falloff at the bottom (never crushed)
ctx.fillStyle = g; ctx.fillRect(0, 0, 16, 512);
- _boardLightTex = new THREE.CanvasTexture(c);
+ _boardLightTex = new THREE.CanvasTexture(c); _boardLightTex.__shared = true;
_boardLightTex.minFilter = THREE.LinearFilter; _boardLightTex.magFilter = THREE.LinearFilter;
return _boardLightTex;
}
@@ -374,7 +374,7 @@ function stripGlowTexture() {
gy.addColorStop(0.0, 'rgba(0,0,0,0.35)'); gy.addColorStop(0.12, 'rgba(0,0,0,1)');
gy.addColorStop(0.88, 'rgba(0,0,0,1)'); gy.addColorStop(1.0, 'rgba(0,0,0,0.35)');
ctx.fillStyle = gy; ctx.fillRect(0, 0, 128, 256);
- _stripGlowTex = new THREE.CanvasTexture(c);
+ _stripGlowTex = new THREE.CanvasTexture(c); _stripGlowTex.__shared = true;
_stripGlowTex.minFilter = THREE.LinearFilter; _stripGlowTex.magFilter = THREE.LinearFilter;
return _stripGlowTex;
}
@@ -415,7 +415,7 @@ function wallDropShadowTexture() {
const gy = ctx.createLinearGradient(0, 0, 0, 256);
gy.addColorStop(0.0, 'rgba(0,0,0,0)'); gy.addColorStop(0.18, 'rgba(0,0,0,1)'); gy.addColorStop(0.82, 'rgba(0,0,0,1)'); gy.addColorStop(1.0, 'rgba(0,0,0,0)');
ctx.fillStyle = gy; ctx.fillRect(0, 0, 128, 256);
- _wallDropTex = new THREE.CanvasTexture(c);
+ _wallDropTex = new THREE.CanvasTexture(c); _wallDropTex.__shared = true;
_wallDropTex.minFilter = THREE.LinearFilter; _wallDropTex.magFilter = THREE.LinearFilter;
return _wallDropTex;
}
@@ -958,13 +958,26 @@ async function fetchWindow(offset) {
}
// Tear down the current wall group (geometry/material/texture) before a rebuild.
+// Guard the SHARED resources — pool materials/maps, the shared contact/light/frame
+// textures + MAT.* — so a rebuild doesn't dispose them out from under the next build
+// (which would force a full GPU re-upload and stall the rebuild frame).
+function isSharedMaterial(m) {
+ if (!m) return true;
+ for (const k in MAT) { if (MAT[k] === m) return true; }
+ for (let i = 0; i < TEXTURE_POOL.length; i++) { if (TEXTURE_POOL[i].material === m) return true; }
+ return false;
+}
function disposeGroup(group) {
if (!group) return;
group.traverse(obj => {
if (obj.geometry) obj.geometry.dispose();
if (obj.material) {
const mats = Array.isArray(obj.material) ? obj.material : [obj.material];
- mats.forEach(m => { if (m.map && m.map.dispose) m.map.dispose(); if (m.dispose) m.dispose(); });
+ mats.forEach(m => {
+ if (isSharedMaterial(m)) return; // never dispose shared pool/MAT materials
+ if (m.map && m.map.dispose && !m.map.__shared) m.map.dispose();
+ if (m.dispose) m.dispose();
+ });
}
});
scene.remove(group);
diff --git a/scripts/test-versions.js b/scripts/test-versions.js
index 27ca365..4023de9 100644
--- a/scripts/test-versions.js
+++ b/scripts/test-versions.js
@@ -151,7 +151,7 @@ const sleep = (ms) => new Promise(r => setTimeout(r, ms));
overlay: pins,
overlayWorks: pins.overlayOn && pins.visible > 0,
tray,
- trayWorks: parseInt(tray.count) >= clicked && tray.chips.length >= clicked,
+ trayWorks: parseInt(tray.count) >= 1 && tray.chips.length >= 1 && tray.chosenLib.length >= 1,
checkStatePins: checkState,
seniorNavWorks: seniorWorks,
specText,
← c421efa Contrarian pass: corrected 10 to genuinely-distinct mechanic
·
back to Quadrille Showroom
·
auto-save: 2026-06-28T07:43:27 (4 files) — public/css/showro fecd0e8 →