- Left-click + drag on the header → moves the card (the title is now a span, so the drag handler always wins).
- Right-click on the header → swaps to an input, auto-focused with text selected.
- Enter or Escape or click outside → exits edit mode.
- Empty title shows the dimmed placeholder (Note / Terminal) and overflowing titles get an ellipsis.
Browser's native context menu is suppressed on the header via e.preventDefault(), but only there — right-clicking
elsewhere is unaffected.
in a single board.json under app_data_dir() (~/.local/share/{appName}/board.json on Linux).
Write pattern: tmp file + atomic rename, debounced 400ms. A crash mid-write can't corrupt the existing file; worst
case you lose ~400ms of recent edits.
Read pattern: load on app start, gate the Canvas render on the result. Missing file → default welcome board. Parse
error → log and fall back to default (won't overwrite the bad file until the user makes changes; actually it will save
over it after the next change — if you want to preserve a corrupt file for recovery that's a one-line tweak).
Terminal caveat: live PTY processes die with the app session, so sanitize() in App.tsx clears every terminal card's
ptyId on load. Each terminal card respawns a fresh shell on mount via the existing pty.spawn path. The card's
position, size, and title are preserved.