mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-09-24 14:55:37 +00:00
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:
@@ -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",
|
||||
}));
|
||||
|
||||
|
||||
@@ -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 }> = [
|
||||
|
||||
Reference in New Issue
Block a user