feat: a lock that actually holds, a page that can move house, and a theme on the tablet #19

Merged
brian merged 1 commit from test into main 2026-08-20 18:58:31 +00:00
Owner

Four things, sharing enough of the same files that separating them would mean
commits that don't build on their own.

The page lock only ever stopped the pen. Everything else that changes a page was
left open: its name, its paper, word mode, the typed doc layer, and deleting it
outright. It now means read-only on both clients. Delete is the exception that
keeps a live button and refuses when pressed — a disabled control explains
nothing, and being told why is the whole point of having locked the page. The
refusal is one string per client so the wording can't drift apart; the same page
can be locked on either, and two different explanations of one lock is how
someone concludes a client is broken.

Two controls stay live on a locked page. The lock itself, because it is the way
out — as is the pin, which is its opposite and clears it. And copy/move, because
neither alters the page: a locked page that couldn't be filed anywhere would be
a trap rather than a guard.

Deleting a page now asks for "yes" to be typed. A page has no name worth typing
— most are called nothing and the rest are called the date they were made — so
the notebook dialog's name-the-thing rule doesn't transfer. On Android the two
dialogs are now one implementation with the phrase passed in, which is what
keeps the existing notebook tests meaningful.

Pages can be copied or moved between a notebook and a journal. No server change:
the page upsert already carries notebook_id and day, and both clients already
push them, so a transfer is an ordinary dirty page naming a different parent.
Copy is a real duplicate — its own id, its own content — not a reference; the
web has to deep-clone for that, Android gets it free from an immutable string.
Move re-files the same row rather than copying and tombstoning, so anything
already pointing at the page still finds it and no delete races a create.

Leaving a journal for a notebook, the day goes into the title, because a
notebook has no calendar to keep it in and "Tuesday's entry" filed alone says
nothing about which Tuesday. Written ISO, not in the reader's locale: the title
syncs, so a locale spelling would differ per device and the last client to touch
the page would rewrite the other's. Going the other way there is no date to
carry, so the dialog asks for one and starts on the day being looked at — a page
filed after the fact usually belongs to the day it was written.

The tablet had no way to choose a theme at all; the web has had one in settings
for as long as there have been settings. Same three, plus following the system,
which the web has no equivalent of and Android users have usually already
answered. Per device like the web's, not on the account: a tablet used at night
and a desktop used at noon want different answers, and syncing it would make one
of them wrong every time the other changed. Warm paper is the web's values
exactly. The paper reads the choice rather than the system, or the sheet and the
chrome part company the moment the two disagree.

Tests: page-transfer.test.mjs runs a real copy and a real move against fakes —
a copy sharing content with its original, and a move that never reaches the
server, both look right on screen until something reloads. PageTransferTest does
the same against a real database, and PageHeaderTest pins which controls the
lock covers, since a lock that quietly stops guarding something is
indistinguishable from one that works until the day it matters.

Co-Authored-By: Claude Opus 5 noreply@anthropic.com

