Skip to content

fix: Prevent draft autosave state loss and editor overwriting#283

Open
orrc wants to merge 3 commits intoemdash-cms:mainfrom
orrc:orrc/draft-autosave-fixes
Open

fix: Prevent draft autosave state loss and editor overwriting#283
orrc wants to merge 3 commits intoemdash-cms:mainfrom
orrc:orrc/draft-autosave-fixes

Conversation

@orrc
Copy link
Copy Markdown

@orrc orrc commented Apr 5, 2026

What does this PR do?

Fixes a cluster of related draft/autosave bugs in the admin editor for revision-enabled content.

This started from a verified repro on a published post:

  • Editing "Content" and waiting for autosave appeared to work locally
  • However, the autosave PUT response returned stale content
  • A redundant follow-up GET request also returned stale base-row data
  • Any edits to "Excerpt" were overwritten immediately after autosave
  • After page refresh, the rich text editor appeared to lose the saved draft content even though the draft revision had actually been updated

Root cause turned out to be three separate compounding issues:

  1. packages/core

    • revision-backed autosave wrote the draft correctly, but the PUT response was built from the stale base content row instead of the active draft revision
  2. packages/admin

    • autosave handling in the edit page was not safe for revision-backed content
    • initially this caused a redundant post-autosave GET
    • more importantly, an autosave response could overwrite newer local edits if the user kept typing while the request was in flight
  3. packages/admin

    • PortableTextEditor treated its initial value as mount-only state
    • when parent state changed after mount (for example after draft load, reload, or reset), the live TipTap editor did not reconcile to that new value

What this PR changes:

  • Returns the correct, draft-aware data from autosave responses for revision-backed content.
  • Removes the redundant post-autosave GET call from the client (as the necessary data is in the autosave response).
  • Only applies an autosave response back into edit-page queries when the local editor state still matches the snapshot that was sent.
  • Reconciles post-mount external value changes into the rich-text editor.

Relates to #272, but extends beyond that admin-only reset loop to also fix the stale revision-backed autosave response, the redundant post-autosave refetch, and the in-flight autosave overwrite edge case.

Type of change

  • Bug fix
  • Feature (requires approved Discussion)
  • Refactor (no behavior change)
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm --silent lint:json | jq '.diagnostics | length' returns 0
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • I have added a changeset (if this PR changes a published package)
  • New features link to an approved Discussion: https://github.com/emdash-cms/emdash/discussions/...

AI-generated code disclosure

  • This PR includes AI-generated code
    But with a lot of prior verification, including with Playwright, and a lot of discussion on the solution

orrc and others added 3 commits April 6, 2026 00:11
Revision-backed autosaves were updating the draft revision but returning the base content row, so the API response could immediately disagree with the saved draft state.

Reload the current draft revision after a successful save and merge its data and draft slug into the returned item. Add a regression test covering skipRevision updates on a revision-backed collection.

Co-authored-by: Codex <codex@openai.com>
Update the admin autosave path so a PUT response is only applied back into the edit-page queries when the local editor state still matches the snapshot that was sent.

This keeps autosave from triggering an extra GET for the same item, but avoids overwriting newer local edits with a stale response that arrives after the user keeps typing.

Add focused regressions for the query-cache update helper and the ContentEditor snapshot guard.

Co-authored-by: Codex <codex@openai.com>
Reconcile PortableTextEditor with post-mount value updates from parent state, while avoiding unnecessary resets for equivalent content.

Add browser regressions covering external value replacement without onChange noise and preserving clean history for equivalent updates.

Co-authored-by: Codex <codex@openai.com>
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 5, 2026

⚠️ No Changeset found

Latest commit: c3020e0

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 5, 2026

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@orrc
Copy link
Copy Markdown
Author

orrc commented Apr 5, 2026

I have read the CLA Document and I hereby sign the CLA

github-actions bot added a commit that referenced this pull request Apr 5, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 6, 2026

Overlapping PRs

This PR modifies files that are also changed by other open PRs:

This may cause merge conflicts or duplicated work. A maintainer will coordinate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant