Julien Calixte 3128bca4ad docs(readme): sync to bench reality and slim via links
Fix stale claims: hardware IS on bench; git is libgit2/git2 as an
esp-idf component (ADR-004 kill-switch fired), not gix; display is the
in-tree SSD1683 driver; TLS handshake heap is the measured ~35 KB; v0.1
auth is build-time TW_* env vars (ADR-011 open). Replace the hardware
table, Gantt, and "why not" sections with links (docs/hardware.md,
docs/roadmap.md, ADR-001/002); update repo layout from planned to
actual; prune resolved open questions and link postmortems.
2026-07-08 00:04:49 +02:00
2026-05-16 14:21:32 +02:00

Typoena

A distraction-free, hackable, DIY writing machine. ESP32-S3 + e-ink + a real mechanical keyboard. You write Markdown, you commit, you push. Nothing else runs on it.

Status: pre-MVP, hardware on bench. Display, USB keyboard, live typing with partial refresh, Wi-Fi + TLS, and on-device git push are all verified in spikes. No release has shipped yet — v0.1 is blocked on SD (waiting on a compatible card), the boot splash, and wiring the save/publish path into the app binary. Live per-item status: docs/roadmap.md · failure write-ups: docs/postmortems/.


Vision

A single-purpose appliance that boots into a text editor with a Vim keymap, edits Markdown files, and (optionally) pushes them to a git remote (GitHub first) over Wi-Fi. No browser, no notifications, no apps. Open lid → write → push (or don't) → close lid.

Two file scopes coexist on the SD card — formal definitions in CONTEXT.md:

  • Tracked — lives in the git working copy, gets Published when the user presses Ctrl-G.
  • Local — never leaves the device. Permanently-private: journal entries, scratch, things that aren't anyone else's business. There is no "promote to Tracked" gesture — scope is fixed at file creation.

Same editor, same keymap; the difference is just whether Ctrl-G (publish to the remote) is offered.


Hardware

ESP32-S3-N16R8 (16 MB flash, 8 MB PSRAM) · GDEY0579T93 5.79″ e-ink strip (792×272, ~2.9:1 — biases the UX toward "current line + recent context", the writing posture we want) · Nuphy wired USB keyboard with the S3 as USB host · microSD over SPI · USB-C wall power for the MVP, battery in v0.8.

Full part table, rationale, and bench status: docs/hardware.md.


Software stack

Language: Rust on esp-idf-rs (std). Every stack decision — language, UI strategy, display, git lib, auth, concurrency, storage, power, keyboard transport — has an ADR in docs/adr.md, including the rejected alternatives (Ratatui, Gleam + Shore on AtomVM, C/Arduino — ADR-001/002). How each decision is weighted against the user-facing requirements lives in docs/qfd.md; the ontology those docs use is defined in GLOSSARY.md.

Layer Choice Notes
HAL / runtime esp-idf-svc, esp-idf-hal std build: heap, threads, VFS, mbedtls, Wi-Fi stack.
Display Custom SSD1683 driver (src/epd.rs) + embedded-graphics Dual-controller 792×272 panel; dirty-rect partial refresh (~630 ms measured).
UI layer Custom thin widget layer Ratatui's API shape without its char-grid terminal model (ADR-002).
Editor core Custom, in-tree (src/editor.rs) Modal (Normal / Insert / View / Command), motions, operators + text objects. Plain-ASCII buffer until the v0.2 UTF-8 work.
USB host esp-idf TinyUSB bindings Boot-protocol HID; verified on hardware (Spike 4).
Git libgit2 via git2, built as an esp-idf component with mbedTLS (firmware/components/libgit2/) gix was the original pick but can't push over HTTPS — the ADR-004 kill-switch fired. On-device add → commit → push verified.
TLS mbedtls via esp-idf GitHub HTTPS with the chain checked against embedded roots; ≈35 KB heap measured during handshake (Spike 6).
Auth HTTPS + GitHub PAT v0.1 bakes credentials in at build time via TW_* env vars; provisioning + at-rest protection is ADR-011 (open), on-device settings land in v0.9.
Filesystem FAT on SD (esp_vfs_fat) Working copy lives here. Internal LittleFS holds config.

UX boundaries set by the medium

E-ink is a brutal honesty filter on UI choices. Hard constraints we design around, not against:

  • No cursor blink. Kills the panel and the battery.
  • Typing latency target: ≤ 200 ms from keypress to glyph on screen, using partial refresh on the affected line only.
  • Full refresh every ~20 partials to clear ghosting. User-visible flash — schedule it on pauses (>1 s of no input).
  • No smooth scrolling. Page-style jumps only.
  • No animations. Anywhere.
  • Render only changed lines, not the viewport.

Roadmap

Frequent releases. Each version is a usable artifact, not a checkpoint. Per-version scope, current [x]/[~] marks, and the macro-plan Gantt live in docs/roadmap.md.

Version Theme One-liner
v0.1 MVP Boots, edits one file, Ctrl-G pushes.
v0.2 Vim nav Normal/Insert, motions, line numbers.
v0.2.5 Intl input US-Intl dead keys: à é ê ç, '+space = '.
v0.3 Vim edit dd yy p, undo/redo, counts.
v0.4 Visual + ex v V, :w :q :e command line.
v0.5 Files Ctrl-P over /repo + /local, buffers.
v0.6 Markdown Headings, list continuation, soft-wrap.
v0.7 Search + git / search, :Gpull.
v0.8 Power 18650 + sleep + lid switch.
v0.9 Robustness Crash-safe writes, reconnect, settings.
v1.0 Polish Boot ≤ 3 s, fonts, themes, enclosure, guide.
v1.x Stretch 10.3″ panel, multi-remote, stats, BLE.

Repo layout

/firmware                 Rust crate, esp-idf-rs target
                          (SD card mounted at runtime contains /repo and /local)
  /src
    main.rs               app binary — editor + display + USB (SD/git not wired yet)
    editor.rs             modal editor core: buffer, modes, keymap, :commands
    epd.rs                SSD1683 dual-controller e-ink driver
    usb_kbd.rs            TinyUSB host glue, HID → key events
    /bin                  on-device spike binaries (sd_fat, wifi_tls, git_push,
                          git_sync, git_smoke)
  /components/libgit2     libgit2 as an esp-idf CMake component (mbedTLS);
                          source vendored as a git submodule
  build.rs                bakes TW_* env vars (Wi-Fi, PAT, author) — v0.1 config path
/spikes                   desktop spikes (spike7 git push proof, pre-device)
/docs                     ADRs, QFD, hardware, roadmap, per-version specs,
                          spikes.md, postmortems/, notes/
CONTEXT.md                project glossary — Tracked / Local / Save / Publish, and
                          the principles that fall out of them
GLOSSARY.md               methodology glossary — the WHAT / Function /
                          Characteristic / Metric / Target ontology layers
package.json              pnpm + oxfmt — formatting toolchain for docs/JSON

Open questions / risks (tracked, not yet resolved)

  • SD on the bench: the 133 GB card rejects CMD59; waiting on a genuine ≤32 GB card (postmortem).
  • Heap fragmentation over a long writing session with the PSRAM allocator.
  • Real-world e-ink ghosting with the current partial-refresh cadence.

Retired risks (gix push, TinyUSB HID stability, TLS heap, libgit2-on-xtensa) and how they died: docs/spikes.md and docs/postmortems/.

These get resolved by writing code, not by deciding harder.

Description
No description provided
Readme 6.8 MiB
Languages
Rust 84.7%
Just 4.8%
C 4.3%
OpenSCAD 2.9%
HTML 2.3%
Other 1%