From 05fe222c6210adccee448f94972e1e74310d32cd Mon Sep 17 00:00:00 2001 From: DecodedXR Date: Sat, 4 Jul 2026 12:04:30 -0600 Subject: [PATCH] feat: bottom-center "Built with Claude Fable 5" build credit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a bottom-middle build-attribution line — the counterpart to the top #app-title. Static presentational chrome: an inline #build-credit div in index.html styled in src/style.css (fixed, bottom:12px, centered, dim --accent-dim monospace), pointer-events:none + z-index:5 so it never intercepts orbit drags on the canvas and the boot loader still fully covers it during boot. No JS changes. Smoke test asserts the string ships in the raw HTML and the live credit is visible, horizontally centered near the bottom, and does not capture pointer events over the canvas. Proven non-tautological (RED on the prior HTML). Autopilot: user-directed polish (build credit) Co-Authored-By: Claude Opus 4.8 (1M context) --- STATUS.md | 18 ++++++++++--- index.html | 6 +++++ src/style.css | 17 +++++++++++++ tests/smoke.spec.js | 61 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 3 deletions(-) diff --git a/STATUS.md b/STATUS.md index 9294fa4..1c3e65b 100644 --- a/STATUS.md +++ b/STATUS.md @@ -4,10 +4,10 @@ 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; three +_Last updated: 2026-07-04 — Milestone 6 (Web Worker WASM inference) done; four user-directed polish items (boot loading bar, a black+green "coding" aesthetic, -then a persistent title + author credit) landed on top; queue empty pending an -M7 proposal._ +a persistent title + author credit, then a bottom "Built with Claude Fable 5" +build credit) landed on top; queue empty pending an M7 proposal._ --- @@ -216,6 +216,18 @@ M7 proposal._ a real build (centered green title + credit above the orbiting cloud, ~21fps). Pre-change HEAD (rollback) `d5546e8`. +- **Polish — bottom "Built with Claude Fable 5" build credit (user-directed, not + a milestone).** A bottom-middle build-attribution line, the counterpart to the + top `#app-title`. Same treatment: an inline `#build-credit` div in `index.html` + styled in `src/style.css` (`position: fixed; bottom: 12px`, centered, dim + `--accent-dim` monospace), `pointer-events: none` + `z-index: 5` so it never + intercepts orbit drags and the boot loader still covers it during boot. **No + JS.** Smoke test asserts the string ships in the raw HTML AND the live credit + is visible, horizontally centered near the bottom, and doesn't capture pointer + events over the canvas. Proven non-tautological (RED on the prior HTML: no + `#build-credit`). Verified visually on a real build. Pre-change HEAD (rollback) + `94b4084`. + **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 0eb44f8..fd0186e 100644 --- a/index.html +++ b/index.html @@ -94,6 +94,12 @@
Live Depth Field
by Noah Federovitch
+ +
Built with Claude Fable 5
diff --git a/src/style.css b/src/style.css index b6f855c..879cc3d 100644 --- a/src/style.css +++ b/src/style.css @@ -52,6 +52,23 @@ canvas { color: var(--accent-dim); } +/* Build attribution, docked bottom-middle over the canvas — counterpart to + #app-title. Same pointer-events:none / below-boot-loader rules so it never + steals orbit drags and the loader still covers it during boot. */ +#build-credit { + position: fixed; + bottom: 12px; + left: 50%; + transform: translateX(-50%); + z-index: 5; + text-align: center; + pointer-events: none; + user-select: none; + font: 11px/1 var(--mono); + letter-spacing: 0.08em; + color: var(--accent-dim); +} + /* Milestone 2: live splat-uniform sliders, docked top-left over the canvas. */ #controls { position: fixed; diff --git a/tests/smoke.spec.js b/tests/smoke.spec.js index 68cf0ef..a33d9f7 100644 --- a/tests/smoke.spec.js +++ b/tests/smoke.spec.js @@ -779,6 +779,67 @@ test('title: Live Depth Field name + Noah Federovitch credit shown at top center expect(errors, `unexpected page errors:\n${errors.join('\n')}`).toEqual([]); }); +// Build credit (user-directed, not a milestone). A "Built with Claude Fable 5" +// attribution must sit at the bottom-middle of the screen — the counterpart to +// the top #app-title. Static presentational chrome inlined in the initial HTML, +// so this asserts (a) it ships in the raw server bytes, (b) it's visible, +// horizontally centered near the BOTTOM of the live page, and (c) the overlay +// never intercepts pointer input (so it can't block orbiting the cloud beneath +// it). Non-tautological: no #build-credit exists before this change. +test('build credit: "Built with Claude Fable 5" shown at bottom center', 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()}`); + }); + + // (a) The credit ships in the exact bytes the server sends — static chrome, + // not JS-injected. Fetched raw so a scripted node can't satisfy it. + const html = await (await page.request.get('/')).text(); + expect(html, 'initial HTML must contain a #build-credit overlay').toMatch( + /id=["']build-credit["']/, + ); + expect(html, 'the credit must name the build tool').toContain( + 'Built with Claude Fable 5', + ); + + // (b) Live: the credit is visible and its text is present once the page loads. + await page.goto('/'); + const credit = page.locator('#build-credit'); + await expect(credit).toBeVisible(); + await expect(credit).toContainText(/Built with Claude Fable 5/i); + + // Bottom-middle placement: horizontally centered, near the bottom edge. + const box = await credit.boundingBox(); + const viewport = page.viewportSize(); + const creditCenter = box.x + box.width / 2; + const viewportCenter = viewport.width / 2; + expect( + Math.abs(creditCenter - viewportCenter), + `credit must be horizontally centered (center ${creditCenter} vs ${viewportCenter})`, + ).toBeLessThan(40); + expect( + box.y + box.height, + 'credit must sit near the bottom of the screen', + ).toBeGreaterThan(viewport.height - 80); + + // (c) The overlay must not steal orbit input — the element under its center + // point must be the canvas, not the credit. + const underCenter = await page.evaluate(() => { + const el = document.getElementById('build-credit'); + const r = el.getBoundingClientRect(); + const hit = document.elementFromPoint(r.left + r.width / 2, r.top + r.height / 2); + return hit ? hit.tagName.toLowerCase() : null; + }); + expect(underCenter, 'credit must not capture pointer events over the canvas').toBe( + 'canvas', + ); + + 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