@djangocfg/ui-nextjs 2.1.65 → 2.1.67

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.
Files changed (92) hide show
  1. package/package.json +13 -8
  2. package/src/blocks/SplitHero/SplitHeroMedia.tsx +2 -1
  3. package/src/stores/index.ts +8 -0
  4. package/src/stores/mediaCache.ts +464 -0
  5. package/src/tools/AudioPlayer/@refactoring/00-PLAN.md +148 -0
  6. package/src/tools/AudioPlayer/@refactoring/01-TYPES.md +301 -0
  7. package/src/tools/AudioPlayer/@refactoring/02-HOOKS.md +281 -0
  8. package/src/tools/AudioPlayer/@refactoring/03-CONTEXT.md +328 -0
  9. package/src/tools/AudioPlayer/@refactoring/04-COMPONENTS.md +251 -0
  10. package/src/tools/AudioPlayer/@refactoring/05-EFFECTS.md +427 -0
  11. package/src/tools/AudioPlayer/@refactoring/06-UTILS-AND-INDEX.md +193 -0
  12. package/src/tools/AudioPlayer/@refactoring/07-EXECUTION-CHECKLIST.md +146 -0
  13. package/src/tools/AudioPlayer/README.md +325 -0
  14. package/src/tools/AudioPlayer/components/AudioEqualizer.tsx +200 -0
  15. package/src/tools/AudioPlayer/components/AudioPlayer.tsx +231 -0
  16. package/src/tools/AudioPlayer/components/AudioShortcutsPopover.tsx +99 -0
  17. package/src/tools/AudioPlayer/components/ReactiveCover/AudioReactiveCover.tsx +147 -0
  18. package/src/tools/AudioPlayer/components/ReactiveCover/effects/GlowEffect.tsx +110 -0
  19. package/src/tools/AudioPlayer/components/ReactiveCover/effects/MeshEffect.tsx +58 -0
  20. package/src/tools/AudioPlayer/components/ReactiveCover/effects/OrbsEffect.tsx +45 -0
  21. package/src/tools/AudioPlayer/components/ReactiveCover/effects/SpotlightEffect.tsx +82 -0
  22. package/src/tools/AudioPlayer/components/ReactiveCover/effects/index.ts +8 -0
  23. package/src/tools/AudioPlayer/components/ReactiveCover/index.ts +6 -0
  24. package/src/tools/AudioPlayer/components/SimpleAudioPlayer.tsx +280 -0
  25. package/src/tools/AudioPlayer/components/VisualizationToggle.tsx +64 -0
  26. package/src/tools/AudioPlayer/components/index.ts +21 -0
  27. package/src/tools/AudioPlayer/context/AudioProvider.tsx +292 -0
  28. package/src/tools/AudioPlayer/context/index.ts +11 -0
  29. package/src/tools/AudioPlayer/context/selectors.ts +96 -0
  30. package/src/tools/AudioPlayer/effects/index.ts +412 -0
  31. package/src/tools/AudioPlayer/hooks/index.ts +29 -0
  32. package/src/tools/AudioPlayer/hooks/useAudioAnalysis.ts +110 -0
  33. package/src/tools/AudioPlayer/hooks/useAudioHotkeys.ts +149 -0
  34. package/src/tools/AudioPlayer/hooks/useSharedWebAudio.ts +106 -0
  35. package/src/tools/AudioPlayer/hooks/useVisualization.tsx +201 -0
  36. package/src/tools/AudioPlayer/index.ts +139 -0
  37. package/src/tools/AudioPlayer/types/audio.ts +107 -0
  38. package/src/tools/AudioPlayer/types/components.ts +98 -0
  39. package/src/tools/AudioPlayer/types/effects.ts +73 -0
  40. package/src/tools/AudioPlayer/types/index.ts +35 -0
  41. package/src/tools/AudioPlayer/utils/formatTime.ts +10 -0
  42. package/src/tools/AudioPlayer/utils/index.ts +5 -0
  43. package/src/tools/ImageViewer/@refactoring/00-PLAN.md +71 -0
  44. package/src/tools/ImageViewer/@refactoring/01-TYPES.md +121 -0
  45. package/src/tools/ImageViewer/@refactoring/02-UTILS.md +143 -0
  46. package/src/tools/ImageViewer/@refactoring/03-HOOKS.md +261 -0
  47. package/src/tools/ImageViewer/@refactoring/04-COMPONENTS.md +427 -0
  48. package/src/tools/ImageViewer/@refactoring/05-EXECUTION-CHECKLIST.md +126 -0
  49. package/src/tools/ImageViewer/README.md +174 -0
  50. package/src/tools/ImageViewer/components/ImageInfo.tsx +44 -0
  51. package/src/tools/ImageViewer/components/ImageToolbar.tsx +150 -0
  52. package/src/tools/ImageViewer/components/ImageViewer.tsx +235 -0
  53. package/src/tools/ImageViewer/components/index.ts +7 -0
  54. package/src/tools/ImageViewer/hooks/index.ts +9 -0
  55. package/src/tools/ImageViewer/hooks/useImageLoading.ts +153 -0
  56. package/src/tools/ImageViewer/hooks/useImageTransform.ts +101 -0
  57. package/src/tools/ImageViewer/index.ts +60 -0
  58. package/src/tools/ImageViewer/types.ts +75 -0
  59. package/src/tools/ImageViewer/utils/constants.ts +59 -0
  60. package/src/tools/ImageViewer/utils/index.ts +16 -0
  61. package/src/tools/ImageViewer/utils/lqip.ts +47 -0
  62. package/src/tools/VideoPlayer/@refactoring/00-PLAN.md +91 -0
  63. package/src/tools/VideoPlayer/@refactoring/01-TYPES.md +284 -0
  64. package/src/tools/VideoPlayer/@refactoring/02-UTILS.md +141 -0
  65. package/src/tools/VideoPlayer/@refactoring/03-HOOKS.md +178 -0
  66. package/src/tools/VideoPlayer/@refactoring/04-COMPONENTS.md +95 -0
  67. package/src/tools/VideoPlayer/@refactoring/05-EXECUTION-CHECKLIST.md +139 -0
  68. package/src/tools/VideoPlayer/README.md +212 -187
  69. package/src/tools/VideoPlayer/{VideoControls.tsx → components/VideoControls.tsx} +8 -9
  70. package/src/tools/VideoPlayer/components/VideoErrorFallback.tsx +174 -0
  71. package/src/tools/VideoPlayer/components/VideoPlayer.tsx +201 -0
  72. package/src/tools/VideoPlayer/components/index.ts +14 -0
  73. package/src/tools/VideoPlayer/context/VideoPlayerContext.tsx +52 -0
  74. package/src/tools/VideoPlayer/context/index.ts +8 -0
  75. package/src/tools/VideoPlayer/hooks/index.ts +9 -0
  76. package/src/tools/VideoPlayer/hooks/useVideoPositionCache.ts +109 -0
  77. package/src/tools/VideoPlayer/index.ts +70 -9
  78. package/src/tools/VideoPlayer/providers/NativeProvider.tsx +206 -0
  79. package/src/tools/VideoPlayer/providers/StreamProvider.tsx +401 -0
  80. package/src/tools/VideoPlayer/providers/VidstackProvider.tsx +332 -0
  81. package/src/tools/VideoPlayer/providers/index.ts +8 -0
  82. package/src/tools/VideoPlayer/types/index.ts +38 -0
  83. package/src/tools/VideoPlayer/types/player.ts +116 -0
  84. package/src/tools/VideoPlayer/types/provider.ts +93 -0
  85. package/src/tools/VideoPlayer/types/sources.ts +97 -0
  86. package/src/tools/VideoPlayer/utils/fileSource.ts +78 -0
  87. package/src/tools/VideoPlayer/utils/index.ts +11 -0
  88. package/src/tools/VideoPlayer/utils/resolvers.ts +75 -0
  89. package/src/tools/index.ts +92 -4
  90. package/src/tools/VideoPlayer/NativePlayer.tsx +0 -141
  91. package/src/tools/VideoPlayer/VideoPlayer.tsx +0 -231
  92. package/src/tools/VideoPlayer/types.ts +0 -118
