@djangocfg/ui-nextjs 2.1.66 → 2.1.68

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 (90) hide show
  1. package/package.json +8 -6
  2. package/src/stores/index.ts +8 -0
  3. package/src/stores/mediaCache.ts +474 -0
  4. package/src/tools/AudioPlayer/@refactoring/00-PLAN.md +148 -0
  5. package/src/tools/AudioPlayer/@refactoring/01-TYPES.md +301 -0
  6. package/src/tools/AudioPlayer/@refactoring/02-HOOKS.md +281 -0
  7. package/src/tools/AudioPlayer/@refactoring/03-CONTEXT.md +328 -0
  8. package/src/tools/AudioPlayer/@refactoring/04-COMPONENTS.md +251 -0
  9. package/src/tools/AudioPlayer/@refactoring/05-EFFECTS.md +427 -0
  10. package/src/tools/AudioPlayer/@refactoring/06-UTILS-AND-INDEX.md +193 -0
  11. package/src/tools/AudioPlayer/@refactoring/07-EXECUTION-CHECKLIST.md +146 -0
  12. package/src/tools/AudioPlayer/README.md +35 -11
  13. package/src/tools/AudioPlayer/{AudioEqualizer.tsx → components/AudioEqualizer.tsx} +29 -64
  14. package/src/tools/AudioPlayer/{AudioPlayer.tsx → components/AudioPlayer.tsx} +22 -14
  15. package/src/tools/AudioPlayer/{AudioShortcutsPopover.tsx → components/AudioShortcutsPopover.tsx} +6 -2
  16. package/src/tools/AudioPlayer/components/ReactiveCover/AudioReactiveCover.tsx +147 -0
  17. package/src/tools/AudioPlayer/components/ReactiveCover/effects/GlowEffect.tsx +110 -0
  18. package/src/tools/AudioPlayer/components/ReactiveCover/effects/MeshEffect.tsx +58 -0
  19. package/src/tools/AudioPlayer/components/ReactiveCover/effects/OrbsEffect.tsx +45 -0
  20. package/src/tools/AudioPlayer/components/ReactiveCover/effects/SpotlightEffect.tsx +82 -0
  21. package/src/tools/AudioPlayer/components/ReactiveCover/effects/index.ts +8 -0
  22. package/src/tools/AudioPlayer/components/ReactiveCover/index.ts +6 -0
  23. package/src/tools/AudioPlayer/{SimpleAudioPlayer.tsx → components/SimpleAudioPlayer.tsx} +12 -7
  24. package/src/tools/AudioPlayer/{VisualizationToggle.tsx → components/VisualizationToggle.tsx} +2 -6
  25. package/src/tools/AudioPlayer/components/index.ts +21 -0
  26. package/src/tools/AudioPlayer/context/AudioProvider.tsx +292 -0
  27. package/src/tools/AudioPlayer/context/index.ts +11 -0
  28. package/src/tools/AudioPlayer/context/selectors.ts +96 -0
  29. package/src/tools/AudioPlayer/hooks/index.ts +29 -0
  30. package/src/tools/AudioPlayer/hooks/useAudioAnalysis.ts +110 -0
  31. package/src/tools/AudioPlayer/{useAudioHotkeys.ts → hooks/useAudioHotkeys.ts} +11 -4
  32. package/src/tools/AudioPlayer/hooks/useSharedWebAudio.ts +106 -0
  33. package/src/tools/AudioPlayer/{useAudioVisualization.tsx → hooks/useVisualization.tsx} +11 -5
  34. package/src/tools/AudioPlayer/index.ts +104 -49
  35. package/src/tools/AudioPlayer/types/audio.ts +107 -0
  36. package/src/tools/AudioPlayer/{types.ts → types/components.ts} +20 -84
  37. package/src/tools/AudioPlayer/types/effects.ts +73 -0
  38. package/src/tools/AudioPlayer/types/index.ts +35 -0
  39. package/src/tools/AudioPlayer/utils/formatTime.ts +10 -0
  40. package/src/tools/AudioPlayer/utils/index.ts +5 -0
  41. package/src/tools/ImageViewer/@refactoring/00-PLAN.md +71 -0
  42. package/src/tools/ImageViewer/@refactoring/01-TYPES.md +121 -0
  43. package/src/tools/ImageViewer/@refactoring/02-UTILS.md +143 -0
  44. package/src/tools/ImageViewer/@refactoring/03-HOOKS.md +261 -0
  45. package/src/tools/ImageViewer/@refactoring/04-COMPONENTS.md +427 -0
  46. package/src/tools/ImageViewer/@refactoring/05-EXECUTION-CHECKLIST.md +126 -0
  47. package/src/tools/ImageViewer/README.md +16 -3
  48. package/src/tools/ImageViewer/components/ImageInfo.tsx +44 -0
  49. package/src/tools/ImageViewer/components/ImageToolbar.tsx +150 -0
  50. package/src/tools/ImageViewer/components/ImageViewer.tsx +235 -0
  51. package/src/tools/ImageViewer/components/index.ts +7 -0
  52. package/src/tools/ImageViewer/hooks/index.ts +9 -0
  53. package/src/tools/ImageViewer/hooks/useImageLoading.ts +153 -0
  54. package/src/tools/ImageViewer/hooks/useImageTransform.ts +101 -0
  55. package/src/tools/ImageViewer/index.ts +47 -3
  56. package/src/tools/ImageViewer/types.ts +75 -0
  57. package/src/tools/ImageViewer/utils/constants.ts +59 -0
  58. package/src/tools/ImageViewer/utils/index.ts +16 -0
  59. package/src/tools/ImageViewer/utils/lqip.ts +47 -0
  60. package/src/tools/VideoPlayer/@refactoring/00-PLAN.md +91 -0
  61. package/src/tools/VideoPlayer/@refactoring/01-TYPES.md +284 -0
  62. package/src/tools/VideoPlayer/@refactoring/02-UTILS.md +141 -0
  63. package/src/tools/VideoPlayer/@refactoring/03-HOOKS.md +178 -0
  64. package/src/tools/VideoPlayer/@refactoring/04-COMPONENTS.md +95 -0
  65. package/src/tools/VideoPlayer/@refactoring/05-EXECUTION-CHECKLIST.md +139 -0
  66. package/src/tools/VideoPlayer/README.md +26 -10
  67. package/src/tools/VideoPlayer/{VideoControls.tsx → components/VideoControls.tsx} +8 -9
  68. package/src/tools/VideoPlayer/{VideoErrorFallback.tsx → components/VideoErrorFallback.tsx} +2 -2
  69. package/src/tools/VideoPlayer/{VideoPlayer.tsx → components/VideoPlayer.tsx} +4 -5
  70. package/src/tools/VideoPlayer/components/index.ts +14 -0
  71. package/src/tools/VideoPlayer/context/VideoPlayerContext.tsx +52 -0
  72. package/src/tools/VideoPlayer/context/index.ts +8 -0
  73. package/src/tools/VideoPlayer/hooks/index.ts +9 -0
  74. package/src/tools/VideoPlayer/hooks/useVideoPositionCache.ts +109 -0
  75. package/src/tools/VideoPlayer/index.ts +29 -20
  76. package/src/tools/VideoPlayer/providers/StreamProvider.tsx +118 -28
  77. package/src/tools/VideoPlayer/providers/VidstackProvider.tsx +89 -11
  78. package/src/tools/VideoPlayer/types/index.ts +38 -0
  79. package/src/tools/VideoPlayer/types/player.ts +116 -0
  80. package/src/tools/VideoPlayer/types/provider.ts +93 -0
  81. package/src/tools/VideoPlayer/types/sources.ts +97 -0
  82. package/src/tools/VideoPlayer/utils/fileSource.ts +78 -0
  83. package/src/tools/VideoPlayer/utils/index.ts +11 -0
  84. package/src/tools/VideoPlayer/utils/resolvers.ts +75 -0
  85. package/src/tools/index.ts +10 -0
  86. package/src/tools/AudioPlayer/AudioReactiveCover.tsx +0 -389
  87. package/src/tools/AudioPlayer/context.tsx +0 -426
  88. package/src/tools/ImageViewer/ImageViewer.tsx +0 -416
  89. package/src/tools/VideoPlayer/VideoPlayerContext.tsx +0 -125
  90. package/src/tools/VideoPlayer/types.ts +0 -367
