editor: fix scrambled timeline clip order after global-prompt edits
The cut order (timeline_order) is a list of scene ids. Editing the global prompt rebuilds the plan and reassigns scene content to ids positionally, but _distributeGlobalPrompt left timeline_order untouched — so a stale id sequence scrambled the live clips against their plan badges (e.g. 01,02,04,03,...), even though ghost re-layout (plan-anchored) stayed correct. Fix: the cut order tracks the plan until the user explicitly pins a custom order via the ◀ ▶ arrows. New Project.timeline_manually_ordered flag, set by moveTimelineClip; _ensureTimelineOrder follows plan order when it's unset and heals (survivors keep their manual order, new appended) when set. Reconcile on global-prompt apply too. Existing drifted projects self-heal on load. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
dc70e2b9c6
commit
c5ead80ae8
@@ -597,6 +597,10 @@ class Project:
|
||||
# Cut order: scene ids in TIMELINE (result) order, independent of plan (scenes) order.
|
||||
# Empty = follow plan order (back-compat). Reordering the plan never touches this.
|
||||
timeline_order: list = field(default_factory=list)
|
||||
# True once the user explicitly reordered a timeline clip (◀ ▶). Until then the cut order
|
||||
# tracks the plan, so editing the global prompt re-derives it cleanly instead of letting a
|
||||
# stale id sequence scramble the clips against the plan badges.
|
||||
timeline_manually_ordered: bool = False
|
||||
created_at: float = field(default_factory=time.time)
|
||||
updated_at: float = field(default_factory=time.time)
|
||||
|
||||
@@ -634,6 +638,7 @@ class Project:
|
||||
scene_renders=dict(d.get("scene_renders") or {}),
|
||||
scene_ghosts=list(d.get("scene_ghosts") or []),
|
||||
timeline_order=list(d.get("timeline_order") or []),
|
||||
timeline_manually_ordered=bool(d.get("timeline_manually_ordered", False)),
|
||||
created_at=float(d.get("created_at", time.time())),
|
||||
updated_at=float(d.get("updated_at", time.time())),
|
||||
)
|
||||
|
||||
@@ -488,6 +488,9 @@
|
||||
_applyDetectedTransitions(next, v.transitions, text);
|
||||
state.project.scenes = next;
|
||||
state.sceneGhosts = ghostsOut;
|
||||
// The global prompt is authoritative on plan order — reconcile the cut order to it now
|
||||
// (no-op when the user has pinned a manual order; otherwise tracks the plan).
|
||||
_ensureTimelineOrder();
|
||||
const prevSel = state.selectedSceneId;
|
||||
const stillSel = prevSel && next.some((s) => s.id === prevSel);
|
||||
const firstId = stillSel ? prevSel : (next[0]?.id || null);
|
||||
@@ -1422,6 +1425,10 @@
|
||||
function _ensureTimelineOrder() {
|
||||
const p = state.project; if (!p) return;
|
||||
const ids = (p.scenes || []).map((s) => s.id);
|
||||
// Until the user explicitly pins a custom cut order (◀ ▶), the timeline follows the plan.
|
||||
// This keeps an edit to the global prompt — which rebuilds the plan in its own order — from
|
||||
// leaving a stale id sequence behind that scrambles the clips against their plan badges.
|
||||
if (!p.timeline_manually_ordered) { p.timeline_order = ids; return; }
|
||||
const idSet = new Set(ids);
|
||||
const cur = (Array.isArray(p.timeline_order) ? p.timeline_order : []).filter((id) => idSet.has(id));
|
||||
const inCur = new Set(cur);
|
||||
@@ -1450,6 +1457,9 @@
|
||||
if (i === j) return;
|
||||
_historyRecord();
|
||||
order.splice(i, 1); order.splice(j, 0, id);
|
||||
// The user has now pinned a custom cut order — a global-prompt edit must preserve it
|
||||
// rather than re-deriving the order from the plan.
|
||||
p.timeline_manually_ordered = true;
|
||||
notify(); scheduleSave();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user