Four things, sharing enough of the same files that separating them would mean commits that don't build on their own. The page lock only ever stopped the pen. Everything else that changes a page was left open: its name, its paper, word mode, the typed doc layer, and deleting it outright. It now means read-only on both clients. Delete is the exception that keeps a live button and refuses when pressed — a disabled control explains nothing, and being told why is the whole point of having locked the page. The refusal is one string per client so the wording can't drift apart; the same page can be locked on either, and two different explanations of one lock is how someone concludes a client is broken. Two controls stay live on a locked page. The lock itself, because it is the way out — as is the pin, which is its opposite and clears it. And copy/move, because neither alters the page: a locked page that couldn't be filed anywhere would be a trap rather than a guard. Deleting a page now asks for "yes" to be typed. A page has no name worth typing — most are called nothing and the rest are called the date they were made — so the notebook dialog's name-the-thing rule doesn't transfer. On Android the two dialogs are now one implementation with the phrase passed in, which is what keeps the existing notebook tests meaningful. Pages can be copied or moved between a notebook and a journal. No server change: the page upsert already carries notebook_id and day, and both clients already push them, so a transfer is an ordinary dirty page naming a different parent. Copy is a real duplicate — its own id, its own content — not a reference; the web has to deep-clone for that, Android gets it free from an immutable string. Move re-files the same row rather than copying and tombstoning, so anything already pointing at the page still finds it and no delete races a create. Leaving a journal for a notebook, the day goes into the title, because a notebook has no calendar to keep it in and "Tuesday's entry" filed alone says nothing about which Tuesday. Written ISO, not in the reader's locale: the title syncs, so a locale spelling would differ per device and the last client to touch the page would rewrite the other's. Going the other way there is no date to carry, so the dialog asks for one and starts on the day being looked at — a page filed after the fact usually belongs to the day it was written. The tablet had no way to choose a theme at all; the web has had one in settings for as long as there have been settings. Same three, plus following the system, which the web has no equivalent of and Android users have usually already answered. Per device like the web's, not on the account: a tablet used at night and a desktop used at noon want different answers, and syncing it would make one of them wrong every time the other changed. Warm paper is the web's values exactly. The paper reads the choice rather than the system, or the sheet and the chrome part company the moment the two disagree. Tests: page-transfer.test.mjs runs a real copy and a real move against fakes — a copy sharing content with its original, and a move that never reaches the server, both look right on screen until something reloads. PageTransferTest does the same against a real database, and PageHeaderTest pins which controls the lock covers, since a lock that quietly stops guarding something is indistinguishable from one that works until the day it matters. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
feat: a lock that actually holds, a page that can move house, and a theme on the tablet
All checks were successful
Build and Deploy / test (push) Successful in 27s
Build and Deploy / build (push) Successful in 1m3s
Build and Deploy / deploy-main (push) Has been skipped
Build and Deploy / deploy (push) Successful in 26s
1fade28dac
Four things, sharing enough of the same files that separating them would mean
commits that don't build on their own.

The page lock only ever stopped the pen. Everything else that changes a page was
left open: its name, its paper, word mode, the typed doc layer, and deleting it
outright. It now means read-only on both clients. Delete is the exception that
keeps a live button and refuses when pressed — a disabled control explains
nothing, and being told why is the whole point of having locked the page. The
refusal is one string per client so the wording can't drift apart; the same page
can be locked on either, and two different explanations of one lock is how
someone concludes a client is broken.

Two controls stay live on a locked page. The lock itself, because it is the way
out — as is the pin, which is its opposite and clears it. And copy/move, because
neither alters the page: a locked page that couldn't be filed anywhere would be
a trap rather than a guard.

Deleting a page now asks for "yes" to be typed. A page has no name worth typing
— most are called nothing and the rest are called the date they were made — so
the notebook dialog's name-the-thing rule doesn't transfer. On Android the two
dialogs are now one implementation with the phrase passed in, which is what
keeps the existing notebook tests meaningful.

Pages can be copied or moved between a notebook and a journal. No server change:
the page upsert already carries notebook_id and day, and both clients already
push them, so a transfer is an ordinary dirty page naming a different parent.
Copy is a real duplicate — its own id, its own content — not a reference; the
web has to deep-clone for that, Android gets it free from an immutable string.
Move re-files the same row rather than copying and tombstoning, so anything
already pointing at the page still finds it and no delete races a create.

Leaving a journal for a notebook, the day goes into the title, because a
notebook has no calendar to keep it in and "Tuesday's entry" filed alone says
nothing about which Tuesday. Written ISO, not in the reader's locale: the title
syncs, so a locale spelling would differ per device and the last client to touch
the page would rewrite the other's. Going the other way there is no date to
carry, so the dialog asks for one and starts on the day being looked at — a page
filed after the fact usually belongs to the day it was written.

The tablet had no way to choose a theme at all; the web has had one in settings
for as long as there have been settings. Same three, plus following the system,
which the web has no equivalent of and Android users have usually already
answered. Per device like the web's, not on the account: a tablet used at night
and a desktop used at noon want different answers, and syncing it would make one
of them wrong every time the other changed. Warm paper is the web's values
exactly. The paper reads the choice rather than the system, or the sheet and the
chrome part company the moment the two disagree.

Tests: page-transfer.test.mjs runs a real copy and a real move against fakes —
a copy sharing content with its original, and a move that never reaches the
server, both look right on screen until something reloads. PageTransferTest does
the same against a real database, and PageHeaderTest pins which controls the
lock covers, since a lock that quietly stops guarding something is
indistinguishable from one that works until the day it matters.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
brian merged commit d730a7a64d into main 2026-08-20 18:58:31 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
brian/Journal!19
No description provided.