@djangocfg/layouts 1.4.27 → 1.4.29

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 (118) hide show
  1. package/package.json +8 -8
  2. package/src/auth/context/AuthContext.tsx +4 -1
  3. package/src/auth/hooks/index.ts +2 -0
  4. package/src/auth/hooks/useAuthForm.ts +2 -0
  5. package/src/auth/hooks/useAuthGuard.ts +2 -0
  6. package/src/auth/hooks/useAutoAuth.ts +16 -11
  7. package/src/auth/hooks/useLocalStorage.ts +2 -0
  8. package/src/auth/hooks/useProfileCache.ts +2 -0
  9. package/src/auth/hooks/useSessionStorage.ts +2 -0
  10. package/src/auth/middlewares/index.ts +1 -1
  11. package/src/auth/middlewares/proxy.ts +10 -2
  12. package/src/layouts/AppLayout/AppLayout.tsx +9 -7
  13. package/src/layouts/AppLayout/components/ErrorBoundary.tsx +6 -3
  14. package/src/layouts/AppLayout/components/PageProgress.tsx +2 -0
  15. package/src/layouts/AppLayout/components/Seo.tsx +2 -0
  16. package/src/layouts/AppLayout/components/UpdateNotifier/UpdateNotifier.tsx +3 -2
  17. package/src/layouts/AppLayout/hooks/index.ts +2 -0
  18. package/src/layouts/AppLayout/hooks/useNavigation.ts +3 -1
  19. package/src/layouts/AppLayout/layouts/AdminLayout/AdminLayout.tsx +1 -0
  20. package/src/layouts/AppLayout/layouts/AuthLayout/AuthContext.tsx +2 -0
  21. package/src/layouts/AppLayout/layouts/AuthLayout/IdentifierForm.tsx +2 -0
  22. package/src/layouts/AppLayout/layouts/AuthLayout/OTPForm.tsx +4 -0
  23. package/src/layouts/AppLayout/layouts/AuthLayout/index.ts +2 -0
  24. package/src/layouts/AppLayout/layouts/PrivateLayout/PrivateLayout.tsx +1 -0
  25. package/src/layouts/AppLayout/providers/CoreProviders.tsx +1 -0
  26. package/src/layouts/PaymentsLayout/PaymentsLayout.tsx +1 -0
  27. package/src/layouts/PaymentsLayout/components/CreatePaymentDialog.tsx +1 -0
  28. package/src/layouts/PaymentsLayout/events.ts +2 -0
  29. package/src/layouts/ProfileLayout/ProfileLayout.tsx +1 -0
  30. package/src/layouts/ProfileLayout/components/ProfileForm.tsx +1 -0
  31. package/src/layouts/SimpleLayout/SimpleLayout.tsx +72 -0
  32. package/src/layouts/SimpleLayout/index.ts +3 -0
  33. package/src/layouts/SupportLayout/SupportLayout.tsx +1 -0
  34. package/src/layouts/SupportLayout/components/CreateTicketDialog.tsx +1 -0
  35. package/src/layouts/SupportLayout/components/TicketList.tsx +6 -5
  36. package/src/layouts/SupportLayout/events.ts +2 -0
  37. package/src/layouts/index.ts +1 -3
  38. package/src/snippets/AuthDialog/useAuthDialog.ts +2 -0
  39. package/src/snippets/Chat/components/MessageList.tsx +12 -11
  40. package/src/snippets/Chat/index.tsx +1 -0
  41. package/src/snippets/ContactForm/ContactForm.tsx +7 -2
  42. package/src/snippets/ContactForm/ContactPage.tsx +16 -3
  43. package/src/snippets/VideoPlayer/README.md +35 -0
  44. package/src/snippets/VideoPlayer/VideoControls.tsx +13 -9
  45. package/src/snippets/VideoPlayer/VideoPlayer.tsx +159 -25
  46. package/src/snippets/VideoPlayer/index.ts +1 -1
  47. package/src/validation/utils/curl-generator.ts +5 -1
  48. package/src/layouts/UILayout/README.md +0 -267
  49. package/src/layouts/UILayout/SUMMARY.md +0 -298
  50. package/src/layouts/UILayout/TOOLS_INTEGRATION.md +0 -216
  51. package/src/layouts/UILayout/components/AutoComponentDemo.tsx +0 -77
  52. package/src/layouts/UILayout/components/CategoryRenderer.tsx +0 -45
  53. package/src/layouts/UILayout/components/TailwindGuideRenderer.tsx +0 -138
  54. package/src/layouts/UILayout/components/index.ts +0 -15
  55. package/src/layouts/UILayout/components/layout/Header/CopyAIButton.tsx +0 -58
  56. package/src/layouts/UILayout/components/layout/Header/Header.tsx +0 -60
  57. package/src/layouts/UILayout/components/layout/Header/HeaderDesktop.tsx +0 -51
  58. package/src/layouts/UILayout/components/layout/Header/HeaderMobile.tsx +0 -71
  59. package/src/layouts/UILayout/components/layout/Header/TestValidationButton.tsx +0 -268
  60. package/src/layouts/UILayout/components/layout/Header/index.ts +0 -11
  61. package/src/layouts/UILayout/components/layout/MobileOverlay/MobileOverlay.tsx +0 -47
  62. package/src/layouts/UILayout/components/layout/MobileOverlay/index.ts +0 -6
  63. package/src/layouts/UILayout/components/layout/Sidebar/Sidebar.tsx +0 -95
  64. package/src/layouts/UILayout/components/layout/Sidebar/SidebarCategory.tsx +0 -54
  65. package/src/layouts/UILayout/components/layout/Sidebar/SidebarContent.tsx +0 -93
  66. package/src/layouts/UILayout/components/layout/Sidebar/SidebarFooter.tsx +0 -49
  67. package/src/layouts/UILayout/components/layout/Sidebar/index.ts +0 -9
  68. package/src/layouts/UILayout/components/layout/index.ts +0 -8
  69. package/src/layouts/UILayout/components/shared/Badge/CountBadge.tsx +0 -38
  70. package/src/layouts/UILayout/components/shared/Badge/index.ts +0 -5
  71. package/src/layouts/UILayout/components/shared/CodeBlock/CodeBlock.tsx +0 -48
  72. package/src/layouts/UILayout/components/shared/CodeBlock/CopyButton.tsx +0 -49
  73. package/src/layouts/UILayout/components/shared/CodeBlock/index.ts +0 -6
  74. package/src/layouts/UILayout/components/shared/Section/Section.tsx +0 -63
  75. package/src/layouts/UILayout/components/shared/Section/index.ts +0 -5
  76. package/src/layouts/UILayout/components/shared/index.ts +0 -8
  77. package/src/layouts/UILayout/config/ai-export.config.ts +0 -89
  78. package/src/layouts/UILayout/config/categories.config.tsx +0 -122
  79. package/src/layouts/UILayout/config/components/blocks.config.tsx +0 -239
  80. package/src/layouts/UILayout/config/components/data.config.tsx +0 -433
  81. package/src/layouts/UILayout/config/components/feedback.config.tsx +0 -290
  82. package/src/layouts/UILayout/config/components/forms.config.tsx +0 -996
  83. package/src/layouts/UILayout/config/components/hooks.config.tsx +0 -168
  84. package/src/layouts/UILayout/config/components/index.ts +0 -72
  85. package/src/layouts/UILayout/config/components/layout.config.tsx +0 -246
  86. package/src/layouts/UILayout/config/components/navigation.config.tsx +0 -352
  87. package/src/layouts/UILayout/config/components/overlay.config.tsx +0 -569
  88. package/src/layouts/UILayout/config/components/specialized.config.tsx +0 -400
  89. package/src/layouts/UILayout/config/components/tools.config.tsx +0 -234
  90. package/src/layouts/UILayout/config/components/types.ts +0 -14
  91. package/src/layouts/UILayout/config/index.ts +0 -42
  92. package/src/layouts/UILayout/config/tailwind.config.ts +0 -131
  93. package/src/layouts/UILayout/constants.ts +0 -23
  94. package/src/layouts/UILayout/context/ShowcaseContext.tsx +0 -81
  95. package/src/layouts/UILayout/context/index.ts +0 -1
  96. package/src/layouts/UILayout/core/UIGuideApp.client.tsx +0 -18
  97. package/src/layouts/UILayout/core/UIGuideApp.tsx +0 -33
  98. package/src/layouts/UILayout/core/UIGuideLanding.tsx +0 -172
  99. package/src/layouts/UILayout/core/UIGuideView.tsx +0 -61
  100. package/src/layouts/UILayout/core/UILayout.tsx +0 -125
  101. package/src/layouts/UILayout/core/UILayoutSidebar.tsx +0 -11
  102. package/src/layouts/UILayout/core/index.ts +0 -10
  103. package/src/layouts/UILayout/hooks/index.ts +0 -9
  104. package/src/layouts/UILayout/hooks/useAIExport.ts +0 -78
  105. package/src/layouts/UILayout/hooks/useCategoryNavigation.ts +0 -92
  106. package/src/layouts/UILayout/hooks/useComponentSearch.ts +0 -81
  107. package/src/layouts/UILayout/hooks/useSidebarState.ts +0 -36
  108. package/src/layouts/UILayout/index.ts +0 -160
  109. package/src/layouts/UILayout/types/component.ts +0 -45
  110. package/src/layouts/UILayout/types/index.ts +0 -23
  111. package/src/layouts/UILayout/types/layout.ts +0 -57
  112. package/src/layouts/UILayout/types/navigation.ts +0 -33
  113. package/src/layouts/UILayout/utils/ai-export/formatters.ts +0 -71
  114. package/src/layouts/UILayout/utils/ai-export/index.ts +0 -5
  115. package/src/layouts/UILayout/utils/component-helpers/filter.ts +0 -109
  116. package/src/layouts/UILayout/utils/component-helpers/index.ts +0 -6
  117. package/src/layouts/UILayout/utils/component-helpers/search.ts +0 -95
  118. package/src/layouts/UILayout/utils/index.ts +0 -6
