fix(site-builder): serialise all saves; honest pill for conflicts and new sites

A manual Save during Publish sent a second save with the same base_rev; the
loser 409'd against its own tab (and could abort the publish).

- useWhpApi: every save runs through one in-flight promise (SaveGuard
  saveInFlight). A save requested while one is out waits, then sends only
  if still dirty, with the rev the first returned; payload is read from the
  editor at send time. Forced (empty/overwrite) saves always send.
- Save buttons are disabled while publishing.
- Pill: a conflict reads 'Unsaved changes' (modal open) from Save and from
  Publish alike; a never-saved site (load rev null) reads 'Not saved yet'.
- Autosave interval reads runSave through a ref, so editing can't keep
  restarting it and starve autosave.
- Fix stale 'Throws like exportBodyHtml' comment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
shadowdaoandClaude Opus 5.5 committed 2026-10-07 10:35:11 -07:00
1 parent 572e736f1f
commit 97ccbee461
7 files changed
+335 -20

No files matched your search

+2 -1
View File
@@ -133,7 +133,8 @@ describe('dirty lifecycle and status pill', () => {
respondWith({ save: [jsonResp({ success: false, error: 'x', message: 'nope' }, 500), jsonResp({ success: true })] });
await mount();
expect(guard.dirty).toBe(false);
expect(pill()).toBe('saved');
// rev: null -- the server has no copy yet, so not "Saved".
expect(pill()).toBe('new');
edit();
expect(guard.dirty).toBe(true);