From 50664026a15383243c6bff1303d0e99d1dbdc9a6 Mon Sep 17 00:00:00 2001 From: OpenClaw Agent Date: Thu, 9 Jul 2026 20:34:40 +0000 Subject: [PATCH] perf: reduce editor drag lookup overhead --- js/ltx_director.js | 65 ++++++++++++++++++++++++++-------------------- ltx_director.py | 3 +++ 2 files changed, 40 insertions(+), 28 deletions(-) diff --git a/js/ltx_director.js b/js/ltx_director.js index d101a47..0c106d0 100644 --- a/js/ltx_director.js +++ b/js/ltx_director.js @@ -73,6 +73,14 @@ function findActiveSegmentAtFrame(segments = [], targetFrame, type) { function segmentMatchesAudioSource(seg, audioFile, blobUrl) { return !!seg && (seg.audioFile === audioFile || seg._blobUrl === blobUrl); } + +function findSegmentIndexById(segments = [], segmentId) { + return (segments || []).findIndex((seg) => seg.id === segmentId); +} + +function findSegmentById(segments = [], segmentId) { + return (segments || []).find((seg) => seg.id === segmentId); +} function hideWidget(w) { if (!w) return; @@ -8075,7 +8083,8 @@ class TimelineEditor { const durationFrames = totalFrames; let dragDelta = Math.round((mouseX - this._dragStartX) * (totalFrames / logicalWidth)); - const selectedIds = this.selectedSegmentIds; + const selectedIds = this.selectedSegmentIds; + const selectedIdSet = new Set(selectedIds); // Group Blocking Physics Calculation let maxLeftShift = Infinity; @@ -8084,8 +8093,8 @@ class TimelineEditor { for (const track of ["image", "motion", "audio"]) { const allTrackSegs = this._multiDragInitialSegments[track]; if (!allTrackSegs) continue; - const selectedOnTrack = allTrackSegs.filter(s => selectedIds.includes(s.id)); - const nonSelectedOnTrack = allTrackSegs.filter(s => !selectedIds.includes(s.id)); + const selectedOnTrack = allTrackSegs.filter(s => selectedIdSet.has(s.id)); + const nonSelectedOnTrack = allTrackSegs.filter(s => !selectedIdSet.has(s.id)); if (selectedOnTrack.length === 0) continue; @@ -8130,7 +8139,7 @@ class TimelineEditor { for (const track of ["image", "motion", "audio"]) { const allTrackSegs = this._multiDragInitialSegments[track]; if (!allTrackSegs) continue; - const nonSelectedOnTrack = allTrackSegs.filter(s => !selectedIds.includes(s.id)); + const nonSelectedOnTrack = allTrackSegs.filter(s => !selectedIdSet.has(s.id)); for (const L of nonSelectedOnTrack) { snapCandidates.push(L.start); snapCandidates.push(L.start + L.length); @@ -8141,7 +8150,7 @@ class TimelineEditor { for (const track of ["image", "motion", "audio"]) { const allTrackSegs = this._multiDragInitialSegments[track]; if (!allTrackSegs) continue; - const selectedOnTrack = allTrackSegs.filter(s => selectedIds.includes(s.id)); + const selectedOnTrack = allTrackSegs.filter(s => selectedIdSet.has(s.id)); for (const S of selectedOnTrack) { const targetStart = S.start + clampedDragDelta; const targetEnd = S.start + S.length + clampedDragDelta; @@ -8896,12 +8905,12 @@ class TimelineEditor { return ps; }); } - if (this._multiDragPreviewTimelines.audio) { - this.timeline.audioSegments = this._multiDragPreviewTimelines.audio.map(ps => { - const orig = this.timeline.audioSegments.find(s => s.id === ps.id); - if (orig) { - if (orig.imgObj) ps.imgObj = orig.imgObj; - if (orig.videoEl) ps.videoEl = orig.videoEl; + if (this._multiDragPreviewTimelines.audio) { + this.timeline.audioSegments = this._multiDragPreviewTimelines.audio.map(ps => { + const orig = findSegmentById(this.timeline.audioSegments, ps.id); + if (orig) { + if (orig.imgObj) ps.imgObj = orig.imgObj; + if (orig.videoEl) ps.videoEl = orig.videoEl; if (orig.thumbnails) ps.thumbnails = orig.thumbnails; if (orig._extractingThumbs !== undefined) ps._extractingThumbs = orig._extractingThumbs; if (orig._uploading !== undefined) ps._uploading = orig._uploading; @@ -8914,12 +8923,12 @@ class TimelineEditor { } this._multiDragPreviewTimelines = null; } else if (this._previewSegments) { - const targetArray = this.getSegmentArray(this.selectionType); - - const mappedArray = this._previewSegments.map(ps => { - const orig = targetArray.find(s => s.id === ps.id); - let finalStart = ps.resolvedStart !== undefined ? ps.resolvedStart : ps.start; - let newPs = { ...ps, start: finalStart }; + const targetArray = this.getSegmentArray(this.selectionType); + + const mappedArray = this._previewSegments.map(ps => { + const orig = findSegmentById(targetArray, ps.id); + let finalStart = ps.resolvedStart !== undefined ? ps.resolvedStart : ps.start; + let newPs = { ...ps, start: finalStart }; if (orig) { if (orig.imgObj) newPs.imgObj = orig.imgObj; if (orig.videoEl) newPs.videoEl = orig.videoEl; @@ -8934,17 +8943,17 @@ class TimelineEditor { return newPs; }); - if (this.selectionType === "audio") { - this.timeline.audioSegments = mappedArray; - if (this._dragTargetId) this.selectedIndex = this.timeline.audioSegments.findIndex(s => s.id === this._dragTargetId); - } else if (this.selectionType === "motion") { - this.timeline.motionSegments = mappedArray; - if (this._dragTargetId) this.selectedIndex = this.timeline.motionSegments.findIndex(s => s.id === this._dragTargetId); - } else { - this.timeline.segments = mappedArray; - if (this._dragTargetId) this.selectedIndex = this.timeline.segments.findIndex(s => s.id === this._dragTargetId); - } - } + if (this.selectionType === "audio") { + this.timeline.audioSegments = mappedArray; + if (this._dragTargetId) this.selectedIndex = findSegmentIndexById(this.timeline.audioSegments, this._dragTargetId); + } else if (this.selectionType === "motion") { + this.timeline.motionSegments = mappedArray; + if (this._dragTargetId) this.selectedIndex = findSegmentIndexById(this.timeline.motionSegments, this._dragTargetId); + } else { + this.timeline.segments = mappedArray; + if (this._dragTargetId) this.selectedIndex = findSegmentIndexById(this.timeline.segments, this._dragTargetId); + } + } if (this._previewSiblingSegments) { let siblingArray = null; diff --git a/ltx_director.py b/ltx_director.py index cd0bebc..b4c9eec 100644 --- a/ltx_director.py +++ b/ltx_director.py @@ -288,6 +288,9 @@ def _time_range_to_latent_indices(rel_start_frames: float, rel_length_frames: fl def _preprocess_prompts_with_characters(global_prompt, local_prompts, char1="", char2="", char3=""): """Invisibly swaps out @character1/@char1 tags with their high-fidelity VLM descriptions.""" + if "@" not in (global_prompt or "") and "@" not in (local_prompts or ""): + return global_prompt or "", local_prompts or "" + replacements = {} for tags, value in zip(CHARACTER_TAG_GROUPS, (char1 or "", char2 or "", char3 or "")): for tag in tags: