fix: undo for resizing timeline elements

This commit is contained in:
Maze Winther
2025-07-21 14:46:28 +02:00
parent aeb9cfc6b8
commit a2a9bef1d8
2 changed files with 68 additions and 25 deletions
@@ -29,7 +29,12 @@ export function useTimelineElementResize({
}: UseTimelineElementResizeProps) { }: UseTimelineElementResizeProps) {
const [resizing, setResizing] = useState<ResizeState | null>(null); const [resizing, setResizing] = useState<ResizeState | null>(null);
const { mediaItems } = useMediaStore(); const { mediaItems } = useMediaStore();
const { updateElementStartTime } = useTimelineStore(); const {
updateElementStartTime,
updateElementTrim,
updateElementDuration,
pushHistory,
} = useTimelineStore();
// Set up document-level mouse listeners during resize (like proper drag behavior) // Set up document-level mouse listeners during resize (like proper drag behavior)
useEffect(() => { useEffect(() => {
@@ -61,6 +66,9 @@ export function useTimelineElementResize({
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
// Push history once at the start of the resize operation
pushHistory();
setResizing({ setResizing({
elementId, elementId,
side, side,
@@ -112,13 +120,14 @@ export function useTimelineElementResize({
const trimDelta = newTrimStart - resizing.initialTrimStart; const trimDelta = newTrimStart - resizing.initialTrimStart;
const newStartTime = element.startTime + trimDelta; const newStartTime = element.startTime + trimDelta;
onUpdateTrim( updateElementTrim(
track.id, track.id,
element.id, element.id,
newTrimStart, newTrimStart,
resizing.initialTrimEnd resizing.initialTrimEnd,
false
); );
updateElementStartTime(track.id, element.id, newStartTime); updateElementStartTime(track.id, element.id, newStartTime, false);
} else { } else {
// Trying to extend beyond trimStart = 0 // Trying to extend beyond trimStart = 0
if (canExtendElementDuration()) { if (canExtendElementDuration()) {
@@ -128,22 +137,29 @@ export function useTimelineElementResize({
const newDuration = element.duration + extensionAmount; const newDuration = element.duration + extensionAmount;
// Keep trimStart at 0 and extend the element // Keep trimStart at 0 and extend the element
onUpdateTrim(track.id, element.id, 0, resizing.initialTrimEnd); updateElementTrim(
onUpdateDuration(track.id, element.id, newDuration); track.id,
updateElementStartTime(track.id, element.id, newStartTime); element.id,
0,
resizing.initialTrimEnd,
false
);
updateElementDuration(track.id, element.id, newDuration, false);
updateElementStartTime(track.id, element.id, newStartTime, false);
} else { } else {
// Video/Audio: can't extend beyond original content - limit to trimStart = 0 // Video/Audio: can't extend beyond original content - limit to trimStart = 0
const newTrimStart = 0; const newTrimStart = 0;
const trimDelta = newTrimStart - resizing.initialTrimStart; const trimDelta = newTrimStart - resizing.initialTrimStart;
const newStartTime = element.startTime + trimDelta; const newStartTime = element.startTime + trimDelta;
onUpdateTrim( updateElementTrim(
track.id, track.id,
element.id, element.id,
newTrimStart, newTrimStart,
resizing.initialTrimEnd resizing.initialTrimEnd,
false
); );
updateElementStartTime(track.id, element.id, newStartTime); updateElementStartTime(track.id, element.id, newStartTime, false);
} }
} }
} else { } else {
@@ -159,27 +175,35 @@ export function useTimelineElementResize({
const newTrimEnd = 0; // Reset trimEnd to 0 since we're extending const newTrimEnd = 0; // Reset trimEnd to 0 since we're extending
// Update duration first, then trim // Update duration first, then trim
onUpdateDuration(track.id, element.id, newDuration); updateElementDuration(track.id, element.id, newDuration, false);
onUpdateTrim( updateElementTrim(
track.id, track.id,
element.id, element.id,
resizing.initialTrimStart, resizing.initialTrimStart,
newTrimEnd newTrimEnd,
false
); );
} else { } else {
// Can't extend - just set trimEnd to 0 (maximum possible extension) // Can't extend - just set trimEnd to 0 (maximum possible extension)
onUpdateTrim(track.id, element.id, resizing.initialTrimStart, 0); updateElementTrim(
track.id,
element.id,
resizing.initialTrimStart,
0,
false
);
} }
} else { } else {
// Normal trimming within original duration // Normal trimming within original duration
const maxTrimEnd = element.duration - resizing.initialTrimStart - 0.1; // Leave at least 0.1s visible const maxTrimEnd = element.duration - resizing.initialTrimStart - 0.1; // Leave at least 0.1s visible
const newTrimEnd = Math.max(0, Math.min(maxTrimEnd, calculated)); const newTrimEnd = Math.max(0, Math.min(maxTrimEnd, calculated));
onUpdateTrim( updateElementTrim(
track.id, track.id,
element.id, element.id,
resizing.initialTrimStart, resizing.initialTrimStart,
newTrimEnd newTrimEnd,
false
); );
} }
} }
+28 -9
View File
@@ -106,17 +106,20 @@ interface TimelineStore {
trackId: string, trackId: string,
elementId: string, elementId: string,
trimStart: number, trimStart: number,
trimEnd: number trimEnd: number,
pushHistory?: boolean
) => void; ) => void;
updateElementDuration: ( updateElementDuration: (
trackId: string, trackId: string,
elementId: string, elementId: string,
duration: number duration: number,
pushHistory?: boolean
) => void; ) => void;
updateElementStartTime: ( updateElementStartTime: (
trackId: string, trackId: string,
elementId: string, elementId: string,
startTime: number startTime: number,
pushHistory?: boolean
) => void; ) => void;
toggleTrackMute: (trackId: string) => void; toggleTrackMute: (trackId: string) => void;
@@ -687,8 +690,14 @@ export const useTimelineStore = create<TimelineStore>((set, get) => {
updateTracksAndSave(newTracks); updateTracksAndSave(newTracks);
}, },
updateElementTrim: (trackId, elementId, trimStart, trimEnd) => { updateElementTrim: (
get().pushHistory(); trackId,
elementId,
trimStart,
trimEnd,
pushHistory = true
) => {
if (pushHistory) get().pushHistory();
updateTracksAndSave( updateTracksAndSave(
get()._tracks.map((track) => get()._tracks.map((track) =>
track.id === trackId track.id === trackId
@@ -705,8 +714,13 @@ export const useTimelineStore = create<TimelineStore>((set, get) => {
); );
}, },
updateElementDuration: (trackId, elementId, duration) => { updateElementDuration: (
get().pushHistory(); trackId,
elementId,
duration,
pushHistory = true
) => {
if (pushHistory) get().pushHistory();
updateTracksAndSave( updateTracksAndSave(
get()._tracks.map((track) => get()._tracks.map((track) =>
track.id === trackId track.id === trackId
@@ -721,8 +735,13 @@ export const useTimelineStore = create<TimelineStore>((set, get) => {
); );
}, },
updateElementStartTime: (trackId, elementId, startTime) => { updateElementStartTime: (
get().pushHistory(); trackId,
elementId,
startTime,
pushHistory = true
) => {
if (pushHistory) get().pushHistory();
updateTracksAndSave( updateTracksAndSave(
get()._tracks.map((track) => get()._tracks.map((track) =>
track.id === trackId track.id === trackId