From 630250c23b1cd8ec608c95334ae2babf8e7a00af Mon Sep 17 00:00:00 2001 From: aashishparuvada Date: Mon, 23 Jun 2025 20:51:36 +0530 Subject: enabled-multiclip-selection --- apps/web/src/stores/timeline-store.ts | 36 ++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) (limited to 'apps/web/src/stores') diff --git a/apps/web/src/stores/timeline-store.ts b/apps/web/src/stores/timeline-store.ts index ea6fc35e..c6ecaea5 100644 --- a/apps/web/src/stores/timeline-store.ts +++ b/apps/web/src/stores/timeline-store.ts @@ -21,10 +21,11 @@ export interface TimelineTrack { interface TimelineStore { tracks: TimelineTrack[]; - // Selection - selectedClip: { trackId: string; clipId: string } | null; - selectClip: (trackId: string, clipId: string) => void; - clearSelectedClip: () => void; + // Multi-selection + selectedClips: { trackId: string; clipId: string }[]; + selectClip: (trackId: string, clipId: string, multi?: boolean) => void; + deselectClip: (trackId: string, clipId: string) => void; + clearSelectedClips: () => void; // Actions addTrack: (type: "video" | "audio" | "effects") => string; @@ -55,13 +56,30 @@ interface TimelineStore { export const useTimelineStore = create((set, get) => ({ tracks: [], - selectedClip: null, + selectedClips: [], - selectClip: (trackId, clipId) => { - set({ selectedClip: { trackId, clipId } }); + selectClip: (trackId, clipId, multi = false) => { + set((state) => { + const exists = state.selectedClips.some( + (c) => c.trackId === trackId && c.clipId === clipId + ); + if (multi) { + // Toggle selection + return exists + ? { selectedClips: state.selectedClips.filter((c) => !(c.trackId === trackId && c.clipId === clipId)) } + : { selectedClips: [...state.selectedClips, { trackId, clipId }] }; + } else { + return { selectedClips: [{ trackId, clipId }] }; + } + }); + }, + deselectClip: (trackId, clipId) => { + set((state) => ({ + selectedClips: state.selectedClips.filter((c) => !(c.trackId === trackId && c.clipId === clipId)), + })); }, - clearSelectedClip: () => { - set({ selectedClip: null }); + clearSelectedClips: () => { + set({ selectedClips: [] }); }, addTrack: (type) => { -- cgit v1.3.1 From f44f0acf7024febdbbecbbf10772b9b9d3863b4f Mon Sep 17 00:00:00 2001 From: aashishparuvada Date: Mon, 23 Jun 2025 21:16:21 +0530 Subject: hotfix-improved-selection-standards --- apps/web/src/components/editor/timeline.tsx | 10 +++------- apps/web/src/stores/timeline-store.ts | 3 +++ 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'apps/web/src/stores') diff --git a/apps/web/src/components/editor/timeline.tsx b/apps/web/src/components/editor/timeline.tsx index 1120fbe0..e824d42e 100644 --- a/apps/web/src/components/editor/timeline.tsx +++ b/apps/web/src/components/editor/timeline.tsx @@ -33,7 +33,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 } = + const { tracks, addTrack, addClipToTrack, removeTrack, toggleTrackMute, removeClipFromTrack, moveClipToTrack, getTotalDuration, selectedClips, selectClip, deselectClip, clearSelectedClips, setSelectedClips } = useTimelineStore(); const { mediaItems, addMediaItem } = useMediaStore(); const { currentTime, duration, seek, setDuration, isPlaying, play, pause, toggle } = usePlaybackStore(); @@ -156,20 +156,18 @@ export function Timeline() { }); if (newSelection.length > 0) { if (marquee.additive) { - // Add to current selection const current = new Set(selectedClips.map((c) => c.trackId + ":" + c.clipId)); newSelection = [ ...selectedClips, ...newSelection.filter((c) => !current.has(c.trackId + ":" + c.clipId)), ]; } - clearSelectedClips(); - newSelection.forEach((c) => selectClip(c.trackId, c.clipId, true)); + setSelectedClips(newSelection); } else if (!marquee.additive) { clearSelectedClips(); } setMarquee(null); - }, [marquee, tracks, zoomLevel, selectedClips, selectClip, clearSelectedClips]); + }, [marquee, tracks, zoomLevel, selectedClips, selectClip, clearSelectedClips, setSelectedClips]); const handleDragEnter = (e: React.DragEvent) => { // When something is dragged over the timeline, show overlay @@ -1398,8 +1396,6 @@ function TimelineTrackContent({ e.stopPropagation(); if (e.metaKey || e.ctrlKey || e.shiftKey) { selectClip(track.id, clip.id, true); - } else if (isSelected) { - deselectClip(track.id, clip.id); } else { selectClip(track.id, clip.id, false); } diff --git a/apps/web/src/stores/timeline-store.ts b/apps/web/src/stores/timeline-store.ts index c6ecaea5..4b55a53d 100644 --- a/apps/web/src/stores/timeline-store.ts +++ b/apps/web/src/stores/timeline-store.ts @@ -26,6 +26,7 @@ interface TimelineStore { selectClip: (trackId: string, clipId: string, multi?: boolean) => void; deselectClip: (trackId: string, clipId: string) => void; clearSelectedClips: () => void; + setSelectedClips: (clips: { trackId: string; clipId: string }[]) => void; // Actions addTrack: (type: "video" | "audio" | "effects") => string; @@ -82,6 +83,8 @@ export const useTimelineStore = create((set, get) => ({ set({ selectedClips: [] }); }, + setSelectedClips: (clips) => set({ selectedClips: clips }), + addTrack: (type) => { const newTrack: TimelineTrack = { id: crypto.randomUUID(), -- cgit v1.3.1