mirror of
https://github.com/OpenCut-app/OpenCut.git
synced 2026-07-13 21:52:53 +02:00
Merge pull request [#483](https://github.com/mazeincoding/AppCut/issues/483)
This commit is contained in:
@@ -26,136 +26,204 @@ export function useEditorActions() {
|
||||
const { activeProject } = useProjectStore();
|
||||
|
||||
// Playback actions
|
||||
useActionHandler("toggle-play", () => {
|
||||
toggle();
|
||||
}, undefined);
|
||||
|
||||
useActionHandler("stop-playback", () => {
|
||||
if (isPlaying) {
|
||||
useActionHandler(
|
||||
"toggle-play",
|
||||
() => {
|
||||
toggle();
|
||||
}
|
||||
seek(0);
|
||||
}, undefined);
|
||||
},
|
||||
undefined
|
||||
);
|
||||
|
||||
useActionHandler("seek-forward", (args) => {
|
||||
const seconds = args?.seconds ?? 1;
|
||||
seek(Math.min(duration, currentTime + seconds));
|
||||
}, undefined);
|
||||
useActionHandler(
|
||||
"stop-playback",
|
||||
() => {
|
||||
if (isPlaying) {
|
||||
toggle();
|
||||
}
|
||||
seek(0);
|
||||
},
|
||||
undefined
|
||||
);
|
||||
|
||||
useActionHandler("seek-backward", (args) => {
|
||||
const seconds = args?.seconds ?? 1;
|
||||
seek(Math.max(0, currentTime - seconds));
|
||||
}, undefined);
|
||||
useActionHandler(
|
||||
"seek-forward",
|
||||
(args) => {
|
||||
const seconds = args?.seconds ?? 1;
|
||||
seek(Math.min(duration, currentTime + seconds));
|
||||
},
|
||||
undefined
|
||||
);
|
||||
|
||||
useActionHandler("frame-step-forward", () => {
|
||||
const projectFps = activeProject?.fps || 30;
|
||||
seek(Math.min(duration, currentTime + 1 / projectFps));
|
||||
}, undefined);
|
||||
useActionHandler(
|
||||
"seek-backward",
|
||||
(args) => {
|
||||
const seconds = args?.seconds ?? 1;
|
||||
seek(Math.max(0, currentTime - seconds));
|
||||
},
|
||||
undefined
|
||||
);
|
||||
|
||||
useActionHandler("frame-step-backward", () => {
|
||||
const projectFps = activeProject?.fps || 30;
|
||||
seek(Math.max(0, currentTime - 1 / projectFps));
|
||||
}, undefined);
|
||||
useActionHandler(
|
||||
"frame-step-forward",
|
||||
() => {
|
||||
const projectFps = activeProject?.fps || 30;
|
||||
seek(Math.min(duration, currentTime + 1 / projectFps));
|
||||
},
|
||||
undefined
|
||||
);
|
||||
|
||||
useActionHandler("jump-forward", (args) => {
|
||||
const seconds = args?.seconds ?? 5;
|
||||
seek(Math.min(duration, currentTime + seconds));
|
||||
}, undefined);
|
||||
useActionHandler(
|
||||
"frame-step-backward",
|
||||
() => {
|
||||
const projectFps = activeProject?.fps || 30;
|
||||
seek(Math.max(0, currentTime - 1 / projectFps));
|
||||
},
|
||||
undefined
|
||||
);
|
||||
|
||||
useActionHandler("jump-backward", (args) => {
|
||||
const seconds = args?.seconds ?? 5;
|
||||
seek(Math.max(0, currentTime - seconds));
|
||||
}, undefined);
|
||||
useActionHandler(
|
||||
"jump-forward",
|
||||
(args) => {
|
||||
const seconds = args?.seconds ?? 5;
|
||||
seek(Math.min(duration, currentTime + seconds));
|
||||
},
|
||||
undefined
|
||||
);
|
||||
|
||||
useActionHandler("goto-start", () => {
|
||||
seek(0);
|
||||
}, undefined);
|
||||
useActionHandler(
|
||||
"jump-backward",
|
||||
(args) => {
|
||||
const seconds = args?.seconds ?? 5;
|
||||
seek(Math.max(0, currentTime - seconds));
|
||||
},
|
||||
undefined
|
||||
);
|
||||
|
||||
useActionHandler("goto-end", () => {
|
||||
seek(duration);
|
||||
}, undefined);
|
||||
useActionHandler(
|
||||
"goto-start",
|
||||
() => {
|
||||
seek(0);
|
||||
},
|
||||
undefined
|
||||
);
|
||||
|
||||
useActionHandler(
|
||||
"goto-end",
|
||||
() => {
|
||||
seek(duration);
|
||||
},
|
||||
undefined
|
||||
);
|
||||
|
||||
// Timeline editing actions
|
||||
useActionHandler("split-element", () => {
|
||||
if (selectedElements.length !== 1) {
|
||||
toast.error("Select exactly one element to split");
|
||||
return;
|
||||
}
|
||||
|
||||
const { trackId, elementId } = selectedElements[0];
|
||||
const track = tracks.find((t: any) => t.id === trackId);
|
||||
const element = track?.elements.find((el: any) => el.id === elementId);
|
||||
|
||||
if (element) {
|
||||
const effectiveStart = element.startTime;
|
||||
const effectiveEnd =
|
||||
element.startTime +
|
||||
(element.duration - element.trimStart - element.trimEnd);
|
||||
|
||||
if (currentTime > effectiveStart && currentTime < effectiveEnd) {
|
||||
splitElement(trackId, elementId, currentTime);
|
||||
} else {
|
||||
toast.error("Playhead must be within selected element");
|
||||
useActionHandler(
|
||||
"split-element",
|
||||
() => {
|
||||
if (selectedElements.length !== 1) {
|
||||
toast.error("Select exactly one element to split");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}, undefined);
|
||||
|
||||
useActionHandler("delete-selected", () => {
|
||||
if (selectedElements.length === 0) {
|
||||
return;
|
||||
}
|
||||
selectedElements.forEach(
|
||||
({ trackId, elementId }: { trackId: string; elementId: string }) => {
|
||||
removeElementFromTrack(trackId, elementId);
|
||||
const { trackId, elementId } = selectedElements[0];
|
||||
const track = tracks.find((t: any) => t.id === trackId);
|
||||
const element = track?.elements.find((el: any) => el.id === elementId);
|
||||
|
||||
if (element) {
|
||||
const effectiveStart = element.startTime;
|
||||
const effectiveEnd =
|
||||
element.startTime +
|
||||
(element.duration - element.trimStart - element.trimEnd);
|
||||
|
||||
if (currentTime > effectiveStart && currentTime < effectiveEnd) {
|
||||
splitElement(trackId, elementId, currentTime);
|
||||
} else {
|
||||
toast.error("Playhead must be within selected element");
|
||||
}
|
||||
}
|
||||
);
|
||||
clearSelectedElements();
|
||||
}, undefined);
|
||||
},
|
||||
undefined
|
||||
);
|
||||
|
||||
useActionHandler("select-all", () => {
|
||||
const allElements = tracks.flatMap((track: any) =>
|
||||
track.elements.map((element: any) => ({
|
||||
trackId: track.id,
|
||||
elementId: element.id,
|
||||
}))
|
||||
);
|
||||
setSelectedElements(allElements);
|
||||
}, undefined);
|
||||
useActionHandler(
|
||||
"delete-selected",
|
||||
() => {
|
||||
if (selectedElements.length === 0) {
|
||||
return;
|
||||
}
|
||||
selectedElements.forEach(
|
||||
({ trackId, elementId }: { trackId: string; elementId: string }) => {
|
||||
removeElementFromTrack(trackId, elementId);
|
||||
}
|
||||
);
|
||||
clearSelectedElements();
|
||||
},
|
||||
undefined
|
||||
);
|
||||
|
||||
useActionHandler("duplicate-selected", () => {
|
||||
if (selectedElements.length !== 1) {
|
||||
toast.error("Select exactly one element to duplicate");
|
||||
return;
|
||||
}
|
||||
useActionHandler(
|
||||
"select-all",
|
||||
() => {
|
||||
const allElements = tracks.flatMap((track: any) =>
|
||||
track.elements.map((element: any) => ({
|
||||
trackId: track.id,
|
||||
elementId: element.id,
|
||||
}))
|
||||
);
|
||||
setSelectedElements(allElements);
|
||||
},
|
||||
undefined
|
||||
);
|
||||
|
||||
const { trackId, elementId } = selectedElements[0];
|
||||
const track = tracks.find((t: any) => t.id === trackId);
|
||||
const element = track?.elements.find((el: any) => el.id === elementId);
|
||||
useActionHandler(
|
||||
"duplicate-selected",
|
||||
() => {
|
||||
if (selectedElements.length !== 1) {
|
||||
toast.error("Select exactly one element to duplicate");
|
||||
return;
|
||||
}
|
||||
|
||||
if (element) {
|
||||
const newStartTime =
|
||||
element.startTime +
|
||||
(element.duration - element.trimStart - element.trimEnd) +
|
||||
0.1;
|
||||
const { id, ...elementWithoutId } = element;
|
||||
const { trackId, elementId } = selectedElements[0];
|
||||
const track = tracks.find((t: any) => t.id === trackId);
|
||||
const element = track?.elements.find((el: any) => el.id === elementId);
|
||||
|
||||
addElementToTrack(trackId, {
|
||||
...elementWithoutId,
|
||||
startTime: newStartTime,
|
||||
});
|
||||
}
|
||||
}, undefined);
|
||||
if (element) {
|
||||
const newStartTime =
|
||||
element.startTime +
|
||||
(element.duration - element.trimStart - element.trimEnd) +
|
||||
0.1;
|
||||
const { id, ...elementWithoutId } = element;
|
||||
|
||||
useActionHandler("toggle-snapping", () => {
|
||||
toggleSnapping();
|
||||
}, undefined);
|
||||
addElementToTrack(trackId, {
|
||||
...elementWithoutId,
|
||||
startTime: newStartTime,
|
||||
});
|
||||
}
|
||||
},
|
||||
undefined
|
||||
);
|
||||
|
||||
useActionHandler(
|
||||
"toggle-snapping",
|
||||
() => {
|
||||
toggleSnapping();
|
||||
},
|
||||
undefined
|
||||
);
|
||||
|
||||
// History actions
|
||||
useActionHandler("undo", () => {
|
||||
undo();
|
||||
}, undefined);
|
||||
useActionHandler(
|
||||
"undo",
|
||||
() => {
|
||||
undo();
|
||||
},
|
||||
undefined
|
||||
);
|
||||
|
||||
useActionHandler("redo", () => {
|
||||
redo();
|
||||
}, undefined);
|
||||
useActionHandler(
|
||||
"redo",
|
||||
() => {
|
||||
redo();
|
||||
},
|
||||
undefined
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user