Commit Graph

217 Commits

Author SHA1 Message Date
Julien Calixte
98a9d1dffe docs: flag that v0.1 keeps wifi up, link git_sync.rs
Ground the off-between-syncs assumption in the code: the shipped firmware
runs the stay-associated strategy the section argues against. Link
run_git_service so the claim is verifiable and mark teardown as v0.8 work.
2026-07-11 15:25:33 +02:00
Julien Calixte
2673a3377a docs: note wifi is off (not modem-sleep) between syncs
Make explicit that the per-sync burst assumes a full radio de-init between
pushes, and that staying associated to skip handshakes loses on energy until
~150 syncs/hr — a regime a writing appliance never reaches.
2026-07-11 15:19:18 +02:00
Julien Calixte
6f7f05baf2 docs: link tradeoff-curves from the root README 2026-07-11 14:57:53 +02:00
Julien Calixte
f4af38d8ef docs: add .typoena.toml config and auto-sync energy tradeoff
Record the git-tracked .typoena.toml preferences file (save_on_idle,
auto_sync) and the palette `>` command mode that edits it in the v0.5
roadmap. Add a tradeoff-curves note deriving Wi-Fi sync energy as a 1/T
curve, which sets auto_sync's default to 10m (opportunistic, min-clamped)
rather than a 5m wall-clock timer.
2026-07-11 14:38:07 +02:00
Julien Calixte
14967a06c6 docs: retire resolved SD risk, re-sync v0.1 status
Spike 3 (SD) is resolved (genuine 32 GB card mounts, verified on its own
SPI3 host per ADR-012) and the mount/save path is now wired into main.rs.

- README: move the SD/CMD59 open question to the retired-risks line; drop
  the stale "blocked on SD (waiting on a compatible card)" from the status
  blurb. Remaining v0.1 gate is the boot splash + wiring git publish.
- roadmap: flip the boot-load and save-to-SD v0.1 items to done, re-sync
  the status blurb and macroplan note, re-point the config-at-boot TODO
  from the (landed) SD wiring to the git-publish wiring.
2026-07-11 14:14:08 +02:00
Julien Calixte
18ed6aa599 docs(roadmap): record the panel snackbar and fix panel status
The side-panel split (writing column + panel) is already built in the
editor crate; correct the stale "not built yet" note and add the
snackbar under v0.1.
2026-07-11 14:01:02 +02:00
Julien Calixte
8e3e6e25a4 feat(firmware): post loaded/saved snackbars to the panel
Boot posts "loaded <name>" (the note's filename without suffix); :w and
:sync post "saved" / "save FAILED - retry :w" via Editor::set_notice, so
save state is visible on-device, where there is no serial console.
2026-07-11 14:00:55 +02:00
Julien Calixte
e797381da6 feat(editor): boot in Normal mode and add a panel snackbar
Two boot/panel UX changes that share the editor's constructor, so they
land in one commit:

- Power-on mode is now Normal (was Insert). with_text() opens a loaded
  note in Normal with the caret on the last char (the resume point);
  draw(false) now suppresses the caret in every mode, so the boot-error
  screen renders as pure text rather than sprouting a block caret.
- notice/set_notice(): a transient side-panel message ("snackbar")
  drawn under the word count and cleared on the next keystroke — no
  timed repaint, which on e-ink would cost a ~630 ms flash to erase.
2026-07-11 14:00:45 +02:00
Julien Calixte
ee00fecdc8 docs(firmware): flip build-mode docs to git-default 2026-07-11 12:47:57 +02:00
Julien Calixte
9101d61045 build(firmware): default to the git build, add build-light
Make `just build`/`just flash` the nominal product build (--features git
+ LIBGIT2_SRC), and move the fast editor-only build to `build-light`/
`flash-light`. Verified the git default links libgit2 into the firmware
ELF (1.2 MB) cleanly.
2026-07-11 12:47:57 +02:00
Julien Calixte
830701e670 docs(firmware): document light vs git build modes 2026-07-11 12:39:24 +02:00
Julien Calixte
235c7e4d24 build(firmware): add git-enabled firmware just recipes 2026-07-11 12:39:24 +02:00
Julien Calixte
4c8c557e7b feat(firmware): gate :sync publish behind the git feature
Route :sync through publish(), whose git-push path is #[cfg(feature =
"git")]. Git code in the firmware binary is now only ever compiled with
--features git, so the default build stays a light editor build with no
git2 crate and (since the justfile only sets LIBGIT2_SRC for git recipes)
no libgit2 component. Feature is off by default; :sync saves locally.
2026-07-11 12:39:24 +02:00
Julien Calixte
acecf9998f Merge GitHub main (empty case-README commit from Remanso)
Reconciles a split-brain: fd62fac was pushed to GitHub only (not apoena)
and is an empty commit. Merging lets both remotes fast-forward to a single
head without rewriting the already-pushed persistence-wiring commits.
2026-07-11 12:20:09 +02:00
Julien Calixte
81d5f818cc docs(persistence): note boot-load and :w/:sync save are wired 2026-07-11 12:17:58 +02:00
Julien Calixte
637fe4a4f4 feat(firmware): boot-load and save the note via persistence
Seed the editor from Storage::load at boot and persist on :w/:sync
through Storage::save. A missing card/repo/unreadable note halts boot
with the reason on the panel rather than starting empty and clobbering
the note on the next save. Save errors are logged, buffer kept in RAM.

