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) => {