Skip to content

auto-save drafts in PostForm (Notion/Coda-style)

jedmund requested to merge jedmund/post-autosave into jedmund/admin-forms-replaceState

Created by: jedmund

Stacked on #89.

Summary

exploratory PR retrying draft auto-save after the previous attempt was deleted in Feb 2026 (commits f227f295, 142d3d18) for being too complex (782 lines across 8 files: localStorage drafts, recovery prompts, offline retry queues, per-keystroke content stringification, etc).

this attempt is intentionally minimal. scope: posts only, drafts only.

what got built

  • src/lib/components/admin/forms/useAutoSave.svelte.ts — small Svelte 5 rune helper. single debounced timer (1500ms), one in-flight request at a time, queues at most one follow-up if changes pile up during a save. exposes { state, flush() } where state ∈ {idle, unsaved, saving, saved, failed, conflict}.
  • src/lib/components/admin/SaveStatus.svelte — small status pill: Saving…, Saved, Unsaved, Save failed (with retry), Conflict — reload (with reload link).
  • src/lib/components/admin/StatusDropdown.svelte — added optional hidePrimary prop so PostForm can hide the "Save draft" button on drafts and let SaveStatus do the talking.
  • src/lib/components/admin/forms/PostForm.svelte
    • replaced the per-keystroke JSON.stringify(content) snapshot with O(1) editVersion/savedVersion integer counters (the perf footgun blamed for the previous attempt)
    • wired useAutoSave for drafts only — published posts keep manual save (avoids re-triggering Bluesky/Mastodon syndication every 1.5s)
    • on draft posts: header shows <SaveStatus /> instead of the manual "Save draft" primary button
    • on published posts: UI unchanged
    • 409 conflict handling — auto-save transitions to conflict state and stops; SaveStatus surfaces a "Reload" action. PostForm previously swallowed 409s silently.
    • flush on Cmd+S, on window blur, and on in-app navigation away from a dirty draft (no more UnsavedChangesModal popping for drafts — it just saves and lets you go)
    • Publish button while a draft has pending edits → flushes auto-save first, then publishes on top of the latest saved snapshot

what auto-save does NOT do (lessons from the deleted attempt)

  • no localStorage / draft recovery prompts
  • no offline mode / retry queue
  • no rollout to ProjectForm / GardenItemForm / AlbumForm in this PR — wait until PostForm proves itself

Test plan

  • new post: type a few words → after ~1.5s SaveStatus shows "Saved" and URL silently swaps to /admin/posts/[id]/edit. continue typing → "Unsaved" → "Saving…" → "Saved". no manual save click required.
  • existing draft: edit a paragraph → auto-saves silently. refresh; changes persisted.
  • Cmd+S during active editing: SaveStatus immediately shows "Saving…" then "Saved" (debounce flushed).
  • Publish on a dirty draft: auto-save flushes first, then publish runs. UI switches to the published variant ("Save changes" / "Move to draft" dropdown).
  • two-tab test: edit and save in tab A → tab B's next auto-save returns 409 → SaveStatus shows "Conflict — reload" with a working reload link, and stops auto-saving.
  • published post: confirm NO auto-save fires when editing. existing manual save UI unchanged.
  • navigate away from a dirty draft via sidebar / link: form auto-saves silently, then navigates. no UnsavedChangesModal.
  • pnpm run check net error count unchanged at 118.

Merge request reports

Loading