Notes: plain text notes on the SD card, with an editor that saves by itself (#19)

The Notes App lists the files of /notes by their first line, newest first:
n starts a note, Enter opens it, r renames its file, d deletes it after
asking, s sorts by name. A new note's file is named after its first line.

The editor wraps at spaces, 38 columns by 8 rows; Fn+arrows move through
the wrapped text, Ctrl+A and Ctrl+E go to the ends of the line. There is no
save key: the note is written five seconds after the last key, on Back, on
leaving the App, when the screen turns off and before the device powers
off. A save writes a temporary file and puts it in the note's place; a save
cut short is put back, or offered, the next time.

A note is up to 16 KB, held in one buffer reserved when it's opened: the
file is read straight into it and typing never makes it grow. A failed
allocation aborts on this device, and with IRC connected the largest free
block is about 31 KB: a first version that copied the note once on loading
restarted the device when a full note was opened with IRC connected.
Editing files of any size is #47.

The Storage App's text viewer gets `e`, which edits a text file up to 16 KB
with the same editor unless the file is read-only.

439 host tests. Checked on the device: docs/milestones/F1.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
This commit is contained in:
2026-10-06 10:08:10 +02:00
co-authored by Claude Opus 5.5
parent c868977f1c
commit e8a654a15f
17 changed files with 1599 additions and 9 deletions
+37 -1
View File
@@ -1,6 +1,6 @@
# F1 — Files and Notes
**Status:** in progress. The Storage App (issue #3) shipped as **v0.9.0** on 2026-10-06. Notes (#19) is being built on branch `notes`. The card as a USB drive (#1) comes after.
**Status:** in progress. The Storage App (issue #3) shipped as **v0.9.0** on 2026-10-06. Notes (#19) is built and checked on the device, on branch `notes`, not merged yet. The card as a USB drive (#1) comes after.
**Goal:** get at what's on the SD card from the device itself: browse it, look inside the files the firmware writes, copy, move, rename and delete, and keep notes. A side milestone, like G1 and S1; Files and Notes were M3's original second half (Q30, Q89).
@@ -123,3 +123,39 @@ Plain text notes on the SD card, written on the device. Q30 settled the base: `.
3. **The Notes App:** the list with titles, new, rename, delete.
4. **`e` in the Storage App.**
5. **Checks on the device**, recorded here.
### As built
- **`NoteText`** (`lib/notes`, host-tested) is the text, its cursor and the screen around it. A line owns the space or the newline it ends with, so every byte is on exactly one line and the cursor has one place for each. **No index of lines is kept:** a note of newlines alone would need twice its own size for one. Where a line starts is worked out from the start of its paragraph.
- **One buffer, 16 KB, for as long as the editor is open.** It's reserved when the note is opened, the file is read straight into it, and typing never makes it grow. On this device a failed allocation is an abort, and with IRC connected the largest free block is about 31 KB whatever the total says: the first version read the file into one string and copied it into another, and opening a full note with IRC connected restarted the device. The editor now also refuses to open without a free block of 24 KB.
- **`NoteEditor`** (`src/apps/note_editor`) is shared by the Notes App and the Storage App's `e`. A save runs on the storage task while the main loop waits for it: no second copy of the note, and at 16 KB the wait is a fraction of a second at a moment when nobody has typed for five.
- **A save** writes `<note>.tmp`, checks its size, deletes the note and renames the temporary file (FAT can't rename onto a file). A cut between the last two steps leaves only the `.tmp`: the Notes list puts such a file back under its name. A `.tmp` next to its note is an unfinished save: opening the note offers it.
- **Titles** in the list are read from the card for the eight rows on screen, when the list moves.
- **Before powering off**, the firmware now leaves the foreground App (`PowerService::beforePowerOff`), which makes the editor save.
- Shift or Alt with Fn+Up and Fn+Down moves a page (not in Q145).
### Found on the way
- **The screen could go "off" for one tick after a key sent through the Debug Console**, and the next key was then swallowed as a wake-up: the `key` command stamps the power timer from `millis()`, the power tick compares with its pass's older time, and the unsigned difference read as 49 days idle. The same shape as #46. Fixed in `PowerPolicy::update` with a test. Keys from the keyboard were never affected. It explains remote keys "lost" in earlier sessions.
- **`scripts/rdbg.py` held back piped lines** written while it was still connecting, until the next line came (a buffered `readline()` behind `select()`). Fixed.
### Checks on the device (2026-10-06, Debug Build of branch `notes`)
Test notes were made in `/notes` and removed afterwards; the folder is left, empty.
| Check | Result |
|---|---|
| Host tests | 439 pass |
| A first note | "No notes yet", `n`, typed three lines: the top line says "typing", then "saved" five seconds after the last key, under `shopping-list.txt`. 63 keys in a row all arrived |
| Leaving | Back saves and returns to the list, which shows the note under its first line. Home in the middle of a new note saved it as `ideas.txt` |
| The cursor | Down, Right, an insertion in the middle of a line; the screen scrolls through a note of about 230 lines |
| A power cut | Typed, waited seven seconds, typed more and restarted the device at once (`reset`): the note has what was saved, whole, and not the last keys |
| An unfinished save | A `.tmp` next to its note: "Unsaved copy... Keep the note / Use the copy"; using it brings its text back and saves it. A `.tmp` alone was put back under its name when the list opened |
| 16 KB | A note of exactly 16,384 bytes opens and scrolls; one more character: "This note is full: 16 KB". A file of 16,398 bytes: "Too big to edit: 16 KB at most" |
| Rename, delete, sort | `r` renamed `orphan.txt` to `orphan2.txt`; `d` asked, then deleted; `s` switched between newest first and by file name |
| `e` in the Storage App | A note opened from the text viewer, edited, saved on Back; the listing shows its new size |
| Memory | IRC connected, the full 16 KB note open: 55 KB free, largest block 31.7 KB (72 KB free before opening) |
**Not checked:** accents through the Compose Key and Ctrl+A / Ctrl+E (the remote `key` command can't send them; the model's tests cover both), the power button's save (it needs a hand on the device), a missing card, and how typing feels on the keyboard itself.
**One slip during the checks:** a key sequence sent right after a restart opened IRC instead of Notes, and the test letters went into IRC's input line. Nothing was sent: the line was cleared and the App left. IRC connected to Libera as it does when opened.