docs(v0.6): specify the snippet library and $/> palette split

Reshape v0.6 snippets from a hard-coded table into a git-synced,
Zed-compatible JSON library (.typoena.snippets.json) with an inline Tab
path and a $ palette launcher. Add the file-format reference and
document the Cmd-P/>/$ verb split and the just init catalog.
This commit is contained in:
Julien Calixte
2026-07-12 10:03:38 +02:00
parent 93f3a43b34
commit 221135cd9b
2 changed files with 230 additions and 29 deletions

View File

@@ -3,36 +3,93 @@
> Part of the [Typoena macro plan](macroplan.md). Requirements and targets:
> [qfd.md](qfd.md). Load-bearing decisions: [adr.md](adr.md).
**Status:** render affordances done early; the snippet
engine remains (snippets are net-new scope, added 2026-07-08).
**Status:** render affordances done early; the snippet engine is the remaining
work (snippets are net-new scope, added 2026-07-08; reshaped 2026-07-12 from a
hard-coded table into a git-synced, Zed-compatible library with a `$` palette
launcher — see [`typoena-snippets.md`](typoena-snippets.md) for the file format).
- [x] Heading lines bolded in render (faux-bold double-strike)
- [x] List continuation on Enter inside `- ` / `1. ` (with empty-item exit)
- [x] Soft-wrap at word boundaries
- [ ] **Snippets** — trigger-driven text expansion for Markdown authoring
(Zed-inspired, but no completion popup: e-ink's ~630 ms refresh rules out
a live filtering menu, and it fights the distraction-free premise). Shape,
mirroring the existing `list_marker` insert-transform:
- [ ] Tab in Insert mode triggers expansion: if the word immediately before
the caret matches a snippet prefix, expand it; otherwise insert spaces
as today (`expand_snippet(word) -> Option<(body, stops)>`, alongside
`list_marker`).
- [ ] A snippet body is literal text plus numbered empty tab stops `$1 … $n`
and a final `$0`. There is no placeholder text (`${1:label}`) — the
editor has no selection/overtype model, so a placeholder would just be
text to delete. There are no dynamic or computed values either (e.g. no
`date` — there's no RTC; the wall clock is valid only after Wi-Fi+SNTP,
so it'd stamp 1970 on a cold boot).
- [ ] After expansion the caret lands on `$1`; Tab advances to the next stop,
forward only (no Shift-Tab). Stored stop offsets shift with edits at the
caret (all pending stops are always after it). The session auto-aborts
on Esc, a mode change, or a motion that leaves the stops.
- [ ] On a typing pause (same throttle as the insert cursor / word-count
refresh — the panel never repaints per keystroke), if the word before
the caret is a snippet prefix, the side panel shows the hint (the target
expansion). Quiet while typing; the hint appears on pause.
- [ ] The snippet table is hard-coded in the binary to start; a git-syncable
file on SD (`/sd/repo/.snippets`) is a later option, deferred while SD
is still blocked.
- [ ] Starter set: link `[$1]($2)$0`, image `![$1]($2)$0`, fenced code block,
etc.
## Snippets
Trigger-driven text expansion for Markdown authoring (Zed-inspired, but **no
completion popup**: e-ink's ~630 ms refresh rules out a live filtering menu, and
it fights the distraction-free premise). The library is a git-synced,
Zed-compatible JSON file — full file-format reference in
[`typoena-snippets.md`](typoena-snippets.md). This section is the *editor*
behaviour.
- [x] **The tab-stop engine** — the shared core both surfaces drive. A body is
literal text plus numbered stops `$1 … $n` and a final `$0`; on insertion
the caret lands on `$1` (or the end), in Insert. Tab advances to the next
stop, **forward only** (no Shift-Tab); pending stops sit after the caret
and shift with edits there. The session auto-aborts on Esc, a mode change,
or a motion that leaves the stops. `${n:label}` parses to a bare `$n` (the
label is stripped — no selection model to fill); no dynamic values (no RTC,
so no `date`).
- [x] **Inline Tab-expansion (Insert mode).** If the word immediately before the
caret matches a snippet prefix, Tab expands it and starts the tab-stop
session; otherwise Tab inserts spaces as today. A check in the Insert
handler alongside the existing `list_marker` transform
(`expand_snippet(word) -> Option<Snippet>`). Tab already arrives as
`Key::Char('\t')`, so no new key event.
- [ ] **Hint-on-pause.** On the typing pause (same throttle as the word-count /
cursor refresh — never a per-keystroke repaint), if the word before the
caret is a prefix, the right side panel shows a quiet hint. The panel is
~17 cols, so the hint is the snippet **name / first line**, not the whole
body — the full preview is the `$` palette's job.
- [x] **`$` palette (browse + insert).** `Cmd-P` then `$` switches the palette to
the snippet list (the same sigil mechanism as `>`). Fuzzy-matches name /
prefix / description; Enter inserts the body at the caret and starts the
tab-stop session. Lists **all** snippets — the fuzzy filter handles clutter,
so there's no hidden "inline-only vs palette-only" split. Rows read
`Name [prefix]`, so browsing also teaches the inline trigger.
- [ ] **Boot wiring.** The host reads `/sd/repo/.typoena.snippets.json` at boot
and calls `Editor::set_snippets` (mirroring `set_prefs`); a missing or
malformed file is non-fatal (no snippets, editor runs). Parse lives in the
host-testable `editor` crate via `serde_json` — the one new dependency.
## The palette, generalised (`Cmd-P` · `>` · `$`)
v0.5 shipped `Cmd-P` = files and `>` = a five-entry settings list (`save_on_idle`,
`format_on_save`, `line_numbers` toggles + `theme`/`auto_sync` rotations). v0.6
makes the sigils a clean split by verb, and the empty-palette placeholder legends:
`Go to file · > settings · $ snippets`.
- **bare `Cmd-P`** → *navigate*: go to file (unchanged).
- **`>`** → *act on the editor* — the command palette. The pref toggles are just
its stateful entries, not a special section. Dispatch differs per entry: a
**toggle** flips and the list **stays open** (as today); a **one-shot** (e.g.
`format`, `publish`) runs and **closes**; a **parameterised** command morphs
the palette into a second **input step** (select `New file` → the box becomes a
filename prompt → Enter creates it, scope read from a `repo/`/`local/` prefix as
`:enew` does today). This retires `:e` (bare `Cmd-P` covers file-opening;
dotfiles get a dedicated `> edit …` command if/when wanted).
- **`$`** → *insert content* — the snippet launcher above.
`format` / `publish` as `>` entries are a small opportunistic add (they prove `>`
is a real action registry, not a settings box); the headline is snippets.
## First-time setup — snippet catalog (`just init`)
[`just init`](../firmware/README.md#provisioning-an-sd-card) gains a step that
seeds the two git-tracked config files into `repo/` (so they commit + sync on the
device's first `:sync`): a starter [`.typoena.toml`](typoena-toml.md) (the four
keys at their defaults, or confirmed at a prompt) and a
[`.typoena.snippets.json`](typoena-snippets.md) chosen from a **curated catalog**
checked into the repo. The catalog is grouped and opt-in — you pick the groups
you want rather than getting all of one writer's personal templates. Not every
Zed snippet is worth proposing: the Slidev/blog-pipeline ones (`@[youtube]`,
`<v-clicks>`, frontmatter, mermaid) and render-dependent or hyper-specific ones
are left out of the menu, since a distraction-free prose appliance can't render
them and you'd never miss them. **Proposed groups (pending sign-off):**
- [ ] **Symbols** (inline, keyboard can't type them): `fleche``→`,
`different``≠`, `fois``×`, `median``·`, `degre``°`, `euro``€`,
`edanso``œ` (dead keys in v0.2.5 cover accents, *not* these).
- [ ] **Structure**: `todo``- [ ] `, `link``[$1]($2)$0`, `img``![$1]($2)$0`
(net-new — obvious for Markdown), `table`, `code` (fenced block).
- [ ] **Prose / PKM templates** (`${n:label}` stripped to `$n`): `fiche`
(book notes), `reference`/`refangl` (reference block), `biais`,
`capture`, `standard`, `5w1h`.