@@ -0,0 +1,292 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * AudioProvider - Shared audio state and controls
5
+ *
6
+ * Provides centralized audio state management using WaveSurfer.js
7
+ * All child components can access playback state and controls
8
+ */
9
+
10
+ import {
11
+ createContext,
12
+ useRef,
13
+ useMemo,
14
+ useCallback,
15
+ useState,
16
+ useEffect,
17
+ type ReactNode,
18
+ } from 'react';
19
+ import { useWavesurfer } from '@wavesurfer/react';
20
+ import type { AudioContextState, AudioSource, WaveformOptions } from '../types';
21
+ import { useSharedWebAudio, useAudioAnalysis } from '../hooks';
22
+ import { useAudioCache } from '../../../stores/mediaCache';
23
+
24
+ // =============================================================================
25
+ // CONTEXT
26
+ // =============================================================================
27
+
28
+ // Named AudioPlayerContext to avoid conflict with browser's AudioContext
29
+ export const AudioPlayerContext = createContext<AudioContextState | null>(null);
30
+
31
+ // =============================================================================
32
+ // PROVIDER PROPS
33
+ // =============================================================================
34
+
35
+ interface AudioProviderProps {
36
+ /** Audio source */
37
+ source: AudioSource;
38
+ /** Auto-play when loaded */
39
+ autoPlay?: boolean;
40
+ /** WaveSurfer options */
41
+ waveformOptions?: WaveformOptions;
42
+ /** Container ref for waveform rendering */
43
+ containerRef: React.RefObject<HTMLDivElement | null>;
44
+ /** Children components */
45
+ children: ReactNode;
46
+ }
47
+
48
+ // =============================================================================
49
+ // PROVIDER
50
+ // =============================================================================
51
+
52
+ export function AudioProvider({
53
+ source,
54
+ autoPlay = false,
55
+ waveformOptions = {},
56
+ containerRef,
57
+ children,
58
+ }: AudioProviderProps) {
59
+ // Cache for playback position persistence
60
+ const { saveAudioPosition, getAudioPosition } = useAudioCache();
61
+ const lastSavedTimeRef = useRef<number>(0);
62
+
63
+ // Memoize WaveSurfer options with theme-aware colors
64
+ const options = useMemo(
65
+ () => ({
66
+ container: containerRef,
67
+ url: source.uri,
68
+ // Theme-aware colors using HSL
69
+ waveColor: waveformOptions.waveColor || 'hsl(217 91% 60% / 0.3)',
70
+ progressColor: waveformOptions.progressColor || 'hsl(217 91% 60%)',
71
+ cursorColor: waveformOptions.cursorColor || 'hsl(217 91% 60%)',
72
+ cursorWidth: waveformOptions.cursorWidth ?? 2,
73
+ height: waveformOptions.height ?? 64,
74
+ barWidth: waveformOptions.barWidth ?? 3,
75
+ barRadius: waveformOptions.barRadius ?? 3,
76
+ barGap: waveformOptions.barGap ?? 2,
77
+ normalize: true,
78
+ interact: true,
79
+ hideScrollbar: true,
80
+ autoplay: autoPlay,
81
+ }),
82
+ [source.uri, autoPlay, waveformOptions, containerRef]
83
+ );
84
+
85
+ // Use official wavesurfer-react hook
86
+ const { wavesurfer, isReady, isPlaying, currentTime } = useWavesurfer(options);
87
+
88
+ // Restore cached playback position when ready
89
+ useEffect(() => {
90
+ if (isReady && wavesurfer && source.uri) {
91
+ const cachedPosition = getAudioPosition(source.uri);
92
+ if (cachedPosition && cachedPosition > 0) {
93
+ const duration = wavesurfer.getDuration();
94
+ // Only restore if position is valid (not at the end)
95
+ if (cachedPosition < duration - 1) {
96
+ wavesurfer.setTime(cachedPosition);
97
+ }
98
+ }
99
+ }
100
+ }, [isReady, wavesurfer, source.uri, getAudioPosition]);
101
+
102
+ // Save playback position periodically and on pause
103
+ useEffect(() => {
104
+ if (!source.uri) return;
105
+
106
+ // Save position every 5 seconds during playback
107
+ if (isPlaying && currentTime > 0) {
108
+ const timeSinceLastSave = currentTime - lastSavedTimeRef.current;
109
+ if (timeSinceLastSave >= 5 || timeSinceLastSave < 0) {
110
+ saveAudioPosition(source.uri, currentTime);
111
+ lastSavedTimeRef.current = currentTime;
112
+ }
113
+ }
114
+
115
+ // Save position immediately when paused
116
+ if (!isPlaying && currentTime > 0) {
117
+ saveAudioPosition(source.uri, currentTime);
118
+ lastSavedTimeRef.current = currentTime;
119
+ }
120
+ }, [isPlaying, currentTime, source.uri, saveAudioPosition]);
121
+
122
+ // Derived state
123
+ const duration = wavesurfer?.getDuration() ?? 0;
124
+ const volume = wavesurfer?.getVolume() ?? 1;
125
+ const isMuted = wavesurfer?.getMuted() ?? false;
126
+
127
+ // Loop state
128
+ const [isLooping, setIsLooping] = useState(false);
129
+
130
+ // Handle loop: restart playback when audio finishes
131
+ useEffect(() => {
132
+ if (!wavesurfer) return;
133
+
134
+ const handleFinish = () => {
135
+ if (isLooping) {
136
+ wavesurfer.seekTo(0);
137
+ wavesurfer.play();
138
+ }
139
+ };
140
+
141
+ wavesurfer.on('finish', handleFinish);
142
+ return () => {
143
+ wavesurfer.un('finish', handleFinish);
144
+ };
145
+ }, [wavesurfer, isLooping]);
146
+
147
+ // Get audio element for equalizer
148
+ const audioElement = useMemo(() => {
149
+ return wavesurfer?.getMediaElement() ?? null;
150
+ }, [wavesurfer]);
151
+
152
+ // Shared Web Audio context for all analyzers (prevents duplicate source nodes)
153
+ const sharedAudio = useSharedWebAudio(audioElement);
154
+
155
+ // Audio analysis for reactive effects (uses shared context)
156
+ const audioLevels = useAudioAnalysis(sharedAudio, isPlaying);
157
+
158
+ // Actions
159
+ const play = useCallback(async () => {
160
+ await wavesurfer?.play();
161
+ }, [wavesurfer]);
162
+
163
+ const pause = useCallback(() => {
164
+ wavesurfer?.pause();
165
+ }, [wavesurfer]);
166
+
167
+ const togglePlay = useCallback(() => {
168
+ wavesurfer?.playPause();
169
+ }, [wavesurfer]);
170
+
171
+ const seek = useCallback(
172
+ (time: number) => {
173
+ if (wavesurfer) {
174
+ const clampedTime = Math.max(0, Math.min(time, duration));
175
+ wavesurfer.setTime(clampedTime);
176
+ }
177
+ },
178
+ [wavesurfer, duration]
179
+ );
180
+
181
+ const seekTo = useCallback(
182
+ (progress: number) => {
183
+ if (wavesurfer) {
184
+ const clampedProgress = Math.max(0, Math.min(progress, 1));
185
+ wavesurfer.seekTo(clampedProgress);
186
+ }
187
+ },
188
+ [wavesurfer]
189
+ );
190
+
191
+ const skip = useCallback(
192
+ (seconds: number) => {
193
+ wavesurfer?.skip(seconds);
194
+ },
195
+ [wavesurfer]
196
+ );
197
+
198
+ const setVolume = useCallback(
199
+ (vol: number) => {
200
+ if (wavesurfer) {
201
+ wavesurfer.setVolume(Math.max(0, Math.min(vol, 1)));
202
+ }
203
+ },
204
+ [wavesurfer]
205
+ );
206
+
207
+ const toggleMute = useCallback(() => {
208
+ if (wavesurfer) {
209
+ wavesurfer.setMuted(!wavesurfer.getMuted());
210
+ }
211
+ }, [wavesurfer]);
212
+
213
+ const restart = useCallback(() => {
214
+ if (wavesurfer) {
215
+ wavesurfer.seekTo(0);
216
+ wavesurfer.play();
217
+ }
218
+ }, [wavesurfer]);
219
+
220
+ const toggleLoop = useCallback(() => {
221
+ setIsLooping((prev) => !prev);
222
+ }, []);
223
+
224
+ const setLoop = useCallback((enabled: boolean) => {
225
+ setIsLooping(enabled);
226
+ }, []);
227
+
228
+ // Context value
229
+ const contextValue = useMemo<AudioContextState>(
230
+ () => ({
231
+ // Core instances
232
+ wavesurfer,
233
+ audioElement,
234
+ sharedAudio,
235
+
236
+ // Playback state
237
+ isReady,
238
+ isPlaying,
239
+ currentTime,
240
+ duration,
241
+ volume,
242
+ isMuted,
243
+ isLooping,
244
+
245
+ // Audio analysis
246
+ audioLevels,
247
+
248
+ // Actions
249
+ play,
250
+ pause,
251
+ togglePlay,
252
+ seek,
253
+ seekTo,
254
+ skip,
255
+ setVolume,
256
+ toggleMute,
257
+ toggleLoop,
258
+ setLoop,
259
+ restart,
260
+ }),
261
+ [
262
+ wavesurfer,
263
+ audioElement,
264
+ sharedAudio,
265
+ isReady,
266
+ isPlaying,
267
+ currentTime,
268
+ duration,
269
+ volume,
270
+ isMuted,
271
+ isLooping,
272
+ audioLevels,
273
+ play,
274
+ pause,
275
+ togglePlay,
276
+ seek,
277
+ seekTo,
278
+ skip,
279
+ setVolume,
280
+ toggleMute,
281
+ toggleLoop,
282
+ setLoop,
283
+ restart,
284
+ ]
285
+ );
286
+
287
+ return (
288
+ <AudioPlayerContext.Provider value={contextValue}>
289
+ {children}
290
+ </AudioPlayerContext.Provider>
291
+ );
292
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * AudioPlayer context - Public API
3
+ *
4
+ * Re-exports provider and selector hooks
5
+ */
6
+
7
+ // Provider
8
+ export { AudioProvider, AudioPlayerContext } from './AudioProvider';
9
+
10
+ // Selector hooks
11
+ export { useAudio, useAudioControls, useAudioState, useAudioElement } from './selectors';
@@ -0,0 +1,96 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * Context selectors - Performance-optimized hooks for accessing audio state
5
+ *
6
+ * These hooks provide selective access to context values,
7
+ * helping to minimize unnecessary re-renders.
8
+ */
9
+
10
+ import { useContext } from 'react';
11
+ import { AudioPlayerContext } from './AudioProvider';
12
+ import type { AudioContextState } from '../types';
13
+
14
+ // =============================================================================
15
+ // MAIN HOOK
16
+ // =============================================================================
17
+
18
+ /**
19
+ * Access full audio context state
20
+ * @throws Error if used outside AudioProvider
21
+ */
22
+ export function useAudio(): AudioContextState {
23
+ const context = useContext(AudioPlayerContext);
24
+ if (!context) {
25
+ throw new Error('useAudio must be used within an AudioProvider');
26
+ }
27
+ return context;
28
+ }
29
+
30
+ // =============================================================================
31
+ // SELECTIVE HOOKS - for performance optimization
32
+ // =============================================================================
33
+
34
+ /**
35
+ * Hook for playback controls only (no re-render on time updates)
36
+ */
37
+ export function useAudioControls() {
38
+ const {
39
+ isReady,
40
+ play,
41
+ pause,
42
+ togglePlay,
43
+ skip,
44
+ restart,
45
+ setVolume,
46
+ toggleMute,
47
+ toggleLoop,
48
+ setLoop,
49
+ } = useAudio();
50
+
51
+ return {
52
+ isReady,
53
+ play,
54
+ pause,
55
+ togglePlay,
56
+ skip,
57
+ restart,
58
+ setVolume,
59
+ toggleMute,
60
+ toggleLoop,
61
+ setLoop,
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Hook for playback state (read-only)
67
+ */
68
+ export function useAudioState() {
69
+ const {
70
+ isReady,
71
+ isPlaying,
72
+ currentTime,
73
+ duration,
74
+ volume,
75
+ isMuted,
76
+ isLooping,
77
+ } = useAudio();
78
+
79
+ return {
80
+ isReady,
81
+ isPlaying,
82
+ currentTime,
83
+ duration,
84
+ volume,
85
+ isMuted,
86
+ isLooping,
87
+ };
88
+ }
89
+
90
+ /**
91
+ * Hook for audio element access (for equalizer and reactive effects)
92
+ */
93
+ export function useAudioElement() {
94
+ const { audioElement, sharedAudio, isPlaying, audioLevels } = useAudio();
95
+ return { audioElement, sharedAudio, isPlaying, audioLevels };
96
+ }