@@ -79,6 +79,41 @@ function AdvancedPlayer() {
79
79
 
80
80
  ## Supported Video Sources
81
81
 
82
+ ### YouTube
83
+ - **URL Format**: `https://www.youtube.com/watch?v=VIDEO_ID` or `youtube/VIDEO_ID`
84
+ - **Auto-conversion**: Full YouTube URLs are automatically converted to `youtube/ID` format
85
+ - **Poster**: ⚠️ YouTube iframe ignores custom poster images and always shows YouTube's thumbnail
86
+ - **Examples**:
87
+ ```tsx
88
+ url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'
89
+ url: 'https://youtu.be/dQw4w9WgXcQ'
90
+ url: 'youtube/dQw4w9WgXcQ'
91
+ ```
92
+
93
+ ### Vimeo
94
+ - **URL Format**: `https://vimeo.com/VIDEO_ID` or `vimeo/VIDEO_ID`
95
+ - **Auto-conversion**: Full Vimeo URLs are automatically converted to `vimeo/ID` format
96
+ - **Poster**: ⚠️ Vimeo may ignore custom poster and use their own thumbnail
97
+ - **Example**: `url: 'vimeo/76979871'`
98
+
99
+ ### Direct Video Files (MP4, WebM, OGG)
100
+ - **Poster**: ✅ **Works perfectly!** Custom poster images are fully supported
101
+ - **Examples**:
102
+ ```tsx
103
+ url: 'https://example.com/video.mp4',
104
+ poster: '/images/video-poster.jpg' // This works!
105
+ ```
106
+
107
+ ### HLS Streams
108
+ - **Poster**: ✅ Custom poster supported
109
+ - **Example**: `url: 'https://example.com/stream.m3u8'`
110
+
111
+ ### DASH Streams
112
+ - **Poster**: ✅ Custom poster supported
113
+ - **Example**: `url: 'https://example.com/stream.mpd'`
114
+
115
+ > **Note**: The `poster` prop works for direct video files, HLS, and DASH streams. For YouTube and Vimeo, the platform's own thumbnail is displayed regardless of the `poster` prop due to iframe limitations.
116
+
82
117
  ### YouTube
83
118
  ```tsx
84
119
  <VideoPlayer
@@ -6,12 +6,12 @@
6
6
 
7
7
  import React from 'react';
8
8
  import { useMediaStore, useMediaRemote } from '@vidstack/react';
9
- import type { MediaPlayerElement } from 'vidstack';
9
+ import type { MediaPlayerInstance } from '@vidstack/react';
10
10
  import { Play, Pause, Volume2, VolumeX, Maximize, Minimize } from 'lucide-react';
11
11
  import { cn } from '@djangocfg/ui';
12
12
 
13
13
  interface VideoControlsProps {
14
- player: React.RefObject<MediaPlayerElement | null>;
14
+ player: React.RefObject<MediaPlayerInstance | null>;
15
15
  className?: string;
16
16
  }
17
17
 
@@ -55,13 +55,17 @@ export function VideoControls({ player, className }: VideoControlsProps) {
55
55
  const progress = duration > 0 ? (currentTime / duration) * 100 : 0;
56
56
 
57
57
  return (
58
- <div className={cn(
59
- "absolute inset-0 flex flex-col justify-end opacity-0 hover:opacity-100 focus-within:opacity-100 transition-opacity duration-300",
60
- "bg-gradient-to-t from-black/80 via-black/20 to-transparent",
61
- className
62
- )}>
58
+ <div
59
+ className={cn(
60
+ "absolute inset-0 flex flex-col justify-end transition-opacity duration-300",
61
+ "bg-gradient-to-t from-black/80 via-black/20 to-transparent",
62
+ "opacity-0 group-hover:opacity-100 focus-within:opacity-100",
63
+ "pointer-events-none group-hover:pointer-events-auto",
64
+ className
65
+ )}
66
+ >
63
67
  {/* Progress Bar */}
64
- <div className="px-4 pb-2">
68
+ <div className="px-4 pb-2 pointer-events-auto">
65
69
  <div
66
70
  className="h-1.5 bg-white/20 rounded-full cursor-pointer hover:h-2 transition-all group"
67
71
  onClick={handleProgressClick}
@@ -76,7 +80,7 @@ export function VideoControls({ player, className }: VideoControlsProps) {
76
80
  </div>
77
81
 
78
82
  {/* Control Bar */}
79
- <div className="flex items-center gap-4 px-4 pb-4">
83
+ <div className="flex items-center gap-4 px-4 pb-4 pointer-events-auto">
80
84
  {/* Play/Pause */}
81
85
  <button
82
86
  onClick={() => remote.togglePaused()}
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck
1
2
  /**
2
3
  * Professional VideoPlayer - Vidstack Implementation
3
4
  * Supports YouTube, Vimeo, MP4, HLS and more with custom controls
@@ -5,12 +6,103 @@
5
6
 
6
7
  'use client';
7
8
 
8
- import React, { forwardRef, useImperativeHandle, useRef } from 'react';
9
- import { MediaPlayer, MediaOutlet } from '@vidstack/react';
10
- import { MediaRemoteControl, type MediaPlayerElement } from 'vidstack';
9
+ import React, { forwardRef, useImperativeHandle, useRef, useMemo } from 'react';
10
+ import { MediaPlayer, MediaProvider, Poster } from '@vidstack/react';
11
+ import { defaultLayoutIcons, DefaultVideoLayout } from '@vidstack/react/player/layouts/default';
12
+ import type { MediaPlayerInstance } from '@vidstack/react';
13
+ import consola from 'consola';
11
14
  import { cn } from '@djangocfg/ui';
12
15
  import { type VideoPlayerProps, type VideoPlayerRef } from './types';
13
- import { VideoControls } from './VideoControls';
16
+
17
+ // Import Vidstack base styles
18
+ import '@vidstack/react/player/styles/base.css';
19
+ // Import default theme
20
+ import '@vidstack/react/player/styles/default/theme.css';
21
+ import '@vidstack/react/player/styles/default/layouts/video.css';
22
+
23
+ /**
24
+ * Custom error class for invalid video URLs
25
+ */
26
+ export class VideoUrlError extends Error {
27
+ constructor(
28
+ message: string,
29
+ public readonly url: string,
30
+ public readonly suggestion?: string
31
+ ) {
32
+ super(message);
33
+ this.name = 'VideoUrlError';
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Validates and normalizes video URL for Vidstack
39
+ * @throws {VideoUrlError} If URL format is invalid for the detected provider
40
+ */
41
+ function normalizeVideoUrl(url: string): string {
42
+ if (!url || typeof url !== 'string') {
43
+ throw new VideoUrlError('Video URL is required', url || '');
44
+ }
45
+
46
+ const trimmedUrl = url.trim();
47
+
48
+ // Already in correct format (youtube/ID, vimeo/ID, or direct URL)
49
+ if (trimmedUrl.startsWith('youtube/') || trimmedUrl.startsWith('vimeo/')) {
50
+ return trimmedUrl;
51
+ }
52
+
53
+ // YouTube URL patterns - auto-convert to youtube/ID format
54
+ const youtubePatterns = [
55
+ /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([a-zA-Z0-9_-]{11})/,
56
+ /youtube\.com\/shorts\/([a-zA-Z0-9_-]{11})/,
57
+ ];
58
+
59
+ for (const pattern of youtubePatterns) {
60
+ const match = trimmedUrl.match(pattern);
61
+ if (match) {
62
+ // Auto-convert YouTube URL to youtube/ID format
63
+ const videoId = match[1];
64
+ if (process.env.NODE_ENV === 'development') {
65
+ consola.info(
66
+ `[VideoPlayer] Auto-converted YouTube URL to "youtube/${videoId}" format`
67
+ );
68
+ }
69
+ return `youtube/${videoId}`;
70
+ }
71
+ }
72
+
73
+ // Vimeo URL patterns - auto-convert to vimeo/ID format
74
+ const vimeoPattern = /vimeo\.com\/(\d+)/;
75
+ const vimeoMatch = trimmedUrl.match(vimeoPattern);
76
+ if (vimeoMatch) {
77
+ const videoId = vimeoMatch[1];
78
+ if (process.env.NODE_ENV === 'development') {
79
+ consola.info(
80
+ `[VideoPlayer] Auto-converted Vimeo URL to "vimeo/${videoId}" format`
81
+ );
82
+ }
83
+ return `vimeo/${videoId}`;
84
+ }
85
+
86
+ // Direct video URLs (mp4, webm, etc.) - allow as-is
87
+ if (/\.(mp4|webm|ogg|m3u8|mpd)(\?.*)?$/i.test(trimmedUrl)) {
88
+ return trimmedUrl;
89
+ }
90
+
91
+ // HLS/DASH streams
92
+ if (trimmedUrl.includes('.m3u8') || trimmedUrl.includes('.mpd')) {
93
+ return trimmedUrl;
94
+ }
95
+
96
+ // Unknown format - return as-is but warn in dev
97
+ if (process.env.NODE_ENV === 'development') {
98
+ consola.warn(
99
+ `[VideoPlayer] Unknown URL format: "${trimmedUrl}". ` +
100
+ `Supported formats: youtube/{id}, vimeo/{id}, or direct video URLs (.mp4, .webm, .m3u8)`
101
+ );
102
+ }
103
+
104
+ return trimmedUrl;
105
+ }
14
106
 
15
107
  export const VideoPlayer = forwardRef<VideoPlayerRef, VideoPlayerProps>(({
16
108
  source,
@@ -27,26 +119,54 @@ export const VideoPlayer = forwardRef<VideoPlayerRef, VideoPlayerProps>(({
27
119
  onEnded,
28
120
  onError,
29
121
  }, ref) => {
30
- const playerRef = useRef<MediaPlayerElement | null>(null);
122
+ const playerRef = useRef<MediaPlayerInstance | null>(null);
123
+
124
+ // Debug log
125
+ if (process.env.NODE_ENV === 'development') {
126
+ consola.info('[VideoPlayer] Received props:', {
127
+ url: source.url,
128
+ poster: source.poster,
129
+ title: source.title,
130
+ });
131
+ }
132
+
133
+ // Validate and normalize URL - throws VideoUrlError if invalid
134
+ const normalizedUrl = useMemo(() => {
135
+ try {
136
+ return normalizeVideoUrl(source.url);
137
+ } catch (error) {
138
+ if (error instanceof VideoUrlError) {
139
+ // Call onError callback and re-throw
140
+ onError?.(error.message + (error.suggestion ? ` Use: "${error.suggestion}"` : ''));
141
+ throw error;
142
+ }
143
+ throw error;
144
+ }
145
+ }, [source.url, onError]);
31
146
 
32
147
  // Expose player methods via ref
33
148
  useImperativeHandle(ref, () => {
34
- const getRemote = () => {
35
- if (!playerRef.current) return null;
36
- const remote = new MediaRemoteControl();
37
- remote.setTarget(playerRef.current as unknown as EventTarget);
38
- return remote;
39
- };
149
+ const player = playerRef.current;
40
150
 
41
151
  return {
42
- play: () => getRemote()?.play(),
43
- pause: () => getRemote()?.pause(),
44
- togglePlay: () => getRemote()?.togglePaused(),
45
- seekTo: (time: number) => getRemote()?.seek(time),
46
- setVolume: (volume: number) => getRemote()?.changeVolume(Math.max(0, Math.min(1, volume))),
47
- toggleMute: () => getRemote()?.toggleMuted(),
48
- enterFullscreen: () => getRemote()?.enterFullscreen(),
49
- exitFullscreen: () => getRemote()?.exitFullscreen(),
152
+ play: () => player?.play(),
153
+ pause: () => player?.pause(),
154
+ togglePlay: () => {
155
+ if (player) {
156
+ player.paused ? player.play() : player.pause();
157
+ }
158
+ },
159
+ seekTo: (time: number) => {
160
+ if (player) player.currentTime = time;
161
+ },
162
+ setVolume: (volume: number) => {
163
+ if (player) player.volume = Math.max(0, Math.min(1, volume));
164
+ },
165
+ toggleMute: () => {
166
+ if (player) player.muted = !player.muted;
167
+ },
168
+ enterFullscreen: () => player?.enterFullscreen(),
169
+ exitFullscreen: () => player?.exitFullscreen(),
50
170
  };
51
171
  }, []);
52
172
 
@@ -71,7 +191,7 @@ export const VideoPlayer = forwardRef<VideoPlayerRef, VideoPlayerProps>(({
71
191
  {/* Video Player */}
72
192
  <div
73
193
  className={cn(
74
- "relative w-full overflow-hidden rounded-sm bg-black",
194
+ "relative w-full rounded-sm bg-black overflow-hidden",
75
195
  theme === 'minimal' && "rounded-none",
76
196
  theme === 'modern' && "rounded-xl shadow-2xl"
77
197
  )}
@@ -80,8 +200,7 @@ export const VideoPlayer = forwardRef<VideoPlayerRef, VideoPlayerProps>(({
80
200
  <MediaPlayer
81
201
  ref={playerRef}
82
202
  title={source.title || 'Video'}
83
- src={source.url}
84
- poster={source.poster}
203
+ src={normalizedUrl}
85
204
  autoPlay={autoplay}
86
205
  muted={muted}
87
206
  playsInline={playsInline}
@@ -91,10 +210,25 @@ export const VideoPlayer = forwardRef<VideoPlayerRef, VideoPlayerProps>(({
91
210
  onError={handleError}
92
211
  className="w-full h-full"
93
212
  >
94
- <MediaOutlet />
213
+ <MediaProvider />
95
214
 
96
- {/* Custom controls */}
97
- {controls && <VideoControls player={playerRef} />}
215
+ {/* Poster with proper aspect ratio handling */}
216
+ {source.poster && (
217
+ <Poster
218
+ className="vds-poster"
219
+ src={source.poster}
220
+ alt={source.title || 'Video poster'}
221
+ style={{ objectFit: 'cover' }}
222
+ />
223
+ )}
224
+
225
+ {/* Use Vidstack's built-in default layout */}
226
+ {controls && (
227
+ <DefaultVideoLayout
228
+ icons={defaultLayoutIcons}
229
+ thumbnails={source.poster}
230
+ />
231
+ )}
98
232
  </MediaPlayer>
99
233
  </div>
100
234
 
@@ -3,6 +3,6 @@
3
3
  * Export all components and types
4
4
  */
5
5
 
6
- export { VideoPlayer } from './VideoPlayer';
6
+ export { VideoPlayer, VideoUrlError } from './VideoPlayer';
7
7
  export { VideoControls } from './VideoControls';
8
8
  export type { VideoSource, VideoPlayerProps, VideoPlayerRef } from './types';
@@ -1,9 +1,13 @@
1
+ "use client"
2
+
1
3
  /**
2
4
  * cURL Generator
3
5
  *
4
6
  * Generates cURL commands from API request details with authentication token
5
7
  */
6
8
 
9
+ import consola from 'consola';
10
+
7
11
  export interface CurlOptions {
8
12
  method: string;
9
13
  path: string;
@@ -28,7 +32,7 @@ export function getAuthToken(): string | null {
28
32
 
29
33
  return token;
30
34
  } catch (error) {
31
- console.error('Failed to get auth token:', error);
35
+ consola.error('Failed to get auth token:', error);
32
36
  return null;
33
37
  }
34
38
  }
@@ -1,267 +0,0 @@
1
- # UILayout - Config-Driven UI Documentation System
2
-
3
- Modern, type-safe layout system for showcasing UI component libraries with built-in "Copy for AI" functionality.
4
-
5
- ## 🎯 Key Features
6
-
7
- - **Config-Driven**: Single source of truth for all component documentation
8
- - **Type-Safe**: Full TypeScript support with strict typing
9
- - **Auto-Rendering**: Components automatically rendered from config
10
- - **AI-Ready**: One-click export of entire documentation for AI consumption
11
- - **Responsive**: Mobile-first design with adaptive sidebar
12
- - **Dark Mode**: Built-in theme support
13
- - **Organized**: Modular config structure by category
14
-
15
- ## 📁 Structure
16
-
17
- ```
18
- UILayout/
19
- ├── config/ # All configuration (Single Source of Truth)
20
- │ ├── components/ # Component configs by category
21
- │ │ ├── forms.config.tsx # 8 form components
22
- │ │ ├── layout.config.tsx # 5 layout components
23
- │ │ ├── navigation.config.tsx # 4 navigation components
24
- │ │ ├── overlay.config.tsx # 11 overlay components
25
- │ │ ├── feedback.config.tsx # 5 feedback components
26
- │ │ ├── data.config.tsx # 5 data display components
27
- │ │ ├── specialized.config.tsx # 2 specialized components
28
- │ │ ├── blocks.config.tsx # 7 landing page blocks
29
- │ │ ├── hooks.config.tsx # 6 custom hooks
30
- │ │ └── index.ts # Aggregates all configs
31
- │ ├── categories.config.tsx # Category definitions
32
- │ ├── tailwind.config.ts # Tailwind 4 guidelines
33
- │ ├── ai-export.config.ts # AI context generator
34
- │ └── index.ts # Main config exports
35
- ├── components/ # React components
36
- │ ├── AutoComponentDemo.tsx # Auto-renders from config
37
- │ ├── CategoryRenderer.tsx # Renders entire category
38
- │ ├── TailwindGuideRenderer.tsx # Renders Tailwind guide
39
- │ ├── Header.tsx # Header with "Copy for AI"
40
- │ ├── Sidebar.tsx
41
- │ └── MobileOverlay.tsx
42
- ├── context/ # React Context
43
- │ └── ShowcaseContext.tsx # Navigation state management
44
- ├── UILayout.tsx # Main layout component
45
- ├── UIGuideView.tsx # Complete UI guide view
46
- ├── UIGuideLanding.tsx # Landing page
47
- └── UIGuideApp.tsx # Full app wrapper
48
- ```
49
-
50
- ## 🚀 Quick Start
51
-
52
- ### Basic Usage
53
-
54
- ```tsx
55
- import { UILayout, CATEGORIES } from '@djangocfg/layouts';
56
-
57
- function MyComponentGuide() {
58
- const [category, setCategory] = useState('forms');
59
-
60
- return (
61
- <UILayout
62
- title="My Component Library"
63
- categories={CATEGORIES}
64
- currentCategory={category}
65
- onCategoryChange={setCategory}
66
- >
67
- {/* Your content */}
68
- </UILayout>
69
- );
70
- }
71
- ```
72
-
73
- ### Using Category Renderer (Auto-render from config)
74
-
75
- ```tsx
76
- import { UILayout, CATEGORIES, CategoryRenderer } from '@djangocfg/layouts';
77
-
78
- function MyComponentGuide() {
79
- const [category, setCategory] = useState('forms');
80
-
81
- return (
82
- <UILayout
83
- title="My Component Library"
84
- categories={CATEGORIES}
85
- currentCategory={category}
86
- onCategoryChange={setCategory}
87
- >
88
- {/* Automatically renders all components in category */}
89
- <CategoryRenderer categoryId={category} />
90
- </UILayout>
91
- );
92
- }
93
- ```
94
-
95
- ### Using Complete UI Guide
96
-
97
- ```tsx
98
- import { UIGuideApp } from '@djangocfg/layouts';
99
-
100
- // Complete pre-configured UI guide with all components
101
- export default function Page() {
102
- return <UIGuideApp />;
103
- }
104
- ```
105
-
106
- ## 📝 Adding New Components
107
-
108
- ### 1. Add to Config
109
-
110
- Edit the appropriate config file in `config/components/`:
111
-
112
- ```tsx
113
- // config/components/forms.config.tsx
114
- export const FORM_COMPONENTS: ComponentConfig[] = [
115
- {
116
- name: 'MyComponent',
117
- category: 'forms',
118
- description: 'A custom form component',
119
- importPath: "import { MyComponent } from '@mylib/ui';",
120
- example: `<MyComponent value="test" onChange={handler} />`,
121
- preview: <MyComponent value="test" onChange={() => {}} />
122
- },
123
- // ... other components
124
- ];
125
- ```
126
-
127
- ### 2. That's It!
128
-
129
- No need to:
130
- - ❌ Create separate demo files
131
- - ❌ Write duplicate rendering code
132
- - ❌ Update multiple places
133
-
134
- The component automatically:
135
- - ✅ Appears in the UI guide
136
- - ✅ Gets included in "Copy for AI"
137
- - ✅ Shows in the category with proper formatting
138
-
139
- ## 🤖 Copy for AI Feature
140
-
141
- Click the "Copy for AI" button in the header to export entire documentation including:
142
-
143
- - ✅ Tailwind CSS v4 guidelines and best practices
144
- - ✅ All 53 components with full examples
145
- - ✅ Import statements
146
- - ✅ Usage examples
147
- - ✅ Properly formatted for AI consumption
148
-
149
- Perfect for giving AI assistants complete context about your UI library!
150
-
151
- ## 📊 Component Statistics
152
-
153
- | Category | Components |
154
- |----------|------------|
155
- | Forms | 8 |
156
- | Layout | 5 |
157
- | Navigation | 4 |
158
- | Overlay | 11 |
159
- | Feedback | 5 |
160
- | Data Display | 5 |
161
- | Specialized | 2 |
162
- | Blocks | 7 |
163
- | Hooks | 6 |
164
- | **Total** | **53** |
165
-
166
- ## 🎨 Customization
167
-
168
- ### Custom Categories
169
-
170
- ```tsx
171
- import type { ComponentCategory } from '@djangocfg/layouts';
172
-
173
- const customCategories: ComponentCategory[] = [
174
- {
175
- id: 'custom',
176
- label: 'My Category',
177
- icon: <MyIcon />,
178
- count: 5,
179
- description: 'Custom component category'
180
- }
181
- ];
182
- ```
183
-
184
- ### Custom Config
185
-
186
- ```tsx
187
- import type { ComponentConfig } from '@djangocfg/layouts';
188
-
189
- const customComponents: ComponentConfig[] = [
190
- {
191
- name: 'Component',
192
- category: 'custom',
193
- description: '...',
194
- importPath: '...',
195
- example: '...',
196
- preview: <Component />
197
- }
198
- ];
199
- ```
200
-
201
- ## 🔧 API Reference
202
-
203
- ### UILayout Props
204
-
205
- ```typescript
206
- interface UILayoutProps {
207
- children: ReactNode;
208
- title?: string;
209
- description?: string;
210
- categories: ComponentCategory[];
211
- currentCategory?: string;
212
- onCategoryChange?: (categoryId: string) => void;
213
- logo?: ReactNode;
214
- projectName?: string;
215
- }
216
- ```
217
-
218
- ### ComponentConfig Type
219
-
220
- ```typescript
221
- interface ComponentConfig {
222
- name: string; // Component name
223
- category: string; // Category ID
224
- description: string; // Short description
225
- importPath: string; // Import statement
226
- example: string; // Code example
227
- preview: ReactNode; // Live preview component
228
- }
229
- ```
230
-
231
- ### Available Exports
232
-
233
- ```typescript
234
- // Components
235
- export { UILayout, CategoryRenderer, AutoComponentDemo };
236
-
237
- // Views
238
- export { UIGuideView, UIGuideApp, UIGuideLanding };
239
-
240
- // Config
241
- export {
242
- CATEGORIES,
243
- COMPONENTS_CONFIG,
244
- generateAIContext
245
- };
246
-
247
- // Types
248
- export type {
249
- ComponentConfig,
250
- ComponentCategory,
251
- UILayoutProps
252
- };
253
- ```
254
-
255
- ## 🎯 Benefits
256
-
257
- 1. **Single Source of Truth**: All component data in one place
258
- 2. **Zero Duplication**: Write component config once, use everywhere
259
- 3. **Type-Safe**: Full TypeScript support prevents errors
260
- 4. **Auto-Update**: Add to config, automatically appears everywhere
261
- 5. **AI-Friendly**: Built-in export for AI assistants
262
- 6. **Maintainable**: Easy to update and extend
263
- 7. **Scalable**: Add categories and components effortlessly
264
-
265
- ## 📖 More Info
266
-
267
- See the main package README for complete documentation and examples.