summaryrefslogtreecommitdiff
path: root/apps/web/src/hooks
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/src/hooks')
-rw-r--r--apps/web/src/hooks/use-editor-actions.ts1
-rw-r--r--apps/web/src/hooks/use-keyboard-shortcuts-help.ts4
-rw-r--r--apps/web/src/hooks/use-timeline-element-resize.ts56
-rw-r--r--apps/web/src/hooks/use-timeline-zoom-actions.ts38
-rw-r--r--apps/web/src/hooks/use-timeline-zoom.ts72
5 files changed, 48 insertions, 123 deletions
diff --git a/apps/web/src/hooks/use-editor-actions.ts b/apps/web/src/hooks/use-editor-actions.ts
index e0d9b9eb..4e8f6aef 100644
--- a/apps/web/src/hooks/use-editor-actions.ts
+++ b/apps/web/src/hooks/use-editor-actions.ts
@@ -102,7 +102,6 @@ export function useEditorActions() {
useActionHandler("delete-selected", () => {
if (selectedElements.length === 0) {
- toast.error("No elements selected");
return;
}
selectedElements.forEach(
diff --git a/apps/web/src/hooks/use-keyboard-shortcuts-help.ts b/apps/web/src/hooks/use-keyboard-shortcuts-help.ts
index d0f177f6..c7de8466 100644
--- a/apps/web/src/hooks/use-keyboard-shortcuts-help.ts
+++ b/apps/web/src/hooks/use-keyboard-shortcuts-help.ts
@@ -60,10 +60,6 @@ const actionDescriptions: Record<
category: "Selection",
},
"toggle-snapping": { description: "Toggle snapping", category: "Editing" },
- "zoom-in": { description: "Zoom in timeline", category: "View" },
- "zoom-out": { description: "Zoom out timeline", category: "View" },
- "zoom-reset": { description: "Reset timeline zoom", category: "View" },
- "zoom-fit": { description: "Fit timeline to window", category: "View" },
undo: { description: "Undo", category: "History" },
redo: { description: "Redo", category: "History" },
};
diff --git a/apps/web/src/hooks/use-timeline-element-resize.ts b/apps/web/src/hooks/use-timeline-element-resize.ts
index f16ca8e2..3afa637b 100644
--- a/apps/web/src/hooks/use-timeline-element-resize.ts
+++ b/apps/web/src/hooks/use-timeline-element-resize.ts
@@ -29,7 +29,12 @@ export function useTimelineElementResize({
}: UseTimelineElementResizeProps) {
const [resizing, setResizing] = useState<ResizeState | null>(null);
const { mediaItems } = useMediaStore();
- const { updateElementStartTime } = useTimelineStore();
+ const {
+ updateElementStartTime,
+ updateElementTrim,
+ updateElementDuration,
+ pushHistory,
+ } = useTimelineStore();
// Set up document-level mouse listeners during resize (like proper drag behavior)
useEffect(() => {
@@ -61,6 +66,9 @@ export function useTimelineElementResize({
e.stopPropagation();
e.preventDefault();
+ // Push history once at the start of the resize operation
+ pushHistory();
+
setResizing({
elementId,
side,
@@ -112,13 +120,14 @@ export function useTimelineElementResize({
const trimDelta = newTrimStart - resizing.initialTrimStart;
const newStartTime = element.startTime + trimDelta;
- onUpdateTrim(
+ updateElementTrim(
track.id,
element.id,
newTrimStart,
- resizing.initialTrimEnd
+ resizing.initialTrimEnd,
+ false
);
- updateElementStartTime(track.id, element.id, newStartTime);
+ updateElementStartTime(track.id, element.id, newStartTime, false);
} else {
// Trying to extend beyond trimStart = 0
if (canExtendElementDuration()) {
@@ -128,22 +137,29 @@ export function useTimelineElementResize({
const newDuration = element.duration + extensionAmount;
// Keep trimStart at 0 and extend the element
- onUpdateTrim(track.id, element.id, 0, resizing.initialTrimEnd);
- onUpdateDuration(track.id, element.id, newDuration);
- updateElementStartTime(track.id, element.id, newStartTime);
+ updateElementTrim(
+ track.id,
+ element.id,
+ 0,
+ resizing.initialTrimEnd,
+ false
+ );
+ updateElementDuration(track.id, element.id, newDuration, false);
+ updateElementStartTime(track.id, element.id, newStartTime, false);
} else {
// Video/Audio: can't extend beyond original content - limit to trimStart = 0
const newTrimStart = 0;
const trimDelta = newTrimStart - resizing.initialTrimStart;
const newStartTime = element.startTime + trimDelta;
- onUpdateTrim(
+ updateElementTrim(
track.id,
element.id,
newTrimStart,
- resizing.initialTrimEnd
+ resizing.initialTrimEnd,
+ false
);
- updateElementStartTime(track.id, element.id, newStartTime);
+ updateElementStartTime(track.id, element.id, newStartTime, false);
}
}
} else {
@@ -159,27 +175,35 @@ export function useTimelineElementResize({
const newTrimEnd = 0; // Reset trimEnd to 0 since we're extending
// Update duration first, then trim
- onUpdateDuration(track.id, element.id, newDuration);
- onUpdateTrim(
+ updateElementDuration(track.id, element.id, newDuration, false);
+ updateElementTrim(
track.id,
element.id,
resizing.initialTrimStart,
- newTrimEnd
+ newTrimEnd,
+ false
);
} else {
// 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 {
// Normal trimming within original duration
const maxTrimEnd = element.duration - resizing.initialTrimStart - 0.1; // Leave at least 0.1s visible
const newTrimEnd = Math.max(0, Math.min(maxTrimEnd, calculated));
- onUpdateTrim(
+ updateElementTrim(
track.id,
element.id,
resizing.initialTrimStart,
- newTrimEnd
+ newTrimEnd,
+ false
);
}
}
diff --git a/apps/web/src/hooks/use-timeline-zoom-actions.ts b/apps/web/src/hooks/use-timeline-zoom-actions.ts
deleted file mode 100644
index c6f9c50c..00000000
--- a/apps/web/src/hooks/use-timeline-zoom-actions.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-"use client";
-
-import { useActionHandler } from "@/constants/actions";
-import { MIN_ZOOM, MAX_ZOOM /*, ZOOM_STEP */ } from "../constants/timeline-constants";
-
-interface UseTimelineZoomActionsProps {
- zoomLevel: number;
- setZoomLevel: (zoomLevel: number | ((prev: number) => number)) => void;
- onFitToWindow?: () => void;
-}
-
-export function useTimelineZoomActions({
- zoomLevel,
- setZoomLevel,
- onFitToWindow,
-}: UseTimelineZoomActionsProps) {
- // Zoom in action
- useActionHandler("zoom-in", () => {
- setZoomLevel((prev) => Math.min(MAX_ZOOM, prev + /*ZOOM_STEP*/ 0.2));
- });
-
- // Zoom out action
- useActionHandler("zoom-out", () => {
- setZoomLevel((prev) => Math.max(MIN_ZOOM, prev - /*ZOOM_STEP*/ 0.2));
- });
-
- // Reset zoom action
- useActionHandler("zoom-reset", () => {
- setZoomLevel(1);
- });
-
- // Fit to window action
- useActionHandler("zoom-fit", () => {
- if (onFitToWindow) {
- onFitToWindow();
- }
- });
-}
diff --git a/apps/web/src/hooks/use-timeline-zoom.ts b/apps/web/src/hooks/use-timeline-zoom.ts
index 1031e4a0..4f5e0af0 100644
--- a/apps/web/src/hooks/use-timeline-zoom.ts
+++ b/apps/web/src/hooks/use-timeline-zoom.ts
@@ -3,7 +3,6 @@ import { useState, useCallback, useEffect, RefObject } from "react";
interface UseTimelineZoomProps {
containerRef: RefObject<HTMLDivElement>;
isInTimeline?: boolean;
- onFitToWindow?: () => void;
}
interface UseTimelineZoomReturn {
@@ -12,83 +11,28 @@ interface UseTimelineZoomReturn {
handleWheel: (e: React.WheelEvent) => void;
}
-const ZOOM_STORAGE_KEY = 'opencut-timeline-zoom';
-const MIN_ZOOM = 0.5; // Minimum zoom to keep element borders visible
-const MAX_ZOOM = 5;
-
export function useTimelineZoom({
containerRef,
isInTimeline = false,
- onFitToWindow,
}: UseTimelineZoomProps): UseTimelineZoomReturn {
- // Load initial zoom level from localStorage
- const [zoomLevel, setZoomLevel] = useState(() => {
- if (typeof window !== 'undefined') {
- const stored = localStorage.getItem(ZOOM_STORAGE_KEY);
- if (stored) {
- const parsed = parseFloat(stored);
- if (!isNaN(parsed) && parsed >= MIN_ZOOM && parsed <= MAX_ZOOM) {
- return parsed;
- }
- }
- }
- return 1;
- });
-
- // Save zoom level to localStorage whenever it changes
- useEffect(() => {
- if (typeof window !== 'undefined') {
- localStorage.setItem(ZOOM_STORAGE_KEY, zoomLevel.toString());
- }
- }, [zoomLevel]);
+ const [zoomLevel, setZoomLevel] = useState(1);
const handleWheel = useCallback((e: React.WheelEvent) => {
// Only zoom if user is using pinch gesture (ctrlKey or metaKey is true)
if (e.ctrlKey || e.metaKey) {
e.preventDefault();
const delta = e.deltaY > 0 ? -0.15 : 0.15;
- setZoomLevel((prev) => Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, prev + delta)));
+ setZoomLevel((prev) => Math.max(0.1, Math.min(10, prev + delta)));
+ }
+ // For horizontal scrolling (when shift is held or horizontal wheel movement),
+ // let the event bubble up to allow ScrollArea to handle it
+ else if (e.shiftKey || Math.abs(e.deltaX) > Math.abs(e.deltaY)) {
+ // Don't prevent default - let ScrollArea handle horizontal scrolling
+ return;
}
// Otherwise, allow normal scrolling
}, []);
- // Keyboard shortcuts for zoom
- useEffect(() => {
- const handleKeyDown = (e: KeyboardEvent) => {
- // Only handle zoom shortcuts when in timeline or when timeline is focused
- if (!isInTimeline) return;
-
- if ((e.ctrlKey || e.metaKey) && !e.shiftKey) {
- switch (e.key) {
- case '=':
- case '+':
- e.preventDefault();
- setZoomLevel((prev) => Math.min(MAX_ZOOM, prev + 0.2));
- break;
- case '-':
- e.preventDefault();
- setZoomLevel((prev) => Math.max(MIN_ZOOM, prev - 0.2));
- break;
- case '0':
- e.preventDefault();
- setZoomLevel(1);
- break;
- case '9':
- e.preventDefault();
- if (onFitToWindow) {
- onFitToWindow();
- }
- break;
- }
- }
- };
-
- if (isInTimeline) {
- document.addEventListener('keydown', handleKeyDown);
- return () => document.removeEventListener('keydown', handleKeyDown);
- }
- }, [isInTimeline, setZoomLevel, onFitToWindow]);
-
// Prevent browser zooming in/out when in timeline
useEffect(() => {
const preventZoom = (e: WheelEvent) => {