From 0ca06b4431ecc7eb021151fb95a9b1d0d4d475ac Mon Sep 17 00:00:00 2001 From: digital-garbage <133010426+digital-garbage@users.noreply.github.com> Date: Sun, 7 Jun 2026 13:19:12 +0300 Subject: [PATCH] Movie Editor: stop autosave from interrupting field editing The inspector rebuilt on every autosave (~1s), destroying the field being typed in; number inputs can't preserve selection (no selectionStart), so a select-all + retype appended instead of replacing ('512' + '640' -> '512640'). Now the inspector skips rebuilding entirely while one of its fields is focused, and re-syncs to state ~60ms after blur (no rebuild if focus moved to another field). Typing is never interrupted. Co-Authored-By: Claude Opus 4.8 --- movie_editor/frontend/inspector.js | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/movie_editor/frontend/inspector.js b/movie_editor/frontend/inspector.js index aaa760f1..ea7ef265 100644 --- a/movie_editor/frontend/inspector.js +++ b/movie_editor/frontend/inspector.js @@ -454,12 +454,13 @@ body.append(sw); } + // While the user is actively editing one of our fields, DON'T rebuild the inspector — + // autosave fires ~1s and a rebuild would yank the field out, drop the selection and + // make typing append (e.g. "512" + "640" -> "512640"). We defer and re-sync on blur. + let _editing = false; + function render(st) { - // preserve focus + caret of a text/number field across the rebuild (autosave-safe) - const a = document.activeElement; - let foc = null; - if (a && body.contains(a) && (a.tagName === "TEXTAREA" || a.tagName === "INPUT") && a.dataset.k) - foc = { k: a.dataset.k, s: a.selectionStart, e: a.selectionEnd }; + if (_editing) return; // editing in progress — leave the DOM (and the user's caret) alone clear(body); if (!st.project) { title.textContent = "Inspector"; body.append(el("div", "pj-meta", "No project open.")); return; } @@ -469,12 +470,20 @@ if (scene) renderScene(st, scene); else renderProject(st); renderExposed(st); renderSplit(st); - - if (foc) { - const n = body.querySelector(`[data-k="${foc.k}"]`); - if (n) { n.focus(); try { if (foc.s != null) n.setSelectionRange(foc.s, foc.e); } catch (_) {} } - } } + // Track edit state so autosave re-renders never interrupt the focused field. + body.addEventListener("focusin", (e) => { + const t = e.target; + if (t && t.dataset && t.dataset.k) _editing = true; + }); + body.addEventListener("focusout", (e) => { + const t = e.target; + if (!(t && t.dataset && t.dataset.k)) return; + _editing = false; + // If focus didn't move to another of our fields, re-sync the inspector to state. + setTimeout(() => { if (!_editing) render(S.get()); }, 60); + }); + S.subscribe(render); })();