fix(site-builder): honest save state (dirty tracking, status pill, beforeunload, error notice)

Track dirty from Craft node changes and page/design/header/footer state, cleared
only by a successful save. Persistent Unsaved/Saving/Saved/Published/Save failed
pill, beforeunload prompt while dirty or saving, one persistent error notice,
resp.ok/non-JSON handling for save and publish, and publish refuses to run
after a failed save. Draft badge no longer flips on a no-op autosave.

Refs #30

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
shadowdaoandClaude Sonnet 5.5 committed 2026-10-07 10:07:03 -07:00
1 parent a406ff8548
commit 9418444916
12 files changed
+842 -170

No files matched your search

+46
View File
@@ -1974,3 +1974,49 @@ body {
cursor: pointer;
padding: 2px 10px;
}
/* Persistent save-state pill in the topbar */
.save-pill {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 11px;
font-weight: 600;
padding: 3px 9px;
border-radius: 10px;
white-space: nowrap;
border: 1px solid transparent;
}
.save-pill.unsaved { background: rgba(245, 158, 11, 0.15); color: #f59e0b; border-color: rgba(245, 158, 11, 0.3); }
.save-pill.saving { background: rgba(59, 130, 246, 0.15); color: #60a5fa; border-color: rgba(59, 130, 246, 0.3); }
.save-pill.saved { background: rgba(16, 185, 129, 0.12); color: #10b981; border-color: rgba(16, 185, 129, 0.25); }
.save-pill.published { background: rgba(16, 185, 129, 0.2); color: #34d399; border-color: rgba(16, 185, 129, 0.4); }
.save-pill.failed { background: rgba(239, 68, 68, 0.15); color: #f87171; border-color: rgba(239, 68, 68, 0.4); }
.topbar-mobile .save-pill { padding: 3px 6px; font-size: 10px; }
/* Error toast: fixed, stays until dismissed */
.error-notice {
position: fixed;
right: 16px;
bottom: 16px;
z-index: 60;
display: flex;
align-items: flex-start;
gap: 8px;
max-width: min(420px, calc(100vw - 32px));
padding: 10px 12px;
background: #450a0a;
border: 1px solid #b91c1c;
border-radius: 6px;
color: #fecaca;
font-size: 12px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
}
.error-notice-text { flex: 1; word-break: break-word; }
.error-notice button {
background: none;
border: none;
color: #fecaca;
cursor: pointer;
padding: 0 4px;
}