add undo/redo to page editors with keyboard shortcuts
All checks were successful
deploy / deploy (push) Successful in 1m29s

History stacks (@history/@future) on both admin editor and live sidebar,
capped at 50 entries. All mutations routed through apply_mutation for
consistent history tracking. EditorKeyboard JS hook combines DirtyGuard
with Ctrl+Z/Ctrl+Shift+Z. Settings panel fade-in animation. 10 new tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jamey
2026-02-28 12:16:15 +00:00
parent 22d7b0e92b
commit 79b5161e02
8 changed files with 379 additions and 37 deletions

View File

@@ -1475,6 +1475,12 @@
padding: 0.75rem 0.75rem 0.25rem;
padding-left: 2.75rem;
border-top: 1px solid var(--t-border-default);
animation: blockSettingsFadeIn 0.15s ease;
}
@keyframes blockSettingsFadeIn {
from { opacity: 0; transform: translateY(-4px); }
to { opacity: 1; transform: translateY(0); }
}
.block-settings-fields {

View File

@@ -651,10 +651,41 @@ const DirtyGuard = {
}
}
// DirtyGuard + Ctrl+Z / Ctrl+Shift+Z undo/redo for page editors
const EditorKeyboard = {
mounted() {
this._beforeUnload = (e) => {
if (this.el.dataset.dirty === "true") {
e.preventDefault()
e.returnValue = ""
}
}
window.addEventListener("beforeunload", this._beforeUnload)
const prefix = this.el.dataset.eventPrefix || ""
this._keydown = (e) => {
if ((e.ctrlKey || e.metaKey) && e.key === "z") {
e.preventDefault()
if (e.shiftKey) {
this.pushEvent(prefix + "redo")
} else {
this.pushEvent(prefix + "undo")
}
}
}
document.addEventListener("keydown", this._keydown)
},
destroyed() {
window.removeEventListener("beforeunload", this._beforeUnload)
document.removeEventListener("keydown", this._keydown)
}
}
const csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
const liveSocket = new LiveSocket("/live", Socket, {
params: {_csrf_token: csrfToken, screen_width: window.innerWidth},
hooks: {...colocatedHooks, ColorSync, Lightbox, CartPersist, CartDrawer, ProductImageScroll, SearchModal, CollectionFilters, CardRadioScroll, AnalyticsInit, AnalyticsExport, ChartTooltip, DirtyGuard},
hooks: {...colocatedHooks, ColorSync, Lightbox, CartPersist, CartDrawer, ProductImageScroll, SearchModal, CollectionFilters, CardRadioScroll, AnalyticsInit, AnalyticsExport, ChartTooltip, DirtyGuard, EditorKeyboard},
})
// Show progress bar on live navigation and form submits