diff --git a/STATUS.md b/STATUS.md index 8d95cff..3b9b0f9 100644 --- a/STATUS.md +++ b/STATUS.md @@ -4,9 +4,9 @@ Single source of truth for "what's next." One milestone per PR/run. Autopilot: pick the one task under **NEXT**, ship it, stop. Do **not** start anything under **BLOCKED**. -_Last updated: 2026-07-04 — Milestone 6 (Web Worker WASM inference) done; a -user-directed polish item (boot loading bar) landed on top; queue empty pending -an M7 proposal._ +_Last updated: 2026-07-04 — Milestone 6 (Web Worker WASM inference) done; two +user-directed polish items (boot loading bar, then a black+green "coding" +aesthetic) landed on top; queue empty pending an M7 proposal._ --- @@ -176,6 +176,25 @@ an M7 proposal._ dismissal was verified RED on the second assertion). Verified visually on a real build (frozen-loader screenshot). Pre-change HEAD (rollback) `789d6ae`. +- **Polish — black + green "coding" aesthetic (user-directed, not a milestone).** + Restyled the whole UI to a phosphor-green-on-black terminal look. `src/style.css` + gains `--accent` (`#00ff9c`) / `--accent-dim` / `--mono` (monospace stack) + custom properties driving the `#controls` panel, slider labels+thumbs + (`accent-color`), the webcam/`::file-selector-button` buttons, `#status`, and + the `#hud` — all green, monospace, with a soft green outer glow on the panel. + `index.html`'s inline boot-loader CSS goes green (title + track + fill + glow), + monospace. `src/main.js`'s pre-upload point-cloud tint flips from blue-white + `(0.55, 0.78, 1.0)` to phosphor green `(0.15, 1.0, 0.45)` — the one + JS-observable change; **post-upload color still samples the image** (M3 path + untouched). `#device-note` stays amber on purpose (a caution signal that must + read distinctly against the green). Geometry, material identity, uniforms, the + render loop, and the M4 decoupling machinery are byte-unchanged. Smoke test + asserts the pre-upload `aColor` reads green (G>0.8, G dominant) — proven + non-tautological (RED on the old blue tint: G=0.78 fails G>0.8, and B=1.0 + dominates). Verified visually on a real build (green splat cloud + green HUD, + 30.7fps). Deployed to the linked Netlify site (`live-depth-field`). + Pre-change HEAD (rollback) `d455809`. + **Carry-over facts from M3/M4 (do not re-derive):** - Depth output contract: `depth` RawImage, Uint8, 1 channel, input W×H, 0–255 diff --git a/index.html b/index.html index d3ad854..bb2924b 100644 --- a/index.html +++ b/index.html @@ -31,24 +31,26 @@ pointer-events: none; } #boot-loader .boot-title { - font: 600 13px/1 system-ui, sans-serif; - letter-spacing: 0.14em; + font: 600 13px/1 'JetBrains Mono', 'Fira Code', ui-monospace, Menlo, + Consolas, monospace; + letter-spacing: 0.18em; text-transform: uppercase; - color: #8fb7ee; + color: #00ff9c; + text-shadow: 0 0 12px rgba(0, 255, 156, 0.6); } #boot-loader .boot-track { width: 220px; height: 4px; border-radius: 4px; - background: rgba(255, 255, 255, 0.12); + background: rgba(0, 255, 156, 0.14); overflow: hidden; } #boot-loader .boot-fill { width: 0; height: 100%; border-radius: 4px; - background: linear-gradient(90deg, #5a8fd6, #8ec7ff); - box-shadow: 0 0 12px rgba(142, 199, 255, 0.7); + background: linear-gradient(90deg, #00b36b, #00ff9c); + box-shadow: 0 0 12px rgba(0, 255, 156, 0.75); animation: boot-advance 2.4s cubic-bezier(0.2, 0.8, 0.2, 1) forwards; transition: width 0.3s ease; } diff --git a/src/main.js b/src/main.js index 3e840f0..2de869e 100644 --- a/src/main.js +++ b/src/main.js @@ -54,14 +54,15 @@ const cloudGeometry = new THREE.BufferGeometry(); cloudGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); // Milestone 3: per-point color, sampled from the uploaded photo. Initialized to -// the M2 blue-white glow tint so the pre-upload cloud renders exactly as M2 did. -// (Named `aColor`, not `color` — Three injects its own `color` attribute -// declaration when vertexColors is on, and we manage this one ourselves.) +// a phosphor-green glow tint — the app's black+green "coding" accent — so the +// pre-upload cloud reads as a terminal-green splat field. (Named `aColor`, not +// `color` — Three injects its own `color` attribute declaration when +// vertexColors is on, and we manage this one ourselves.) const colors = new Float32Array(GRID * GRID * 3); for (let i = 0; i < colors.length; i += 3) { - colors[i] = 0.55; - colors[i + 1] = 0.78; - colors[i + 2] = 1.0; + colors[i] = 0.15; + colors[i + 1] = 1.0; + colors[i + 2] = 0.45; } cloudGeometry.setAttribute('aColor', new THREE.BufferAttribute(colors, 3)); diff --git a/src/style.css b/src/style.css index 34694a3..29eeb2e 100644 --- a/src/style.css +++ b/src/style.css @@ -1,3 +1,13 @@ +/* Black + green "coding" aesthetic. A phosphor-green HUD on a pure-black stage, + monospace throughout, so the whole tool reads like a terminal. --accent is the + bright phosphor green; --accent-dim the calmer readout green. */ +:root { + --mono: 'JetBrains Mono', 'Fira Code', 'SF Mono', ui-monospace, 'Cascadia Code', + Menlo, Consolas, monospace; + --accent: #00ff9c; + --accent-dim: #3fd68a; +} + html, body { margin: 0; @@ -17,13 +27,14 @@ canvas { top: 12px; left: 12px; padding: 10px 12px; - background: rgba(20, 22, 28, 0.6); - border: 1px solid rgba(255, 255, 255, 0.12); + background: rgba(0, 12, 6, 0.62); + border: 1px solid rgba(0, 255, 156, 0.28); border-radius: 8px; - font: 12px/1.4 system-ui, sans-serif; - color: #cdd3df; + font: 12px/1.4 var(--mono); + color: var(--accent-dim); user-select: none; backdrop-filter: blur(4px); + box-shadow: 0 0 20px rgba(0, 255, 156, 0.12), inset 0 0 24px rgba(0, 255, 156, 0.04); } #controls .ctrl-row { @@ -35,10 +46,35 @@ canvas { #controls .ctrl-row span { width: 52px; + color: var(--accent); + letter-spacing: 0.06em; } #controls input[type='range'] { width: 130px; + accent-color: var(--accent); +} + +#controls button { + font: inherit; + color: var(--accent); + background: rgba(0, 255, 156, 0.08); + border: 1px solid rgba(0, 255, 156, 0.4); + border-radius: 5px; + padding: 4px 10px; + cursor: pointer; + letter-spacing: 0.04em; + transition: background 0.15s ease, box-shadow 0.15s ease; +} + +#controls button:hover:not(:disabled) { + background: rgba(0, 255, 156, 0.18); + box-shadow: 0 0 10px rgba(0, 255, 156, 0.35); +} + +#controls button:disabled { + opacity: 0.5; + cursor: default; } /* Milestone 3: photo upload + async depth status line. */ @@ -46,23 +82,37 @@ canvas { margin-top: 6px; max-width: 200px; font-size: 11px; - color: #cdd3df; + font-family: var(--mono); + color: var(--accent-dim); +} + +#controls input[type='file']::file-selector-button { + font: 11px var(--mono); + color: var(--accent); + background: rgba(0, 255, 156, 0.08); + border: 1px solid rgba(0, 255, 156, 0.4); + border-radius: 5px; + padding: 3px 8px; + margin-right: 8px; + cursor: pointer; } #status { margin-top: 6px; max-width: 210px; - color: #9fb4d8; + color: var(--accent-dim); } /* Milestone 5: FPS / inference readouts + the WASM slower-device warning. */ #hud { margin-top: 6px; padding-top: 6px; - border-top: 1px solid rgba(255, 255, 255, 0.12); - color: #9fb4d8; + border-top: 1px solid rgba(0, 255, 156, 0.2); + color: var(--accent-dim); } +/* WASM slower-device warning: kept amber on purpose — a caution signal that + reads distinctly against the green so it never blends into the HUD. */ #device-note { max-width: 210px; color: #e8c46a; diff --git a/tests/smoke.spec.js b/tests/smoke.spec.js index 127e59f..0d29b7a 100644 --- a/tests/smoke.spec.js +++ b/tests/smoke.spec.js @@ -685,6 +685,38 @@ test('boot loader: shipped in the initial HTML before JS, dismissed once the app expect(errors, `unexpected page errors:\n${errors.join('\n')}`).toEqual([]); }); +// Polish — black + green "coding" aesthetic (user-directed, not a milestone). +// The one JS-observable part of the restyle is the pre-upload point-cloud tint: +// before any photo/webcam upload, the cloud renders in the app's accent color. +// A green coding aesthetic means that default tint reads GREEN — the green +// channel dominates and is strong. (Post-upload the cloud is colored from the +// image; this asserts only the default fill, so the M3 image-color path is +// untouched.) Non-tautological: RED on the prior blue-white (0.55, 0.78, 1.0) +// tint where blue dominates; green only once the tint is changed. +test('aesthetic: pre-upload cloud tint reads green (coding aesthetic)', async ({ page }) => { + const errors = []; + page.on('pageerror', (e) => errors.push(`pageerror: ${e}`)); + page.on('console', (m) => { + if (m.type() === 'error') errors.push(`console.error: ${m.text()}`); + }); + await page.goto('/'); + await page.waitForFunction(() => window.__app?.getPointCount() === 128 * 128); + + const tint = await page.evaluate(() => { + let cloud = null; + window.__app.scene.traverse((o) => { + if (o.isPoints) cloud = o; + }); + const col = cloud.geometry.attributes.aColor; + return { r: col.getX(0), g: col.getY(0), b: col.getZ(0) }; + }); + // Green channel dominates and is strong — a green cloud, not blue-white. + expect(tint.g, 'green channel must be strong').toBeGreaterThan(0.8); + expect(tint.g, 'green must dominate red').toBeGreaterThan(tint.r); + expect(tint.g, 'green must dominate blue').toBeGreaterThan(tint.b); + expect(errors, `unexpected page errors:\n${errors.join('\n')}`).toEqual([]); +}); + // Milestone 6: WASM inference moves into a Web Worker so the main thread stays // responsive during a pass. The worker itself can't run in CI (it loads the // real ~100MB model — the worker-path proof is a local real-model probe, like