24 Commits

Author SHA1 Message Date
L'électron rare 828c4e8870 fix: orient stop panels toward the road
Each stop's group faced world +Z regardless of the road's local
tangent, so IDENTITÉS and GREETINGS showed mirrored text and the
GREETINGS plane read as crossing the road. Compute yaw once per stop
via atan2 from the anchor to pointAt(stop.t) so the panel's +Z always
points at the road it belongs to. Lamp yaw is world-space absolute, so
subtract the group's new rotation to keep its facing unchanged.
2026-07-09 14:04:57 +02:00
L'électron rare 031a102dac docs: credit kenney assets
Note the Car Kit, City Kit Roads, and Furniture Kit sources (CC0)
and add Kenney to the greetings scroller.
2026-07-09 13:42:29 +02:00
L'électron rare ae77d8f623 feat: replace primitives with kenney cc0 models
Swap hand-built boxes/cylinders for Kenney Car Kit, City Kit Roads,
and Furniture Kit low-poly models across the bus, stops, and world,
keeping ride/camera logic and Html panels untouched.
2026-07-09 13:42:22 +02:00
L'électron rare d0fe0ec9dd docs: add music credits and descope notes
Only the scroller text credited ride.mod; add CREDITS.md and a
semantic-fallback line so no-JS visitors also see the "11th-hour"
by TDK CC BY 4.0 attribution (The Mod Archive, unmodified).

Also record the two spec items descoped during implementation:
chunky-pixel retro look instead of ordered dithering, and a
single MOD track instead of per-stop ambience shifts.
2026-07-09 12:40:26 +02:00
L'électron rare 41a12724f6 build: pin docker base images by digest
Floating tags node:22-alpine and nginx:1.27-alpine let upstream
swap image contents without review. Pin both FROM lines to their
current digest per the supply-chain policy, keeping the tag as
a trailing comment for readability.
2026-07-09 12:40:21 +02:00
L'électron rare 311cd58a6e fix: harden worklet caching and build guard
Un-hashed libopenmpt.worklet.js was inheriting the immutable
1-year cache from /assets/, risking stale audio on chiptune3
bumps. Add a dedicated short-cache location for it.

Also fail closeBundle loudly if the worklet source vanishes,
instead of silently shipping a build with hanging audio.
2026-07-09 12:40:16 +02:00
L'électron rare 2287868d2a fix: apply security headers in all locations
nginx add_header directives are not inherited when a location block
declares its own add_header. Duplicated X-Content-Type-Options and
Referrer-Policy headers in all three location blocks.
2026-07-09 12:27:45 +02:00
L'électron rare b3964df9b2 build: add nginx docker image and compose
Dockerfile implements multi-stage build: node:22-alpine for build
stage, nginx:1.27-alpine for production runtime. nginx.conf provides
SPA routing with gzip compression and cache headers. docker-compose.yml
prepared for Tower Traefik integration with external network and Host
route. All files match specification verbatim.
2026-07-09 12:22:42 +02:00
L'électron rare aa81b39600 test: add playwright smoke suite
Add playwright.config.ts with webServer configured to run build and
preview on port 4173. Add e2e/smoke.spec.ts with 4 smoke tests:
rel=me anchor presence, all identity links in HTML, canvas and HUD
visibility, and reduced motion fallback rendering.

Restructure App.tsx to render Scroller outside #demo div to fix
selector ambiguity in e2e tests. Add test.exclude to vite.config.ts
to prevent vitest from running playwright tests.
2026-07-09 12:15:16 +02:00
L'électron rare 7ca96d049a feat: add lazy mod tracker player
Implement createModPlayer with a TDD-first ModPlayer contract
(idle/loading/playing/paused/unavailable), wired into App and
TrackerPanel. Uses chiptune3@0.8.7, loaded lazily via a deep
import (the package ships no main/exports field). Adds two Vite
fixes needed for the nested worklet asset to actually load: copy
libopenmpt.worklet.js next to the built worklet chunk, and
exclude chiptune3 from dep pre-bundling so dev mode resolves the
same sibling file.

Ships public/music/ride.mod: "11th-hour" by TDK, CC BY 4.0, from
The Mod Archive (modarchive.org, module ID 67104). Credited in
the HUD scroller text.
2026-07-09 12:05:15 +02:00
L'électron rare fc5621bd24 feat: add sine scroller and tracker panel hud
Implement Task 10: HUD components for LE BUS demoscene page.
- Scroller.tsx: canvas-2D sine-wave text animation at bottom
- TrackerPanel.tsx: play/mute control panel (top-right)
- modPlayer.ts: interface stubs (ModState, ModPlayer)
- CSS styling for both components appended to style.css
2026-07-09 11:50:24 +02:00
L'électron rare f424026117 feat: add the four content stops in 3d
Context: the ride passed 4 stops (boot, identities, electron
fou, greetings) but rendered no content at any of them.

Approach: shared StopPanel component places a pole, a yellow
sign with the stop title, and a drei Html transform panel
anchored beside the road via stopAnchor(); each stop composes
it with its own content module.

Changes:
- StopPanel.tsx: pole + sign + Html panel, panel--active class
  driven by useRide selector on the active stop id
