diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 6a0dceb..f0b6d2e 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -11,7 +11,8 @@ "hls.js": "^1.6.15", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-router-dom": "^6.28.0" + "react-router-dom": "^6.28.0", + "wavesurfer.js": "^7.12.5" }, "devDependencies": { "@types/react": "^18.3.12", @@ -1884,6 +1885,12 @@ } } }, + "node_modules/wavesurfer.js": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/wavesurfer.js/-/wavesurfer.js-7.12.5.tgz", + "integrity": "sha512-MSZcA13R9ZlxgYpzfakaSYf8dz5tCdZKYbjtN1qnKbCi+UoyfaTuhvjlXHrITi/fgeO3qWfsH7U3BP1AKnwRNg==", + "license": "BSD-3-Clause" + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index a08cf73..2d20444 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,7 +12,8 @@ "hls.js": "^1.6.15", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-router-dom": "^6.28.0" + "react-router-dom": "^6.28.0", + "wavesurfer.js": "^7.12.5" }, "devDependencies": { "@types/react": "^18.3.12", diff --git a/frontend/src/App.css b/frontend/src/App.css index 2dc1025..64b60e9 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -5909,6 +5909,25 @@ a.app-footer__about:hover, /* ── Player Controls ───────────────────────────────────────────────────────── */ +/* ── Audio Waveform (wavesurfer.js) ────────────────────────────────────────── */ + +.audio-waveform { + background: #000; + border-radius: 8px; + padding: 1rem; + min-height: 160px; + display: flex; + flex-direction: column; + justify-content: center; + border: 1px solid var(--color-border); +} + +.audio-waveform__canvas { + width: 100%; +} + +/* ── Player Controls (continued) ───────────────────────────────────────────── */ + .player-controls { display: flex; align-items: center; diff --git a/frontend/src/components/AudioWaveform.tsx b/frontend/src/components/AudioWaveform.tsx new file mode 100644 index 0000000..ad1ea2f --- /dev/null +++ b/frontend/src/components/AudioWaveform.tsx @@ -0,0 +1,58 @@ +import { useEffect, useRef } from "react"; +import WaveSurfer from "wavesurfer.js"; +import type { MediaSyncState } from "../hooks/useMediaSync"; + +interface AudioWaveformProps { + mediaSync: MediaSyncState; + src: string; +} + +/** + * Audio-only waveform visualiser powered by wavesurfer.js. + * Renders a hidden