mirror of
https://github.com/OpenCut-app/OpenCut.git
synced 2026-07-13 21:52:53 +02:00
-Made the clickable area larger by increasing the container height -Fixed lint errors.
230 lines
4.8 KiB
TypeScript
230 lines
4.8 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect } from "react";
|
|
import { useActionHandler } from "@/constants/actions";
|
|
import { useTimelineStore } from "@/stores/timeline-store";
|
|
import { usePlaybackStore } from "@/stores/playback-store";
|
|
import { useProjectStore } from "@/stores/project-store";
|
|
import { toast } from "sonner";
|
|
|
|
export function useEditorActions() {
|
|
const {
|
|
tracks,
|
|
selectedElements,
|
|
clearSelectedElements,
|
|
setSelectedElements,
|
|
removeElementFromTrack,
|
|
splitElement,
|
|
addElementToTrack,
|
|
snappingEnabled,
|
|
toggleSnapping,
|
|
undo,
|
|
redo,
|
|
} = useTimelineStore();
|
|
|
|
const { currentTime, duration, isPlaying, toggle, seek } = usePlaybackStore();
|
|
const { activeProject } = useProjectStore();
|
|
|
|
// Playback actions
|
|
useActionHandler(
|
|
"toggle-play",
|
|
() => {
|
|
toggle();
|
|
},
|
|
undefined
|
|
);
|
|
|
|
useActionHandler(
|
|
"stop-playback",
|
|
() => {
|
|
if (isPlaying) {
|
|
toggle();
|
|
}
|
|
seek(0);
|
|
},
|
|
undefined
|
|
);
|
|
|
|
useActionHandler(
|
|
"seek-forward",
|
|
(args) => {
|
|
const seconds = args?.seconds ?? 1;
|
|
seek(Math.min(duration, currentTime + seconds));
|
|
},
|
|
undefined
|
|
);
|
|
|
|
useActionHandler(
|
|
"seek-backward",
|
|
(args) => {
|
|
const seconds = args?.seconds ?? 1;
|
|
seek(Math.max(0, currentTime - seconds));
|
|
},
|
|
undefined
|
|
);
|
|
|
|
useActionHandler(
|
|
"frame-step-forward",
|
|
() => {
|
|
const projectFps = activeProject?.fps || 30;
|
|
seek(Math.min(duration, currentTime + 1 / projectFps));
|
|
},
|
|
undefined
|
|
);
|
|
|
|
useActionHandler(
|
|
"frame-step-backward",
|
|
() => {
|
|
const projectFps = activeProject?.fps || 30;
|
|
seek(Math.max(0, currentTime - 1 / projectFps));
|
|
},
|
|
undefined
|
|
);
|
|
|
|
useActionHandler(
|
|
"jump-forward",
|
|
(args) => {
|
|
const seconds = args?.seconds ?? 5;
|
|
seek(Math.min(duration, currentTime + seconds));
|
|
},
|
|
undefined
|
|
);
|
|
|
|
useActionHandler(
|
|
"jump-backward",
|
|
(args) => {
|
|
const seconds = args?.seconds ?? 5;
|
|
seek(Math.max(0, currentTime - seconds));
|
|
},
|
|
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");
|
|
}
|
|
}
|
|
},
|
|
undefined
|
|
);
|
|
|
|
useActionHandler(
|
|
"delete-selected",
|
|
() => {
|
|
if (selectedElements.length === 0) {
|
|
return;
|
|
}
|
|
selectedElements.forEach(
|
|
({ trackId, elementId }: { trackId: string; elementId: string }) => {
|
|
removeElementFromTrack(trackId, elementId);
|
|
}
|
|
);
|
|
clearSelectedElements();
|
|
},
|
|
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(
|
|
"duplicate-selected",
|
|
() => {
|
|
if (selectedElements.length !== 1) {
|
|
toast.error("Select exactly one element to duplicate");
|
|
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 newStartTime =
|
|
element.startTime +
|
|
(element.duration - element.trimStart - element.trimEnd) +
|
|
0.1;
|
|
const { id, ...elementWithoutId } = element;
|
|
|
|
addElementToTrack(trackId, {
|
|
...elementWithoutId,
|
|
startTime: newStartTime,
|
|
});
|
|
}
|
|
},
|
|
undefined
|
|
);
|
|
|
|
useActionHandler(
|
|
"toggle-snapping",
|
|
() => {
|
|
toggleSnapping();
|
|
},
|
|
undefined
|
|
);
|
|
|
|
// History actions
|
|
useActionHandler(
|
|
"undo",
|
|
() => {
|
|
undo();
|
|
},
|
|
undefined
|
|
);
|
|
|
|
useActionHandler(
|
|
"redo",
|
|
() => {
|
|
redo();
|
|
},
|
|
undefined
|
|
);
|
|
}
|