From e514c8f178f5b9171e0ef9ff7cb7be708e7c60de Mon Sep 17 00:00:00 2001 From: digital-garbage <133010426+digital-garbage@users.noreply.github.com> Date: Wed, 10 Jun 2026 16:44:46 +0300 Subject: [PATCH] =?UTF-8?q?Speed=20up=20global=20prompt=20Apply=20?= =?UTF-8?q?=E2=86=92=20scene=20text=20sync?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update timeline and preview optimistically from the parse response, save immediately instead of waiting on the autosave debounce, refresh preview without delay after commit, and force an inspector rebuild even when the global prompt field still has focus. --- movie_editor/frontend/inspector.js | 16 +++++++--- movie_editor/frontend/store.js | 47 +++++++++++++++++++++++------- 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/movie_editor/frontend/inspector.js b/movie_editor/frontend/inspector.js index bb4ade4e..e65aad51 100644 --- a/movie_editor/frontend/inspector.js +++ b/movie_editor/frontend/inspector.js @@ -529,10 +529,18 @@ apply.title = "Split this prompt into anchor, scenes and transitions on the timeline (overwrites it)"; apply.disabled = !val.trim(); // clickable whenever there's a prompt to (re)split — not only after edits apply.onclick = async () => { - apply.disabled = true; apply.textContent = "Applying…"; - await S.applyGlobalPrompt(gpDraft != null ? gpDraft : live); - gpDraft = null; - S.set({}); // re-render so the field returns to its live view + _editing = false; + ta.blur(); + apply.disabled = true; + apply.textContent = "Applying…"; + try { + const ok = await S.applyGlobalPrompt(gpDraft != null ? gpDraft : live); + if (ok) gpDraft = null; + } finally { + apply.disabled = false; + apply.textContent = "Apply →"; + render(S.get()); + } }; head.append(apply); sec.append(head); diff --git a/movie_editor/frontend/store.js b/movie_editor/frontend/store.js index b0bdc95e..27443c3e 100644 --- a/movie_editor/frontend/store.js +++ b/movie_editor/frontend/store.js @@ -167,7 +167,7 @@ _pruneSelection(); notify(); refreshProjectList(); - refreshPreview(); + refreshPreview(true); } catch (e) { state.saving = false; notify(); console.error("save failed", e); @@ -544,15 +544,15 @@ // anchor + one scene per parsed segment + transitions matched from the library. // Existing per-scene source / length settings are carried over by index. async function applyGlobalPrompt(text) { - if (!state.project) return; + if (!state.project) return false; state.project.global_prompt = text; let res; try { res = await API.parsePrompt(state.project.id, text); } - catch (e) { alert("Could not parse the global prompt: " + e.message); return; } + catch (e) { alert("Could not parse the global prompt: " + e.message); return false; } // The verbatim split is authoritative: correct (shortcut-aware) boundaries, scene // text kept exactly as typed, anchor + scenes reproduce the global prompt. const v = res.parsed_verbatim || res.parsed_raw || res.parsed || {}; - if (!(v.scenes || []).length) { alert("Nothing parsed — no scenes detected."); return; } + if (!(v.scenes || []).length) { alert("Nothing parsed — no scenes detected."); return false; } state.project.anchor = v.anchor || ""; const old = state.project.scenes || []; @@ -565,10 +565,32 @@ _applyDetectedTransitions(next, v.transitions); state.project.scenes = next; state.sceneGhosts = []; // scene ids changed — old ghosts no longer anchor correctly - state.selectedSceneId = null; // show project view so the result is visible - state.selectedSceneIds = []; - _selectionAnchorId = null; - notify(); scheduleSave(); + const firstId = next[0]?.id || null; + state.selectedSceneId = firstId; + state.selectedSceneIds = firstId ? [firstId] : []; + _selectionAnchorId = firstId; + // Refresh the inspector immediately — don't wait for save debounce + preview round-trip. + state.preview = { + ...(state.preview || {}), + combined_prompt: text, + display_prompt: text, + parsed: res.parsed, + parsed_raw: res.parsed_raw, + parsed_verbatim: res.parsed_verbatim, + for_generation: false, + }; + notify(); + clearTimeout(saveTimer); + saveTimer = null; + state.saving = true; + notify(); + try { + await commit(); + } catch (e) { + console.error("save after apply failed", e); + return false; + } + return true; } // Map detected transitions (visual_effect) onto scene seams via the library (display @@ -585,14 +607,17 @@ // ── preview ────────────────────────────────────────────────────────────────── let previewTimer = null; - function refreshPreview() { + function refreshPreview(immediate = false) { clearTimeout(previewTimer); - previewTimer = setTimeout(async () => { + previewTimer = null; + const run = async () => { if (!state.project) return; try { state.preview = await API.preview(state.project.id, false, true); } catch (e) { state.preview = { parse_error: e.message }; } notify(); - }, 250); + }; + if (immediate) return run(); + previewTimer = setTimeout(run, 250); } // ── generation ───────────────────────────────────────────────────────────────