@@ -1,389 +0,0 @@
1
- 'use client';
2
-
3
- /**
4
- * AudioReactiveCover - Album art with audio-reactive animations
5
- *
6
- * Uses effects utilities for clean data preparation before render.
7
- * Click on cover to switch between effect variants.
8
- *
9
- * Audio analysis is performed in the AudioProvider context to persist
10
- * across variant changes (including switching to 'none' and back).
11
- */
12
-
13
- import { type ReactNode } from 'react';
14
- import { cn } from '@djangocfg/ui-nextjs';
15
- import { useAudioElement } from './context';
16
- import {
17
- type EffectVariant,
18
- type EffectIntensity,
19
- type EffectColorScheme,
20
- getEffectConfig,
21
- prepareEffectColors,
22
- calculateGlowLayers,
23
- calculateOrbs,
24
- calculateMeshGradients,
25
- calculateSpotlight,
26
- EFFECT_ANIMATIONS,
27
- } from './effects';
28
-
29
- // =============================================================================
30
- // TYPES
31
- // =============================================================================
32
-
33
- export interface AudioReactiveCoverProps {
34
- children: ReactNode;
35
- size?: 'sm' | 'md' | 'lg';
36
- variant?: EffectVariant;
37
- intensity?: EffectIntensity;
38
- colorScheme?: EffectColorScheme;
39
- onClick?: () => void;
40
- className?: string;
41
- }
42
-
43
- // =============================================================================
44
- // CONSTANTS
45
- // =============================================================================
46
-
47
- const SIZES = {
48
- sm: { container: 'w-32 h-32', orbBase: 40 },
49
- md: { container: 'w-40 h-40', orbBase: 50 },
50
- lg: { container: 'w-48 h-48', orbBase: 60 },
51
- };
52
-
53
- // =============================================================================
54
- // COMPONENT
55
- // =============================================================================
56
-
57
- export function AudioReactiveCover({
58
- children,
59
- size = 'lg',
60
- variant = 'spotlight',
61
- intensity = 'medium',
62
- colorScheme = 'primary',
63
- onClick,
64
- className,
65
- }: AudioReactiveCoverProps) {
66
- // Get audio levels from context (persists across variant changes)
67
- const { isPlaying, audioLevels: levels } = useAudioElement();
68
-
69
- // =========================================================================
70
- // PREPARE DATA BEFORE RENDER
71
- // =========================================================================
72
-
73
- const sizeConfig = SIZES[size];
74
- const effectConfig = getEffectConfig(intensity);
75
- const { colors, hueShift } = prepareEffectColors(colorScheme, levels);
76
-
77
- // Calculate scale based on overall level
78
- const containerScale = 1 + levels.overall * effectConfig.scale;
79
-
80
- // Prepare effect-specific data - NO memoization for real-time reactivity
81
- const glowData = variant === 'glow' ? {
82
- layers: calculateGlowLayers(levels, effectConfig, colors),
83
- hueShift,
84
- showPulseRings: levels.bass > 0.5,
85
- showSparkle: levels.high > 0.4,
86
- } : null;
87
-
88
- const orbsData = variant === 'orbs'
89
- ? calculateOrbs(levels, effectConfig, colors, sizeConfig.orbBase)
90
- : null;
91
-
92
- const meshData = variant === 'mesh'
93
- ? calculateMeshGradients(levels, effectConfig, colors)
94
- : null;
95
-
96
- const spotlightData = variant === 'spotlight'
97
- ? calculateSpotlight(levels, effectConfig, colors, levels.mid * 360)
98
- : null;
99
-
100
- // =========================================================================
101
- // RENDER
102
- // =========================================================================
103
-
104
- return (
105
- <div
106
- className={cn('relative', sizeConfig.container, className)}
107
- style={{
108
- transform: `scale(${containerScale})`,
109
- transition: 'transform 0.1s ease-out',
110
- }}
111
- >
112
- {/* Effect layers */}
113
- {glowData && (
114
- <GlowEffect data={glowData} colors={colors} isPlaying={isPlaying} />
115
- )}
116
-
117
- {orbsData && (
118
- <OrbsEffect orbs={orbsData} blur={effectConfig.blur} isPlaying={isPlaying} />
119
- )}
120
-
121
- {spotlightData && (
122
- <SpotlightEffect data={spotlightData} colors={colors} blur={effectConfig.blur} isPlaying={isPlaying} />
123
- )}
124
-
125
- {meshData && (
126
- <MeshEffect gradients={meshData} blur={effectConfig.blur} isPlaying={isPlaying} />
127
- )}
128
-
129
- {/* Content (cover art) */}
130
- <div
131
- className="relative w-full h-full rounded-lg overflow-hidden shadow-2xl z-10 bg-background cursor-pointer"
132
- onClick={onClick}
133
- role={onClick ? 'button' : undefined}
134
- tabIndex={onClick ? 0 : undefined}
135
- onKeyDown={onClick ? (e) => e.key === 'Enter' && onClick() : undefined}
136
- >
137
- {children}
138
- </div>
139
-
140
- {/* Inject animations once */}
141
- <style dangerouslySetInnerHTML={{ __html: EFFECT_ANIMATIONS }} />
142
- </div>
143
- );
144
- }
145
-
146
- // =============================================================================
147
- // EFFECT COMPONENTS (Pure render, no logic)
148
- // =============================================================================
149
-
150
- interface GlowEffectData {
151
- layers: ReturnType<typeof calculateGlowLayers>;
152
- hueShift: number;
153
- showPulseRings: boolean;
154
- showSparkle: boolean;
155
- }
156
-
157
- function GlowEffect({
158
- data,
159
- colors,
160
- isPlaying
161
- }: {
162
- data: GlowEffectData;
163
- colors: string[];
164
- isPlaying: boolean;
165
- }) {
166
- const { layers, hueShift, showPulseRings, showSparkle } = data;
167
-
168
- return (
169
- <>
170
- {/* Main glow layers */}
171
- {layers.map((layer, i) => (
172
- <div
173
- key={i}
174
- className={cn('absolute rounded-2xl -z-10', layer.blur)}
175
- style={{
176
- inset: `-${layer.inset}px`,
177
- background: layer.background,
178
- opacity: isPlaying ? layer.opacity : 0,
179
- transform: i < 2 ? `scaleY(${layer.scale})` : `scale(${layer.scale})`,
180
- animation: isPlaying && layer.animation ? layer.animation : 'none',
181
- transition: 'opacity 0.3s',
182
- }}
183
- />
184
- ))}
185
-
186
- {/* Rotating color sweep */}
187
- <div
188
- className="absolute rounded-2xl blur-xl overflow-hidden -z-10"
189
- style={{
190
- inset: '-75px',
191
- opacity: isPlaying ? 0.6 : 0,
192
- transition: 'opacity 0.5s',
193
- }}
194
- >
195
- <div
196
- className="absolute inset-0"
197
- style={{
198
- background: `conic-gradient(
199
- from ${hueShift}deg at 50% 50%,
200
- hsl(${colors[0]} / 0.4) 0deg,
201
- hsl(${colors[1] || colors[0]} / 0.3) 90deg,
202
- hsl(${colors[2] || colors[0]} / 0.3) 180deg,
203
- hsl(${colors[3] || colors[0]} / 0.3) 270deg,
204
- hsl(${colors[0]} / 0.4) 360deg
205
- )`,
206
- animation: isPlaying ? 'glow-rotate 6s linear infinite' : 'none',
207
- }}
208
- />
209
- </div>
210
-
211
- {/* Pulse rings on bass hits */}
212
- {showPulseRings && (
213
- <>
214
- <div
215
- className="absolute -inset-6 rounded-xl border-2 animate-ping -z-10"
216
- style={{
217
- borderColor: `hsl(${colors[0]} / 0.4)`,
218
- animationDuration: '1s',
219
- }}
220
- />
221
- <div
222
- className="absolute -inset-12 rounded-2xl border animate-ping -z-10"
223
- style={{
224
- borderColor: `hsl(${colors[1] || colors[0]} / 0.3)`,
225
- animationDuration: '1.5s',
226
- animationDelay: '0.2s',
227
- }}
228
- />
229
- </>
230
- )}
231
-
232
- {/* Sparkle on high frequencies */}
233
- {showSparkle && (
234
- <div
235
- className="absolute -inset-18 rounded-3xl -z-10"
236
- style={{
237
- background: `radial-gradient(circle at 50% 30%, hsl(${colors[2] || colors[0]} / 0.5) 0%, transparent 30%)`,
238
- animation: 'sparkle-move 0.5s ease-out',
239
- }}
240
- />
241
- )}
242
- </>
243
- );
244
- }
245
-
246
- function OrbsEffect({
247
- orbs,
248
- blur,
249
- isPlaying
250
- }: {
251
- orbs: ReturnType<typeof calculateOrbs>;
252
- blur: string;
253
- isPlaying: boolean;
254
- }) {
255
- return (
256
- <>
257
- {orbs.map((orb, i) => (
258
- <div
259
- key={i}
260
- className={cn('absolute rounded-full -z-10', blur)}
261
- style={{
262
- width: orb.size,
263
- height: orb.size,
264
- left: `${orb.x}%`,
265
- top: `${orb.y}%`,
266
- background: `radial-gradient(circle at 30% 30%, hsl(${orb.color}) 0%, hsl(${orb.color} / 0.5) 40%, transparent 70%)`,
267
- opacity: isPlaying ? orb.opacity : 0,
268
- transform: `translate(-50%, -50%) scale(${orb.scale})`,
269
- transition: 'all 0.08s ease-out',
270
- }}
271
- />
272
- ))}
273
- </>
274
- );
275
- }
276
-
277
- function SpotlightEffect({
278
- data,
279
- colors,
280
- blur,
281
- isPlaying
282
- }: {
283
- data: ReturnType<typeof calculateSpotlight>;
284
- colors: string[];
285
- blur: string;
286
- isPlaying: boolean;
287
- }) {
288
- const inset = 'inset' in data ? data.inset : 12;
289
- const pulseInset = 'pulseInset' in data ? data.pulseInset : 24;
290
- const ringOpacity = 'ringOpacity' in data ? data.ringOpacity : 0.3;
291
- const ringScale = 'ringScale' in data ? data.ringScale : 1;
292
-
293
- return (
294
- <>
295
- {/* Rotating conic gradient - reactive size */}
296
- <div
297
- className={cn('absolute rounded-xl -z-10', blur)}
298
- style={{
299
- inset: `-${inset}px`,
300
- background: `conic-gradient(
301
- from ${data.rotation}deg,
302
- hsl(${colors[0]} / ${data.colors[0]?.opacity || 0.5}),
303
- hsl(${colors[1] || colors[0]} / ${data.colors[1]?.opacity || 0.7}),
304
- hsl(${colors[2] || colors[0]} / ${data.colors[2]?.opacity || 0.5}),
305
- hsl(${colors[0]} / ${data.colors[1]?.opacity || 0.7}),
306
- hsl(${colors[0]} / ${data.colors[0]?.opacity || 0.5})
307
- )`,
308
- opacity: isPlaying ? 1 : 0,
309
- transition: 'all 0.08s ease-out',
310
- }}
311
- />
312
-
313
- {/* Inner border */}
314
- <div
315
- className="absolute -inset-1 rounded-lg bg-background -z-10"
316
- style={{ opacity: isPlaying ? 1 : 0, transition: 'opacity 0.1s' }}
317
- />
318
-
319
- {/* Bass pulse glow - reactive size */}
320
- <div
321
- className={cn('absolute rounded-2xl -z-10', blur)}
322
- style={{
323
- inset: `-${pulseInset}px`,
324
- background: `radial-gradient(circle, hsl(${colors[0]} / 0.7) 0%, hsl(${colors[0]} / 0.3) 50%, transparent 70%)`,
325
- opacity: isPlaying ? data.pulseOpacity : 0,
326
- transform: `scale(${data.pulseScale})`,
327
- transition: 'all 0.08s ease-out',
328
- }}
329
- />
330
-
331
- {/* Outer ring glow */}
332
- <div
333
- className="absolute rounded-3xl -z-10 blur-2xl"
334
- style={{
335
- inset: `-${pulseInset + 30}px`,
336
- background: `radial-gradient(circle, hsl(${colors[1] || colors[0]} / 0.4) 0%, transparent 60%)`,
337
- opacity: isPlaying ? ringOpacity : 0,
338
- transform: `scale(${ringScale})`,
339
- transition: 'all 0.08s ease-out',
340
- }}
341
- />
342
- </>
343
- );
344
- }
345
-
346
- function MeshEffect({
347
- gradients,
348
- isPlaying
349
- }: {
350
- gradients: ReturnType<typeof calculateMeshGradients>;
351
- blur: string;
352
- isPlaying: boolean;
353
- }) {
354
- return (
355
- <>
356
- {gradients.map((g, i) => {
357
- const isCenter = 'isCenter' in g && g.isCenter;
358
- const scale = 'scale' in g ? g.scale : 1;
359
- const rotation = 'rotation' in g ? g.rotation : 0;
360
- const itemBlur = 'blur' in g ? g.blur : 'blur-2xl';
361
-
362
- return (
363
- <div
364
- key={i}
365
- className={cn('absolute rounded-full -z-10', itemBlur)}
366
- style={{
367
- width: g.width,
368
- height: g.height,
369
- top: 'top' in g ? g.top : undefined,
370
- bottom: 'bottom' in g ? g.bottom : undefined,
371
- left: 'left' in g ? g.left : undefined,
372
- right: 'right' in g ? g.right : undefined,
373
- background: isCenter
374
- ? `radial-gradient(circle, hsl(${g.color} / 0.6) 0%, hsl(${g.color} / 0.3) 30%, transparent 60%)`
375
- : `radial-gradient(circle, hsl(${g.color}) 0%, hsl(${g.color} / 0.5) 30%, transparent 65%)`,
376
- opacity: isPlaying ? g.opacity : 0,
377
- transform: isCenter
378
- ? `translate(-50%, -50%) scale(${scale})`
379
- : `scale(${scale}) rotate(${rotation}deg)`,
380
- transition: 'all 0.08s ease-out',
381
- }}
382
- />
383
- );
384
- })}
385
- </>
386
- );
387
- }
388
-
389
- export default AudioReactiveCover;