Compare commits
3 Commits
5a076e3226
...
767742ba12
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
767742ba12 | ||
|
|
85cbeceea9 | ||
|
|
8527f75bf8 |
@@ -127,9 +127,11 @@ rather than refusing to start over a stray comma.
|
||||
the device on the next clone/sync. This is deliberately where the heavy editing
|
||||
happens; the appliance is for writing, not for maintaining a JSON library.
|
||||
- **First-time setup.** [`just init`](../firmware/README.md#provisioning-an-sd-card)
|
||||
seeds this file from a curated catalog — you pick which snippet groups you
|
||||
want and it writes the selected subset into `repo/.typoena.snippets.json`
|
||||
(committed on the device's first `:sync`). See
|
||||
seeds this file from a curated catalog (`firmware/snippets-catalog/`) — you pick
|
||||
which snippet groups you want and it `jq`-merges the selected subset into
|
||||
`repo/.typoena.snippets.json` (committed on the device's first `:sync`). It
|
||||
writes **only if the file is absent**, so re-running `init` on a card whose
|
||||
clone already carries your library never overwrites it. See
|
||||
[`v0.6-markdown.md`](v0.6-markdown.md) for the catalog.
|
||||
- **On-device hand-edit — deferred.** The palette hides dotfiles, and `:e` was
|
||||
dropped in v0.6, so there is no in-editor path to this file yet. When one is
|
||||
|
||||
@@ -53,10 +53,12 @@ behaviour.
|
||||
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
|
||||
- [x] **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.
|
||||
host-testable `editor` crate via `serde_json` — the one new dependency,
|
||||
confirmed to build for xtensa (`cargo check`, firmware 0.6.0). On-device
|
||||
smoke-test still pending.
|
||||
|
||||
## The palette, generalised (`Cmd-P` · `>` · `$`) — done in core
|
||||
|
||||
@@ -81,25 +83,30 @@ them: `Go to file · > settings · $ snippets`.
|
||||
Labels avoid `…`/`↹` — the palette/panel fonts are ISO-8859-15, which has neither
|
||||
glyph — so `new file...` uses ASCII dots and the pause hint leads with `»`.
|
||||
|
||||
## First-time setup — snippet catalog (`just init`)
|
||||
## First-time setup — snippet catalog (`just init`) — done
|
||||
|
||||
[`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):**
|
||||
[`just init`](../firmware/README.md#provisioning-an-sd-card) seeds the two
|
||||
git-tracked config files into `repo/` (so they commit + sync on the device's
|
||||
first `:sync`), **writing only a file that is absent** so a re-`init` never
|
||||
clobbers a synced library: a starter [`.typoena.toml`](typoena-toml.md) (the five
|
||||
keys at their defaults) and a [`.typoena.snippets.json`](typoena-snippets.md)
|
||||
assembled from a **curated catalog** (`firmware/snippets-catalog/`, `jq`-merged).
|
||||
The catalog is grouped and opt-in — you pick the groups (each `[Y/n]`, all-yes on
|
||||
a non-interactive run) rather than getting one writer's whole personal set. Not
|
||||
every Zed snippet is worth proposing: the Slidev/blog-pipeline ones (`@[youtube]`,
|
||||
`<v-clicks>`, frontmatter, mermaid) and hyper-specific personal ones are left out,
|
||||
since a distraction-free prose appliance can't render them and you'd never miss
|
||||
them. Prefixes are **English** (except `edanso`, the mnemonic for `œ`), bodies
|
||||
translated from the source Zed snippets. **Three groups, 17 snippets:**
|
||||
|
||||
- [ ] **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`→`$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`.
|
||||
- [x] **Symbols** (inline, the keyboard can't type them): `arrow`→`→`,
|
||||
`neq`→`≠`, `times`→`×`, `middot`→`·`, `deg`→`°`, `euro`→`€`, `edanso`→`œ`
|
||||
(dead keys in v0.2.5 cover accents, *not* these). **Caveat:** `→` (U+2192)
|
||||
and `≠` (U+2260) are outside ISO-8859-15, so they store correctly and sync
|
||||
but render as a missing-glyph box on the device panel until the font is
|
||||
extended; the other five are Latin-9 and draw fine. Kept deliberately.
|
||||
- [x] **Structure**: `todo`→`- [ ] `, `link`→`[$1]($2)$0`, `img`→`$0`,
|
||||
`table` (2-col), `code` (fenced block). `img`/`code` are net-new.
|
||||
- [x] **Prose / PKM templates** (`${n:label}` stripped to `$n`): `booknotes`,
|
||||
`reference` (the English reference block — `refangl` folded in), `bias`,
|
||||
`capture`, `standard`. (`5w1h` dropped.)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "firmware"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
authors = ["Julien Calixte <juliencalixte@gmail.com>"]
|
||||
edition = "2024"
|
||||
resolver = "2"
|
||||
|
||||
@@ -161,13 +161,15 @@ ports:
|
||||
# The `_`-prefixed recipes are shared internals (hidden from `just --list`).
|
||||
sd_repo_dir := "repo"
|
||||
|
||||
# Full prep of a fresh card: copy the notes repo + write config, then eject.
|
||||
# Run this once per card. <repo-src> is a clone made on this computer.
|
||||
# Full prep of a fresh card: copy the notes repo, seed the git-tracked config
|
||||
# files (first-time only), write the runtime config, then eject. Run this once
|
||||
# per card. <repo-src> is a clone made on this computer.
|
||||
init repo_src sd_volume="":
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
vol="$(just _card "{{sd_volume}}" | tail -n1)"
|
||||
just _load-repo "{{repo_src}}" "$vol"
|
||||
just _seed-configs "$vol"
|
||||
just _write-conf "$vol" "{{repo_src}}"
|
||||
just _eject "$vol"
|
||||
|
||||
@@ -280,6 +282,67 @@ _load-repo repo_src vol:
|
||||
# -P for progress on the ~700 MB copy. Scoped to repo/, never the card root.
|
||||
rsync -rtP --delete --modify-window=1 --exclude-from="$ignore_list" "$src/" "$dest/"
|
||||
|
||||
# First-time setup: seed the two git-tracked config files into the card's repo/
|
||||
# so they commit + sync on the device's first `:sync`. Writes ONLY a file that is
|
||||
# absent — a card whose clone already carries `.typoena.toml` /
|
||||
# `.typoena.snippets.json` keeps its own, so a re-`init` never clobbers a synced
|
||||
# library. The starter `.typoena.toml` mirrors the editor defaults; the snippet
|
||||
# library is assembled from the curated catalog (snippets-catalog/, opt-in per
|
||||
# group) with `jq`. Non-interactive runs take every default (all groups).
|
||||
_seed-configs vol:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
repo="{{vol}}/{{sd_repo_dir}}"
|
||||
catalog="{{justfile_directory()}}/snippets-catalog"
|
||||
interactive=1; [ -t 0 ] || interactive=0
|
||||
|
||||
# ask_yn "label" → 0 (yes) / 1 (no); default yes, non-interactive → yes.
|
||||
ask_yn() {
|
||||
local ans
|
||||
if [ "$interactive" = 0 ]; then return 0; fi
|
||||
read -r -p " $1 [Y/n]: " ans || ans=""
|
||||
[[ "${ans:-Y}" =~ ^[Yy] ]]
|
||||
}
|
||||
|
||||
# ── starter prefs (.typoena.toml) ────────────────────────────────────────
|
||||
prefs="$repo/.typoena.toml"
|
||||
if [ -e "$prefs" ]; then
|
||||
echo "keeping existing .typoena.toml"
|
||||
elif ask_yn "seed a starter .typoena.toml (editor defaults)?"; then
|
||||
# printf per line (matching _write-conf), so no heredoc indentation trap.
|
||||
{
|
||||
printf '# Typoena editor preferences — hand-editable, git-tracked.\n'
|
||||
printf '# Edit here, or change live from the Cmd-P palette (type `>`).\n'
|
||||
printf 'save_on_idle = true\n'
|
||||
printf 'format_on_save = true\n'
|
||||
printf 'line_numbers = true\n'
|
||||
printf 'theme = "light"\n'
|
||||
printf 'auto_sync = "10m"\n'
|
||||
} > "$prefs"
|
||||
echo "wrote .typoena.toml"
|
||||
fi
|
||||
|
||||
# ── snippet library (.typoena.snippets.json) from the catalog ────────────
|
||||
snips="$repo/.typoena.snippets.json"
|
||||
if [ -e "$snips" ]; then
|
||||
echo "keeping existing .typoena.snippets.json"
|
||||
elif ! command -v jq >/dev/null 2>&1; then
|
||||
echo "warning: jq not found — skipping the snippet catalog (install jq, or" >&2
|
||||
echo " hand-write $snips later)" >&2
|
||||
else
|
||||
echo "snippet catalog — choose groups to include:" >&2
|
||||
picked=()
|
||||
ask_yn "Symbols (→ ≠ × · ° € œ)" && picked+=("$catalog/symbols.json")
|
||||
ask_yn "Structure (todo, link, img, table, code)" && picked+=("$catalog/structure.json")
|
||||
ask_yn "Prose (booknotes, reference, bias, capture, standard)" && picked+=("$catalog/prose.json")
|
||||
if [ "${#picked[@]}" -gt 0 ]; then
|
||||
jq -s 'add' "${picked[@]}" > "$snips"
|
||||
echo "wrote .typoena.snippets.json ($(jq 'length' "$snips") snippets, ${#picked[@]} group(s))"
|
||||
else
|
||||
echo "no groups chosen — skipping .typoena.snippets.json"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Resolve + write <vol>/typoena.conf (Wi-Fi + PAT + git identity). Each value
|
||||
# runs a ladder: firmware/.env (loaded via `set dotenv-load`) → derived from
|
||||
# tools already on the machine (git config, gh, the active Wi-Fi network + its
|
||||
|
||||
58
firmware/snippets-catalog/prose.json
Normal file
58
firmware/snippets-catalog/prose.json
Normal file
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"Book notes": {
|
||||
"prefix": "booknotes",
|
||||
"body": ["# $1", "", "## $2 - $3", "", "## What the book is about", ""],
|
||||
"description": "Reading notes (title / author - year)"
|
||||
},
|
||||
"Reference block": {
|
||||
"prefix": "reference",
|
||||
"body": ["___", "", "## References", ""],
|
||||
"description": "Reference block"
|
||||
},
|
||||
"Cognitive bias": {
|
||||
"prefix": "bias",
|
||||
"body": [
|
||||
"# $1",
|
||||
"",
|
||||
"## Misconception",
|
||||
"",
|
||||
"$3",
|
||||
"",
|
||||
"## The reality",
|
||||
"",
|
||||
"$4",
|
||||
"",
|
||||
"___",
|
||||
"",
|
||||
"## References",
|
||||
"",
|
||||
"- You are not so smart - p. $2",
|
||||
""
|
||||
],
|
||||
"description": "Cognitive-bias note"
|
||||
},
|
||||
"Capture": {
|
||||
"prefix": "capture",
|
||||
"body": ["# $1", "", "## Captured inspiration", "", "$2", ""],
|
||||
"description": "Inspiration capture"
|
||||
},
|
||||
"Standard": {
|
||||
"prefix": "standard",
|
||||
"body": [
|
||||
"# $1",
|
||||
"",
|
||||
"## Key points",
|
||||
"",
|
||||
"## Mistakes to avoid",
|
||||
"",
|
||||
"___",
|
||||
"",
|
||||
"## Examples",
|
||||
"",
|
||||
"___",
|
||||
"",
|
||||
"## References"
|
||||
],
|
||||
"description": "Standard / playbook"
|
||||
}
|
||||
}
|
||||
27
firmware/snippets-catalog/structure.json
Normal file
27
firmware/snippets-catalog/structure.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"Todo item": {
|
||||
"prefix": "todo",
|
||||
"body": "- [ ] ",
|
||||
"description": "Task-list item"
|
||||
},
|
||||
"Link": {
|
||||
"prefix": "link",
|
||||
"body": "[$1]($2)$0",
|
||||
"description": "Inline link"
|
||||
},
|
||||
"Image": {
|
||||
"prefix": "img",
|
||||
"body": "$0",
|
||||
"description": "Image"
|
||||
},
|
||||
"Table": {
|
||||
"prefix": "table",
|
||||
"body": ["| $1 | $2 |", "|-|-|", "| $3 | $4 |"],
|
||||
"description": "Two-column table"
|
||||
},
|
||||
"Code block": {
|
||||
"prefix": "code",
|
||||
"body": ["```$1", "$2", "```"],
|
||||
"description": "Fenced code block"
|
||||
}
|
||||
}
|
||||
37
firmware/snippets-catalog/symbols.json
Normal file
37
firmware/snippets-catalog/symbols.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"Arrow": {
|
||||
"prefix": "arrow",
|
||||
"body": "→",
|
||||
"description": "Rightwards arrow (→)"
|
||||
},
|
||||
"Not equal": {
|
||||
"prefix": "neq",
|
||||
"body": "≠",
|
||||
"description": "Not-equal sign (≠)"
|
||||
},
|
||||
"Times": {
|
||||
"prefix": "times",
|
||||
"body": "×",
|
||||
"description": "Multiplication sign (×)"
|
||||
},
|
||||
"Middle dot": {
|
||||
"prefix": "middot",
|
||||
"body": "·",
|
||||
"description": "Middle dot (·), inclusive writing"
|
||||
},
|
||||
"Degree": {
|
||||
"prefix": "deg",
|
||||
"body": "°",
|
||||
"description": "Degree sign (°)"
|
||||
},
|
||||
"Euro": {
|
||||
"prefix": "euro",
|
||||
"body": "€",
|
||||
"description": "Euro sign (€)"
|
||||
},
|
||||
"OE ligature": {
|
||||
"prefix": "edanso",
|
||||
"body": "œ",
|
||||
"description": "Ligature œ (e dans l'o)"
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,10 @@ use esp_idf_svc::hal::spi::{Dma, SpiBusDriver, SpiDriver};
|
||||
use esp_idf_svc::hal::units::FromValueType;
|
||||
|
||||
use display::Frame;
|
||||
use editor::{Editor, Effect, Mode, Prefs, Scope, CH, LOCAL_DIR, PREFS_PATH, REPO_DIR};
|
||||
use editor::{
|
||||
Editor, Effect, Mode, Prefs, Scope, Snippets, CH, LOCAL_DIR, PREFS_PATH, REPO_DIR,
|
||||
SNIPPETS_PATH,
|
||||
};
|
||||
use firmware::epd::{self, Epd};
|
||||
use firmware::persistence::{Storage, NOTES};
|
||||
|
||||
@@ -130,6 +133,21 @@ fn main() -> anyhow::Result<()> {
|
||||
};
|
||||
log::info!("prefs: {prefs:?}");
|
||||
ed.set_prefs(prefs);
|
||||
// Snippet library (.typoena.snippets.json, git-tracked). Parsed with
|
||||
// serde_json in the editor crate; a missing / unreadable / malformed file is
|
||||
// non-fatal — the editor simply has no snippets and runs unchanged.
|
||||
let snippets = match storage.load_path(SNIPPETS_PATH) {
|
||||
Ok(src) => match Snippets::parse(&src) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
log::warn!("snippets parse FAILED ({e}); none loaded");
|
||||
Snippets::default()
|
||||
}
|
||||
},
|
||||
Err(_) => Snippets::default(),
|
||||
};
|
||||
log::info!("snippets: {} loaded", snippets.0.len());
|
||||
ed.set_snippets(snippets);
|
||||
let mut updates: u32 = 0;
|
||||
let mut cursor_shown = true; // the initial render includes the caret
|
||||
let mut last_activity = Instant::now();
|
||||
|
||||
Reference in New Issue
Block a user