diff options
| author | cashmere <cashmere@autistici.org> | 2026-07-26 22:13:01 +0200 |
|---|---|---|
| committer | cashmere <cashmere@autistici.org> | 2026-07-26 22:13:01 +0200 |
| commit | 12539fedf3020468fded3945d5dfc4c8396e7750 (patch) | |
| tree | a2c4122bed6a8aa31e76d0598287afca45104541 /apps/web/src/actions/use-editor-actions.ts | |
| parent | 3fcf892dfa19e0fb2f1487b91971f48dcc26fc50 (diff) | |
| download | opencut-12539fedf3020468fded3945d5dfc4c8396e7750.tar.gz | |
feat: single undo step for freeze frame
The handler fired four separate commands (asset add, split, move, insert),
producing four undo steps. It now builds one BatchCommand and executes it
once. The new ShiftSplitRemainderCommand resolves the split's right-side
elements lazily at execute() so it can be composed after the split in the
batch, and rebuilds the inner move on redo since a re-executed split
generates fresh element ids.
Diffstat (limited to 'apps/web/src/actions/use-editor-actions.ts')
| -rw-r--r-- | apps/web/src/actions/use-editor-actions.ts | 46 |
1 files changed, 25 insertions, 21 deletions
diff --git a/apps/web/src/actions/use-editor-actions.ts b/apps/web/src/actions/use-editor-actions.ts index 41408635..598e73b0 100644 --- a/apps/web/src/actions/use-editor-actions.ts +++ b/apps/web/src/actions/use-editor-actions.ts @@ -28,6 +28,13 @@ import { import { useCommittedRef } from "@/hooks/use-committed-ref"; import { toast } from "sonner"; import { generateImageThumbnail } from "@/media/processing"; +import { + AddMediaAssetCommand, + BatchCommand, + InsertElementCommand, + ShiftSplitRemainderCommand, + SplitElementsCommand, +} from "@/commands"; export function useEditorActions() { const editor = useEditor(); @@ -413,7 +420,9 @@ export function useEditorActions() { const { thumbnailUrl } = await generateImageThumbnail({ imageFile: file, }); - const asset = await editor.media.addMediaAsset({ + + const freezeDuration = mediaTimeFromSeconds({ seconds: 3 }); + const addMediaCommand = new AddMediaAssetCommand({ projectId: activeProject.metadata.id, asset: { file, @@ -425,32 +434,19 @@ export function useEditorActions() { height: activeProject.settings.canvasSize.height, }, }); - if (!asset) { - return; - } - - const freezeDuration = mediaTimeFromSeconds({ seconds: 3 }); - const rightSideElements = editor.timeline.splitElements({ + const splitCommand = new SplitElementsCommand({ elements: [ { trackId: target.track.id, elementId: target.element.id }, ], splitTime: currentTime, }); - - if (rightSideElements.length > 0) { - editor.timeline.moveElements({ - moves: rightSideElements.map((element) => ({ - sourceTrackId: element.trackId, - targetTrackId: element.trackId, - elementId: element.elementId, - newStartTime: addMediaTime({ a: currentTime, b: freezeDuration }), - })), - }); - } - - editor.timeline.insertElement({ + const shiftCommand = new ShiftSplitRemainderCommand({ + split: splitCommand, + newStartTime: addMediaTime({ a: currentTime, b: freezeDuration }), + }); + const insertCommand = new InsertElementCommand({ element: buildElementFromMedia({ - mediaId: asset.id, + mediaId: addMediaCommand.getAssetId(), mediaType: "image", name: "Freeze frame", duration: freezeDuration, @@ -458,6 +454,14 @@ export function useEditorActions() { }), placement: { mode: "explicit", trackId: target.track.id }, }); + editor.command.execute({ + command: new BatchCommand([ + addMediaCommand, + splitCommand, + shiftCommand, + insertCommand, + ]), + }); }, undefined, ); |
