Skip to content

Commit 4dfaef2

Browse files
committed
Commit remaining workspace changes
1 parent e172186 commit 4dfaef2

13 files changed

Lines changed: 451 additions & 346 deletions

File tree

.claude/settings.local.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@
44
"Bash(find:*)",
55
"Bash(lsof:*)",
66
"Bash(npm run build)",
7-
"Bash(npx tsc --noEmit)"
7+
"Bash(npx tsc --noEmit)",
8+
"Bash(grep -rn \"SharedArrayBuffer\\\\|crossOriginIsolated\\\\|COOP\\\\|COEP\" /Users/szy/CascadeProjects/modern-ab-loop/ --include=\"*.ts\" --include=\"*.tsx\" --include=\"*.json\" 2>/dev/null | head -10)",
9+
"Bash(grep -rn \"audio/wav\\\\|audio/mp3\\\\|audio/mp4\\\\|audio/webm\\\\|audio/ogg\\\\|audio/flac\\\\|type.*audio\" /Users/szy/CascadeProjects/modern-ab-loop/src/ --include=\"*.ts\" --include=\"*.tsx\" 2>/dev/null | head -20)",
10+
"Bash(grep -rn \"file.*size\\\\|maxFileSize\\\\|25MB\\\\|25 MB\\\\|fileSize\\\\|MAX_SIZE\" /Users/szy/CascadeProjects/modern-ab-loop/src/ --include=\"*.ts\" --include=\"*.tsx\" 2>/dev/null | head -10)",
11+
"Bash(grep -rn \"local.*server\\\\|localhost\\\\|127\\\\.0\\\\.0\\\\.1\\\\|http://\\\\|proxy\\\\|API_URL\\\\|BASE_URL\\\\|VITE_\" /Users/szy/CascadeProjects/modern-ab-loop/src/ --include=\"*.ts\" --include=\"*.tsx\" 2>/dev/null | grep -v \"//.*http\\\\|node_modules\" | head -20)",
12+
"Bash(grep -rn \"worker\\\\|Worker\" /Users/szy/CascadeProjects/modern-ab-loop/src/ --include=\"*.ts\" --include=\"*.tsx\" 2>/dev/null | grep -v \"node_modules\\\\|WorkerController\\\\|service-worker\\\\|ServiceWorker\" | head -15)",
13+
"Bash(grep -rn \"sampleRate\\\\|48000\\\\|16000\\\\|44100\" /Users/szy/CascadeProjects/modern-ab-loop/src/ --include=\"*.ts\" --include=\"*.tsx\" 2>/dev/null | head -15)"
814
]
915
}
1016
}