- StopBoot.tsx: avatar art, name, tagline, bio lines
- StopIdentities.tsx: identity links list
- StopElectronFou.tsx: music blurb + pulsing tube amp with VU
  needles animated via useFrame
- StopGreetings.tsx: greetings list + ride-again reset button
- Stops.tsx: mounts the four stop components
- style.css: .panel styling, active glow, sign buttons/links
- App.tsx: mount <Stops /> in the Canvas, drop the placeholder

Impact: the ride now has readable, interactive content at every
stop; build (tsc -b + vite build) and full vitest suite (12/12)
pass.
2026-07-09 11:43:06 +02:00
L'électron rare 2c4a4bd168 feat: add copper bars sky and boing ball
Implement Task 8: create fullscreen animated copper-bars backdrop
via fragment shader, plus red/white checkered Amiga bouncing ball
using canvas texture. Mount <Sky /> in App.tsx containing <BoingBall />.

- Sky.tsx: shaderMaterial with 5-color palette, sine-based bar
  animation, depthWrite false for depth sorting
- BoingBall.tsx: canvas checkerTexture (256x128 SRGB), orbit +
  bounce animation, sphereGeometry 24/16 resolution
- App.tsx: import Sky, mount in Canvas, update placeholder
2026-07-09 11:38:08 +02:00
L'électron rare dd92bb6154 feat: add bus and i2c frame passengers
Implement Bus component (low-poly orange van following the path with
engine judder) and Passengers component (glowing I2C frames with
labels that board the bus at stops). Mount both in App.tsx. All
tests pass (12).
2026-07-09 11:34:04 +02:00
L'électron rare d5134b8ba5 feat: add pcb world with instanced components
Implements Task 6: seeded PRNG for deterministic PCB layout,
substrate, copper trace road, and instanced chip/cap/resistor/via
components. All tests pass. Build successful.
2026-07-09 11:29:37 +02:00
L'électron rare cc133d5734 feat: mount 3d app shell with camera rig
Add busPath curve helpers (pointAt/tangentAt), CameraRig as the
sole consumer of useRide.frame(), and App.tsx wiring the R3F
Canvas at low dpr for chunky pixels. Replace the boot.ts stub
with the real React root mount and append fixed-canvas CSS.
2026-07-09 11:24:39 +02:00
L'électron rare ce40fa93c1 feat: add ride progression store and inputs
Implement ride progression logic with pure functions
(clamp01, ease, activeStop), Zustand store for state
management, and React input hook for wheel/keyboard/touch.
2026-07-09 11:19:52 +02:00
L'électron rare 4436877e45 feat: semantic fallback page with rel=me injection
Add a Vite plugin that renders bio, links and greetings from the
Task 2 content modules into the raw served index.html at build
time, so non-JS clients (Mastodon's rel=me verifier included) see
real content and the mastodon.saillant.cc/@clement rel=me anchor.

Hide the fallback for sighted users once the 3D demo boots, but
keep it in the accessibility tree. Add @types/node so tsc can
type-check the node:fs import in the new test.
2026-07-09 11:12:28 +02:00
L'électron rare 017ffe0cf3 feat: wire real bandcamp listen url 2026-07-09 11:08:46 +02:00
L'électron rare 36cb91ccf5 feat: add content config as single source of truth
Implement content modules (links, bio, greetings, music, stops) as a
single source of truth for the UI, HUD, and 3D scene. All content is
defined in TypeScript with interfaces for type safety.

The Bandcamp URL for L'Électron Fou is not available yet, so the music
module defaults to the Mastodon profile URL as a fallback (per spec).
Update this URL when the Bandcamp page is created.
2026-07-09 11:04:54 +02:00
L'électron rare 47e7b67015 chore: ignore typescript build cache 2026-07-09 11:01:40 +02:00
L'électron rare 96c3062662 chore: scaffold vite react-three project
Establish initial project structure for LE BUS (clement.saillant.cc),
a demoscene-style personal identity hub built with Vite, React, and
react-three-fiber.

Changes:
- Add package.json with dependencies (React 19, Three.js,
  react-three/fiber, zustand, playwright for e2e)
- Add tsconfig.json with ES2022 target and react-jsx support
- Add vite.config.ts with React plugin
- Add minimal index.html, src/main.tsx, and src/style.css
- Add README.md with usage commands
- Add .gitignore (node_modules, dist, test artifacts, .DS_Store)
- Generate package-lock.json via npm install

Note: chiptune3 removed from dependencies (not on npm registry;
will be isolated behind src/audio/modPlayer.ts in Task 11 with mocks).

Build and test harness verified:
- npm run build succeeds → dist/index.html created
- npx vitest run --passWithNoTests exits 0
2026-07-09 10:56:10 +02:00
L'électron rare 6b2ed06925 docs: add le-bus implementation plan
14 TDD tasks: scaffold, content config, rel=me fallback,
ride logic, PCB world, bus, sky, stops, HUD, MOD player,
smoke tests, docker, Tower deploy.
2026-07-09 10:50:29 +02:00
L'électron rare f87c549b90 docs: add clement.saillant.cc design spec
Demoscene-style personal identity hub: bus on a giant PCB,
4 content stops, MOD tracker audio, semantic DOM fallback
with rel=me Mastodon verification. Static build, deployed
on Tower behind Traefik + cloudflared tunnel.
2026-07-09 10:39:47 +02:00