diff --git a/src/components/video-editor/timeline/Item.tsx b/src/components/video-editor/timeline/Item.tsx index 27bac665..82b85145 100644 --- a/src/components/video-editor/timeline/Item.tsx +++ b/src/components/video-editor/timeline/Item.tsx @@ -82,18 +82,6 @@ export default function Item({ ? glassStyles.glassDarkGreen : glassStyles.glassYellow; - const endCapColor = isZoom - ? "#a855f7" - : isTrim - ? "#ef4444" - : isClip - ? "#06b6d4" - : isSpeed - ? "#d97706" - : isAudio - ? "#166534" - : "#B4A046"; - const timeLabel = useMemo( () => `${formatMs(span.start)} – ${formatMs(span.end)}`, [span.start, span.end], @@ -147,24 +135,12 @@ export default function Item({ >
{/* Content */} diff --git a/src/components/video-editor/timeline/ItemGlass.module.css b/src/components/video-editor/timeline/ItemGlass.module.css index d6bc8fcb..048c7602 100644 --- a/src/components/video-editor/timeline/ItemGlass.module.css +++ b/src/components/video-editor/timeline/ItemGlass.module.css @@ -178,19 +178,12 @@ } .glassGreen:hover .zoomEndCap, -.glassGreen.selected .zoomEndCap, .glassRed:hover .zoomEndCap, -.glassRed.selected .zoomEndCap, .glassYellow:hover .zoomEndCap, -.glassYellow.selected .zoomEndCap, .glassAmber:hover .zoomEndCap, -.glassAmber.selected .zoomEndCap, .glassPurple:hover .zoomEndCap, -.glassPurple.selected .zoomEndCap, .glassCyan:hover .zoomEndCap, -.glassCyan.selected .zoomEndCap, -.glassDarkGreen:hover .zoomEndCap, -.glassDarkGreen.selected .zoomEndCap { +.glassDarkGreen:hover .zoomEndCap { opacity: 1; } diff --git a/src/components/video-editor/timeline/TimelineEditor.tsx b/src/components/video-editor/timeline/TimelineEditor.tsx index e1438824..cc0efa36 100644 --- a/src/components/video-editor/timeline/TimelineEditor.tsx +++ b/src/components/video-editor/timeline/TimelineEditor.tsx @@ -64,10 +64,8 @@ import { type AudioPeaksData, useAudioPeaks } from "./useAudioPeaks"; import { buildInteractionZoomSuggestions } from "./zoomSuggestionUtils"; const ZOOM_ROW_ID = "row-zoom"; -const TRIM_ROW_ID = "row-trim"; const CLIP_ROW_ID = "row-clip"; const ANNOTATION_ROW_ID = "row-annotation"; -const SPEED_ROW_ID = "row-speed"; const AUDIO_ROW_ID = "row-audio"; const ANNOTATION_ROW_PREFIX = `${ANNOTATION_ROW_ID}-`; const AUDIO_ROW_PREFIX = "row-audio-"; @@ -493,7 +491,7 @@ function TimelineAxis({ return (
); @@ -510,18 +508,19 @@ function TimelineAxis({ flexDirection: "row", alignItems: "flex-end", [sideProperty]: `${offset}px`, + transform: "translateX(-50%)", }; return (
-
+
{marker.label} @@ -534,6 +533,52 @@ function TimelineAxis({ ); } +function ClipMarkerOverlay({ videoDurationMs }: { videoDurationMs: number }) { + const { direction, range, valueToPixels } = useTimelineContext(); + const sideProperty = direction === "rtl" ? "right" : "left"; + + const { intervalMs } = useMemo( + () => calculateAxisScale(range.end - range.start), + [range.end, range.start], + ); + + const markers = useMemo(() => { + if (intervalMs <= 0) return []; + const maxTime = videoDurationMs > 0 ? videoDurationMs : range.end; + const visibleStart = Math.max(0, range.start); + const visibleEnd = Math.min(range.end, maxTime); + const firstMarker = Math.ceil(visibleStart / intervalMs) * intervalMs; + const result: { time: number; offset: number }[] = []; + for (let time = firstMarker; time <= maxTime; time += intervalMs) { + if (time > visibleStart && time < visibleEnd) { + result.push({ + time: Math.round(time), + offset: valueToPixels(Math.round(time) - range.start), + }); + } + } + return result; + }, [intervalMs, range.start, range.end, videoDurationMs, valueToPixels]); + + return ( +
+ {markers.map(({ time, offset }) => ( +
+ ))} +
+ ); +} + function Timeline({ items, videoDurationMs, @@ -630,11 +675,8 @@ function Timeline({ ); const zoomItems = items.filter((item) => item.rowId === ZOOM_ROW_ID); - const trimItems = items.filter((item) => item.rowId === TRIM_ROW_ID); - const clipItems = items.filter((item) => item.rowId === CLIP_ROW_ID); const annotationItems = items.filter((item) => isAnnotationTrackRowId(item.rowId)); - const speedItems = items.filter((item) => item.rowId === SPEED_ROW_ID); const audioItems = items.filter((item) => isAudioTrackRowId(item.rowId)); const audioRowIds = useMemo( () => @@ -677,6 +719,7 @@ function Timeline({
{audioPeaks && } + {clipItems.map((item) => ( - - {trimItems.map((item) => ( - onSelectTrim?.(item.id)} - variant="trim" - > - {item.label} - - ))} - - {zoomItems.map((item) => ( - - {speedItems.map((item) => ( - onSelectSpeed?.(item.id)} - speedValue={item.speedValue} - variant="speed" - > - {item.label} - - ))} - - {annotationRowIds.map((rowId, index) => { const rowItems = annotationItems.filter( (item) => @@ -1726,14 +1732,6 @@ const TimelineEditor = forwardRef( variant: "zoom", })); - const trims: TimelineRenderItem[] = trimRegions.map((region, index) => ({ - id: region.id, - rowId: TRIM_ROW_ID, - span: { start: region.startMs, end: region.endMs }, - label: `Trim ${index + 1}`, - variant: "trim", - })); - const clips: TimelineRenderItem[] = clipRegions.map((region, index) => ({ id: region.id, rowId: CLIP_ROW_ID, @@ -1764,15 +1762,6 @@ const TimelineEditor = forwardRef( }; }); - const speeds: TimelineRenderItem[] = speedRegions.map((region, index) => ({ - id: region.id, - rowId: SPEED_ROW_ID, - span: { start: region.startMs, end: region.endMs }, - label: `Speed ${index + 1}`, - speedValue: region.speed, - variant: "speed", - })); - const audios: TimelineRenderItem[] = audioRegions.map((region) => { const fileName = region.audioPath @@ -1788,8 +1777,8 @@ const TimelineEditor = forwardRef( }; }); - return [...zooms, ...trims, ...clips, ...annotations, ...speeds, ...audios]; - }, [zoomRegions, trimRegions, clipRegions, annotationRegions, speedRegions, audioRegions]); + return [...zooms, ...clips, ...annotations, ...audios]; + }, [zoomRegions, clipRegions, annotationRegions, audioRegions]); // Flat list of draggable row spans for neighbour-clamping during drag/resize. const allRegionSpans = useMemo(() => { @@ -1799,32 +1788,20 @@ const TimelineEditor = forwardRef( end: r.endMs, rowId: ZOOM_ROW_ID, })); - const trims = trimRegions.map((r) => ({ - id: r.id, - start: r.startMs, - end: r.endMs, - rowId: TRIM_ROW_ID, - })); const clips = clipRegions.map((r) => ({ id: r.id, start: r.startMs, end: r.endMs, rowId: CLIP_ROW_ID, })); - const speeds = speedRegions.map((r) => ({ - id: r.id, - start: r.startMs, - end: r.endMs, - rowId: SPEED_ROW_ID, - })); const audios = audioRegions.map((r) => ({ id: r.id, start: r.startMs, end: r.endMs, rowId: getAudioTrackRowId(r.trackIndex ?? 0), })); - return [...zooms, ...trims, ...clips, ...speeds, ...audios]; - }, [zoomRegions, trimRegions, clipRegions, speedRegions, audioRegions]); + return [...zooms, ...clips, ...audios]; + }, [zoomRegions, clipRegions, audioRegions]); const handleItemSpanChange = useCallback( (id: string, span: Span) => {