Skip to content

Autosave the board and restore it at the next start - #123

Merged
marcosqlbi merged 2 commits into
mainfrom
feature/autosave-and-session-restore
Sep 11, 2026
Merged

marcosqlbi merged 2 commits into
mainfrom
feature/autosave-and-session-restore

Conversation

@marcosqlbi

@marcosqlbi marcosqlbi commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

Closes #121. Closes #122.

The board is copied into a session slot every thirty seconds and again on the way out, and
the next start brings it back. Nothing on this path writes to the .wboard the person
named
— that file changes when they ask for it to change, and every call below follows
from it. The reasoning is decision 30 in docs/decisions.md.

Knowing whether the board has changed came first

All three parts of #121 need it, and the application could not answer it.
ConfirmDiscardUnsaved stood in by asking whether the board was empty, so it nagged right
after a save and said nothing when everything had just been erased.

It is two signals, because one cannot cover it:

  • CommandHistory carries a save point — the command on top of the undo stack when the
    file was written. Undoing back to what was saved stops counting as a change; undoing
    past it starts counting again. A counter cannot express that, since undo moves it the
    same way drawing does.
  • A flag covers what never reaches a command. In practice only one caller survived the
    audit: a .wimport opened as a new board, which clears the history around content no file
    holds. The LiveView paths deliberately set neither signal, so a frame arriving on its own
    cannot put an unasked question in front of someone who left the application running beside
    a feed.

The window title now shows the board and an asterisk, so the question asked on the way out
has something on screen explaining it.

The exit question

Four answers, each a row carrying its own one-line consequence: Save, Keep for next time,
Discard changes, Cancel. There is no prose above them — an earlier draft explained the four
in two paragraphs, which made the reader map sentences onto buttons before they could act.
The LiveView line appears only on a board that has a LiveView container.

Keep is IsDefault, which decision 24 refused for the mouse mode offer. The difference is
that there both answers changed something, so neither could be primed; here Keep decides
nothing — the file is untouched and the board returns — so Enter landing on it is never the
wrong outcome. Decision 30 records this so it does not read as an oversight to be tidied up.

Cancel stays, and LiveView is the reason. Keep and Cancel look interchangeable, but a
restored LiveView container comes back as its last frame and has to be reconnected by hand.
Cancel is the only answer that keeps a feed live — which matters most in the case where the
window gets closed by accident, mid-recording. The camera position is carried in the session
to narrow what is left of the gap.

One slot per running copy

Each copy takes a slot named by a GUID and holds a lock file open while it runs, so two
windows never write to the same files. Forbidding a second window was considered and
rejected: two boards side by side is a real thing to want. A slot whose lock can be taken
belonged to a copy that has gone, and its sidecar says whether it went on purpose — which is
what separates a silent restore from an offer of recovery.

Declining a recovery discards that slot, and the question says so. The alternative is asking
the same person the same thing at every start for thirty days.

Opening a board asks the slots too (#122)

The startup offer only covers the newest slot, so without this the moment somebody most wants
their recovered work — opening the very board they lost — is the moment the application says
nothing. Every route into a board goes through OpenPathAsync, so the Open dialog, a drop,
and a double-click in Explorer are covered once rather than three times. Yes takes the
changes, No opens the saved file and discards them, Cancel opens neither and keeps the slot.

Checks

dotnet build Whiteboard.sln -c Release clean, both smoke harnesses pass. The Core harness
covers the save point through the cases that actually come up — save, undo, redo, undo back
onto the point, a different action at the same depth — plus the snapshot autosave writes, the
sidecar round-trip and what it does with damaged input, and the clamping on a restored camera.

Also walked through the built application rather than trusting the tests:

  • A crafted session slot restored on launch with the title reading Untitled board *, the
    camera where the sidecar put it, the slot re-homed into this copy's own before the old one
    was released, and the sidecar flipping to exitedCleanly on the way out.
  • For Opening a .wboard should offer unsaved changes a crashed session left for it #122: a saved board holding one stroke, and a crashed slot holding five for that same
    file. Opening the file offered the changes; answering Yes gave demo.wboard * in the title
    and a board of five strokes, while the file on disk still held one. Killing the process
    then left a slot that correctly read as a crash.
  • One thing only that found: an empty lock file survived each launch, so Prune now sweeps
    orphans.

VersionPrefix moves to 1.5.0 with the matching CHANGELOG.md section, and site/guide.html
gets one paragraph.

🤖 Generated with Claude Code

marcosqlbi and others added 2 commits September 11, 2026 13:30
Closes #121. The board is copied into a session slot every thirty seconds
and again on the way out, and the next start brings it back. Nothing on
this path writes to the .wboard the person named.

Knowing whether the board has changed came first, since all three parts
need it and nothing in the application could answer it: ConfirmDiscardUnsaved
stood in by asking whether the board was empty, so it nagged right after a
save and stayed silent when everything had been erased. The command history
now carries a save point, so undoing back to what was saved stops counting
as a change and undoing past it starts counting again; the handful of places
that change the document without a command set a flag instead. LiveView sets
neither, so a frame arriving on its own cannot raise the question.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Closes #122, and reworks the unsaved-changes question so each answer
carries its own consequence.

Opening a board now asks the slots whether anything newer is waiting for
it. Without this the moment somebody most wants their recovered work -
opening the very board they lost - is the moment the application says
nothing, because the offer only appeared at startup and only for the
newest slot. Every route in goes through OpenPathAsync, so the Open
dialog, a drop and a double-click in Explorer are all covered once.

The exit question was four buttons explained in two paragraphs of prose,
which made the reader map sentences onto buttons before they could act.
It is now four rows, each with its own one-line consequence, and no prose
at all. The LiveView line appears only on a board that has a LiveView
container, so the common case does not carry a sentence about a feature
nobody in it is using.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@marcosqlbi
marcosqlbi merged commit af7f816 into main Sep 11, 2026
5 checks passed
@marcosqlbi
marcosqlbi deleted the feature/autosave-and-session-restore branch September 11, 2026 11:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Opening a .wboard should offer unsaved changes a crashed session left for it Autosave at exit

1 participant