Skip to content

fix(web): flush pending raw-markdown on unload (BUG-2024)#883

Merged
xarmian merged 2 commits into
mainfrom
fix/bug-2024-raw-editor-unload
Jul 9, 2026
Merged

fix(web): flush pending raw-markdown on unload (BUG-2024)#883
xarmian merged 2 commits into
mainfrom
fix/bug-2024-raw-editor-unload

Conversation

@xarmian

@xarmian xarmian commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

BUG-2024 — Raw-markdown editor loses typed content on tab close/reload

From the PLAN-1984 Web-UX audit: up to ~1.2s of typed raw-markdown content is lost on tab close/reload. The item-detail beforeunload handler only flushed the collaborative (Yjs) path and ignored pending raw markdown debounced edits, and never warned on dirty state.

Before

  • Raw-markdown typing is debounced (1200ms) before it PATCHes items.content.
  • On tab close/reload within that window, onBeforeUnload flushed only the collab Y.Doc snapshot — the pending raw edit (rawPendingMarkdown) was silently dropped.
  • No native "unsaved changes" prompt.

After

  • onBeforeUnload still flushes the collab path unchanged, and now additionally, when raw markdown is dirty (rawPendingMarkdown !== null):
    • Fires an immediate keepalive PATCH /items/{id} of the pending content so the request survives page teardown (keepalive: true, the modern sendBeacon equivalent; markdown bodies are well under the ~64KB cap).
    • Cancels the queued debounce timer so it can't fire a second, older-content PATCH racing the keepalive one.
    • Sets event.preventDefault() + event.returnValue so the browser shows its native "unsaved changes" prompt — only when actually dirty, never on a clean page.
    • Clears rawPendingMarkdown + the dirty flag once the keepalive save lands, so if the user picks "Stay" a later unload doesn't re-prompt / re-PATCH already-saved content.

rawPendingMarkdown is the authoritative dirty signal — set on every raw keystroke, cleared to null after each normal debounced save. So the prompt only fires when there's genuinely unsaved content.

client.ts

api.items.update gains an optional { keepalive?: boolean } — passed straight to fetch (mirrors the existing flushCollabContent keepalive option). No behavior change for existing callers.

Test plan

  • npm run check — 0 errors (only pre-existing unrelated warnings)
  • npm run build — succeeds
  • npm run test — 138/138 pass
  • Handler reasoned through: keepalive PATCH + returnValue fire only when rawPendingMarkdown !== null; dirty cleared after normal save (existing path) and after keepalive save; collab flush preserved untouched.

Refs BUG-2024, PLAN-1984.

@xarmian xarmian merged commit 350617e into main Jul 9, 2026
3 of 4 checks passed
@xarmian xarmian deleted the fix/bug-2024-raw-editor-unload branch July 9, 2026 20:09
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.

1 participant