fix: address CodeRabbit review feedback

- Empty clips now treated as fully trimmed (trim-all) not untrimmed
- Preserve sourceStartMs when moving clips (freeze resolved source start)
- Cascade trim cleanup to annotations and audio, not just zooms
- Pass source-coordinate bounds to AudioWaveform for correct waveform display
- Widen SpeedRegion.speed to number to match actual consumer behavior
- Narrow release.yml Windows glob from release/*.yml to release/latest*.yml
This commit is contained in:
webadderall
2026-04-19 17:15:35 +10:00
parent c07893ae98
commit 7f35970385
4 changed files with 42 additions and 25 deletions
+1 -1
View File
@@ -380,7 +380,7 @@ jobs:
path: |
release/*.exe
release/*.blockmap
release/*.yml
release/latest*.yml
if-no-files-found: error
build-linux-x64:
@@ -87,7 +87,10 @@ export function useEditorRegions({
// Trim regions are derived from clips (gaps = sections to remove in export)
const trimRegions = useMemo<TrimRegion[]>(() => {
if (totalMs <= 0 || clipRegions.length === 0) return [];
if (totalMs <= 0) return [];
if (clipRegions.length === 0) {
return [{ id: "trim-all", startMs: 0, endMs: totalMs }];
}
return clipsToTrims(clipRegions, totalMs);
}, [clipRegions, totalMs]);
@@ -153,7 +156,7 @@ export function useEditorRegions({
id: `clip-speed-${c.id}`,
startMs: getClipSourceStartMs(c),
endMs: getClipSourceEndMs(c),
speed: c.speed as SpeedRegion["speed"],
speed: c.speed,
})),
[clipRegions],
);
@@ -356,32 +359,39 @@ export function useEditorRegions({
const endDelta = newEnd - oldClip.endMs;
const isMove =
Math.abs(startDelta - endDelta) < 1 && Math.abs(startDelta) > 0;
if (!isMove && Math.abs(startDelta) > 0) {
// Left-edge resize: shift sourceStartMs by startDelta * speed
const speed =
Number.isFinite(oldClip.speed) && oldClip.speed > 0
? oldClip.speed
: 1;
updated.sourceStartMs = Math.max(
0,
Math.round(getClipSourceStartMs(oldClip) + startDelta * speed),
);
}
// Move: sourceStartMs stays the same (explicit or fallback unchanged since we set it)
const sourceStart = getClipSourceStartMs(oldClip);
const speed =
Number.isFinite(oldClip.speed) && oldClip.speed > 0
? oldClip.speed
: 1;
if (isMove) {
// Move: freeze the resolved source start
updated.sourceStartMs = sourceStart;
} else if (Math.abs(startDelta) > 0) {
// Left-edge resize: shift sourceStartMs by startDelta * speed
updated.sourceStartMs = Math.max(
0,
Math.round(sourceStart + startDelta * speed),
);
}
}
return updated;
}),
);
// Remove zooms that no longer overlap any clip after the change
// Remove regions that no longer overlap any clip after the change
const updatedClips = clipRegions.map((c) =>
c.id === id ? { ...c, startMs: newStart, endMs: newEnd } : c,
);
setZoomRegions((prev) =>
prev.filter((z) =>
updatedClips.some((c) => z.startMs < c.endMs && z.endMs > c.startMs),
),
);
const keepOverlapping = <T extends { startMs: number; endMs: number }>(
regions: T[],
): T[] =>
regions.filter((r) =>
updatedClips.some((c) => r.startMs < c.endMs && r.endMs > c.startMs),
);
setZoomRegions((prev) => keepOverlapping(prev));
setAnnotationRegions((prev) => keepOverlapping(prev));
setAudioRegions((prev) => keepOverlapping(prev));
},
[clipRegions],
);
@@ -54,6 +54,7 @@ import type {
ZoomMode,
ZoomRegion,
} from "../types";
import { getClipSourceStartMs, getClipSourceEndMs } from "../types";
import AudioWaveform from "./AudioWaveform";
import Item from "./Item";
import KeyframeMarkers from "./KeyframeMarkers";
@@ -171,6 +172,8 @@ interface TimelineRenderItem {
zoomDepth?: number;
zoomMode?: ZoomMode;
speedValue?: number;
sourceStartMs?: number;
sourceEndMs?: number;
variant: "zoom" | "trim" | "clip" | "annotation" | "speed" | "audio";
}
@@ -705,11 +708,13 @@ function Timeline({
onSelect={() => onSelectClip?.(item.id)}
variant="clip"
backgroundLayer={
audioPeaks ? (
audioPeaks &&
item.sourceStartMs !== undefined &&
item.sourceEndMs !== undefined ? (
<AudioWaveform
peaks={audioPeaks}
timeStartMs={item.span.start}
timeEndMs={item.span.end}
timeStartMs={item.sourceStartMs}
timeEndMs={item.sourceEndMs}
/>
) : undefined
}
@@ -1518,6 +1523,8 @@ const TimelineEditor = forwardRef<TimelineEditorHandle, TimelineEditorProps>(
rowId: CLIP_ROW_ID,
span: { start: region.startMs, end: region.endMs },
label: `Clip ${index + 1}`,
sourceStartMs: getClipSourceStartMs(region),
sourceEndMs: getClipSourceEndMs(region),
variant: "clip",
}));
+1 -1
View File
@@ -387,7 +387,7 @@ export interface SpeedRegion {
id: string;
startMs: number;
endMs: number;
speed: PlaybackSpeed;
speed: number;
}
export const SPEED_OPTIONS: Array<{ speed: PlaybackSpeed; label: string }> = [