@djangocfg/ui-nextjs 2.1.83 → 2.1.85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -4
- package/src/tools/AudioPlayer/README.md +60 -166
- package/src/tools/AudioPlayer/components/HybridAudioPlayer.tsx +0 -35
- package/src/tools/AudioPlayer/components/HybridSimplePlayer.tsx +0 -11
- package/src/tools/AudioPlayer/components/ReactiveCover/AudioReactiveCover.tsx +23 -21
- package/src/tools/AudioPlayer/components/index.ts +4 -8
- package/src/tools/AudioPlayer/context/index.ts +1 -8
- package/src/tools/AudioPlayer/hooks/index.ts +6 -13
- package/src/tools/AudioPlayer/index.ts +25 -89
- package/src/tools/AudioPlayer/types/index.ts +10 -18
- package/src/tools/index.ts +51 -56
- package/src/tools/AudioPlayer/@refactoring3/00-IMPLEMENTATION-ROADMAP.md +0 -1146
- package/src/tools/AudioPlayer/@refactoring3/01-WAVESURFER-STREAMING-ANALYSIS.md +0 -611
- package/src/tools/AudioPlayer/@refactoring3/02-MEDIA-VIEWER-ANALYSIS.md +0 -560
- package/src/tools/AudioPlayer/@refactoring3/03-HYBRID-ARCHITECTURE-PROPOSAL.md +0 -769
- package/src/tools/AudioPlayer/@refactoring3/04-CRACKLING-ISSUE-DIAGNOSIS.md +0 -373
- package/src/tools/AudioPlayer/components/AudioEqualizer.tsx +0 -200
- package/src/tools/AudioPlayer/components/AudioPlayer.tsx +0 -236
- package/src/tools/AudioPlayer/components/AudioShortcutsPopover.tsx +0 -99
- package/src/tools/AudioPlayer/components/SimpleAudioPlayer.tsx +0 -278
- package/src/tools/AudioPlayer/components/VisualizationToggle.tsx +0 -64
- package/src/tools/AudioPlayer/context/AudioProvider.tsx +0 -376
- package/src/tools/AudioPlayer/context/selectors.ts +0 -96
- package/src/tools/AudioPlayer/hooks/useAudioAnalysis.ts +0 -110
- package/src/tools/AudioPlayer/hooks/useAudioHotkeys.ts +0 -150
- package/src/tools/AudioPlayer/hooks/useAudioSource.ts +0 -155
- package/src/tools/AudioPlayer/hooks/useSharedWebAudio.ts +0 -109
- package/src/tools/AudioPlayer/progressive/ProgressiveAudioPlayer.tsx +0 -303
- package/src/tools/AudioPlayer/progressive/WaveformCanvas.tsx +0 -381
- package/src/tools/AudioPlayer/progressive/index.ts +0 -40
- package/src/tools/AudioPlayer/progressive/peaks.ts +0 -234
- package/src/tools/AudioPlayer/progressive/types.ts +0 -179
- package/src/tools/AudioPlayer/progressive/useAudioElement.ts +0 -340
- package/src/tools/AudioPlayer/progressive/useProgressiveWaveform.ts +0 -267
- package/src/tools/AudioPlayer/types/audio.ts +0 -121
- package/src/tools/AudioPlayer/types/components.ts +0 -98
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/ui-nextjs",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.85",
|
|
4
4
|
"description": "Next.js UI component library with Radix UI primitives, Tailwind CSS styling, charts, and form components",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ui-components",
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
"check": "tsc --noEmit"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
|
-
"@djangocfg/api": "^2.1.
|
|
62
|
-
"@djangocfg/ui-core": "^2.1.
|
|
61
|
+
"@djangocfg/api": "^2.1.85",
|
|
62
|
+
"@djangocfg/ui-core": "^2.1.85",
|
|
63
63
|
"@types/react": "^19.1.0",
|
|
64
64
|
"@types/react-dom": "^19.1.0",
|
|
65
65
|
"consola": "^3.4.2",
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
"wavesurfer.js": "^7.12.1"
|
|
111
111
|
},
|
|
112
112
|
"devDependencies": {
|
|
113
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
113
|
+
"@djangocfg/typescript-config": "^2.1.85",
|
|
114
114
|
"@types/node": "^24.7.2",
|
|
115
115
|
"eslint": "^9.37.0",
|
|
116
116
|
"tailwindcss-animate": "1.0.7",
|
|
@@ -1,30 +1,21 @@
|
|
|
1
1
|
# AudioPlayer
|
|
2
2
|
|
|
3
|
-
Audio player
|
|
3
|
+
Audio player with native HTML5 streaming and audio-reactive visualizations.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Features
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
| **Reactive Effects** | ✅ Full support | ✅ Full support | ❌ No |
|
|
13
|
-
| **Streaming Support** | ✅ Native | ⚠️ Requires prefetch | ✅ Native |
|
|
14
|
-
| **Seek Any Position** | ✅ Yes | ⚠️ Limited by buffer | ✅ Yes |
|
|
15
|
-
| **No Crackling** | ✅ Guaranteed | ⚠️ Fixed in v2.1.82 | ✅ Yes |
|
|
16
|
-
| **Memory Usage** | Low | High (full file) | Low |
|
|
7
|
+
- Native HTML5 audio playback (no crackling, native streaming support)
|
|
8
|
+
- Web Audio API for real-time frequency analysis
|
|
9
|
+
- Audio-reactive cover effects (glow, orbs, spotlight, mesh)
|
|
10
|
+
- Frequency visualization waveform
|
|
11
|
+
- Full playback controls
|
|
17
12
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
## Quick Start (Recommended)
|
|
21
|
-
|
|
22
|
-
### HybridSimplePlayer — Best for Most Cases
|
|
13
|
+
## Quick Start
|
|
23
14
|
|
|
24
15
|
```tsx
|
|
25
16
|
import { HybridSimplePlayer } from '@djangocfg/ui-nextjs';
|
|
26
17
|
|
|
27
|
-
//
|
|
18
|
+
// Simple usage
|
|
28
19
|
<HybridSimplePlayer src="https://example.com/audio.mp3" />
|
|
29
20
|
|
|
30
21
|
// With metadata and reactive cover
|
|
@@ -43,15 +34,17 @@ import { HybridSimplePlayer } from '@djangocfg/ui-nextjs';
|
|
|
43
34
|
title="Track Title"
|
|
44
35
|
coverArt={coverUrl}
|
|
45
36
|
showWaveform
|
|
37
|
+
waveformMode="frequency" // 'frequency' | 'static'
|
|
38
|
+
showLoop
|
|
46
39
|
reactiveCover
|
|
47
|
-
variant="spotlight"
|
|
48
|
-
intensity="medium"
|
|
49
|
-
colorScheme="vibrant
|
|
50
|
-
layout="horizontal"
|
|
40
|
+
variant="spotlight" // 'glow' | 'orbs' | 'spotlight' | 'mesh' | 'none'
|
|
41
|
+
intensity="medium" // 'subtle' | 'medium' | 'strong'
|
|
42
|
+
colorScheme="primary" // 'primary' | 'vibrant' | 'cool' | 'warm'
|
|
43
|
+
layout="horizontal" // 'vertical' | 'horizontal'
|
|
51
44
|
/>
|
|
52
45
|
```
|
|
53
46
|
|
|
54
|
-
|
|
47
|
+
## Props
|
|
55
48
|
|
|
56
49
|
| Prop | Type | Default | Description |
|
|
57
50
|
|------|------|---------|-------------|
|
|
@@ -65,7 +58,7 @@ import { HybridSimplePlayer } from '@djangocfg/ui-nextjs';
|
|
|
65
58
|
| `waveformHeight` | `number` | `64` | Waveform height in pixels |
|
|
66
59
|
| `showTimer` | `boolean` | `true` | Show time display |
|
|
67
60
|
| `showVolume` | `boolean` | `true` | Show volume control |
|
|
68
|
-
| `showLoop` | `boolean` | `true` | Show loop
|
|
61
|
+
| `showLoop` | `boolean` | `true` | Show loop button |
|
|
69
62
|
| `reactiveCover` | `boolean` | `true` | Enable reactive effects |
|
|
70
63
|
| `variant` | `VisualizationVariant` | `'spotlight'` | Effect variant |
|
|
71
64
|
| `intensity` | `EffectIntensity` | `'medium'` | Effect intensity |
|
|
@@ -74,26 +67,27 @@ import { HybridSimplePlayer } from '@djangocfg/ui-nextjs';
|
|
|
74
67
|
| `loop` | `boolean` | `false` | Loop playback |
|
|
75
68
|
| `layout` | `'vertical' \| 'horizontal'` | `'vertical'` | Layout direction |
|
|
76
69
|
|
|
77
|
-
---
|
|
78
|
-
|
|
79
70
|
## Advanced Usage
|
|
80
71
|
|
|
81
|
-
###
|
|
72
|
+
### HybridAudioProvider + HybridAudioPlayer
|
|
82
73
|
|
|
83
|
-
For
|
|
74
|
+
For custom layouts:
|
|
84
75
|
|
|
85
76
|
```tsx
|
|
86
|
-
import {
|
|
77
|
+
import {
|
|
78
|
+
HybridAudioProvider,
|
|
79
|
+
HybridAudioPlayer,
|
|
80
|
+
AudioReactiveCover,
|
|
81
|
+
useHybridAudioContext
|
|
82
|
+
} from '@djangocfg/ui-nextjs';
|
|
87
83
|
|
|
88
84
|
function MyPlayer({ audioUrl }: { audioUrl: string }) {
|
|
89
85
|
return (
|
|
90
86
|
<HybridAudioProvider src={audioUrl}>
|
|
91
|
-
<
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
showVolume
|
|
96
|
-
/>
|
|
87
|
+
<AudioReactiveCover variant="spotlight" onClick={handleClick}>
|
|
88
|
+
<img src={coverUrl} alt="Cover" />
|
|
89
|
+
</AudioReactiveCover>
|
|
90
|
+
<HybridAudioPlayer showWaveform showControls />
|
|
97
91
|
<CustomControls />
|
|
98
92
|
</HybridAudioProvider>
|
|
99
93
|
);
|
|
@@ -104,7 +98,6 @@ function CustomControls() {
|
|
|
104
98
|
|
|
105
99
|
return (
|
|
106
100
|
<div>
|
|
107
|
-
<p>Playing: {state.isPlaying ? 'Yes' : 'No'}</p>
|
|
108
101
|
<p>Bass level: {(audioLevels.bass * 100).toFixed(0)}%</p>
|
|
109
102
|
<button onClick={controls.togglePlay}>
|
|
110
103
|
{state.isPlaying ? 'Pause' : 'Play'}
|
|
@@ -114,26 +107,26 @@ function CustomControls() {
|
|
|
114
107
|
}
|
|
115
108
|
```
|
|
116
109
|
|
|
117
|
-
|
|
110
|
+
## Hooks
|
|
118
111
|
|
|
119
|
-
|
|
112
|
+
### useHybridAudioContext
|
|
120
113
|
|
|
121
|
-
Full access
|
|
114
|
+
Full context access:
|
|
122
115
|
|
|
123
116
|
```tsx
|
|
124
117
|
const {
|
|
125
|
-
state, // { isReady, isPlaying, currentTime, duration, volume, isMuted, isLooping
|
|
126
|
-
controls, // { play, pause, togglePlay, seek,
|
|
127
|
-
audioLevels, // { bass, mid, high, overall }
|
|
128
|
-
webAudio, // { context, analyser, sourceNode }
|
|
118
|
+
state, // { isReady, isPlaying, currentTime, duration, volume, isMuted, isLooping }
|
|
119
|
+
controls, // { play, pause, togglePlay, seek, skip, setVolume, toggleMute, toggleLoop }
|
|
120
|
+
audioLevels, // { bass, mid, high, overall }
|
|
121
|
+
webAudio, // { context, analyser, sourceNode }
|
|
129
122
|
audioRef, // React ref to HTMLAudioElement
|
|
130
123
|
} = useHybridAudioContext();
|
|
131
124
|
```
|
|
132
125
|
|
|
133
|
-
|
|
126
|
+
### Specialized Hooks
|
|
134
127
|
|
|
135
128
|
```tsx
|
|
136
|
-
// State only
|
|
129
|
+
// State only
|
|
137
130
|
const { isPlaying, currentTime, duration } = useHybridAudioState();
|
|
138
131
|
|
|
139
132
|
// Controls only (no re-render on time updates)
|
|
@@ -146,100 +139,36 @@ const { bass, mid, high, overall } = useHybridAudioLevels();
|
|
|
146
139
|
const { context, analyser, sourceNode } = useHybridWebAudio();
|
|
147
140
|
```
|
|
148
141
|
|
|
149
|
-
---
|
|
150
|
-
|
|
151
|
-
## WaveSurfer-based Player (Legacy)
|
|
152
|
-
|
|
153
|
-
For cases where you need the static waveform visualization:
|
|
154
|
-
|
|
155
|
-
```tsx
|
|
156
|
-
import { SimpleAudioPlayer } from '@djangocfg/ui-nextjs';
|
|
157
|
-
|
|
158
|
-
<SimpleAudioPlayer
|
|
159
|
-
src={audioUrl}
|
|
160
|
-
prefetch={true} // Required for streaming URLs
|
|
161
|
-
title="Track Title"
|
|
162
|
-
showWaveform
|
|
163
|
-
reactiveCover
|
|
164
|
-
/>
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
> **Note:** `prefetch={true}` downloads the entire file before playing. For large files or streaming, use `HybridSimplePlayer` instead.
|
|
168
|
-
|
|
169
|
-
---
|
|
170
|
-
|
|
171
142
|
## Components
|
|
172
143
|
|
|
173
|
-
### HybridWaveform
|
|
174
|
-
|
|
175
|
-
Real-time frequency visualization canvas:
|
|
176
|
-
|
|
177
|
-
```tsx
|
|
178
|
-
<HybridWaveform
|
|
179
|
-
mode="frequency" // 'frequency' | 'static'
|
|
180
|
-
height={64}
|
|
181
|
-
barWidth={3}
|
|
182
|
-
barGap={2}
|
|
183
|
-
barRadius={2}
|
|
184
|
-
progressColor="hsl(217 91% 60%)"
|
|
185
|
-
waveColor="hsl(217 91% 60% / 0.3)"
|
|
186
|
-
bufferedColor="hsl(217 91% 60% / 0.15)"
|
|
187
|
-
onSeek={(time) => console.log('Seeked to', time)}
|
|
188
|
-
/>
|
|
189
|
-
```
|
|
190
|
-
|
|
191
144
|
### AudioReactiveCover
|
|
192
145
|
|
|
193
146
|
Album art wrapper with audio-reactive effects:
|
|
194
147
|
|
|
195
148
|
```tsx
|
|
196
149
|
<AudioReactiveCover
|
|
197
|
-
variant="spotlight"
|
|
198
|
-
intensity="medium"
|
|
199
|
-
colorScheme="primary"
|
|
150
|
+
variant="spotlight" // 'glow' | 'orbs' | 'spotlight' | 'mesh'
|
|
151
|
+
intensity="medium" // 'subtle' | 'medium' | 'strong'
|
|
152
|
+
colorScheme="primary" // 'primary' | 'vibrant' | 'cool' | 'warm'
|
|
200
153
|
onClick={() => nextVariant()}
|
|
201
154
|
>
|
|
202
155
|
<img src={coverArt} alt="Album cover" />
|
|
203
156
|
</AudioReactiveCover>
|
|
204
157
|
```
|
|
205
158
|
|
|
206
|
-
###
|
|
159
|
+
### HybridWaveform
|
|
207
160
|
|
|
208
|
-
Real-time frequency
|
|
161
|
+
Real-time frequency visualization:
|
|
209
162
|
|
|
210
163
|
```tsx
|
|
211
|
-
<
|
|
212
|
-
|
|
213
|
-
height={
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
barColor="hsl(217 91% 60%)"
|
|
217
|
-
peakColor="hsl(217 91% 70%)"
|
|
164
|
+
<HybridWaveform
|
|
165
|
+
mode="frequency" // 'frequency' | 'static'
|
|
166
|
+
height={64}
|
|
167
|
+
barWidth={3}
|
|
168
|
+
barGap={2}
|
|
218
169
|
/>
|
|
219
170
|
```
|
|
220
171
|
|
|
221
|
-
---
|
|
222
|
-
|
|
223
|
-
## Keyboard Shortcuts
|
|
224
|
-
|
|
225
|
-
| Key | Action |
|
|
226
|
-
|-----|--------|
|
|
227
|
-
| `Space` | Play/Pause |
|
|
228
|
-
| `←` / `J` | Skip 10s backward |
|
|
229
|
-
| `→` / `L` | Skip 10s forward |
|
|
230
|
-
| `↑` | Volume up |
|
|
231
|
-
| `↓` | Volume down |
|
|
232
|
-
| `M` | Mute/Unmute |
|
|
233
|
-
| `0-9` | Jump to 0-90% |
|
|
234
|
-
|
|
235
|
-
Enable with:
|
|
236
|
-
|
|
237
|
-
```tsx
|
|
238
|
-
useAudioHotkeys({ enabled: true, skipDuration: 10, volumeStep: 0.1 });
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
---
|
|
242
|
-
|
|
243
172
|
## Effect Variants
|
|
244
173
|
|
|
245
174
|
| Variant | Description |
|
|
@@ -250,8 +179,6 @@ useAudioHotkeys({ enabled: true, skipDuration: 10, volumeStep: 0.1 });
|
|
|
250
179
|
| `mesh` | Large gradient blobs with movement |
|
|
251
180
|
| `none` | Effects disabled |
|
|
252
181
|
|
|
253
|
-
---
|
|
254
|
-
|
|
255
182
|
## Architecture
|
|
256
183
|
|
|
257
184
|
```
|
|
@@ -259,54 +186,21 @@ AudioPlayer/
|
|
|
259
186
|
├── index.ts # Public API exports
|
|
260
187
|
├── types/ # TypeScript types
|
|
261
188
|
├── hooks/
|
|
262
|
-
│ ├── useHybridAudio.ts #
|
|
263
|
-
│ ├── useHybridAudioAnalysis.ts #
|
|
264
|
-
│ ├── useSharedWebAudio.ts # Shared AudioContext (WaveSurfer)
|
|
265
|
-
│ ├── useAudioAnalysis.ts # Web Audio frequency analysis
|
|
266
|
-
│ ├── useAudioHotkeys.ts # Keyboard shortcuts
|
|
189
|
+
│ ├── useHybridAudio.ts # HTML5 audio + Web Audio hook
|
|
190
|
+
│ ├── useHybridAudioAnalysis.ts # Frequency analysis
|
|
267
191
|
│ └── useVisualization.tsx # Visualization settings
|
|
268
192
|
├── context/
|
|
269
|
-
│
|
|
270
|
-
│ ├── AudioProvider.tsx # WaveSurfer context
|
|
271
|
-
│ └── selectors.ts # Hook selectors
|
|
193
|
+
│ └── HybridAudioProvider.tsx # Audio context provider
|
|
272
194
|
├── components/
|
|
273
|
-
│ ├── HybridAudioPlayer.tsx #
|
|
274
|
-
│ ├── HybridSimplePlayer.tsx #
|
|
275
|
-
│ ├── HybridWaveform.tsx #
|
|
276
|
-
│ ├── AudioPlayer.tsx # WaveSurfer player
|
|
277
|
-
│ ├── SimpleAudioPlayer.tsx # WaveSurfer wrapper
|
|
278
|
-
│ ├── AudioEqualizer.tsx # Frequency bars
|
|
195
|
+
│ ├── HybridAudioPlayer.tsx # Main player component
|
|
196
|
+
│ ├── HybridSimplePlayer.tsx # All-in-one wrapper
|
|
197
|
+
│ ├── HybridWaveform.tsx # Frequency visualization
|
|
279
198
|
│ └── ReactiveCover/ # Reactive effects
|
|
280
|
-
├──
|
|
281
|
-
└──
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
---
|
|
285
|
-
|
|
286
|
-
## Migration Guide
|
|
287
|
-
|
|
288
|
-
### From SimpleAudioPlayer to HybridSimplePlayer
|
|
289
|
-
|
|
290
|
-
```tsx
|
|
291
|
-
// Before
|
|
292
|
-
<SimpleAudioPlayer
|
|
293
|
-
src={url}
|
|
294
|
-
prefetch={true}
|
|
295
|
-
showWaveform
|
|
296
|
-
reactiveCover
|
|
297
|
-
variant="spotlight"
|
|
298
|
-
/>
|
|
299
|
-
|
|
300
|
-
// After
|
|
301
|
-
<HybridSimplePlayer
|
|
302
|
-
src={url}
|
|
303
|
-
showWaveform
|
|
304
|
-
reactiveCover
|
|
305
|
-
variant="spotlight"
|
|
306
|
-
/>
|
|
199
|
+
├── effects/ # Effect calculations
|
|
200
|
+
└── utils/ # Utilities
|
|
307
201
|
```
|
|
308
202
|
|
|
309
|
-
Key
|
|
310
|
-
-
|
|
311
|
-
-
|
|
312
|
-
-
|
|
203
|
+
Key design:
|
|
204
|
+
- HTML5 `<audio>` for playback (native streaming, no crackling)
|
|
205
|
+
- Web Audio API AnalyserNode for visualization only (not connected to output)
|
|
206
|
+
- Audio graph: `source → destination` + `source → analyser` (passive)
|
|
@@ -30,9 +30,6 @@ import {
|
|
|
30
30
|
import { Button, Slider, cn } from '@djangocfg/ui-nextjs';
|
|
31
31
|
import { useHybridAudioContext } from '../context/HybridAudioProvider';
|
|
32
32
|
import { HybridWaveform } from './HybridWaveform';
|
|
33
|
-
import { AudioEqualizer } from './AudioEqualizer';
|
|
34
|
-
import { AudioShortcutsPopover } from './AudioShortcutsPopover';
|
|
35
|
-
import { useAudioHotkeys } from '../hooks';
|
|
36
33
|
import { formatTime } from '../utils';
|
|
37
34
|
|
|
38
35
|
// =============================================================================
|
|
@@ -48,23 +45,12 @@ export interface HybridAudioPlayerProps {
|
|
|
48
45
|
waveformMode?: 'frequency' | 'static';
|
|
49
46
|
/** Waveform height in pixels */
|
|
50
47
|
waveformHeight?: number;
|
|
51
|
-
/** Show equalizer bars */
|
|
52
|
-
showEqualizer?: boolean;
|
|
53
48
|
/** Show time display */
|
|
54
49
|
showTimer?: boolean;
|
|
55
50
|
/** Show volume control */
|
|
56
51
|
showVolume?: boolean;
|
|
57
52
|
/** Show loop button */
|
|
58
53
|
showLoop?: boolean;
|
|
59
|
-
/** Equalizer options */
|
|
60
|
-
equalizerOptions?: {
|
|
61
|
-
barCount?: number;
|
|
62
|
-
height?: number;
|
|
63
|
-
gap?: number;
|
|
64
|
-
showPeaks?: boolean;
|
|
65
|
-
barColor?: string;
|
|
66
|
-
peakColor?: string;
|
|
67
|
-
};
|
|
68
54
|
/** Additional CSS class */
|
|
69
55
|
className?: string;
|
|
70
56
|
/** Inline styles */
|
|
@@ -80,19 +66,14 @@ export const HybridAudioPlayer = memo(function HybridAudioPlayer({
|
|
|
80
66
|
showWaveform = true,
|
|
81
67
|
waveformMode = 'frequency',
|
|
82
68
|
waveformHeight = 64,
|
|
83
|
-
showEqualizer = false,
|
|
84
69
|
showTimer = true,
|
|
85
70
|
showVolume = true,
|
|
86
71
|
showLoop = true,
|
|
87
|
-
equalizerOptions = {},
|
|
88
72
|
className,
|
|
89
73
|
style,
|
|
90
74
|
}: HybridAudioPlayerProps) {
|
|
91
75
|
const { state, controls } = useHybridAudioContext();
|
|
92
76
|
|
|
93
|
-
// Enable keyboard shortcuts
|
|
94
|
-
useAudioHotkeys({ enabled: state.isReady });
|
|
95
|
-
|
|
96
77
|
const isLoading = !state.isReady;
|
|
97
78
|
|
|
98
79
|
const handleVolumeChange = (value: number[]) => {
|
|
@@ -120,19 +101,6 @@ export const HybridAudioPlayer = memo(function HybridAudioPlayer({
|
|
|
120
101
|
</div>
|
|
121
102
|
)}
|
|
122
103
|
|
|
123
|
-
{/* Equalizer animation */}
|
|
124
|
-
{showEqualizer && (
|
|
125
|
-
<AudioEqualizer
|
|
126
|
-
barCount={equalizerOptions.barCount}
|
|
127
|
-
height={equalizerOptions.height}
|
|
128
|
-
gap={equalizerOptions.gap}
|
|
129
|
-
showPeaks={equalizerOptions.showPeaks}
|
|
130
|
-
barColor={equalizerOptions.barColor}
|
|
131
|
-
peakColor={equalizerOptions.peakColor}
|
|
132
|
-
className="px-1"
|
|
133
|
-
/>
|
|
134
|
-
)}
|
|
135
|
-
|
|
136
104
|
{/* Timer */}
|
|
137
105
|
{showTimer && (
|
|
138
106
|
<div className="flex justify-between text-xs text-muted-foreground tabular-nums px-1">
|
|
@@ -239,9 +207,6 @@ export const HybridAudioPlayer = memo(function HybridAudioPlayer({
|
|
|
239
207
|
<Repeat className="h-4 w-4" />
|
|
240
208
|
</Button>
|
|
241
209
|
)}
|
|
242
|
-
|
|
243
|
-
{/* Shortcuts help */}
|
|
244
|
-
<AudioShortcutsPopover compact />
|
|
245
210
|
</div>
|
|
246
211
|
)}
|
|
247
212
|
</div>
|
|
@@ -33,7 +33,6 @@ import { HybridAudioProvider } from '../context/HybridAudioProvider';
|
|
|
33
33
|
import { HybridAudioPlayer } from './HybridAudioPlayer';
|
|
34
34
|
import { AudioReactiveCover } from './ReactiveCover';
|
|
35
35
|
import { VisualizationProvider, useVisualization } from '../hooks';
|
|
36
|
-
import type { EqualizerOptions } from '../types';
|
|
37
36
|
import type { EffectIntensity, EffectColorScheme } from '../effects';
|
|
38
37
|
import type { VisualizationVariant } from '../hooks';
|
|
39
38
|
|
|
@@ -66,9 +65,6 @@ export interface HybridSimplePlayerProps {
|
|
|
66
65
|
/** Waveform height in pixels */
|
|
67
66
|
waveformHeight?: number;
|
|
68
67
|
|
|
69
|
-
/** Show equalizer bars */
|
|
70
|
-
showEqualizer?: boolean;
|
|
71
|
-
|
|
72
68
|
/** Show timer */
|
|
73
69
|
showTimer?: boolean;
|
|
74
70
|
|
|
@@ -99,9 +95,6 @@ export interface HybridSimplePlayerProps {
|
|
|
99
95
|
/** Initial volume (0-1) */
|
|
100
96
|
initialVolume?: number;
|
|
101
97
|
|
|
102
|
-
/** Equalizer customization */
|
|
103
|
-
equalizerOptions?: EqualizerOptions;
|
|
104
|
-
|
|
105
98
|
/** Layout direction */
|
|
106
99
|
layout?: 'vertical' | 'horizontal';
|
|
107
100
|
|
|
@@ -146,7 +139,6 @@ function HybridSimplePlayerContent({
|
|
|
146
139
|
showWaveform = true,
|
|
147
140
|
waveformMode = 'frequency',
|
|
148
141
|
waveformHeight = 64,
|
|
149
|
-
showEqualizer = false,
|
|
150
142
|
showTimer = true,
|
|
151
143
|
showVolume = true,
|
|
152
144
|
showLoop = true,
|
|
@@ -157,7 +149,6 @@ function HybridSimplePlayerContent({
|
|
|
157
149
|
autoPlay = false,
|
|
158
150
|
loop = false,
|
|
159
151
|
initialVolume = 1,
|
|
160
|
-
equalizerOptions,
|
|
161
152
|
layout = 'vertical',
|
|
162
153
|
className,
|
|
163
154
|
onPlay,
|
|
@@ -275,11 +266,9 @@ function HybridSimplePlayerContent({
|
|
|
275
266
|
showWaveform={showWaveform}
|
|
276
267
|
waveformMode={waveformMode}
|
|
277
268
|
waveformHeight={waveformHeight}
|
|
278
|
-
showEqualizer={showEqualizer}
|
|
279
269
|
showTimer={showTimer}
|
|
280
270
|
showVolume={showVolume}
|
|
281
271
|
showLoop={showLoop}
|
|
282
|
-
equalizerOptions={equalizerOptions}
|
|
283
272
|
className="border-0 bg-transparent"
|
|
284
273
|
/>
|
|
285
274
|
</div>
|
|
@@ -6,13 +6,12 @@
|
|
|
6
6
|
* Uses effects utilities for clean data preparation before render.
|
|
7
7
|
* Click on cover to switch between effect variants.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
* across variant changes (including switching to 'none' and back).
|
|
9
|
+
* Must be used within HybridAudioProvider context.
|
|
11
10
|
*/
|
|
12
11
|
|
|
13
12
|
import { type ReactNode } from 'react';
|
|
14
13
|
import { cn } from '@djangocfg/ui-nextjs';
|
|
15
|
-
import {
|
|
14
|
+
import { useHybridAudioLevels, useHybridAudioState } from '../../context/HybridAudioProvider';
|
|
16
15
|
import {
|
|
17
16
|
type EffectVariant,
|
|
18
17
|
type EffectIntensity,
|
|
@@ -64,8 +63,9 @@ export function AudioReactiveCover({
|
|
|
64
63
|
onClick,
|
|
65
64
|
className,
|
|
66
65
|
}: AudioReactiveCoverProps) {
|
|
67
|
-
// Get audio
|
|
68
|
-
const { isPlaying
|
|
66
|
+
// Get audio state from HybridAudioProvider context
|
|
67
|
+
const { isPlaying } = useHybridAudioState();
|
|
68
|
+
const levels = useHybridAudioLevels();
|
|
69
69
|
|
|
70
70
|
// =========================================================================
|
|
71
71
|
// PREPARE DATA BEFORE RENDER
|
|
@@ -110,22 +110,24 @@ export function AudioReactiveCover({
|
|
|
110
110
|
transition: 'transform 0.1s ease-out',
|
|
111
111
|
}}
|
|
112
112
|
>
|
|
113
|
-
{/* Effect layers */}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
113
|
+
{/* Effect layers container - under cover, non-interactive */}
|
|
114
|
+
<div className="absolute inset-0 z-0 pointer-events-none overflow-visible">
|
|
115
|
+
{glowData && (
|
|
116
|
+
<GlowEffect data={glowData} colors={colors} isPlaying={isPlaying} />
|
|
117
|
+
)}
|
|
118
|
+
|
|
119
|
+
{orbsData && (
|
|
120
|
+
<OrbsEffect orbs={orbsData} blur={effectConfig.blur} isPlaying={isPlaying} />
|
|
121
|
+
)}
|
|
122
|
+
|
|
123
|
+
{spotlightData && (
|
|
124
|
+
<SpotlightEffect data={spotlightData} colors={colors} blur={effectConfig.blur} isPlaying={isPlaying} />
|
|
125
|
+
)}
|
|
126
|
+
|
|
127
|
+
{meshData && (
|
|
128
|
+
<MeshEffect gradients={meshData} blur={effectConfig.blur} isPlaying={isPlaying} />
|
|
129
|
+
)}
|
|
130
|
+
</div>
|
|
129
131
|
|
|
130
132
|
{/* Content (cover art) */}
|
|
131
133
|
<div
|
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AudioPlayer components - Public API
|
|
3
|
+
*
|
|
4
|
+
* Uses HTML5 audio for playback + Web Audio API for visualization.
|
|
5
|
+
* No crackling, native streaming support.
|
|
3
6
|
*/
|
|
4
7
|
|
|
5
|
-
//
|
|
6
|
-
export { AudioPlayer } from './AudioPlayer';
|
|
7
|
-
export { SimpleAudioPlayer, type SimpleAudioPlayerProps } from './SimpleAudioPlayer';
|
|
8
|
-
export { AudioEqualizer } from './AudioEqualizer';
|
|
9
|
-
export { AudioShortcutsPopover } from './AudioShortcutsPopover';
|
|
10
|
-
export { VisualizationToggle, type VisualizationToggleProps } from './VisualizationToggle';
|
|
11
|
-
|
|
12
|
-
// Hybrid player components (HTML5 audio + Web Audio for visualization)
|
|
8
|
+
// Player components
|
|
13
9
|
export { HybridAudioPlayer, type HybridAudioPlayerProps } from './HybridAudioPlayer';
|
|
14
10
|
export { HybridSimplePlayer, type HybridSimplePlayerProps } from './HybridSimplePlayer';
|
|
15
11
|
export { HybridWaveform, type HybridWaveformProps } from './HybridWaveform';
|
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AudioPlayer context - Public API
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* HybridAudioProvider: HTML5 audio + Web Audio API for visualization
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
// WaveSurfer-based provider (original)
|
|
8
|
-
export { AudioProvider, AudioPlayerContext } from './AudioProvider';
|
|
9
|
-
|
|
10
|
-
// Selector hooks for WaveSurfer provider
|
|
11
|
-
export { useAudio, useAudioControls, useAudioState, useAudioElement } from './selectors';
|
|
12
|
-
|
|
13
|
-
// Hybrid provider (HTML5 audio + Web Audio for visualization)
|
|
14
7
|
export {
|
|
15
8
|
HybridAudioProvider,
|
|
16
9
|
useHybridAudioContext,
|
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* AudioPlayer hooks -
|
|
2
|
+
* AudioPlayer hooks - Public API
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
//
|
|
6
|
-
export { useSharedWebAudio } from './useSharedWebAudio';
|
|
7
|
-
export { useAudioAnalysis } from './useAudioAnalysis';
|
|
8
|
-
export { useAudioSource } from './useAudioSource';
|
|
9
|
-
export type { UseAudioSourceResult } from './useAudioSource';
|
|
10
|
-
|
|
11
|
-
// Hybrid player hooks
|
|
5
|
+
// Core hybrid audio hook
|
|
12
6
|
export { useHybridAudio } from './useHybridAudio';
|
|
13
7
|
export type {
|
|
14
8
|
UseHybridAudioOptions,
|
|
@@ -17,15 +11,14 @@ export type {
|
|
|
17
11
|
HybridWebAudioAPI,
|
|
18
12
|
UseHybridAudioReturn,
|
|
19
13
|
} from './useHybridAudio';
|
|
20
|
-
export { useHybridAudioAnalysis } from './useHybridAudioAnalysis';
|
|
21
14
|
|
|
22
|
-
//
|
|
23
|
-
export {
|
|
24
|
-
export type { AudioHotkeyOptions, ShortcutItem, ShortcutGroup } from './useAudioHotkeys';
|
|
15
|
+
// Frequency analysis hook
|
|
16
|
+
export { useHybridAudioAnalysis } from './useHybridAudioAnalysis';
|
|
25
17
|
|
|
18
|
+
// Visualization settings (localStorage persistence)
|
|
26
19
|
export {
|
|
27
20
|
useVisualization,
|
|
28
|
-
useAudioVisualization,
|
|
21
|
+
useAudioVisualization,
|
|
29
22
|
VisualizationProvider,
|
|
30
23
|
VARIANT_INFO,
|
|
31
24
|
INTENSITY_INFO,
|