From d605cd762cc3c784d908d36a12605d7d3547f1f1 Mon Sep 17 00:00:00 2001 From: aashishparuvada Date: Mon, 23 Jun 2025 22:49:56 +0530 Subject: hotfix:fixed-cmd-for-deselection --- apps/web/src/components/editor/timeline.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'apps/web/src/components/editor') diff --git a/apps/web/src/components/editor/timeline.tsx b/apps/web/src/components/editor/timeline.tsx index 675b398c..b95f9839 100644 --- a/apps/web/src/components/editor/timeline.tsx +++ b/apps/web/src/components/editor/timeline.tsx @@ -1409,9 +1409,18 @@ function TimelineTrackContent({ style={{ width: `${clipWidth}px`, left: `${clipLeft}px` }} onClick={(e) => { e.stopPropagation(); + const isSelected = selectedClips.some( + (c) => c.trackId === track.id && c.clipId === clip.id + ); + if (e.metaKey || e.ctrlKey || e.shiftKey) { + // Multi-selection mode: toggle the clip selectClip(track.id, clip.id, true); + } else if (isSelected) { + // If clip is already selected, deselect it + deselectClip(track.id, clip.id); } else { + // If clip is not selected, select it (replacing other selections) selectClip(track.id, clip.id, false); } }} -- cgit v1.3.1 From 4a9b102ce1d6824167cdb57f514b66d115bf016e Mon Sep 17 00:00:00 2001 From: aashishparuvada Date: Mon, 23 Jun 2025 23:26:41 +0530 Subject: feature:deselect-clips-on-click --- apps/web/src/components/editor/timeline.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'apps/web/src/components/editor') diff --git a/apps/web/src/components/editor/timeline.tsx b/apps/web/src/components/editor/timeline.tsx index b95f9839..d6a6d627 100644 --- a/apps/web/src/components/editor/timeline.tsx +++ b/apps/web/src/components/editor/timeline.tsx @@ -299,9 +299,13 @@ export function Timeline() { }; const handleWheel = (e: React.WheelEvent) => { - e.preventDefault(); - const delta = e.deltaY > 0 ? -0.05 : 0.05; - setZoomLevel((prev) => Math.max(0.1, Math.min(10, prev + delta))); + // 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.05 : 0.05; + setZoomLevel((prev) => Math.max(0.1, Math.min(10, prev + delta))); + } + // Otherwise, allow normal scrolling }; const dragProps = { @@ -620,8 +624,7 @@ export function Timeline() { className="relative cursor-pointer select-none" style={{ width: `${Math.max(1000, duration * 50 * zoomLevel)}px`, - minHeight: - tracks.length > 0 ? `${tracks.length * 60}px` : "200px", + minHeight: '600px', // Always at least 600px tall for easy empty area clicking }} onClick={handleTimelineAreaClick} onMouseDown={handleTimelineMouseDown} -- cgit v1.3.1 From f17d848b170361970d118074ded3553d6910f939 Mon Sep 17 00:00:00 2001 From: aashishparuvada Date: Mon, 23 Jun 2025 23:48:15 +0530 Subject: feat:assigned-actions-for-selected-clips --- apps/web/src/components/editor/timeline.tsx | 101 +++++++++++++++++++++++++--- 1 file changed, 93 insertions(+), 8 deletions(-) (limited to 'apps/web/src/components/editor') diff --git a/apps/web/src/components/editor/timeline.tsx b/apps/web/src/components/editor/timeline.tsx index d6a6d627..967951e3 100644 --- a/apps/web/src/components/editor/timeline.tsx +++ b/apps/web/src/components/editor/timeline.tsx @@ -40,7 +40,7 @@ export function Timeline() { // Timeline shows all tracks (video, audio, effects) and their clips. // You can drag media here to add it to your project. // Clips can be trimmed, deleted, and moved. - const { tracks, addTrack, addClipToTrack, removeTrack, toggleTrackMute, removeClipFromTrack, moveClipToTrack, getTotalDuration, selectedClips, selectClip, deselectClip, clearSelectedClips, setSelectedClips } = + const { tracks, addTrack, addClipToTrack, removeTrack, toggleTrackMute, removeClipFromTrack, moveClipToTrack, getTotalDuration, selectedClips, selectClip, deselectClip, clearSelectedClips, setSelectedClips, updateClipTrim } = useTimelineStore(); const { mediaItems, addMediaItem } = useMediaStore(); const { currentTime, duration, seek, setDuration, isPlaying, play, pause, toggle, setSpeed, speed } = usePlaybackStore(); @@ -315,6 +315,92 @@ export function Timeline() { onDrop: handleDrop, }; + // Action handlers for toolbar + const handleSplitSelected = () => { + if (selectedClips.length === 0) { + toast.error("No clips selected"); + return; + } + selectedClips.forEach(({ trackId, clipId }) => { + const track = tracks.find(t => t.id === trackId); + const clip = track?.clips.find(c => c.id === clipId); + if (clip && track) { + const splitTime = currentTime; + const effectiveStart = clip.startTime; + const effectiveEnd = clip.startTime + (clip.duration - clip.trimStart - clip.trimEnd); + if (splitTime > effectiveStart && splitTime < effectiveEnd) { + updateClipTrim(track.id, clip.id, clip.trimStart, clip.trimEnd + (effectiveEnd - splitTime)); + addClipToTrack(track.id, { + mediaId: clip.mediaId, + name: clip.name + " (split)", + duration: clip.duration, + startTime: splitTime, + trimStart: clip.trimStart + (splitTime - effectiveStart), + trimEnd: clip.trimEnd, + }); + } + } + }); + toast.success("Split selected clip(s)"); + }; + + const handleDuplicateSelected = () => { + if (selectedClips.length === 0) { + toast.error("No clips selected"); + return; + } + selectedClips.forEach(({ trackId, clipId }) => { + const track = tracks.find(t => t.id === trackId); + const clip = track?.clips.find(c => c.id === clipId); + if (clip && track) { + addClipToTrack(track.id, { + mediaId: clip.mediaId, + name: clip.name + " (copy)", + duration: clip.duration, + startTime: clip.startTime + (clip.duration - clip.trimStart - clip.trimEnd) + 0.1, + trimStart: clip.trimStart, + trimEnd: clip.trimEnd, + }); + } + }); + toast.success("Duplicated selected clip(s)"); + }; + + const handleFreezeSelected = () => { + if (selectedClips.length === 0) { + toast.error("No clips selected"); + return; + } + selectedClips.forEach(({ trackId, clipId }) => { + const track = tracks.find(t => t.id === trackId); + const clip = track?.clips.find(c => c.id === clipId); + if (clip && track) { + // Add a new freeze frame clip at the playhead + addClipToTrack(track.id, { + mediaId: clip.mediaId, + name: clip.name + " (freeze)", + duration: 1, // 1 second freeze frame + startTime: currentTime, + trimStart: 0, + trimEnd: clip.duration - 1, + }); + } + }); + toast.success("Freeze frame added for selected clip(s)"); + }; + + const handleDeleteSelected = () => { + if (selectedClips.length === 0) { + toast.error("No clips selected"); + return; + } + selectedClips.forEach(({ trackId, clipId }) => { + removeClipFromTrack(trackId, clipId); + }); + clearSelectedClips(); + toast.success("Deleted selected clip(s)"); + }; + return (
- @@ -423,7 +509,7 @@ export function Timeline() { - @@ -432,7 +518,7 @@ export function Timeline() { - @@ -441,7 +527,7 @@ export function Timeline() { - @@ -748,7 +834,7 @@ export function Timeline() { const effectiveEnd = clip.startTime + (clip.duration - clip.trimStart - clip.trimEnd); if (splitTime > effectiveStart && splitTime < effectiveEnd) { - useTimelineStore.getState().updateClipTrim( + updateClipTrim( track.id, clip.id, clip.trimStart, @@ -1496,8 +1582,7 @@ function TimelineTrackContent({ {/* Drop position indicator */} {isDraggedOver && dropPosition !== null && (
Date: Mon, 23 Jun 2025 23:53:18 +0530 Subject: hotfix:disabled-vertical-scrolling-in-timeline --- apps/web/src/components/editor/timeline.tsx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'apps/web/src/components/editor') diff --git a/apps/web/src/components/editor/timeline.tsx b/apps/web/src/components/editor/timeline.tsx index 967951e3..fb94b374 100644 --- a/apps/web/src/components/editor/timeline.tsx +++ b/apps/web/src/components/editor/timeline.tsx @@ -703,14 +703,14 @@ export function Timeline() {
{/* Timeline Tracks Content */} -
- +
+
+ {/* Timeline grid and clips area (with left margin for sidebar) */}
-
))} @@ -768,7 +767,7 @@ export function Timeline() { )}
- +
-- cgit v1.3.1 From 59ca411c534f17b038ee5a074f981cc26ffffce4 Mon Sep 17 00:00:00 2001 From: SamSyntax Date: Mon, 23 Jun 2025 21:13:18 +0200 Subject: Implementing playhead scrubbing over timeline --- apps/web/bun.lockb | Bin 0 -> 185948 bytes apps/web/src/components/editor/timeline.tsx | 24 +++++++++++++++++++++--- apps/web/src/components/header.tsx | 3 ++- apps/web/src/lib/media-processing.ts | 1 + 4 files changed, 24 insertions(+), 4 deletions(-) create mode 100755 apps/web/bun.lockb (limited to 'apps/web/src/components/editor') diff --git a/apps/web/bun.lockb b/apps/web/bun.lockb new file mode 100755 index 00000000..30e5a2f5 Binary files /dev/null and b/apps/web/bun.lockb differ diff --git a/apps/web/src/components/editor/timeline.tsx b/apps/web/src/components/editor/timeline.tsx index 675b398c..83ff5904 100644 --- a/apps/web/src/components/editor/timeline.tsx +++ b/apps/web/src/components/editor/timeline.tsx @@ -557,8 +557,26 @@ export function Timeline() { {/* Playhead in ruler */}
{ + e.preventDefault(); + e.stopPropagation(); + const handleMouseMove = (e: MouseEvent) => { + const timeline = timelineRef.current; + if (!timeline) return; + const rect = timeline.getBoundingClientRect(); + const mouseX = Math.max(0, e.clientX - rect.left); + const newTime = mouseX / (50 * zoomLevel); + seek(newTime); + }; + const handleMouseUp = () => { + window.removeEventListener("mousemove", handleMouseMove); + window.removeEventListener("mouseup", handleMouseUp); + }; + window.addEventListener("mousemove", handleMouseMove); + window.addEventListener("mouseup", handleMouseUp); + }} >
@@ -670,7 +688,7 @@ export function Timeline() { {/* Playhead for tracks area */}
); -} \ No newline at end of file +} diff --git a/apps/web/src/components/header.tsx b/apps/web/src/components/header.tsx index 109d022e..86ff8785 100644 --- a/apps/web/src/components/header.tsx +++ b/apps/web/src/components/header.tsx @@ -23,7 +23,8 @@ export function Header() { GitHub - + {/* */} +