From 753f332eca4e82f93ceadc7b2c55793ad80b2d92 Mon Sep 17 00:00:00 2001
From: webadderall <131426131+webadderall@users.noreply.github.com>
Date: Sun, 19 Apr 2026 00:18:15 +1000
Subject: [PATCH] fix: bound audio waveform to track row and cascade-delete
regions on clip removal
---
src/components/video-editor/VideoEditor.tsx | 171 ++++++++++--------
.../video-editor/timeline/AudioWaveform.tsx | 2 +-
src/components/video-editor/timeline/Row.tsx | 2 +-
.../video-editor/timeline/TimelineEditor.tsx | 4 +-
4 files changed, 95 insertions(+), 84 deletions(-)
diff --git a/src/components/video-editor/VideoEditor.tsx b/src/components/video-editor/VideoEditor.tsx
index 42debcad..381cdca5 100644
--- a/src/components/video-editor/VideoEditor.tsx
+++ b/src/components/video-editor/VideoEditor.tsx
@@ -745,8 +745,7 @@ export default function VideoEditor() {
}
context.imageSmoothingEnabled = true;
context.imageSmoothingQuality = "high";
- const editorBgHsl = getComputedStyle(document.documentElement).getPropertyValue("--editor-bg").trim();
- context.fillStyle = editorBgHsl ? `hsl(${editorBgHsl})` : "#111113";
+ context.fillStyle = "#111113";
context.fillRect(0, 0, targetWidth, targetHeight);
const previewWidth = previewHandle?.containerRef.current?.clientWidth || 1920;
@@ -781,7 +780,7 @@ export default function VideoEditor() {
padding,
cropRegion,
webcam,
- webcamUrl: resolvedWebcamVideoUrl ?? (webcam.sourcePath ? toFileUrl(webcam.sourcePath) : null),
+ webcamUrl: webcam.sourcePath ? toFileUrl(webcam.sourcePath) : null,
videoWidth: previewVideo.videoWidth,
videoHeight: previewVideo.videoHeight,
annotationRegions,
@@ -1807,6 +1806,7 @@ export default function VideoEditor() {
setResolvedWebcamVideoUrl(null);
return;
}
+ setResolvedWebcamVideoUrl(null);
void resolveVideoUrl(webcam.sourcePath).then((url) => {
if (!cancelled) setResolvedWebcamVideoUrl(url);
});
@@ -2808,12 +2808,22 @@ export default function VideoEditor() {
const handleClipDelete = useCallback(
(id: string) => {
+ const deletedClip = clipRegions.find((c) => c.id === id);
setClipRegions((prev) => prev.filter((clip) => clip.id !== id));
+ if (deletedClip) {
+ const { startMs, endMs } = deletedClip;
+ // Cascade: remove all timeline items fully within the deleted clip's span
+ setZoomRegions((prev) => prev.filter((r) => r.startMs < startMs || r.endMs > endMs));
+ setAnnotationRegions((prev) => prev.filter((r) => r.startMs < startMs || r.endMs > endMs));
+ setTrimRegions((prev) => prev.filter((r) => r.startMs < startMs || r.endMs > endMs));
+ setSpeedRegions((prev) => prev.filter((r) => r.startMs < startMs || r.endMs > endMs));
+ setAudioRegions((prev) => prev.filter((r) => r.startMs < startMs || r.endMs > endMs));
+ }
if (selectedClipId === id) {
setSelectedClipId(null);
}
},
- [selectedClipId],
+ [clipRegions, selectedClipId],
);
const handleSelectSpeed = useCallback((id: string | null) => {
@@ -3539,7 +3549,7 @@ export default function VideoEditor() {
videoPadding: padding,
cropRegion,
webcam,
- webcamUrl: resolvedWebcamVideoUrl ?? (webcam.sourcePath ? toFileUrl(webcam.sourcePath) : null),
+ webcamUrl: webcam.sourcePath ? toFileUrl(webcam.sourcePath) : null,
annotationRegions,
autoCaptions,
autoCaptionSettings,
@@ -3708,7 +3718,7 @@ export default function VideoEditor() {
padding,
cropRegion,
webcam,
- webcamUrl: resolvedWebcamVideoUrl ?? (webcam.sourcePath ? toFileUrl(webcam.sourcePath) : null),
+ webcamUrl: webcam.sourcePath ? toFileUrl(webcam.sourcePath) : null,
annotationRegions,
autoCaptions,
autoCaptionSettings,
@@ -4177,8 +4187,8 @@ export default function VideoEditor() {
? Math.min(
typeof exportProgress?.renderProgress === "number"
? exportProgress.renderProgress
- : (exportProgress?.percentage ?? 100),
- 100,
+ : (exportProgress?.percentage ?? 99),
+ 99,
)
: null;
const isLightningExportInProgress =
@@ -4234,7 +4244,7 @@ export default function VideoEditor() {
})
: isExportFinalizing
? t("editor.exportStatus.finalizingPercent", "Finalizing {{percent}}%", {
- percent: Math.round(exportFinalizingProgress ?? 100),
+ percent: Math.round(exportFinalizingProgress ?? 99),
})
: t("editor.exportStatus.completePercent", "{{percent}}% complete", {
percent: Math.round(exportProgress.percentage),
@@ -4258,7 +4268,7 @@ export default function VideoEditor() {
Loading video...
{projectBrowser}
-
+
);
}
@@ -4271,21 +4281,21 @@ export default function VideoEditor() {
ref={projectBrowserFallbackTriggerRef}
type="button"
onClick={handleOpenProjectBrowser}
- className="rounded-[5px] bg-neutral-800 px-3 py-1.5 text-sm font-semibold text-white shadow-[0_14px_32px_rgba(0,0,0,0.18)] transition-colors hover:bg-neutral-700 dark:bg-white dark:text-black dark:hover:bg-white/90"
+ className="rounded-[5px] bg-white px-3 py-1.5 text-sm font-semibold text-black shadow-[0_14px_32px_rgba(0,0,0,0.18)] transition-colors hover:bg-white/92"
>
Open Projects
{projectBrowser}
-
+
);
}
return (
-
+
-
+
@@ -4348,7 +4358,7 @@ export default function VideoEditor() {
ref={projectBrowserTriggerRef}
type="button"
onClick={handleOpenProjectBrowser}
- className="inline-flex h-8 min-w-[96px] items-center justify-center gap-1.5 rounded-[5px] bg-neutral-800 px-4 text-white shadow-[0_14px_32px_rgba(0,0,0,0.18)] transition-colors hover:bg-neutral-700 dark:bg-white dark:text-black dark:hover:bg-white/90"
+ className="inline-flex h-8 min-w-[96px] items-center justify-center gap-1.5 rounded-[5px] bg-white px-4 text-black shadow-[0_14px_32px_rgba(0,0,0,0.18)] transition-colors hover:bg-white/92"
>
@@ -4358,7 +4368,7 @@ export default function VideoEditor() {
-
+
{isExporting ? (
-
+
-
+
{t("editor.exportStatus.exporting", "Exporting")}
-
+
{t(
"editor.exportStatus.renderingFile",
"Rendering your file.",
)}
{isLightningExportInProgress ? (
-
+
PLEASE
@@ -4422,7 +4432,7 @@ export default function VideoEditor() {
) : null}
{isLegacyExportInProgress ? (
-
+
Export too slow? Cancel and try Lightning
export!
@@ -4437,7 +4447,7 @@ export default function VideoEditor() {
{t("common.actions.cancel")}
-
+
{isExportSaving ? (
) : (
@@ -4449,35 +4459,36 @@ export default function VideoEditor() {
/>
)}
-
+
{exportPercentLabel}
{isRenderingAudio ? (
-
- {t("editor.export.processingAudioEdits", "Processing audio with speed/overlay edits")}
+
+ Audio requires real-time playback for speed/overlay
+ edits
) : exportRenderSpeedLabel ? (
-
+
{exportRenderSpeedLabel}
) : null}
{exportRuntimeLabel ? (
-
+
Path: {exportRuntimeLabel}
) : null}
) : exportError ? (
-
-
+
+
{t("editor.exportStatus.issue", "Export issue")}
{exportRuntimeLabel ? (
-
+
Path: {exportRuntimeLabel}
) : null}
-
+
{exportError}
@@ -4494,29 +4505,29 @@ export default function VideoEditor() {
type="button"
variant="outline"
onClick={handleExportDropdownClose}
- className="h-8 flex-1 border-foreground/10 bg-foreground/5 text-xs text-muted-foreground hover:bg-foreground/10"
+ className="h-8 flex-1 border-white/10 bg-white/5 text-xs text-slate-300 hover:bg-white/10"
>
{t("common.actions.close", "Close")}
) : exportedFilePath ? (
-
-
+
+
{t("editor.exportStatus.complete", "Export complete")}
-
+
{t(
"editor.exportStatus.savedSuccessfully",
"Your file was saved successfully.",
)}
{exportRuntimeLabel ? (
-
+
Path: {exportRuntimeLabel}
) : null}
-
+
{exportedFilePath.split("/").pop()}
@@ -4531,7 +4542,7 @@ export default function VideoEditor() {
type="button"
variant="outline"
onClick={handleExportDropdownClose}
- className="h-8 flex-1 border-foreground/10 bg-foreground/5 text-xs text-muted-foreground hover:bg-foreground/10"
+ className="h-8 flex-1 border-white/10 bg-white/5 text-xs text-slate-300 hover:bg-white/10"
>
Done
@@ -4567,7 +4578,7 @@ export default function VideoEditor() {
-
+
{/* Settings sidebar */}
{/* Icon rail */}
@@ -4587,7 +4598,7 @@ export default function VideoEditor() {
{isActive && (
@@ -4636,16 +4647,16 @@ export default function VideoEditor() {
);
})}
-
+
toast.info("Account coming soon")}
title="Account"
- className="group relative flex h-9 w-9 items-center justify-center rounded-lg text-foreground/55 outline-none transition hover:text-foreground focus:outline-none focus-visible:outline-none"
+ className="group relative flex h-9 w-9 items-center justify-center rounded-lg text-white/55 outline-none transition hover:text-white focus:outline-none focus-visible:outline-none"
whileHover={{ opacity: 1 }}
initial={{ opacity: 0.55 }}
>
-
+
@@ -4813,7 +4824,7 @@ export default function VideoEditor() {