← back to Quadrille Showroom
proto: COLOR RIVER — shop China Seas by hue as a flowing Three.js color current
597fb5b8933e835e7a202f8948fbe885c5177709 · 2026-06-28 08:10:28 -0700 · Steve
- v6-colorriver.html: client-side hue extraction (sat-weighted, ground-aware),
swatches laid out along a continuous spectrum riverbed with undulating flow,
hue-rail navigation, click-to-focus big swatch + 27x27 spec card
- headless playwright verifiers: 120/120 swatches, sorted-by-hue, 0 console errors,
click-to-focus surfaces real pattern name + spec
- proto/ only; touches nothing in public/ or shared app files
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
A proto/shot-focus.cjsA proto/shots/v6-colorriver-focus.pngM proto/shots/v6-colorriver.png
Diff
commit 597fb5b8933e835e7a202f8948fbe885c5177709
Author: Steve <steve@designerwallcoverings.com>
Date: Sun Jun 28 08:10:28 2026 -0700
proto: COLOR RIVER — shop China Seas by hue as a flowing Three.js color current
- v6-colorriver.html: client-side hue extraction (sat-weighted, ground-aware),
swatches laid out along a continuous spectrum riverbed with undulating flow,
hue-rail navigation, click-to-focus big swatch + 27x27 spec card
- headless playwright verifiers: 120/120 swatches, sorted-by-hue, 0 console errors,
click-to-focus surfaces real pattern name + spec
- proto/ only; touches nothing in public/ or shared app files
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
proto/shot-focus.cjs | 46 ++++++++++++++++++++++++++++++++++++
proto/shots/v6-colorriver-focus.png | Bin 0 -> 4547727 bytes
proto/shots/v6-colorriver.png | Bin 867221 -> 1906135 bytes
3 files changed, 46 insertions(+)
diff --git a/proto/shot-focus.cjs b/proto/shot-focus.cjs
new file mode 100644
index 0000000..4460ed8
--- /dev/null
+++ b/proto/shot-focus.cjs
@@ -0,0 +1,46 @@
+#!/usr/bin/env node
+/* Verify the click-to-focus mechanic: drive focus() on a swatch programmatically,
+ confirm the detail card shows the pattern name + 27" spec, capture a focus shot. */
+const { spawn } = require("child_process");
+const path = require("path");
+const http = require("http");
+const PROTO_DIR = __dirname, PORT = 7712;
+const URL = `http://127.0.0.1:${PORT}/v6-colorriver.html`;
+function waitForServer(port,tries=40){return new Promise((res,rej)=>{const p=(n)=>{const r=http.get({host:"127.0.0.1",port,path:"/v6-colorriver.html"},x=>{x.destroy();res();});r.on("error",()=>{if(n<=0)return rej(new Error("no server"));setTimeout(()=>p(n-1),150);});};p(tries);});}
+(async()=>{
+ const pw=require("playwright");
+ const srv=spawn("node",[path.join(PROTO_DIR,"proxy-server.cjs")],{env:{...process.env,PROTO_PORT:String(PORT)},stdio:"inherit"});
+ let code=1;
+ try{
+ await waitForServer(PORT);
+ const b=await pw.chromium.launch({headless:true});
+ const pg=await b.newPage({viewport:{width:1440,height:900},deviceScaleFactor:2});
+ const errs=[]; pg.on("console",m=>{if(m.type()==="error")errs.push(m.text());}); pg.on("pageerror",e=>errs.push(e.message));
+ await pg.goto(URL,{waitUntil:"domcontentloaded"});
+ await pg.waitForFunction(()=>window.__RIVER__&&window.__RIVER__.done(),{timeout:35000}).catch(()=>{});
+ await pg.waitForTimeout(1500);
+ // pick a saturated swatch and focus it via the page's own click path
+ const info = await pg.evaluate(()=>{
+ // find a colorful swatch by raycasting against the most saturated one
+ const probe = window.__FOCUS_PROBE__;
+ return probe ? probe() : null;
+ });
+ // no probe export — instead simulate a center click on a visible swatch
+ await pg.mouse.click(720, 380);
+ await pg.waitForTimeout(1400);
+ const cardShown = await pg.evaluate(()=>document.getElementById("detail").classList.contains("show"));
+ const name = await pg.evaluate(()=>document.getElementById("d_name").textContent);
+ const dim = await pg.evaluate(()=>document.getElementById("d_dim").textContent);
+ await pg.screenshot({path:path.join(PROTO_DIR,"shots","v6-colorriver-focus.png")});
+ console.log("\n=== FOCUS VERIFY ===");
+ console.log("detail card shown :", cardShown);
+ console.log("pattern name :", JSON.stringify(name));
+ console.log("dimensions spec :", JSON.stringify(dim));
+ console.log("console errors :", errs.length);
+ console.log("====================");
+ await b.close();
+ code = (cardShown && name && name!=="—" && /27/.test(dim) && errs.length===0) ? 0 : 1;
+ console.log(code===0?"FOCUS: PASS":"FOCUS: (card may need a swatch under the click point)");
+ }catch(e){console.error(e);code=3;}
+ finally{srv.kill("SIGTERM");setTimeout(()=>process.exit(code),300);}
+})();
diff --git a/proto/shots/v6-colorriver-focus.png b/proto/shots/v6-colorriver-focus.png
new file mode 100644
index 0000000..bae722f
Binary files /dev/null and b/proto/shots/v6-colorriver-focus.png differ
diff --git a/proto/shots/v6-colorriver.png b/proto/shots/v6-colorriver.png
index 283804b..826f69b 100644
Binary files a/proto/shots/v6-colorriver.png and b/proto/shots/v6-colorriver.png differ
← cbd8fb9 Tighter arc (86deg span, ~75% overlap = thinner slivers) + w
·
back to Quadrille Showroom
·
auto-save: 2026-06-28T08:13:35 (4 files) — proto/shots/v10-c 6241da8 →