diff options
| author | Danilo M. <danix@danix.xyz> | 2026-10-16 21:08:04 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-10-16 21:08:04 +0200 |
| commit | cf99dde4805f1792d6a56156b6a0338aa684005c (patch) | |
| tree | c3c04b8e0457b855d50d2e0ca41ffe417630ed3a /docs/superpowers | |
| parent | d423ec2646d444523fd44c92864988445cf072d2 (diff) | |
| download | quickshell-cf99dde4805f1792d6a56156b6a0338aa684005c.tar.gz quickshell-cf99dde4805f1792d6a56156b6a0338aa684005c.zip | |
notes: an empty note body gets a file, and the second click writes
Reported from use: a note created with only a title then saved left a db.json
entry with no file beside it, and reopening it said the file was missing.
Cause: FileView.setText("") on a path that view has not loaded creates no
file at all and reports no error. The index write (always non-empty JSON)
was fine, so only the body was lost, which is why it looked like a missing
file rather than a failed save. Bodies are now written by a Process running
cat > <file> with the text on stdin: verified against empty and non-empty
payloads, consecutive writes on one Process, and a store with no db.json at
all. The exit code is the real outcome, so a refused write reports it.
Second report in the same flow: the editor stays editable after the first
click, so typing more and then pressing Commit & push dropped the edit, the
push carrying only what the first click wrote. The second click now writes
the current title and body before it commits and pushes.
Diffstat (limited to 'docs/superpowers')
| -rw-r--r-- | docs/superpowers/specs/2026-10-16-notes-design.md | 29 |
1 files changed, 19 insertions, 10 deletions
diff --git a/docs/superpowers/specs/2026-10-16-notes-design.md b/docs/superpowers/specs/2026-10-16-notes-design.md index 459b526..b8524a2 100644 --- a/docs/superpowers/specs/2026-10-16-notes-design.md +++ b/docs/superpowers/specs/2026-10-16-notes-design.md @@ -91,10 +91,12 @@ Opened by a row (existing note) or **New note**. renders the body as Markdown; Edit shows the raw Markdown in a `TextEdit`. An existing note opens in Preview, a new one in Edit. - **Save** is two clicks, so the note is on disk before git is involved: the - first writes it and the button becomes **Commit & push?**; the second runs - the git half (see Git) and returns to the list. Leaving with the second click - unmade keeps the saved note and does not push it. With git off there is no - git half, so Save writes and returns to the list in one click. + first writes it and the button becomes **Commit & push?**; the second writes + whatever the editor holds then, commits and pushes (see Git), and returns to + the list. The editor stays editable between the clicks, so the second has to + write as well: pushing alone would drop an edit made after the first. Leaving + with the second click unmade keeps the saved note and does not push it. With + git off there is no second half, so Save writes and returns in one click. - **Cancel** returns to the list without saving. **Delete**, existing notes only, asks first: the first click arms the button, the second removes the note and pushes the removal. Nothing is destroyed until the second click, a @@ -109,9 +111,15 @@ Opened by a row (existing note) or **New note**. ## Writes All index writes go through the module's `db.json` `FileView` with -`atomicWrites: true`, so a write that dies mid-way cannot leave a half -`db.json` for `notes.sh` to trip on. A body is written with a `FileView` on -its own path, which creates the file. +`atomicWrites: true` and `blockWrites`, so a write that dies mid-way cannot +leave a half `db.json` for `notes.sh` to trip on, and its outcome is known when +the call returns. + +A body is written by a `Process` running `cat > <file>` with the text on stdin, +not by a `FileView`. `setText` with an **empty** string, on a path that view has +not loaded, creates no file at all and reports no error, which put a note with +an empty body into `db.json` with no file beside it. `cat` takes any content at +any length, including none, and its exit code is the outcome. **A write is refused when `db.json` cannot be read as the index.** That is not hypothetical: `git pull --rebase --autostash` resolves a remote `db.json` @@ -122,9 +130,10 @@ and writing it back would delete every title, so the write instead reports git to resolve. `Notes.valid` is the check; `Notes.parse` stays tolerant for display, returning an empty store for the same input. -A write that fails is reported, not assumed to have landed: both writes block -(`blockWrites`), because `setText` is asynchronous otherwise, and a failure -that nobody observes reports a save that never happened. +A write that fails is reported, not assumed to have landed: the index write +blocks (`blockWrites`) and the body write's `cat` exit code is checked, because +`setText` is asynchronous otherwise and a failure nobody observes reports a +save that never happened. - **Create**: the first click pulls first (see Git), then re-reads `db.json` so the append is based on what the pull brought in, not on what was read |
