summaryrefslogtreecommitdiff
path: root/apps/web/src/stores
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/src/stores')
-rw-r--r--apps/web/src/stores/playback-store.ts30
-rw-r--r--apps/web/src/stores/timeline-store.ts80
2 files changed, 82 insertions, 28 deletions
diff --git a/apps/web/src/stores/playback-store.ts b/apps/web/src/stores/playback-store.ts
new file mode 100644
index 00000000..7db1fb93
--- /dev/null
+++ b/apps/web/src/stores/playback-store.ts
@@ -0,0 +1,30 @@
+import { create } from "zustand";
+import type { PlaybackState, PlaybackControls } from "@/types/playback";
+
+interface PlaybackStore extends PlaybackState, PlaybackControls {
+ setDuration: (duration: number) => void;
+ setCurrentTime: (time: number) => void;
+}
+
+export const usePlaybackStore = create<PlaybackStore>((set, get) => ({
+ isPlaying: false,
+ currentTime: 0,
+ duration: 0,
+ volume: 1,
+
+ play: () => set({ isPlaying: true }),
+ pause: () => set({ isPlaying: false }),
+ toggle: () => set((state) => ({ isPlaying: !state.isPlaying })),
+ seek: (time: number) => {
+ const { duration } = get();
+ const clampedTime = Math.max(0, Math.min(duration, time));
+ set({ currentTime: clampedTime });
+
+ // Notify video element to seek
+ const event = new CustomEvent('playback-seek', { detail: { time: clampedTime } });
+ window.dispatchEvent(event);
+ },
+ setVolume: (volume: number) => set({ volume: Math.max(0, Math.min(1, volume)) }),
+ 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 12129afa..4b56e6bd 100644
--- a/apps/web/src/stores/timeline-store.ts
+++ b/apps/web/src/stores/timeline-store.ts
@@ -5,6 +5,9 @@ export interface TimelineClip {
mediaId: string;
name: string;
duration: number;
+ startTime: number;
+ trimStart: number;
+ trimEnd: number;
}
export interface TimelineTrack {
@@ -25,13 +28,18 @@ interface TimelineStore {
moveClipToTrack: (
fromTrackId: string,
toTrackId: string,
+ clipId: string
+ ) => void;
+ updateClipTrim: (
+ trackId: string,
clipId: string,
- insertIndex?: number
+ trimStart: number,
+ trimEnd: number
) => void;
- reorderClipInTrack: (
+ updateClipStartTime: (
trackId: string,
clipId: string,
- newIndex: number
+ startTime: number
) => void;
// Computed values
@@ -64,6 +72,9 @@ export const useTimelineStore = create<TimelineStore>((set, get) => ({
const newClip: TimelineClip = {
...clipData,
id: crypto.randomUUID(),
+ startTime: clipData.startTime || 0,
+ trimStart: 0,
+ trimEnd: 0,
};
set((state) => ({
@@ -88,9 +99,8 @@ export const useTimelineStore = create<TimelineStore>((set, get) => ({
}));
},
- moveClipToTrack: (fromTrackId, toTrackId, clipId, insertIndex) => {
+ moveClipToTrack: (fromTrackId, toTrackId, clipId) => {
set((state) => {
- // Find the clip to move
const fromTrack = state.tracks.find((track) => track.id === fromTrackId);
const clipToMove = fromTrack?.clips.find((clip) => clip.id === clipId);
@@ -99,20 +109,14 @@ export const useTimelineStore = create<TimelineStore>((set, get) => ({
return {
tracks: state.tracks.map((track) => {
if (track.id === fromTrackId) {
- // Remove clip from source track
return {
...track,
clips: track.clips.filter((clip) => clip.id !== clipId),
};
} else if (track.id === toTrackId) {
- // Add clip to destination track
- const newClips = [...track.clips];
- const index =
- insertIndex !== undefined ? insertIndex : newClips.length;
- newClips.splice(index, 0, clipToMove);
return {
...track,
- clips: newClips,
+ clips: [...track.clips, clipToMove],
};
}
return track;
@@ -121,20 +125,39 @@ export const useTimelineStore = create<TimelineStore>((set, get) => ({
});
},
- reorderClipInTrack: (trackId, clipId, newIndex) => {
- set((state) => ({
- tracks: state.tracks.map((track) => {
- if (track.id !== trackId) return track;
- const clipIndex = track.clips.findIndex((clip) => clip.id === clipId);
- if (clipIndex === -1) return track;
- const newClips = [...track.clips];
- const [movedClip] = newClips.splice(clipIndex, 1);
- newClips.splice(newIndex, 0, movedClip);
+ updateClipTrim: (trackId, clipId, trimStart, trimEnd) => {
+ set((state) => ({
+ tracks: state.tracks.map((track) =>
+ track.id === trackId
+ ? {
+ ...track,
+ clips: track.clips.map((clip) =>
+ clip.id === clipId
+ ? { ...clip, trimStart, trimEnd }
+ : clip
+ ),
+ }
+ : track
+ ),
+ }));
+ },
- return { ...track, clips: newClips };
- }),
+ updateClipStartTime: (trackId, clipId, startTime) => {
+ set((state) => ({
+ tracks: state.tracks.map((track) =>
+ track.id === trackId
+ ? {
+ ...track,
+ clips: track.clips.map((clip) =>
+ clip.id === clipId
+ ? { ...clip, startTime }
+ : clip
+ ),
+ }
+ : track
+ ),
}));
},
@@ -142,12 +165,13 @@ export const useTimelineStore = create<TimelineStore>((set, get) => ({
const { tracks } = get();
if (tracks.length === 0) return 0;
- // Calculate the duration of each track (sum of all clips in that track)
- const trackDurations = tracks.map((track) =>
- track.clips.reduce((total, clip) => total + clip.duration, 0)
+ const trackEndTimes = tracks.map((track) =>
+ track.clips.reduce((maxEnd, clip) => {
+ const clipEnd = clip.startTime + clip.duration - clip.trimStart - clip.trimEnd;
+ return Math.max(maxEnd, clipEnd);
+ }, 0)
);
- // Return the maximum track duration (longest track determines project duration)
- return Math.max(...trackDurations, 0);
+ return Math.max(...trackEndTimes, 0);
},
}));