SIGN IN SIGN UP

fix(canvas): ask before a delete takes a note's text

Delete and Backspace removed the canvas selection outright, and the board has no
undo. A note is single-click to SELECT and double-click to EDIT, so the click
that reads as "put the cursor in my note" leaves the board holding the key — and
in a webview Backspace has decades of "go back" behind it. Select-all then Delete
cleared every note on the board in one gesture. The dock's trash button was a
second door to the same loss, taking whatever single node was selected without
the selection gesture running at all.

A delete that would destroy prose the user typed now stops to ask. The guard sits
on the single-node delete and on the selection delete, which is every path in:
the dock's note button, its region button and the card's unpin come through the
first, the keyboard chord and the multi-select button through the second. The ask
is scoped to notes with text in them on purpose — confirming every delete would
train the reflex it is meant to interrupt, and there is nothing to protect
otherwise, since removing a card unpins it and removing a region unframes it
while the conversation itself is untouched. The dialog captures the delete it
asked about rather than re-reading the live selection on confirm, so the prompt
and the deletion cannot describe different sets of nodes.

A note commits its text on the way out of the editor and the cached row only
learns it when the backend answers, so for one round trip that row still reads
EMPTY: text typed into a fresh note and deleted right after would have looked
like a blank sheet. Notes whose text is in flight are tracked in the store, by
COUNT — a note saved twice in quick succession must stay marked until the LAST
write lands — and as module state rather than component state, since the save
fired from a note's unmount is exactly the one still in the air when the board
goes away and a fresh ref on remount would not know about it. Each handle carries
the epoch it was minted in, so a write stranded across a backend switch cannot
clear a mark on a node id the new scope has reused; that is the guard `fetchEpoch`
already applies to stale snapshot fetches. The marking is deliberately
conservative: a patch that CLEARS a note is in flight too, and one needless dialog
costs less than the paragraph the opposite error loses.

The keyboard guard also learns `alertdialog`, which is its own ARIA role rather
than a kind of `dialog`, so a Delete pressed on the confirmation's own buttons
cannot re-enter the gesture that opened it.

The dialog's title, body and two buttons land across all ten locales.
X
xintaofei committed
b9fb8dc5b0069f0777035d7d1a4f3e80f17c9f7a
Parent: e6e56e3