Home screen gains shutter, aperture and ISO as dropdowns plus a focus stepper
(three nudge sizes each way, and AF) where the body drives focus over PTP.
Those three drop out of the read-only strip rather than being shown twice.
Everything is built from what the camera actually reports, so a body without
one of these controls doesn't get it rather than showing something that
silently fails. The 450D marks shutter and aperture readonly unless the mode
dial is somewhere they apply, so the dropdowns disable themselves and say why.
Focus uses the EOS manualfocusdrive steps and autofocusdrive, and warns when
live view is off, which Canon bodies generally require for focus commands.
The settings drawer is now tabbed: app settings stay on the first tab, and
each config section the camera reports gets its own. Empty sections are
dropped, and a selected section that disappears falls back to the first tab.
Tabs wrap rather than scroll - a scrolled-off tab is an undiscoverable one.
Reverts the automatic camera search added in the previous commit, restoring
the Select camera button. Auto-connect could park with no way to reach the
device chooser: WebUSB permissions are per-origin, so a grant on localhost
doesn't carry to the deployed copy, and a camera that's asleep or held by
another app never arrives.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QK7PKpRVoy69Fpa32R8abb
Replaces the gPhoto2 demo UI (live view beside the raw config tree) with a
timelapse intervalometer. The home screen carries only what changes between
runs - interval, frame count, start delay, start/stop, countdown, progress
and a log - while every camera setting moves into a settings drawer.
- intervalometer.js: schedules frames on an absolute grid (start + n *
interval) so transfer time doesn't accumulate as drift over a long run. An
overrun logs and fires as soon as the camera is free rather than dropping a
frame; three consecutive failures abort.
- storage.js: frames stream into a folder via the File System Access API,
named for capture time (20260801-172713_00001.JPG) so sorting by name is
sorting by time. Falls back to downloads. Also holds the screen wake lock,
since background tabs get their timers throttled.
- config-utils.js: config tree lookups, shutter speed parsing, and bulb
capability detection (bulb toggle or Canon eosremoterelease).
- home.js: sequence controls plus a read-only exposure readout and pre-flight
warnings when the interval can't fit the exposure and transfer.
- settings.js / index.js: app prefs and the full config tree behind a drawer.
Config polling now only runs while that drawer is open, leaving the USB link
to the captures during a sequence.
Live view stays up between frames and steps aside only while the shutter
fires, which is what the EOS driver requires; it recovers afterwards with
backoff instead of hammering a busy camera.
Deployed as an assets-only Cloudflare Worker. The WASM is built with pthreads
and allocates a shared WebAssembly.Memory, so _headers reproduces the COOP/COEP
pair from serve.json - without cross-origin isolation the app fails to start.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QK7PKpRVoy69Fpa32R8abb