diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index e7ad6f0..fd9eae2 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -4,6 +4,9 @@ import { Inter } from "next/font/google";
import "./globals.css";
import { ServiceWrapper } from "@/components/ServiceWrapper";
import Tag from "@/tag/Tag";
+import { AudioPlayer } from "@/components/AudioPlayer";
+
+// Create new file: src/components/AudioPlayer.tsx
const notoSans = Noto_Sans({
variable: "--font-noto-sans", subsets: ["latin"],
@@ -28,6 +31,7 @@ export default function RootLayout({
+
{children}
diff --git a/src/components/AudioPlayer.tsx b/src/components/AudioPlayer.tsx
new file mode 100644
index 0000000..c4641d2
--- /dev/null
+++ b/src/components/AudioPlayer.tsx
@@ -0,0 +1,162 @@
+'use client';
+
+import { useState, useRef, useEffect } from 'react';
+import { Play, Pause, Volume2, VolumeX } from 'lucide-react';
+
+interface AudioPlayerProps {}
+
+export function AudioPlayer({}: AudioPlayerProps) {
+ const audioRef = useRef(null);
+ const [isPlaying, setIsPlaying] = useState(false);
+ const [volume, setVolume] = useState(0.5);
+ const [currentTime, setCurrentTime] = useState(0);
+ const [duration, setDuration] = useState(0);
+ const [isMuted, setIsMuted] = useState(false);
+ const [previousVolume, setPreviousVolume] = useState(0.5);
+
+ useEffect(() => {
+ const audio = audioRef.current;
+ if (!audio) return;
+
+ const updateTime = () => setCurrentTime(audio.currentTime);
+ const updateDuration = () => setDuration(audio.duration);
+ const handleEnded = () => setIsPlaying(false);
+
+ audio.addEventListener('timeupdate', updateTime);
+ audio.addEventListener('loadedmetadata', updateDuration);
+ audio.addEventListener('ended', handleEnded);
+
+ return () => {
+ audio.removeEventListener('timeupdate', updateTime);
+ audio.removeEventListener('loadedmetadata', updateDuration);
+ audio.removeEventListener('ended', handleEnded);
+ };
+ }, []);
+
+ useEffect(() => {
+ if (audioRef.current) {
+ audioRef.current.volume = isMuted ? 0 : volume;
+ }
+ }, [volume, isMuted]);
+
+ const togglePlayPause = () => {
+ if (audioRef.current) {
+ if (isPlaying) {
+ audioRef.current.pause();
+ } else {
+ audioRef.current.play();
+ }
+ setIsPlaying(!isPlaying);
+ }
+ };
+
+ const toggleMute = () => {
+ if (isMuted) {
+ setIsMuted(false);
+ setVolume(previousVolume);
+ } else {
+ setPreviousVolume(volume);
+ setIsMuted(true);
+ }
+ };
+
+ const handleVolumeChange = (e: React.ChangeEvent) => {
+ const newVolume = parseFloat(e.target.value);
+ setVolume(newVolume);
+ if (isMuted && newVolume > 0) {
+ setIsMuted(false);
+ }
+ };
+
+ const handleProgressChange = (e: React.ChangeEvent) => {
+ const newTime = parseFloat(e.target.value);
+ setCurrentTime(newTime);
+ if (audioRef.current) {
+ audioRef.current.currentTime = newTime;
+ }
+ };
+
+ const formatTime = (time: number) => {
+ if (!time || isNaN(time)) return '0:00';
+ const minutes = Math.floor(time / 60);
+ const seconds = Math.floor(time % 60);
+ return `${minutes}:${seconds.toString().padStart(2, '0')}`;
+ };
+
+ return (
+
+
+
+
+ {/* Progress Bar */}
+
+
+ {formatTime(currentTime)}
+
+
+
+ {formatTime(duration)}
+
+
+
+ {/* Controls */}
+
+
+
+
+
+ Background Music
+
+
+
+ {/* Volume Control */}
+
+
+
+
+
+
+ {Math.round((isMuted ? 0 : volume) * 100)}%
+
+
+
+
+
+ );
+}
\ No newline at end of file