The git-push half of :sync stays a TODO — it needs git_sync graduated
into a module on the dedicated git thread; :sync saves for now.
2026-07-11 12:17:58 +02:00
Julien Calixte
a61e0a2196 feat(editor): add text() getter and with_text seed constructor
The host had no way to read the buffer out or seed it from a loaded
file. with_text lands the caret at the end so boot-load resumes writing
where the user left off; text() exposes the buffer for :w/:sync.
2026-07-11 12:17:51 +02:00
fd62fac10e Updating hardware/case/README.md from Remanso 2026-07-11 11:07:07 +01:00
Julien Calixte
7a1f25f5d1 docs(persistence): document module and refine FAT recovery to two cases
Record that persistence is now implemented and hardware-verified, and correct
the crash-recovery rule everywhere it appeared as "just promote the tmp": a
crash during the tmp write leaves a partial tmp, so recovery keeps the
committed target when both files are present and only promotes the tmp when
the target was already unlinked. Also fix the stale README heading that still
said the SD was on shared SPI2.
2026-07-11 12:02:30 +02:00
Julien Calixte
1e3220a95f refactor(sd): drive Spike 3 harness through persistence module
sd_fat is now a thin on-device harness over firmware::persistence instead of
duplicating the mount FFI. It mounts, reports FAT usage + negotiated clock,
loads notes.md (non-destructive), and only runs the write round-trip when
notes.md is empty so a provisioned card is never clobbered. Verified on
hardware 2026-07-11: mount at 10 MHz, 91-byte round-trip byte-identical.
2026-07-11 12:02:22 +02:00
Julien Calixte
9c338571f0 feat(persistence): add SD mount + atomic save/load module
Graduate the proven Spike 3 storage stack into firmware::persistence so the
editor and the spike share one implementation. Storage::mount brings up the
dedicated SPI3 bus (ADR-012) and mounts FAT with format_if_mount_failed=false
(never reformat the user's card). save() does the FAT-safe atomic write
(tmp -> fsync -> unlink -> rename); recover() reconciles a leftover *.tmp at
boot, keeping the committed file when the crash point is ambiguous and only
promoting the tmp when the target was already unlinked. load() caps reads at
256 KiB.
2026-07-11 12:02:15 +02:00
Julien Calixte
5540f901de docs(sd): mark SD-on-SPI3 verified on hardware
Re-ran the spike on the dedicated SPI3 wiring (CLK 14 / MOSI 15) with
the same clean mount + atomic round-trip result. Flip the docs from
"pending a re-run" to verified.
2026-07-11 11:43:34 +02:00
Julien Calixte
1b9987c4ee fix(sd): correct stale shared-bus log strings to SPI3
The boot banner, success line, and round-trip payload still said
"shared SPI2" / "shared bus" after the move to SPI3. Cosmetic only —
serial output now matches the dedicated-bus wiring.
2026-07-11 11:43:34 +02:00
Julien Calixte
4bc9236bd7 docs(adr): record ADR-012 — SD on its own SPI3 host
Document the shared-bus arbitration decision: rather than rework the
proven EPD SPI layer and add a cross-thread mutex on the save path, the
SD moves to a dedicated SPI3. Update the boot sequence, risk table,
Spike 3 postmortem follow-up, hardware overview, and firmware README.
2026-07-11 11:28:22 +02:00
Julien Calixte
9129f04d0e feat(sd): move the SD spike to its own SPI3 host
The EPD's SpiBusDriver holds an exclusive SPI2 lock for its lifetime, so
an SD on SPI2 is locked out while the panel driver is alive. Give the SD
its own SPI3 (SCK 14, MOSI 15; MISO 13 / CS 10 unchanged) and drop the
now-pointless EPD-CS deselect. See ADR-012.
2026-07-11 11:28:16 +02:00
Julien Calixte
54a9e31ee5 docs(firmware): document the SD card provisioning workflow
Add a "Provisioning an SD card" section covering the just init/load/provision
entry points, the config-resolution ladder (env → derive → prompt), the macOS
Keychain Wi-Fi lookup, and the plaintext-secret threat model for the card.
2026-07-11 11:14:53 +02:00
Julien Calixte
fc4ce4d017 feat(provision): fill card config from env, git, keychain, or prompts
typoena.conf no longer requires a hand-written firmware/.env. _write-conf now
resolves each value through a ladder: .env if set, else derived from tools
already on the machine (git config, gh, the active Wi-Fi network + its System
keychain password), else an interactive prompt with the derived value as the
default. The PAT is never derived (a broad gh token on a plaintext card would
defeat the scoped-token model) and is always typed by hand. Missing required
values now abort before touching the card instead of ejecting a blank config.
2026-07-11 11:14:48 +02:00
Julien Calixte
5335751323 docs(sd): record Spike 3 hardware verification and findings
Mark Spike 3 resolved (genuine 32 GB SDHC mounts, round-trips clean on the
shared SPI2 bus; the 133 GB SDXC's CMD59 rejection was the sole fault).
Capture the FatFS f_rename caveat and the ≤32 GB card-compatibility note in
ADR-007, the resolution in the postmortem, and the verified writeup in the
firmware README.
2026-07-11 11:14:31 +02:00
Julien Calixte
b899286639 feat(sd): reformat a fresh card on mount failure in the spike
Flip format_if_mount_failed to true so a fresh exFAT/unformatted bench card is
reformatted to FAT on the device instead of failing — no Mac-side prep for the
spike. Fires only on a filesystem mount failure, not the CMD59 protocol
rejection. The real persistence module must keep this false.
2026-07-11 11:14:15 +02:00
Julien Calixte
698c10c8c0 fix(sd): unlink target before f_rename to overwrite on FAT
FatFS's f_rename returns FR_EXIST on an existing destination — unlike POSIX
rename(2) it won't replace it, so re-running the round-trip failed at rename.
Unlink the target first (tolerating a missing one for the first save). This
opens a crash window the real persistence module must close with *.tmp boot
recovery (ADR-007).
2026-07-11 11:14:04 +02:00
Julien Calixte
bef9587a34 chore(sd): drop Spike 3 CMD59 diagnostic logging
The per-command sdmmc/sdspi DEBUG logs only existed to diagnose the CMD59
init failure, now resolved (verified 2026-07-11). Remove the build-time log
ceiling and the runtime esp_log_level_set block.
2026-07-11 11:13:42 +02:00
Julien Calixte
195311a395 feat(case): add a reset button hole in the back wall
Momentary switch wired to the board's EN/GND, mounted past the µSD so
it's never hit while typing. BOOT is omitted on purpose: on the S3,
auto-download makes it recovery-only. Positions are << MEASURE >>
placeholders — a custom carrier PCB will re-fix them later.
2026-07-11 10:01:21 +02:00
Julien Calixte
3faff25fba chore(case): pin $fn in the render recipe for stable previews
Preview quality no longer depends on whatever $fn the model happens to be
left at for fast editing.
2026-07-11 09:45:00 +02:00
Julien Calixte
208c929650 feat(case): relieve the bracket and foam for the FPC U-turn
The flex leaves the glass's back plane on the left and must fold ~180 deg
to dive into the cavity toward the breakout. A safe bend radius makes
that loop ~4 mm deep, too deep for the 1 mm foam gap, so it fouled the
rigid bracket. Open the bracket's left frame member and the foam's left
border over the FPC span, aligned with the body's existing slot, to give
the U-turn room. Re-adds the relief dropped in c4320e2, correctly scoped.
2026-07-11 09:44:43 +02:00
Julien Calixte
32e9623411 chore(case): expand just render to regenerate every preview
Was only rendering assembled + body, which let bracket.png drift stale.
Now covers all 11 views. Recovered the two bespoke cameras that were
never recorded: nameplate (straight-on body) and front34 (low back 3/4).
2026-07-11 09:34:34 +02:00
Julien Calixte
c4320e2239 revert(case): keep the bracket a solid frame (no FPC notch)
Reverts the notch added in 6af959f. The flex U-turns off the glass's
left edge and returns inboard through the ~1 mm foam gap above the
bracket, then drops into the cavity through the aperture — it never
crosses the bracket plane, so the relief in the body's pocket wall is
enough. A solid frame also clamps that edge more evenly. Comment added
so the notch doesn't get reintroduced.
2026-07-11 09:34:26 +02:00
Julien Calixte
6af959f92d feat(case): cut an FPC ribbon relief into the bracket frame
Gap in the left frame member aligned with the body's FPC clearance so
the flex folds back through the bracket plane into the cavity instead
of being pinched. Refresh renders and the bracket alt-text.
2026-07-11 09:27:25 +02:00
Julien Calixte
757dd492b9 docs: trim AI writing tells from READMEs and docs prose
Drop magic adverbs (silently, arguably), a stakes-inflation flourish,
and the bold-first Snippets bullets; de-duplicate the "usable artifact,
not a checkpoint" line shared by the README and roadmap.
2026-07-11 09:19:58 +02:00
Julien Calixte
91287899a4 style(case): apply markdown formatter to the enclosure README 2026-07-11 09:08:40 +02:00
Julien Calixte
314fefc9b6 docs: link the enclosure to its README, not the folder 2026-07-11 09:08:40 +02:00
Julien Calixte
e8482e4e2d style(case): recolor the body to soft sage, refresh renders 2026-07-11 09:03:22 +02:00
Julien Calixte
d47de98c22 docs(case): document plan_up/plan_down and add their renders 2026-07-11 08:51:34 +02:00
Julien Calixte
896ca8073b feat(case): split the plan section into plan_up and plan_down
Factor the two halves of the horizontal cut into shared modules so each
can be viewed alone (`plan_up` = deck/screen, `plan_down` = cavity),
while `plan` keeps the exploded combo. Cut height and gap are now the
`plan_z` / `plan_explode` params.
2026-07-11 08:51:33 +02:00
Julien Calixte
d63a66f4cf docs(case): document the plan section and embed its render 2026-07-11 08:46:47 +02:00
Julien Calixte
e28a6a6d19 feat(case): add exploded horizontal plan section
New `show="plan"` lifts the deck/screen half off the cavity half so the
board layout reads at a glance — standoffs, corner posts, ports, and the
internal FPC clearance. Tunable via `plan_z` and `explode`.
2026-07-11 08:46:47 +02:00
Julien Calixte
c521d68ad7 docs(case): describe the FPC clearance as internal, refresh renders 2026-07-11 03:04:48 +02:00
Julien Calixte
8fa727702e fix(case): keep the FPC clearance internal, unbroken bezel
The ribbon accommodation must not be visible from outside. Drop the cut
below the bezel lip so the flex folds back into the cavity with no notch
breaking the external face.
2026-07-11 03:04:48 +02:00
Julien Calixte
0c3b5b50b1 docs(case): refresh renders and document the FPC bay + section
Regenerate all previews for the current model, embed the nameplate and
section views, and correct the screen/FPC captions (ribbon exits the
left, breakout on the left, wiring to the ESP32).
2026-07-11 03:01:26 +02:00
Julien Calixte
ff0ff0b5ae feat(case): section view, foam gasket, and left-side FPC bay
The screen's flex leaves the LEFT short edge (user's left), so route it
there: an open ribbon bay in the left bezel, the DESPI-C579 breakout in
the cavity below it, and SPI wiring across to the ESP32. Add a modeled
foam gasket, a `show="section"` cutaway of the screen clamp, and
`active_off_x/y` for the panel's off-centre active area.
2026-07-11 03:01:19 +02:00
Julien Calixte
704fd743a0 docs: link the enclosure concept from README and hardware.md 2026-07-11 02:31:50 +02:00