Drafts and saving
Markdown on screen, browser recovery text, saved server source, generated output and deployed reader are distinct. Draft recovery protects unsaved text in one browser profile when storage works. Save writes source explicitly. Neither automatically publishes or deploys.
State matrix
| State | Meaning |
|---|---|
| Loaded | Baseline from selected server file |
| Unsaved | Current text differs from confirmed baseline |
| Saving | Exact text snapshot request in flight |
| Saved | Confirmed snapshot equals current text |
| Not saved | Failed/unconfirmed request; text remains |
| Draft | Browser text plus original server baseline |
| Published | Separate build/output operation |
Save is button or Ctrl+S/Cmd+S. Typing updates local recovery but is not automatic server autosave. A blank status after initial load is not a write failure; a save of unchanged loaded text can mark Saved without sending a new request. It does not reread a concurrently changed server file.
Save one reviewed change
- Verify intended account, server and source path.
- Read loaded text and coordinate other writers.
- Edit intended passage and inspect preview.
- Save once; wait for confirmed result.
- If newer typing remains Unsaved, save that separately.
- Reopen/inspect server source without discarding intended work.
- Build/publish/deploy only when required and authorized.
| During save | Result |
|---|---|
| Type newer text | Confirmed old snapshot, newer text remains dirty |
| Press Save again | One queued continuation if newer text remains |
| Repeated Save presses | Not an unlimited request queue |
| First request fails | Queue dropped; explicit diagnosed retry |
| Open another file | Late old result cannot mark new file saved |
| Nothing changed between requests | Extra queued write omitted |
The state machine tracks request sequence/path and exact sent text. This protects local status from late responses; it is not multi-writer conflict detection. A completed request for an old file may have changed that server file even when another file is now selected. Verify affected source before repeating.
“Saved (not published yet)” uses the editor’s current publish-state check. It is a reminder, not proof of remote deployment or cryptographic content equality. Publishing stages explains marker limitations.
Failed or uncertain saves
| Symptom | Safe response |
|---|---|
| Session expired | Preserve text, sign in privately, compare current source |
| Permission denied | Check current account/grants; do not bypass path gates |
| Connection failed | Inspect reachability and actual file before retry |
| Disk/write failure | Preserve text; operator repairs selected storage |
| Non-JSON/bad response | No confirmed success; investigate |
| Lost success response | Reconcile source, not blind repetition |
Error status says Not saved and provides Retry/dismiss controls. Clicking an error indicator also retries. Dismiss hides the error presentation; it does not establish a successful write or erase dirty text. A successful save removes that file’s local recovery record.
The server’s ordinary Markdown save has no revision token or three-way merge. A second writer’s newer file can be overwritten by an older screen. Unlike Admin section/settings forms, ordinary Save does not refuse every stale source. Coordinate edits and compare latest source before replacing it.
There is also no per-page version-history/undo service or guaranteed atomic Markdown-save transaction. Keep Git/protected source backups. Editor Save can ensure parent directories exist, so saving an old authorized path after another writer moves/deletes it may recreate source; investigate missing/moved files before saving old drafts.
Browser recovery model
| Item | Storage |
|---|---|
| Draft text | Current unsaved Markdown |
| Base | Server text that draft began from |
| Time | Local timestamp, not server approval |
| Key | Encoded account plus source path |
| Last page | Separate per-account remembered path |
| Scope | This origin/browser profile only |
Recovery uses unencrypted localStorage, not server/database synchronization. It remains after sign-out and does not follow a user to another device/browser/origin. Changing scheme/host/port can select another browser storage area. Account-key separation is organization, not encryption or protection from someone with profile access.
Storage errors/quota/private browser policies can prevent put/get/remove. Invalid JSON or nonstring text/base records are ignored. There is no promised draft retention period, off-device backup or reliable deletion after every browser event. Maintain actual source history rather than relying on recovery alone.
Recover versus server text
Opening an authorized file reads current server source before asking about a differing stored draft. If draft baseline differs from current source, the dialog warns server changed since draft started.
| Choice | Result |
|---|---|
| Recover draft | Loads local text for review, not save |
| Use server text | Keeps current source on screen |
| Draft equals server | Redundant draft removed |
| Unauthorized/missing remembered path | Not automatically reopened from tree |
Choosing server text leaves differing recovery available until deliberate save/edit behavior changes/removes it. Do not assume closing the prompt securely erased old text. Choosing recovery is not a merge: compare current server source and intended draft passages before Save.
Startup prefers explicit ?file= then remembered last path, but only reopens a file present in the current authorized tree. Old drafts can remain even when no old path opens. Missing reopening is not proof the draft never existed or source was deleted.
In-app departures
Admin navigation/sign-out or ordinary same-window departure can use the native unsaved dialog. Guard attempts to store draft before presenting choices.
| Choice | Effect |
|---|---|
| Stay here | Do not leave |
| Keep draft and leave | Leave with local recovery only |
| Save and leave | Attempt Save; leave only when current text no longer dirty |
| Escape/cancel | Stay |
| Storage unavailable | Keep disabled; save/preserve first |
Typing while a Save-and-leave request is pending can leave newer text dirty, so the page remains. Read its status rather than assuming departure means every keystroke was saved. The dialog’s “kept” statement refers to local browser storage, never publication.
Selecting another file has a different Discard confirmation, not these three options. Confirming removes current draft before loading selected server file. Preserve wanted text first; do not treat Discard as a harmless background switch.
Refresh/tab-close attempts a local draft write and asks the browser’s native beforeunload warning when dirty. Browser policy controls whether/how that warning appears. Crashes, killed tabs, cleanup or unavailable storage can lose recovery. No UI promises protection against all failures.
Rename and delete interactions
The same editor’s rename checks whether target is open file/ancestor and uses departure guard. On success it rebinds current path, removes old draft key, remembers new path and preserves current text against its server baseline. Dirty text becomes recovery at the new path when storage works.
Admin/other-profile/other-tab rename does not migrate every draft. A section folder change also updates assignments and ends affected sessions, not all local recovery keys. Coordinate writers and save/preserve before shared reorganizations.
Delete affecting the open file also guards departure, then removes current recovery record and clears editor after successful source deletion. “Keep draft” before deletion is not an archive promise: successful deletion can remove that open draft record. Preserve required text separately before destructive actions. Other unrelated/profile drafts may remain.
Privacy and completion
| Verify | Evidence |
|---|---|
| Correct target | Actual account/server/path |
| Recovery reviewed | Latest source versus local draft |
| Save confirmed | Intended exact text on server |
| Newer typing | No unsaved remainder you intended to keep |
| Shared work | Other writer changes reconciled |
| Reader | Separate build/host check if requested |
| Private profile | Sensitive local copies handled under policy |
Do not put passwords, tokens or private runtime data into Markdown drafts. Ending sessions is not browser storage erasure. Password/settings/access forms have separate unsaved behavior; this recovery store is not a universal form backup.
Local regression tests cover draft key/error paths and save-state timing. They do not certify real Safari/browser quota, crash recovery, multi-writer merge or production disk restoration. Validation limits records the boundary.