diff options
| author | Maze Winther <mazewinther@gmail.com> | 2025-06-24 01:49:12 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2025-06-24 01:49:12 +0200 |
| commit | 5bff26d3e51d775bb44b6c9fa1337570642d284d (patch) | |
| tree | d82908098f4f1438fbd578b063c97ace94460694 /apps/web/src/stores | |
| parent | 69566ffa83b9f192397073f37de0d9a8a9437fef (diff) | |
| parent | a6a520db70ef639e066d1012fa2ecf7e26c53049 (diff) | |
| download | opencut-5bff26d3e51d775bb44b6c9fa1337570642d284d.tar.gz | |
Merge branch 'main' of https://github.com/mazeincoding/AppCut
Diffstat (limited to 'apps/web/src/stores')
| -rw-r--r-- | apps/web/src/stores/playback-store.ts | 34 | ||||
| -rw-r--r-- | apps/web/src/stores/timeline-store.ts | 34 |
2 files changed, 61 insertions, 7 deletions
diff --git a/apps/web/src/stores/playback-store.ts b/apps/web/src/stores/playback-store.ts index 3c0bfdec..a4794b7f 100644 --- a/apps/web/src/stores/playback-store.ts +++ b/apps/web/src/stores/playback-store.ts @@ -6,15 +6,20 @@ interface PlaybackStore extends PlaybackState, PlaybackControls { setCurrentTime: (time: number) => void; } -let playbackTimer: NodeJS.Timeout | null = null; +let playbackTimer: number | null = null; const startTimer = (store: any) => { - if (playbackTimer) clearInterval(playbackTimer); + if (playbackTimer) cancelAnimationFrame(playbackTimer); - playbackTimer = setInterval(() => { + // Use requestAnimationFrame for smoother updates + const updateTime = () => { const state = store(); if (state.isPlaying && state.currentTime < state.duration) { - const newTime = state.currentTime + 0.1; + const now = performance.now(); + const delta = (now - lastUpdate) / 1000; // Convert to seconds + lastUpdate = now; + + const newTime = state.currentTime + (delta * state.speed); if (newTime >= state.duration) { state.pause(); } else { @@ -23,12 +28,16 @@ const startTimer = (store: any) => { window.dispatchEvent(new CustomEvent('playback-update', { detail: { time: newTime } })); } } - }, 100); + playbackTimer = requestAnimationFrame(updateTime); + }; + + let lastUpdate = performance.now(); + playbackTimer = requestAnimationFrame(updateTime); }; const stopTimer = () => { if (playbackTimer) { - clearInterval(playbackTimer); + cancelAnimationFrame(playbackTimer); playbackTimer = null; } }; @@ -38,6 +47,7 @@ export const usePlaybackStore = create<PlaybackStore>((set, get) => ({ currentTime: 0, duration: 0, volume: 1, + speed: 1.0, play: () => { set({ isPlaying: true }); @@ -64,10 +74,20 @@ export const usePlaybackStore = create<PlaybackStore>((set, get) => ({ set({ currentTime: clampedTime }); // Notify video elements to seek - window.dispatchEvent(new CustomEvent('playback-seek', { detail: { time: clampedTime } })); + const event = new CustomEvent('playback-seek', { detail: { time: clampedTime } }); + window.dispatchEvent(event); }, setVolume: (volume: number) => set({ volume: Math.max(0, Math.min(1, volume)) }), + + setSpeed: (speed: number) => { + const newSpeed = Math.max(0.1, Math.min(2.0, speed)); + set({ speed: newSpeed }); + + const event = new CustomEvent('playback-speed', { detail: { speed: newSpeed } }); + window.dispatchEvent(event); + }, + setDuration: (duration: number) => set({ duration }), setCurrentTime: (time: number) => set({ currentTime: time }), }));
\ No newline at end of file diff --git a/apps/web/src/stores/timeline-store.ts b/apps/web/src/stores/timeline-store.ts index c569ae11..4b55a53d 100644 --- a/apps/web/src/stores/timeline-store.ts +++ b/apps/web/src/stores/timeline-store.ts @@ -21,6 +21,13 @@ export interface TimelineTrack { interface TimelineStore {
tracks: TimelineTrack[];
+ // Multi-selection
+ selectedClips: { trackId: string; clipId: string }[];
+ 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;
removeTrack: (trackId: string) => void;
@@ -50,6 +57,33 @@ interface TimelineStore { export const useTimelineStore = create<TimelineStore>((set, get) => ({
tracks: [],
+ selectedClips: [],
+
+ 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)),
+ }));
+ },
+ clearSelectedClips: () => {
+ set({ selectedClips: [] });
+ },
+
+ setSelectedClips: (clips) => set({ selectedClips: clips }),
addTrack: (type) => {
const newTrack: TimelineTrack = {
|
