Files
typewriter/CONTEXT.md
Julien Calixte abbd9d2e6e docs(context): align the glossary with the shipped v0.7 surface
Save is :w (not Ctrl-S), Publish is :gp (not Ctrl-G), :e was retired in
v0.6, and Publish's internals are the journaled-dirty-path splice with a
fetch-and-replay reconcile — never a merge. Also fix the principles
section's own use of the retired "status line" term.
2026-07-14 12:49:17 +02:00

8.3 KiB
Raw Blame History

Typoena

A single-purpose writing appliance: e-ink + mechanical keyboard + ESP32-S3. The user opens the lid, writes Markdown, and (when they choose) publishes to a git remote. This glossary fixes the language of that workflow, and of the screen the writer looks at while doing it.

Related docs: README.md — project overview, hardware, macro roadmap. docs/adr.md — load-bearing decisions; ADR-010 is the formal record of the Publish UX defined below. docs/qfd.md — requirements ↔ functions ↔ components, ranked by user-facing weight. References the terms in this file as canonical. docs/v0.1-mvp-product.md — the v0.1 product surface, expressed in this vocabulary. docs/v0.1-mvp-technical.md — how v0.1 is built. docs/macroplan.md — per-version scope, where new terms enter this glossary as versions land (the v0.5 multi-file Buffer terms are in).

Language

File scopes

Tracked: A file that lives in the device's git working copy and can be published to the remote. Lives under /sd/repo/. Avoid: synced, public, remote, committable.

Local: A file that exists only on the device and can never be published. A permanently-private scope, not a draft staging area — files are born Local and stay Local for their lifetime. Lives under /sd/local/. Avoid: draft, private, untracked, scratch (these all imply impermanence or promotability, which is not the model).

Editing model

Buffer: A File loaded into memory for editing, with its own caret, scroll position, and undo history. Opening a file makes it the active buffer — the one the Writing column shows. Up to three buffers stay resident at once (the active one plus two parked in the background); switching back to a resident buffer restores its caret and undo without re-reading the card. A fourth open evicts the least-recently-used resident buffer — saved first if it has unsaved edits, so nothing is lost. Avoid: tab, window, document (a buffer is not a UI chrome element); "the file" when you mean the in-memory copy rather than the bytes on the card.

Open: Bringing a File into the active buffer, via Cmd-P (the file palette); new files are created with :enew <file> or the palette's > new file (:e was retired in v0.6). Scope is read from where the file lives (/sd/repoTracked, /sd/localLocal), never chosen at open time. Avoid: load (implementation talk for the disk read behind an Open).

User-facing actions

Save: The act of durably writing the current buffer to the SD card. Triggered by :w (and by the idle auto-save when save_on_idle is on). Applies to both Tracked and Local files. Avoid: write, flush, persist (use them only in implementation talk).

Publish: The atomic act of pushing the current state of the entire Tracked working copy to the git remote. Workspace-scoped, not buffer-scoped: a Publish ships every dirty Tracked file on the device, not just the one the user is viewing. Triggered by :gp. Internally: splice the journaled dirty paths onto HEAD's tree → commit with a timestamped message → push → on a rejected push, fetch and replay the commit onto the remote tip (no merge) → push again. Unavailable in Local. Avoid: push, commit, sync, upload, git-push (these leak transport details into user-facing language).

Commit is deliberately not a user-facing term. The device authors all commit messages itself (a timestamped message); the user never sees a commit prompt. A Publish is the only user-observable unit of "shipping work"; internal commits are an implementation detail of that.

Screen regions

Writing column: The left region of the panel showing the text being edited — the only region that repaints per keystroke. A 63-col region split into a line-number gutter (absolute numbers, 24 cols wide, sized to the buffer's line count) and the text column it steals from (~60 cols for a file ≤ 99 lines). Full panel height; straddles the driver's x = 396 seam invisibly. Avoid: edit area, text area, main pane (superseded — they named the old full-width text region before the side panel carved out its right edge).

Side panel: The right region (~160 px / ~17 cols at its FONT_9X15 metadata font, full height) holding all metadata: filename + dirty dot, word count, elapsed time, clock, Wi-Fi, keyboard-disconnect flag, publish state, and the mode indicator at its bottom-left. Sits entirely in the master half (right of the x = 396 seam). Every field is static, event-driven, or throttled — never per-keystroke. Avoid: header, status line, status bar (retired — the old top header band and bottom status band are both collapsed into this one right-hand region); sidebar. Do not write bare panel: it collides with the transient panel (the modal full-screen help/config view that swaps in over the editor — a later release, see docs/spikes.md Spike 11). Always qualify: side panel vs transient panel.

Relationships

  • A File belongs to exactly one scope (Tracked or Local), fixed at creation. There is no operation that moves a file between scopes.
  • Save applies to any File; Publish applies only to Tracked.
  • A single Publish is atomic from the user's view: a Wi-Fi failure or remote divergence surfaces as a single retry-able outcome, not as a multi- step progression the user has to reason about.

Example dialogue

Dev: "If I'm in a Local file and I run :gp, what happens?" Domain expert: "Nothing — Publish is unavailable in Local. The side panel says so. There is no path from Local to the remote." Dev: "So if I want to publish something that started as a journal entry, I have to copy-paste it into a Tracked file?" Domain expert: "Yes, deliberately. Promotion is a manual gesture, not a built-in operation." Dev: "And if the remote has changed since I last pulled — does Publish fail?" Domain expert: "It fetches, replays the device's commit onto the remote tip — no merge commit — and pushes again. From the user's view it's one action with one outcome — success or retry."

Principles

  • The device is a writing tool, not a sync engine. Every git operation is the direct, in-session consequence of a :gp (or :gl pull) the user ran. The device does not auto-publish, auto-pull, retry-on-boot, or otherwise reconcile remote state in the background. If a previous Publish ended mid-flight and left a local commit unpushed, the next user-initiated Publish picks it up; until then, the device is silent about it.
  • Publish is sync, not history. The user's mental model is a Google Doc that happens to be backed by git: the point is "I want to read this on my phone later," not "I want a curated commit log." Commits are a transport detail the device authors itself. Branches are out of scope for the same reason — the device tracks one linear stream of work on whichever branch the remote was cloned on, and never switches.
  • Durability before delivery. A Publish's user-meaningful moment is when the local commit lands (a few seconds — the splice), not when the push completes (the rest of a ~1224 s :gp on the real notes repo). The side panel surfaces the commit-landed state as soon as it exists; the remaining push time is the transport of an already-safe thing. Long-form rationale: docs/notes/ctrl-g-perceived-latency.md.
  • No state the user didn't ask for. No banners about pending work, no prompts about divergence, no "did you mean to publish" warnings. The side panel reflects the current action's outcome, nothing else.

Flagged ambiguities

  • "Local" was initially ambiguous between (a) a draft pen that gets promoted, (b) a permanently-private scope, (c) a second git repo, (d) .gitignore'd files inside the working copy. Resolved: (b). Each File's scope is fixed at creation; there is no promotion operation.
  • "Commit" was used loosely across early docs as if it were a user-facing action. Resolved: it is not. The user has Save and Publish. Commits are an internal unit inside Publish, never authored or named by the user.