src/components/layout/AppLayout.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useState, Dispatch, SetStateAction } from "react";
22
import { useNavigate } from "react-router-dom";
33
import { useTranslation } from "react-i18next";
44
import { usePlayerStore } from "../../stores/playerStore";
5+
import { useShallow } from "zustand/react/shallow";
56
import { SettingsDrawer } from "./SettingsDrawer";
67
import { Moon, Sun, Info, Settings, Layout, Eye, EyeOff, Music, Video, Youtube } from "lucide-react";
78
import * as Dialog from "@radix-ui/react-dialog";
@@ -31,7 +32,23 @@ export const AppLayout = ({
3132
const [isSettingsDrawerOpen, setIsSettingsDrawerOpen] = useState(false);
3233
const [isLayoutPopoverOpen, setIsLayoutPopoverOpen] = useState(false);
3334

34-
const { currentFile, currentYouTube, theme, setTheme, seekStepSeconds, seekSmallStepSeconds } = usePlayerStore();
35+
const {
36+
currentFile,
37+
currentYouTube,
38+
theme,
39+
setTheme,
40+
seekStepSeconds,
41+
seekSmallStepSeconds,
42+
} = usePlayerStore(
43+
useShallow((state) => ({
44+
currentFile: state.currentFile,
45+
currentYouTube: state.currentYouTube,
46+
theme: state.theme,
47+
setTheme: state.setTheme,
48+
seekStepSeconds: state.seekStepSeconds,
49+
seekSmallStepSeconds: state.seekSmallStepSeconds,
50+
}))
51+
);
3552

3653
// Toggle theme
3754
const toggleTheme = () => {

src/components/player/FileUploader.tsx

Lines changed: 4 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export const FileUploader = () => {
1212
const { setCurrentFile } = usePlayerStore();
1313

1414
const onDrop = useCallback(
15-
(acceptedFiles: File[]) => {
15+
async (acceptedFiles: File[]) => {
1616
if (acceptedFiles.length === 0) return;
1717

1818
const file = acceptedFiles[0];
@@ -23,80 +23,17 @@ export const FileUploader = () => {
2323
return;
2424
}
2525

26-
// Store the file in IndexedDB and get a persistent ID
2726
try {
28-
// First set with temporary URL so UI can show something immediately
27+
const storageId = await storeMediaFile(file);
2928
const tempUrl = URL.createObjectURL(file);
29+
3030
setCurrentFile({
3131
name: file.name,
3232
type: file.type,
3333
size: file.size,
3434
url: tempUrl,
35-
// We'll update the storageId after storing in IndexedDB
35+
storageId,
3636
});
37-
console.log("🎸 zy 760625 FileUploader.tsx 36 ▷", file);
38-
39-
// Store the file in IndexedDB
40-
storeMediaFile(file)
41-
.then((storageId) => {
42-
console.log("File stored successfully with ID:", storageId);
43-
44-
// Immediately test retrieving the file to verify storage
45-
import("../../utils/mediaStorage").then(({ retrieveMediaFile }) => {
46-
retrieveMediaFile(storageId)
47-
.then((retrievedFile) => {
48-
if (retrievedFile) {
49-
console.log(
50-
"File retrieval test successful:",
51-
retrievedFile
52-
);
53-
54-
// Create a new object URL from the retrieved file
55-
const retrievedUrl = URL.createObjectURL(retrievedFile);
56-
console.log(
57-
"Created new URL from retrieved file:",
58-
retrievedUrl
59-
);
60-
61-
// Update the file in the store with both the storage ID and the new URL
62-
setCurrentFile({
63-
name: file.name,
64-
type: file.type,
65-
size: file.size,
66-
url: retrievedUrl, // Use the URL from the retrieved file
67-
storageId, // Add the storage ID for persistence
68-
});
69-
} else {
70-
console.error(
71-
"File retrieval test failed - couldn't retrieve file"
72-
);
73-
// Fall back to the temporary URL if retrieval test fails
74-
setCurrentFile({
75-
name: file.name,
76-
type: file.type,
77-
size: file.size,
78-
url: tempUrl,
79-
storageId,
80-
});
81-
}
82-
})
83-
.catch((err) => {
84-
console.error("Error in file retrieval test:", err);
85-
// Fall back to the temporary URL if retrieval test fails
86-
setCurrentFile({
87-
name: file.name,
88-
type: file.type,
89-
size: file.size,
90-
url: tempUrl,
91-
storageId,
92-
});
93-
});
94-
});
95-
})
96-
.catch((error) => {
97-
console.error("Failed to store file in IndexedDB:", error);
98-
toast.error(t("upload.storageError"));
99-
});
10037
} catch (error) {
10138
console.error("Error in file upload:", error);
10239
toast.error(t("upload.uploadError"));

src/components/player/MediaPlayer.tsx

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useRef, useEffect, useState, useCallback } from "react";
22
import { usePlayerStore } from "../../stores/playerStore";
33
import { toast } from "react-hot-toast";
44
import { Play, Pause } from "lucide-react";
5+
import { useShallow } from "zustand/react/shallow";
56

67
interface MediaPlayerProps {
78
hiddenMode?: boolean;
@@ -16,6 +17,7 @@ export const MediaPlayer = ({ hiddenMode = false }: MediaPlayerProps) => {
1617
const isDelayingRef = useRef(false);
1718
// Track pending play intent so we can start playback once the element is ready
1819
const pendingPlayRef = useRef(false);
20+
const lastReportedTimeRef = useRef(0);
1921

2022
const {
2123
currentFile,
@@ -32,7 +34,24 @@ export const MediaPlayer = ({ hiddenMode = false }: MediaPlayerProps) => {
3234
setCurrentTime,
3335
setDuration,
3436
setIsPlaying,
35-
} = usePlayerStore();
37+
} = usePlayerStore(
38+
useShallow((state) => ({
39+
currentFile: state.currentFile,
40+
isPlaying: state.isPlaying,
41+
currentTime: state.currentTime,
42+
volume: state.volume,
43+
mediaVolume: state.mediaVolume,
44+
muted: state.muted,
45+
playbackRate: state.playbackRate,
46+
loopStart: state.loopStart,
47+
loopEnd: state.loopEnd,
48+
isLooping: state.isLooping,
49+
showWaveform: state.showWaveform,
50+
setCurrentTime: state.setCurrentTime,
51+
setDuration: state.setDuration,
52+
setIsPlaying: state.setIsPlaying,
53+
}))
54+
);
3655

3756
// Keep local state in sync with global state
3857
useEffect(() => {
@@ -234,7 +253,10 @@ export const MediaPlayer = ({ hiddenMode = false }: MediaPlayerProps) => {
234253

235254
const handleTimeUpdate = () => {
236255
const currentTimeValue = mediaElement.currentTime;
237-
setCurrentTime(currentTimeValue);
256+
if (Math.abs(currentTimeValue - lastReportedTimeRef.current) >= 0.05) {
257+
lastReportedTimeRef.current = currentTimeValue;
258+
setCurrentTime(currentTimeValue);
259+
}
238260

239261
// Handle A-B looping
240262
if (isLooping && loopStart !== null && loopEnd !== null) {
@@ -311,14 +333,10 @@ export const MediaPlayer = ({ hiddenMode = false }: MediaPlayerProps) => {
311333
}
312334
};
313335

314-
// Use less frequent checking to rely more on native timeupdate
315-
const checkInterval = setInterval(handleTimeUpdate, 100);
316-
317336
// Also keep the timeupdate event for standard time tracking
318337
mediaElement.addEventListener("timeupdate", handleTimeUpdate);
319338

320339
return () => {
321-
clearInterval(checkInterval);
322340
mediaElement.removeEventListener("timeupdate", handleTimeUpdate);
323341
};
324342
}, [currentFile, isLooping, loopStart, loopEnd, setCurrentTime]);

src/components/player/YouTubePlayer.tsx

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useState, useEffect, useRef } from "react";
22
import { usePlayerStore } from "../../stores/playerStore";
33
import { toast } from "react-hot-toast";
44
import { useTranslation } from "react-i18next";
5+
import { useShallow } from "zustand/react/shallow";
56

67
// Define YouTube player interface
78
interface YTPlayer {
@@ -83,7 +84,22 @@ export const YouTubePlayer = ({
8384
setCurrentTime,
8485
setDuration,
8586
setIsPlaying,
86-
} = usePlayerStore();
87+
} = usePlayerStore(
88+
useShallow((state) => ({
89+
isPlaying: state.isPlaying,
90+
volume: state.volume,
91+
mediaVolume: state.mediaVolume,
92+
muted: state.muted,
93+
playbackRate: state.playbackRate,
94+
loopStart: state.loopStart,
95+
loopEnd: state.loopEnd,
96+
isLooping: state.isLooping,
97+
currentTime: state.currentTime,
98+
setCurrentTime: state.setCurrentTime,
99+
setDuration: state.setDuration,
100+
setIsPlaying: state.setIsPlaying,
101+
}))
102+
);
87103
const { t } = useTranslation();
88104

89105
// Load YouTube API
@@ -273,13 +289,16 @@ export const YouTubePlayer = ({
273289
}
274290
};
275291

276-
// Use less frequent checking to rely more on native events
277-
const checkInterval = setInterval(checkTime, 100);
292+
if (!isPlaying && !isLooping) {
293+
return;
294+
}
295+
296+
const checkInterval = setInterval(checkTime, 250);
278297

279298
return () => {
280299
clearInterval(checkInterval);
281300
};
282-
}, [player, isLooping, isSeeking, loopStart, loopEnd, setCurrentTime]);
301+
}, [player, isPlaying, isLooping, isSeeking, loopStart, loopEnd, setCurrentTime]);
283302

284303
// For hidden mode, render a minimal container but still initialize the player
285304
if (hiddenMode) {

0 commit comments

Comments
 (0)