summaryrefslogtreecommitdiff
path: root/apps/web/src/stores
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2025-06-24 01:49:12 +0200
committerMaze Winther <mazewinther@gmail.com>2025-06-24 01:49:12 +0200
commit5bff26d3e51d775bb44b6c9fa1337570642d284d (patch)
treed82908098f4f1438fbd578b063c97ace94460694 /apps/web/src/stores
parent69566ffa83b9f192397073f37de0d9a8a9437fef (diff)
parenta6a520db70ef639e066d1012fa2ecf7e26c53049 (diff)
downloadopencut-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.ts34
-rw-r--r--apps/web/src/stores/timeline-store.ts34
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 = {