Correct a claim about the operator's data, and the first-row delete
CI & Build / Build now, or wait for Android? (push) Successful in 3s
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 8s
CI & Build / Python tests (push) Successful in 12s
CI & Build / integration (push) Successful in 17s
CI & Build / Build & push image (push) Skipped
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 3m2s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 5m39s
Desktop (Tauri) / Update manifest (push) Successful in 4s
Android / Kotlin + Rust (APK) (push) Successful in 8m12s

Two things, one of which I got wrong in a place that outlives the session.

**The claim.** Migration 0027's docstring said "The Google Keep import is genuine
content on this instance, not fixtures." That is not true and I had no basis for it.
Note 2916's headline is the opposite — "there is no work that anyone has done that
isn't test data" — and its clause about imports is CONDITIONAL: text arriving from
another app would be real, and any import path has to treat it that way. I read a
rule about how import code must behave as a fact about what is in the database, then
repeated it in a migration that will be read long after anyone remembers this week.

The operator has never run the importer. They did not know it existed.

Nothing about the migration changes. Content-preserving was cheap and is right for
anything that rewrites somebody's text — and it is what the rule will demand the day
an import does happen. Only the reason recorded in the file was wrong, and a wrong
reason in a migration is how a later decision gets made on a false premise.

**The delete.** Removing the FIRST checklist row asked to focus `index - 1`, which is
-1, so nothing took focus and the keyboard stayed up over a list with no cursor in
it. It now focuses whichever row takes the deleted one's place, which also does the
right thing when the deleted row was the only one — `withoutIndex` leaves a fresh
empty block behind, and that block is what gets the caret.

Found by reading the path the operator said they were about to test, rather than by
waiting for them to find it.
This commit is contained in:
2026-08-26 07:44:31 -04:00
parent a45a44ef11
commit 44b3bcb2b2
2 changed files with 17 additions and 5 deletions
@@ -83,8 +83,12 @@ fun BlockBody(
onFocus(if (blocks[index].value.text.isBlank()) block.id else next)
},
onDelete = {
onChange(blocks.withoutIndex(index))
onFocus(blocks.getOrNull(index - 1)?.id)
val remaining = blocks.withoutIndex(index)
onChange(remaining)
// The row above — or, for the FIRST row, whichever one takes
// its place. `index - 1` alone is -1 there, which left the
// keyboard up with nothing focused.
onFocus(remaining.getOrNull((index - 1).coerceAtLeast(0))?.id)
},
)
} else {