diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-04-01 19:27:49 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-04-01 19:27:49 +0200 |
| commit | bd9ec023eefc4b5d26fa8b66c4338e97104f0f96 (patch) | |
| tree | 08350eaa107b27f026eebdb65df54b1ca25521fd /apps/web/src/hooks | |
| parent | f4b9f40ab1ba1cedf208bba708f4331d36bb30cc (diff) | |
| download | opencut-bd9ec023eefc4b5d26fa8b66c4338e97104f0f96.tar.gz | |
refactor: split constants by domain and isolate timeline panel from core
Diffstat (limited to 'apps/web/src/hooks')
10 files changed, 72 insertions, 54 deletions
diff --git a/apps/web/src/hooks/timeline/element/use-element-interaction.ts b/apps/web/src/hooks/timeline/element/use-element-interaction.ts index 3e0a6997..193379ed 100644 --- a/apps/web/src/hooks/timeline/element/use-element-interaction.ts +++ b/apps/web/src/hooks/timeline/element/use-element-interaction.ts @@ -10,12 +10,10 @@ import { useEditor } from "@/hooks/use-editor"; import { useShiftKey } from "@/hooks/use-shift-key"; import { useTimelineStore } from "@/stores/timeline-store"; import { useElementSelection } from "@/hooks/timeline/element/use-element-selection"; -import { - DRAG_THRESHOLD_PX, - TIMELINE_CONSTANTS, -} from "@/constants/timeline-constants"; +import { BASE_TIMELINE_PIXELS_PER_SECOND } from "@/lib/timeline/scale"; +import { TIMELINE_DRAG_THRESHOLD_PX } from "@/components/editor/panels/timeline/interaction"; import { snapTimeToFrame } from "opencut-wasm"; -import { computeDropTarget } from "@/lib/timeline/drop-utils"; +import { computeDropTarget } from "@/components/editor/panels/timeline/drop-target"; import { getMouseTimeFromClientX } from "@/lib/timeline/drag-utils"; import { generateUUID } from "@/utils/id"; import { snapElementEdge, type SnapPoint } from "@/lib/timeline/snap-utils"; @@ -70,7 +68,7 @@ function getClickOffsetTime({ zoomLevel: number; }): number { const clickOffsetX = clientX - elementRect.left; - return clickOffsetX / (TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel); + return clickOffsetX / (BASE_TIMELINE_PIXELS_PER_SECOND * zoomLevel); } function getVerticalDragDirection({ @@ -136,7 +134,7 @@ function getDragDropTarget({ playheadTime: snappedTime, isExternalDrop: false, elementDuration, - pixelsPerSecond: TIMELINE_CONSTANTS.PIXELS_PER_SECOND, + pixelsPerSecond: BASE_TIMELINE_PIXELS_PER_SECOND, zoomLevel, startTimeOverride: snappedTime, excludeElementId: movingElement.id, @@ -288,7 +286,10 @@ export function useElementInteraction({ if (isPendingDrag && pendingDragRef.current) { const deltaX = Math.abs(clientX - pendingDragRef.current.startMouseX); const deltaY = Math.abs(clientY - pendingDragRef.current.startMouseY); - if (deltaX > DRAG_THRESHOLD_PX || deltaY > DRAG_THRESHOLD_PX) { + if ( + deltaX > TIMELINE_DRAG_THRESHOLD_PX || + deltaY > TIMELINE_DRAG_THRESHOLD_PX + ) { const activeProject = editor.project.getActive(); if (!activeProject) return; const scrollLeft = scrollContainer.scrollLeft; @@ -416,7 +417,10 @@ export function useElementInteraction({ if (mouseDownLocationRef.current) { const deltaX = Math.abs(clientX - mouseDownLocationRef.current.x); const deltaY = Math.abs(clientY - mouseDownLocationRef.current.y); - if (deltaX <= DRAG_THRESHOLD_PX && deltaY <= DRAG_THRESHOLD_PX) { + if ( + deltaX <= TIMELINE_DRAG_THRESHOLD_PX && + deltaY <= TIMELINE_DRAG_THRESHOLD_PX + ) { mouseDownLocationRef.current = null; endDrag(); onSnapPointChange?.(null); @@ -596,7 +600,10 @@ export function useElementInteraction({ if (mouseDownLocationRef.current) { const deltaX = Math.abs(event.clientX - mouseDownLocationRef.current.x); const deltaY = Math.abs(event.clientY - mouseDownLocationRef.current.y); - if (deltaX > DRAG_THRESHOLD_PX || deltaY > DRAG_THRESHOLD_PX) { + if ( + deltaX > TIMELINE_DRAG_THRESHOLD_PX || + deltaY > TIMELINE_DRAG_THRESHOLD_PX + ) { mouseDownLocationRef.current = null; return; } diff --git a/apps/web/src/hooks/timeline/element/use-element-resize.ts b/apps/web/src/hooks/timeline/element/use-element-resize.ts index 26bd8ef9..890820d7 100644 --- a/apps/web/src/hooks/timeline/element/use-element-resize.ts +++ b/apps/web/src/hooks/timeline/element/use-element-resize.ts @@ -1,5 +1,5 @@ import { useState, useEffect, useRef, useCallback } from "react"; -import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants"; +import { BASE_TIMELINE_PIXELS_PER_SECOND } from "@/lib/timeline/scale"; import { snapTimeToFrame } from "opencut-wasm"; import type { TimelineElement, TimelineTrack } from "@/lib/timeline"; import { useEditor } from "@/hooks/use-editor"; @@ -190,7 +190,7 @@ export function useTimelineElementResize({ const deltaX = clientX - resizing.startX; let deltaTime = - deltaX / (TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel); + deltaX / (BASE_TIMELINE_PIXELS_PER_SECOND * zoomLevel); let resizeSnapPoint: SnapPoint | null = null; const projectFps = editor.project.getActive().settings.fps; diff --git a/apps/web/src/hooks/timeline/element/use-keyframe-drag.ts b/apps/web/src/hooks/timeline/element/use-keyframe-drag.ts index e0bf8084..bbe843f2 100644 --- a/apps/web/src/hooks/timeline/element/use-keyframe-drag.ts +++ b/apps/web/src/hooks/timeline/element/use-keyframe-drag.ts @@ -9,10 +9,8 @@ import { useEditor } from "@/hooks/use-editor"; import { useKeyframeSelection } from "./use-keyframe-selection"; import { snapTimeToFrame, getSnappedSeekTime } from "opencut-wasm"; import { timelineTimeToSnappedPixels } from "@/lib/timeline"; -import { - DRAG_THRESHOLD_PX, - TIMELINE_CONSTANTS, -} from "@/constants/timeline-constants"; +import { BASE_TIMELINE_PIXELS_PER_SECOND } from "@/lib/timeline/scale"; +import { TIMELINE_DRAG_THRESHOLD_PX } from "@/components/editor/panels/timeline/interaction"; import { RetimeKeyframeCommand } from "@/lib/commands/timeline/element/keyframes/retime-keyframe"; import { BatchCommand } from "@/lib/commands"; import type { SelectedKeyframeRef } from "@/lib/animation/types"; @@ -64,7 +62,7 @@ export function useKeyframeDrag({ const activeProject = editor.project.getActive(); const fps = activeProject.settings.fps; - const pixelsPerSecond = TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel; + const pixelsPerSecond = BASE_TIMELINE_PIXELS_PER_SECOND * zoomLevel; const endDrag = useCallback(() => { setDragState(initialDragState); @@ -127,7 +125,7 @@ export function useKeyframeDrag({ const handleMouseMove = ({ clientX }: MouseEvent) => { if (isPendingDrag && pendingDragRef.current) { const deltaX = Math.abs(clientX - pendingDragRef.current.startMouseX); - if (deltaX <= DRAG_THRESHOLD_PX) return; + if (deltaX <= TIMELINE_DRAG_THRESHOLD_PX) return; const pending = pendingDragRef.current; pendingDragRef.current = null; @@ -248,7 +246,8 @@ export function useKeyframeDrag({ const wasDrag = mouseDownXRef.current !== null && - Math.abs(event.clientX - mouseDownXRef.current) > DRAG_THRESHOLD_PX; + Math.abs(event.clientX - mouseDownXRef.current) > + TIMELINE_DRAG_THRESHOLD_PX; mouseDownXRef.current = null; if (wasDrag) return; diff --git a/apps/web/src/hooks/timeline/use-bookmark-drag.ts b/apps/web/src/hooks/timeline/use-bookmark-drag.ts index 4a1185bd..14d58d9a 100644 --- a/apps/web/src/hooks/timeline/use-bookmark-drag.ts +++ b/apps/web/src/hooks/timeline/use-bookmark-drag.ts @@ -7,7 +7,7 @@ import { } from "react"; import { useEditor } from "@/hooks/use-editor"; import { useShiftKey } from "@/hooks/use-shift-key"; -import { DRAG_THRESHOLD_PX } from "@/constants/timeline-constants"; +import { TIMELINE_DRAG_THRESHOLD_PX } from "@/components/editor/panels/timeline/interaction"; import { snapTimeToFrame } from "opencut-wasm"; import { getMouseTimeFromClientX } from "@/lib/timeline/drag-utils"; import { @@ -131,7 +131,10 @@ export function useBookmarkDrag({ const deltaX = Math.abs(event.clientX - startMouseX); const deltaY = Math.abs(event.clientY - startMouseY); - if (deltaX <= DRAG_THRESHOLD_PX && deltaY <= DRAG_THRESHOLD_PX) { + if ( + deltaX <= TIMELINE_DRAG_THRESHOLD_PX && + deltaY <= TIMELINE_DRAG_THRESHOLD_PX + ) { return; } diff --git a/apps/web/src/hooks/timeline/use-snap-indicator-position.ts b/apps/web/src/hooks/timeline/use-snap-indicator-position.ts index 3dc34b7f..b941dcb5 100644 --- a/apps/web/src/hooks/timeline/use-snap-indicator-position.ts +++ b/apps/web/src/hooks/timeline/use-snap-indicator-position.ts @@ -1,6 +1,6 @@ import { useEffect, useState } from "react";
import { timelineTimeToSnappedPixels } from "@/lib/timeline";
-import { TRACK_LABELS_WIDTH_PX } from "@/constants/timeline-constants";
+import { TIMELINE_TRACK_LABELS_COLUMN_WIDTH_PX } from "@/components/editor/panels/timeline/layout";
interface UseSnapIndicatorPositionParams {
snapPoint: { time: number } | null;
zoomLevel: number;
@@ -44,7 +44,8 @@ export function useSnapIndicatorPosition({ time: snapPoint?.time ?? 0,
zoomLevel,
});
- const leftPosition = TRACK_LABELS_WIDTH_PX + timelinePosition - scrollLeft;
+ const leftPosition =
+ TIMELINE_TRACK_LABELS_COLUMN_WIDTH_PX + timelinePosition - scrollLeft;
return {
leftPosition,
diff --git a/apps/web/src/hooks/timeline/use-timeline-drag-drop.ts b/apps/web/src/hooks/timeline/use-timeline-drag-drop.ts index ce6f1341..8e87510c 100644 --- a/apps/web/src/hooks/timeline/use-timeline-drag-drop.ts +++ b/apps/web/src/hooks/timeline/use-timeline-drag-drop.ts @@ -3,7 +3,8 @@ import { useEditor } from "@/hooks/use-editor"; import { processMediaAssets } from "@/lib/media/processing"; import { toast } from "sonner"; import { showMediaUploadToast } from "@/lib/media/upload-toast"; -import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants"; +import { DEFAULT_NEW_ELEMENT_DURATION_SECONDS } from "@/lib/timeline/creation"; +import { BASE_TIMELINE_PIXELS_PER_SECOND } from "@/lib/timeline/scale"; import { snapTimeToFrame } from "opencut-wasm"; import { buildTextElement, @@ -14,7 +15,7 @@ import { } from "@/lib/timeline/element-utils"; import { AddTrackCommand, InsertElementCommand } from "@/lib/commands/timeline"; import { BatchCommand } from "@/lib/commands"; -import { computeDropTarget } from "@/lib/timeline/drop-utils"; +import { computeDropTarget } from "@/components/editor/panels/timeline/drop-target"; import { getDragData, hasDragData } from "@/lib/drag-data"; import { isMainTrack } from "@/lib/timeline/placement"; import type { TrackType, DropTarget, ElementType } from "@/lib/timeline"; @@ -82,14 +83,14 @@ export function useTimelineDragDrop({ elementType === "sticker" || elementType === "effect" ) { - return TIMELINE_CONSTANTS.DEFAULT_ELEMENT_DURATION; + return DEFAULT_NEW_ELEMENT_DURATION_SECONDS; } if (mediaId) { const mediaAssets = editor.media.getAssets(); const media = mediaAssets.find((m) => m.id === mediaId); - return media?.duration ?? TIMELINE_CONSTANTS.DEFAULT_ELEMENT_DURATION; + return media?.duration ?? DEFAULT_NEW_ELEMENT_DURATION_SECONDS; } - return TIMELINE_CONSTANTS.DEFAULT_ELEMENT_DURATION; + return DEFAULT_NEW_ELEMENT_DURATION_SECONDS; }, [editor], ); @@ -158,7 +159,7 @@ export function useTimelineDragDrop({ playheadTime: currentTime, isExternalDrop: isExternal, elementDuration: duration, - pixelsPerSecond: TIMELINE_CONSTANTS.PIXELS_PER_SECOND, + pixelsPerSecond: BASE_TIMELINE_PIXELS_PER_SECOND, zoomLevel, targetElementTypes, }); @@ -330,7 +331,7 @@ export function useTimelineDragDrop({ dragData.mediaType === "audio" ? "audio" : "video"; const duration = - mediaAsset.duration ?? TIMELINE_CONSTANTS.DEFAULT_ELEMENT_DURATION; + mediaAsset.duration ?? DEFAULT_NEW_ELEMENT_DURATION_SECONDS; const element = buildElementFromMedia({ mediaId: mediaAsset.id, mediaType: mediaAsset.type, @@ -445,7 +446,7 @@ export function useTimelineDragDrop({ const duration = createdAsset.duration ?? - TIMELINE_CONSTANTS.DEFAULT_ELEMENT_DURATION; + DEFAULT_NEW_ELEMENT_DURATION_SECONDS; const currentTracks = editor.timeline.getTracks(); const currentTime = editor.playback.getCurrentTime(); const onlyTrack = currentTracks[0]; @@ -467,7 +468,7 @@ export function useTimelineDragDrop({ playheadTime: currentTime, isExternalDrop: true, elementDuration: duration, - pixelsPerSecond: TIMELINE_CONSTANTS.PIXELS_PER_SECOND, + pixelsPerSecond: BASE_TIMELINE_PIXELS_PER_SECOND, zoomLevel, }); diff --git a/apps/web/src/hooks/timeline/use-timeline-playhead.ts b/apps/web/src/hooks/timeline/use-timeline-playhead.ts index d4cfcc06..fce84135 100644 --- a/apps/web/src/hooks/timeline/use-timeline-playhead.ts +++ b/apps/web/src/hooks/timeline/use-timeline-playhead.ts @@ -8,7 +8,7 @@ import { getCenteredLineLeft, timelineTimeToSnappedPixels, } from "@/lib/timeline"; -import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants"; +import { BASE_TIMELINE_PIXELS_PER_SECOND } from "@/lib/timeline/scale"; interface UseTimelinePlayheadProps { zoomLevel: number; @@ -67,7 +67,7 @@ export function useTimelinePlayhead({ const relativeMouseX = event.clientX - rulerRect.left; const timelineContentWidth = - duration * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel; + duration * BASE_TIMELINE_PIXELS_PER_SECOND * zoomLevel; const clampedMouseX = Math.max( 0, @@ -78,7 +78,7 @@ export function useTimelinePlayhead({ 0, Math.min( duration, - clampedMouseX / (TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel), + clampedMouseX / (BASE_TIMELINE_PIXELS_PER_SECOND * zoomLevel), ), ); @@ -161,7 +161,7 @@ export function useTimelinePlayhead({ getMouseClientX: () => lastMouseXRef.current, rulerScrollRef, tracksScrollRef, - contentWidth: duration * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel, + contentWidth: duration * BASE_TIMELINE_PIXELS_PER_SECOND * zoomLevel, }); useEffect(() => { @@ -248,7 +248,7 @@ export function useTimelinePlayhead({ if (!rulerViewport || !tracksViewport) return; const playheadPixels = - time * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevelRef.current; + time * BASE_TIMELINE_PIXELS_PER_SECOND * zoomLevelRef.current; const viewportWidth = rulerViewport.clientWidth; const scrollMinimum = 0; const scrollMaximum = rulerViewport.scrollWidth - viewportWidth; diff --git a/apps/web/src/hooks/timeline/use-timeline-seek.ts b/apps/web/src/hooks/timeline/use-timeline-seek.ts index 2672062f..1690153e 100644 --- a/apps/web/src/hooks/timeline/use-timeline-seek.ts +++ b/apps/web/src/hooks/timeline/use-timeline-seek.ts @@ -1,6 +1,6 @@ import { useCallback, useRef } from "react"; import type { MutableRefObject, RefObject } from "react"; -import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants"; +import { BASE_TIMELINE_PIXELS_PER_SECOND } from "@/lib/timeline/scale"; import { getSnappedSeekTime } from "opencut-wasm"; import { useEditor } from "../use-editor"; @@ -132,7 +132,7 @@ export function useTimelineSeek({ Math.min( duration, (mouseX + scrollLeft) / - (TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel), + (BASE_TIMELINE_PIXELS_PER_SECOND * zoomLevel), ), ); diff --git a/apps/web/src/hooks/timeline/use-timeline-zoom.ts b/apps/web/src/hooks/timeline/use-timeline-zoom.ts index 64a0fb58..a1740384 100644 --- a/apps/web/src/hooks/timeline/use-timeline-zoom.ts +++ b/apps/web/src/hooks/timeline/use-timeline-zoom.ts @@ -7,7 +7,14 @@ import { useRef, useState, } from "react"; -import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants"; +import { + TIMELINE_ZOOM_ANCHOR_PLAYHEAD_THRESHOLD, +} from "@/components/editor/panels/timeline/interaction"; +import { + TIMELINE_ZOOM_MAX, + TIMELINE_ZOOM_MIN, + BASE_TIMELINE_PIXELS_PER_SECOND, +} from "@/lib/timeline/scale"; import { useEditor } from "@/hooks/use-editor"; import { zoomToSlider } from "@/lib/timeline/zoom-utils"; @@ -30,7 +37,7 @@ interface UseTimelineZoomReturn { export function useTimelineZoom({ containerRef, - minZoom = TIMELINE_CONSTANTS.ZOOM_MIN, + minZoom = TIMELINE_ZOOM_MIN, initialZoom, initialScrollLeft, initialPlayheadTime, @@ -49,7 +56,7 @@ export function useTimelineZoom({ hasInitializedRef.current = true; return Math.max( minZoom, - Math.min(TIMELINE_CONSTANTS.ZOOM_MAX, initialZoom), + Math.min(TIMELINE_ZOOM_MAX, initialZoom), ); } return minZoom; @@ -91,7 +98,7 @@ export function useTimelineZoom({ setZoomLevel((prev) => { const nextZoom = Math.max( minZoom, - Math.min(TIMELINE_CONSTANTS.ZOOM_MAX, prev * zoomFactor), + Math.min(TIMELINE_ZOOM_MAX, prev * zoomFactor), ); return nextZoom; }); @@ -105,7 +112,7 @@ export function useTimelineZoom({ if (initialZoom !== undefined && !hasInitializedRef.current) { hasInitializedRef.current = true; setZoomLevel( - Math.max(minZoom, Math.min(TIMELINE_CONSTANTS.ZOOM_MAX, initialZoom)), + Math.max(minZoom, Math.min(TIMELINE_ZOOM_MAX, initialZoom)), ); return; } @@ -126,7 +133,7 @@ export function useTimelineZoom({ : zoomLevelOrUpdater; const clampedZoom = Math.max( minZoom, - Math.min(TIMELINE_CONSTANTS.ZOOM_MAX, nextZoom), + Math.min(TIMELINE_ZOOM_MAX, nextZoom), ); return clampedZoom; }); @@ -153,12 +160,12 @@ export function useTimelineZoom({ }); const isCrossingThresholdUp = previousSliderPercent < - TIMELINE_CONSTANTS.ZOOM_ANCHOR_PLAYHEAD_THRESHOLD && - sliderPercent >= TIMELINE_CONSTANTS.ZOOM_ANCHOR_PLAYHEAD_THRESHOLD; + TIMELINE_ZOOM_ANCHOR_PLAYHEAD_THRESHOLD && + sliderPercent >= TIMELINE_ZOOM_ANCHOR_PLAYHEAD_THRESHOLD; const isCrossingThresholdDown = previousSliderPercent >= - TIMELINE_CONSTANTS.ZOOM_ANCHOR_PLAYHEAD_THRESHOLD && - sliderPercent < TIMELINE_CONSTANTS.ZOOM_ANCHOR_PLAYHEAD_THRESHOLD; + TIMELINE_ZOOM_ANCHOR_PLAYHEAD_THRESHOLD && + sliderPercent < TIMELINE_ZOOM_ANCHOR_PLAYHEAD_THRESHOLD; const syncScroll = (scrollLeft: number) => { scrollElement.scrollLeft = scrollLeft; @@ -178,11 +185,11 @@ export function useTimelineZoom({ isInPlayheadAnchorModeRef.current = true; } - if (sliderPercent >= TIMELINE_CONSTANTS.ZOOM_ANCHOR_PLAYHEAD_THRESHOLD) { + if (sliderPercent >= TIMELINE_ZOOM_ANCHOR_PLAYHEAD_THRESHOLD) { const playheadPixelsBefore = - playheadTime * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * previousZoom; + playheadTime * BASE_TIMELINE_PIXELS_PER_SECOND * previousZoom; const playheadPixelsAfter = - playheadTime * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel; + playheadTime * BASE_TIMELINE_PIXELS_PER_SECOND * zoomLevel; const viewportOffset = playheadPixelsBefore - currentScrollLeft; const newScrollLeft = playheadPixelsAfter - viewportOffset; diff --git a/apps/web/src/hooks/use-paste-media.ts b/apps/web/src/hooks/use-paste-media.ts index 2c9c43ea..1cba34c0 100644 --- a/apps/web/src/hooks/use-paste-media.ts +++ b/apps/web/src/hooks/use-paste-media.ts @@ -7,7 +7,7 @@ import { buildElementFromMedia } from "@/lib/timeline/element-utils"; import { AddMediaAssetCommand } from "@/lib/commands/media";
import { InsertElementCommand } from "@/lib/commands/timeline";
import { BatchCommand } from "@/lib/commands";
-import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants";
+import { DEFAULT_NEW_ELEMENT_DURATION_SECONDS } from "@/lib/timeline/creation";
import { isTypableDOMElement } from "@/utils/browser";
import type { MediaType } from "@/lib/media/types";
@@ -74,7 +74,7 @@ export function usePasteMedia() { );
const assetId = addMediaCmd.getAssetId();
const duration =
- asset.duration ?? TIMELINE_CONSTANTS.DEFAULT_ELEMENT_DURATION;
+ asset.duration ?? DEFAULT_NEW_ELEMENT_DURATION_SECONDS;
const trackType = asset.type === "audio" ? "audio" : "video";
const element = buildElementFromMedia({
|
