radtools 0.1.0

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 (133) hide show
  1. package/README.md +108 -0
  2. package/bin/radtools.js +5 -0
  3. package/dist/cli/index.js +427 -0
  4. package/package.json +55 -0
  5. package/templates/api-routes/assets/optimize/route.ts +94 -0
  6. package/templates/api-routes/assets/route.ts +159 -0
  7. package/templates/api-routes/components/create-folder/route.ts +55 -0
  8. package/templates/api-routes/components/route.ts +156 -0
  9. package/templates/api-routes/fonts/route.ts +96 -0
  10. package/templates/api-routes/fonts/upload/route.ts +79 -0
  11. package/templates/api-routes/read-css/route.ts +29 -0
  12. package/templates/api-routes/write-css/route.ts +423 -0
  13. package/templates/components/Rad_os/AppWindow.tsx +423 -0
  14. package/templates/components/Rad_os/MobileAppModal.tsx +76 -0
  15. package/templates/components/Rad_os/WindowTitleBar.tsx +290 -0
  16. package/templates/components/icons/Icon.tsx +224 -0
  17. package/templates/components/icons/README.md +85 -0
  18. package/templates/components/icons/index.ts +20 -0
  19. package/templates/components/icons.tsx +164 -0
  20. package/templates/components/ui/Accordion.tsx +268 -0
  21. package/templates/components/ui/Alert.tsx +111 -0
  22. package/templates/components/ui/Badge.tsx +87 -0
  23. package/templates/components/ui/Breadcrumbs.tsx +88 -0
  24. package/templates/components/ui/Button.tsx +249 -0
  25. package/templates/components/ui/Card.tsx +137 -0
  26. package/templates/components/ui/Checkbox.tsx +137 -0
  27. package/templates/components/ui/ContextMenu.tsx +220 -0
  28. package/templates/components/ui/Dialog.tsx +264 -0
  29. package/templates/components/ui/Divider.tsx +70 -0
  30. package/templates/components/ui/DropdownMenu.tsx +301 -0
  31. package/templates/components/ui/HelpPanel.tsx +119 -0
  32. package/templates/components/ui/Input.tsx +176 -0
  33. package/templates/components/ui/Popover.tsx +211 -0
  34. package/templates/components/ui/Progress.tsx +158 -0
  35. package/templates/components/ui/Select.tsx +134 -0
  36. package/templates/components/ui/Sheet.tsx +316 -0
  37. package/templates/components/ui/Slider.tsx +223 -0
  38. package/templates/components/ui/Switch.tsx +155 -0
  39. package/templates/components/ui/Tabs.tsx +253 -0
  40. package/templates/components/ui/Toast.tsx +192 -0
  41. package/templates/components/ui/Tooltip.tsx +129 -0
  42. package/templates/components/ui/hooks/useModalBehavior.ts +66 -0
  43. package/templates/components/ui/index.ts +84 -0
  44. package/templates/devtools/DevToolsPanel.tsx +261 -0
  45. package/templates/devtools/DevToolsProvider.tsx +43 -0
  46. package/templates/devtools/components/BreakpointIndicator.tsx +49 -0
  47. package/templates/devtools/components/ColorPicker.tsx +33 -0
  48. package/templates/devtools/components/ComponentsSecondaryNav.tsx +44 -0
  49. package/templates/devtools/components/ContextualFooter.tsx +56 -0
  50. package/templates/devtools/components/DraggablePanel.tsx +43 -0
  51. package/templates/devtools/components/PrimaryNavigationFooter.tsx +254 -0
  52. package/templates/devtools/components/SearchableColorDropdown.tsx +253 -0
  53. package/templates/devtools/components/SecondaryNavigation.tsx +36 -0
  54. package/templates/devtools/components/TokenDropdown.tsx +47 -0
  55. package/templates/devtools/components/TypographyFooter.tsx +145 -0
  56. package/templates/devtools/hooks/useMockState.ts +16 -0
  57. package/templates/devtools/index.ts +17 -0
  58. package/templates/devtools/lib/componentScanner.ts +78 -0
  59. package/templates/devtools/lib/cssParser.ts +465 -0
  60. package/templates/devtools/lib/searchIndexes.ts +45 -0
  61. package/templates/devtools/lib/selectorGenerator.ts +86 -0
  62. package/templates/devtools/store/index.ts +66 -0
  63. package/templates/devtools/store/slices/assetsSlice.ts +106 -0
  64. package/templates/devtools/store/slices/componentsSlice.ts +59 -0
  65. package/templates/devtools/store/slices/mockStatesSlice.ts +77 -0
  66. package/templates/devtools/store/slices/panelSlice.ts +17 -0
  67. package/templates/devtools/store/slices/typographySlice.ts +538 -0
  68. package/templates/devtools/store/slices/variablesSlice.ts +167 -0
  69. package/templates/devtools/tabs/AssetsTab/AssetGrid.tsx +76 -0
  70. package/templates/devtools/tabs/AssetsTab/FolderTree.tsx +53 -0
  71. package/templates/devtools/tabs/AssetsTab/UploadDropzone.tsx +76 -0
  72. package/templates/devtools/tabs/AssetsTab/index.tsx +182 -0
  73. package/templates/devtools/tabs/ComponentsTab/AddTabButton.tsx +63 -0
  74. package/templates/devtools/tabs/ComponentsTab/ComponentList.tsx +153 -0
  75. package/templates/devtools/tabs/ComponentsTab/DesignSystemTab.tsx +1515 -0
  76. package/templates/devtools/tabs/ComponentsTab/DynamicFolderTab.tsx +113 -0
  77. package/templates/devtools/tabs/ComponentsTab/PropDisplay.tsx +55 -0
  78. package/templates/devtools/tabs/ComponentsTab/index.tsx +167 -0
  79. package/templates/devtools/tabs/ComponentsTab/previews/.gitkeep +4 -0
  80. package/templates/devtools/tabs/ComponentsTab/previews/Rad_os.tsx +262 -0
  81. package/templates/devtools/tabs/ComponentsTab/tabConfig.ts +53 -0
  82. package/templates/devtools/tabs/MockStatesTab/index.tsx +29 -0
  83. package/templates/devtools/tabs/TypographyTab/FontManager.tsx +421 -0
  84. package/templates/devtools/tabs/TypographyTab/TypographyStylesDisplay.tsx +290 -0
  85. package/templates/devtools/tabs/TypographyTab/index.tsx +98 -0
  86. package/templates/devtools/tabs/VariablesTab/BaseColorEditor.tsx +267 -0
  87. package/templates/devtools/tabs/VariablesTab/BorderRadiusEditor.tsx +37 -0
  88. package/templates/devtools/tabs/VariablesTab/ColorModeSelector.tsx +235 -0
  89. package/templates/devtools/tabs/VariablesTab/index.tsx +100 -0
  90. package/templates/devtools/types/index.ts +99 -0
  91. package/templates/globals.css +574 -0
  92. package/templates/hooks/index.ts +1 -0
  93. package/templates/hooks/useWindowManager.ts +212 -0
  94. package/templates/public/assets/icons/avatar.svg +18 -0
  95. package/templates/public/assets/icons/checkmark-filled.svg +14 -0
  96. package/templates/public/assets/icons/checkmark.svg +14 -0
  97. package/templates/public/assets/icons/chevron-down.svg +14 -0
  98. package/templates/public/assets/icons/close.svg +14 -0
  99. package/templates/public/assets/icons/copy.svg +14 -0
  100. package/templates/public/assets/icons/download.svg +14 -0
  101. package/templates/public/assets/icons/expand.svg +31 -0
  102. package/templates/public/assets/icons/file-blank.svg +17 -0
  103. package/templates/public/assets/icons/file-image.svg +19 -0
  104. package/templates/public/assets/icons/file-written.svg +17 -0
  105. package/templates/public/assets/icons/folder-closed.svg +17 -0
  106. package/templates/public/assets/icons/folder-open.svg +17 -0
  107. package/templates/public/assets/icons/hamburger.svg +18 -0
  108. package/templates/public/assets/icons/home-outline.svg +28 -0
  109. package/templates/public/assets/icons/home.svg +30 -0
  110. package/templates/public/assets/icons/hourglass.svg +25 -0
  111. package/templates/public/assets/icons/information-circle.svg +14 -0
  112. package/templates/public/assets/icons/information.svg +17 -0
  113. package/templates/public/assets/icons/lightning.svg +14 -0
  114. package/templates/public/assets/icons/locked.svg +17 -0
  115. package/templates/public/assets/icons/not-allowed.svg +14 -0
  116. package/templates/public/assets/icons/plus.svg +5 -0
  117. package/templates/public/assets/icons/power-thin.svg +17 -0
  118. package/templates/public/assets/icons/power.svg +17 -0
  119. package/templates/public/assets/icons/question-block.svg +14 -0
  120. package/templates/public/assets/icons/question.svg +17 -0
  121. package/templates/public/assets/icons/refresh-block.svg +14 -0
  122. package/templates/public/assets/icons/refresh.svg +17 -0
  123. package/templates/public/assets/icons/save.svg +14 -0
  124. package/templates/public/assets/icons/search.svg +25 -0
  125. package/templates/public/assets/icons/settings.svg +14 -0
  126. package/templates/public/assets/icons/trash-full.svg +21 -0
  127. package/templates/public/assets/icons/trash-open.svg +23 -0
  128. package/templates/public/assets/icons/trash.svg +18 -0
  129. package/templates/public/assets/icons/unlocked.svg +17 -0
  130. package/templates/public/assets/icons/waring-triangle-filled.svg +17 -0
  131. package/templates/public/assets/icons/warning-triangle-filled-2.svg +30 -0
  132. package/templates/public/assets/icons/warning-triangle-lines.svg +29 -0
  133. package/templates/public/assets/icons/wrench.svg +17 -0
@@ -0,0 +1,84 @@
1
+ /**
2
+ * UI Component Library
3
+ *
4
+ * Centralized exports for all design system components.
5
+ * Import from '@/components/ui' for convenience.
6
+ */
7
+
8
+ // ============================================================================
9
+ // Core Components
10
+ // ============================================================================
11
+
12
+ export { Button } from './Button';
13
+ export { Card, CardHeader, CardBody, CardFooter } from './Card';
14
+ export { Tabs, TabList, TabTrigger, TabContent } from './Tabs';
15
+ export { Input, TextArea, Label } from './Input';
16
+ export { Badge } from './Badge';
17
+ export { Divider } from './Divider';
18
+
19
+ // ============================================================================
20
+ // Form Components
21
+ // ============================================================================
22
+
23
+ export { Select } from './Select';
24
+ export { Checkbox, Radio } from './Checkbox';
25
+ export { Switch } from './Switch';
26
+ export { Slider } from './Slider';
27
+
28
+ // ============================================================================
29
+ // Feedback Components
30
+ // ============================================================================
31
+
32
+ export { Progress, Spinner } from './Progress';
33
+ export { Tooltip } from './Tooltip';
34
+ export { Alert } from './Alert';
35
+ export { ToastProvider, useToast } from './Toast';
36
+
37
+ // ============================================================================
38
+ // Navigation Components
39
+ // ============================================================================
40
+
41
+ export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from './Accordion';
42
+ export { Breadcrumbs } from './Breadcrumbs';
43
+
44
+ // ============================================================================
45
+ // Overlay Components
46
+ // ============================================================================
47
+
48
+ export { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuSeparator } from './ContextMenu';
49
+ export { HelpPanel } from './HelpPanel';
50
+ export {
51
+ Dialog,
52
+ DialogTrigger,
53
+ DialogContent,
54
+ DialogHeader,
55
+ DialogTitle,
56
+ DialogDescription,
57
+ DialogBody,
58
+ DialogFooter,
59
+ DialogClose,
60
+ } from './Dialog';
61
+ export {
62
+ Popover,
63
+ PopoverTrigger,
64
+ PopoverContent,
65
+ } from './Popover';
66
+ export {
67
+ Sheet,
68
+ SheetTrigger,
69
+ SheetContent,
70
+ SheetHeader,
71
+ SheetTitle,
72
+ SheetDescription,
73
+ SheetBody,
74
+ SheetFooter,
75
+ SheetClose,
76
+ } from './Sheet';
77
+ export {
78
+ DropdownMenu,
79
+ DropdownMenuTrigger,
80
+ DropdownMenuContent,
81
+ DropdownMenuItem,
82
+ DropdownMenuSeparator,
83
+ DropdownMenuLabel,
84
+ } from './DropdownMenu';
@@ -0,0 +1,261 @@
1
+ 'use client';
2
+
3
+ import Draggable from 'react-draggable';
4
+ import { useRef, useState, useEffect, useCallback } from 'react';
5
+ import { useDevToolsStore } from './store';
6
+ import type { Tab } from './types';
7
+
8
+ // Import UI components
9
+ import { Button } from '@/components/ui/Button';
10
+ import { WindowTitleBar } from '@/components/Rad_os/WindowTitleBar';
11
+
12
+ // Import actual tab components
13
+ import { VariablesTab } from './tabs/VariablesTab';
14
+ import { TypographyTab } from './tabs/TypographyTab';
15
+ import { ComponentsTab } from './tabs/ComponentsTab';
16
+ import { AssetsTab } from './tabs/AssetsTab';
17
+ import { MockStatesTab } from './tabs/MockStatesTab';
18
+ import { ContextualFooter } from './components/ContextualFooter';
19
+
20
+ export function DevToolsPanel() {
21
+ // Store hooks must be first - all functions below depend on these values
22
+ const {
23
+ activeTab,
24
+ setActiveTab,
25
+ panelPosition,
26
+ setPanelPosition,
27
+ panelSize,
28
+ setPanelSize,
29
+ togglePanel,
30
+ isFullscreen,
31
+ toggleFullscreen,
32
+ } = useDevToolsStore();
33
+
34
+ const nodeRef = useRef<HTMLDivElement>(null);
35
+ const resizeHandleRef = useRef<HTMLDivElement>(null);
36
+
37
+ const [isResizing, setIsResizing] = useState(false);
38
+ const [resizeStart, setResizeStart] = useState({ x: 0, y: 0, width: 0, height: 0 });
39
+
40
+ // Footer state
41
+ const [componentSubTab, setComponentSubTab] = useState<string>('design-system');
42
+ const [componentSearchQuery, setComponentSearchQuery] = useState<string>('');
43
+ const [typographySearchQuery, setTypographySearchQuery] = useState<string>('');
44
+ const [componentTabs, setComponentTabs] = useState<Array<{ id: string; label: string }>>([]);
45
+
46
+ const handleDragStop = (_: unknown, data: { x: number; y: number }) => {
47
+ setPanelPosition({ x: data.x, y: data.y });
48
+ };
49
+
50
+ // Resize functionality
51
+ const handleResizeStart = useCallback((e: React.MouseEvent) => {
52
+ e.preventDefault();
53
+ e.stopPropagation();
54
+ if (!nodeRef.current) return;
55
+
56
+ const rect = nodeRef.current.getBoundingClientRect();
57
+ setIsResizing(true);
58
+ setResizeStart({
59
+ x: e.clientX,
60
+ y: e.clientY,
61
+ width: rect.width,
62
+ height: rect.height,
63
+ });
64
+ }, []);
65
+
66
+ useEffect(() => {
67
+ if (!isResizing) return;
68
+
69
+ const handleMouseMove = (e: MouseEvent) => {
70
+ if (!nodeRef.current) return;
71
+
72
+ const deltaX = e.clientX - resizeStart.x;
73
+ const deltaY = e.clientY - resizeStart.y;
74
+
75
+ const minWidth = 300;
76
+ const minHeight = 200;
77
+ const maxWidth = window.innerWidth * 0.9;
78
+ const maxHeight = window.innerHeight * 0.9;
79
+
80
+ const newWidth = Math.min(Math.max(resizeStart.width + deltaX, minWidth), maxWidth);
81
+ const newHeight = Math.min(Math.max(resizeStart.height + deltaY, minHeight), maxHeight);
82
+
83
+ setPanelSize({ width: newWidth, height: newHeight });
84
+ };
85
+
86
+ const handleMouseUp = () => {
87
+ setIsResizing(false);
88
+ };
89
+
90
+ document.addEventListener('mousemove', handleMouseMove);
91
+ document.addEventListener('mouseup', handleMouseUp);
92
+
93
+ return () => {
94
+ document.removeEventListener('mousemove', handleMouseMove);
95
+ document.removeEventListener('mouseup', handleMouseUp);
96
+ };
97
+ }, [isResizing, resizeStart, setPanelSize]);
98
+
99
+ // Handle tab change from Tabs component
100
+ const handleTabChange = (value: string) => {
101
+ setActiveTab(value as Tab);
102
+ };
103
+
104
+ // Fullscreen styles
105
+ const panelStyles = isFullscreen
106
+ ? {
107
+ top: 0,
108
+ left: 0,
109
+ right: 0,
110
+ bottom: 0,
111
+ width: '100%',
112
+ height: '100%',
113
+ }
114
+ : {
115
+ top: 0,
116
+ left: 0,
117
+ width: `${panelSize.width}px`,
118
+ height: `${panelSize.height}px`,
119
+ minWidth: '300px',
120
+ minHeight: '200px',
121
+ maxWidth: '90vw',
122
+ maxHeight: '90vh',
123
+ };
124
+
125
+ return (
126
+ <Draggable
127
+ nodeRef={nodeRef}
128
+ handle="[data-drag-handle]"
129
+ position={isFullscreen ? { x: 0, y: 0 } : panelPosition}
130
+ onStop={handleDragStop}
131
+ bounds="parent"
132
+ disabled={isResizing || isFullscreen}
133
+ >
134
+ <div
135
+ ref={nodeRef}
136
+ className={`fixed z-[9999] overflow-hidden flex flex-col border ${
137
+ isFullscreen ? 'inset-0' : 'rounded-[8px] shadow-[4px_4px_0_0_var(--color-black)]'
138
+ }`}
139
+ style={{
140
+ ...panelStyles,
141
+ background: 'linear-gradient(0deg, rgba(252, 225, 132, 1) 0%, rgba(254, 248, 226, 1) 100%)',
142
+ borderStyle: 'solid',
143
+ borderWidth: '1px',
144
+ borderColor: 'var(--color-black)',
145
+ }}
146
+ >
147
+ {/* Header */}
148
+ <WindowTitleBar
149
+ title="RADTOOLS"
150
+ windowId="radtools"
151
+ onClose={togglePanel}
152
+ iconName="lightning"
153
+ showTitle={true}
154
+ showCopyButton={false}
155
+ showCloseButton={true}
156
+ showHelpButton={true}
157
+ showActionButton={false}
158
+ showFullscreenButton={true}
159
+ onFullscreen={toggleFullscreen}
160
+ helpTitle="How to Use RadTools"
161
+ helpContent={
162
+ <div className="space-y-4">
163
+ <div>
164
+ <h3 className="font-joystix text-sm uppercase mb-2">Getting Started</h3>
165
+ <p className="font-mondwest text-sm text-black/80">
166
+ RadTools is a design system development tool that helps you manage components, typography, variables, and assets.
167
+ </p>
168
+ </div>
169
+ <div>
170
+ <h3 className="font-joystix text-sm uppercase mb-2">Features</h3>
171
+ <ul className="font-mondwest text-sm text-black/80 space-y-1 list-disc list-inside pl-0">
172
+ <li>Components: Browse and inspect your UI components</li>
173
+ <li>Typography: Manage font families and typography styles</li>
174
+ <li>Variables: Edit design tokens and color variables</li>
175
+ <li>Assets: Upload and organize images, icons, and other assets</li>
176
+ <li>Mock States: Test components with different state configurations</li>
177
+ </ul>
178
+ </div>
179
+ <div>
180
+ <h3 className="font-joystix text-sm uppercase mb-2">Tips</h3>
181
+ <ul className="font-mondwest text-sm text-black/80 space-y-1 list-disc list-inside">
182
+ <li>Drag the panel by the title bar to reposition it</li>
183
+ <li>Use the fullscreen button for a larger workspace</li>
184
+ <li>Click the close button to hide the panel</li>
185
+ </ul>
186
+ </div>
187
+ </div>
188
+ }
189
+ />
190
+
191
+ {/* Tab Content */}
192
+ <div className="flex-1 overflow-y-auto">
193
+ {activeTab === 'variables' && (
194
+ <div className="h-full pr-2 pl-2 pb-2 rounded">
195
+ <VariablesTab />
196
+ </div>
197
+ )}
198
+ {activeTab === 'typography' && (
199
+ <div className="h-full pr-2 pl-2 pb-2 rounded">
200
+ <TypographyTab searchQuery={typographySearchQuery} />
201
+ </div>
202
+ )}
203
+ {activeTab === 'components' && (
204
+ <ComponentsTab
205
+ activeSubTab={componentSubTab}
206
+ searchQuery={componentSearchQuery}
207
+ onTabsChange={setComponentTabs}
208
+ />
209
+ )}
210
+ {activeTab === 'assets' && (
211
+ <div className="h-full pr-2 pl-2 pb-2 rounded">
212
+ <AssetsTab />
213
+ </div>
214
+ )}
215
+ {activeTab === 'mockStates' && (
216
+ <div className="h-full pr-2 pl-2 pb-2 rounded">
217
+ <MockStatesTab />
218
+ </div>
219
+ )}
220
+ </div>
221
+
222
+ {/* Contextual Footer */}
223
+ <ContextualFooter
224
+ activeTab={activeTab}
225
+ onTabChange={handleTabChange}
226
+ componentSubTab={componentSubTab}
227
+ componentSearchQuery={componentSearchQuery}
228
+ onComponentSubTabChange={setComponentSubTab}
229
+ onComponentSearchChange={setComponentSearchQuery}
230
+ componentTabs={componentTabs}
231
+ onAddComponentFolder={async (folderName) => {
232
+ // Trigger folder creation via ComponentsTab's exposed handler
233
+ if ((window as any).__componentsTabAddFolder) {
234
+ (window as any).__componentsTabAddFolder(folderName);
235
+ // Switch to the new tab
236
+ setComponentSubTab(`folder-${folderName}`);
237
+ }
238
+ }}
239
+ typographySearchQuery={typographySearchQuery}
240
+ onTypographySearchChange={setTypographySearchQuery}
241
+ />
242
+
243
+ {/* Resize Handle (hidden in fullscreen) */}
244
+ {!isFullscreen && (
245
+ <div
246
+ ref={resizeHandleRef}
247
+ onMouseDown={handleResizeStart}
248
+ className="absolute bottom-0 right-0 w-4 h-4 cursor-nwse-resize z-10 flex items-center justify-center text-black"
249
+ style={{
250
+ fontFamily: '"PixelCode", monospace',
251
+ fontSize: '10px',
252
+ lineHeight: 1,
253
+ }}
254
+ >
255
+
256
+ </div>
257
+ )}
258
+ </div>
259
+ </Draggable>
260
+ );
261
+ }
@@ -0,0 +1,43 @@
1
+ 'use client';
2
+
3
+ import { ReactNode, useEffect } from 'react';
4
+ import { DevToolsPanel } from './DevToolsPanel';
5
+ import { BreakpointIndicator } from './components/BreakpointIndicator';
6
+ import { useDevToolsStore } from './store';
7
+ import { ToastProvider } from '@/components/ui/Toast';
8
+
9
+ interface DevToolsProviderProps {
10
+ children: ReactNode;
11
+ }
12
+
13
+ export function DevToolsProvider({ children }: DevToolsProviderProps) {
14
+ const { isOpen, togglePanel } = useDevToolsStore();
15
+
16
+ // Keyboard shortcut: Shift+Cmd+K (Mac) / Shift+Ctrl+K (Windows)
17
+ useEffect(() => {
18
+ const handleKeyDown = (e: KeyboardEvent) => {
19
+ // Toggle panel with Shift+Cmd+K or Shift+Ctrl+K
20
+ if (e.shiftKey && (e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') {
21
+ e.preventDefault();
22
+ togglePanel();
23
+ }
24
+ };
25
+
26
+ window.addEventListener('keydown', handleKeyDown);
27
+ return () => window.removeEventListener('keydown', handleKeyDown);
28
+ }, [togglePanel]);
29
+
30
+ // Production: render only children
31
+ if (process.env.NODE_ENV === 'production') {
32
+ return <>{children}</>;
33
+ }
34
+
35
+ return (
36
+ <ToastProvider>
37
+ {children}
38
+ {isOpen && <DevToolsPanel />}
39
+ <BreakpointIndicator />
40
+ </ToastProvider>
41
+ );
42
+ }
43
+
@@ -0,0 +1,49 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect } from 'react';
4
+
5
+ const BREAKPOINTS = {
6
+ sm: 640,
7
+ md: 768,
8
+ lg: 1024,
9
+ xl: 1280,
10
+ '2xl': 1536,
11
+ };
12
+
13
+ export function BreakpointIndicator() {
14
+ const [width, setWidth] = useState(0);
15
+ const [breakpoint, setBreakpoint] = useState('');
16
+
17
+ useEffect(() => {
18
+ const updateWidth = () => {
19
+ const w = window.innerWidth;
20
+ setWidth(w);
21
+
22
+ if (w >= BREAKPOINTS['2xl']) setBreakpoint('2xl');
23
+ else if (w >= BREAKPOINTS.xl) setBreakpoint('xl');
24
+ else if (w >= BREAKPOINTS.lg) setBreakpoint('lg');
25
+ else if (w >= BREAKPOINTS.md) setBreakpoint('md');
26
+ else if (w >= BREAKPOINTS.sm) setBreakpoint('sm');
27
+ else setBreakpoint('xs');
28
+ };
29
+
30
+ updateWidth();
31
+ window.addEventListener('resize', updateWidth);
32
+ return () => window.removeEventListener('resize', updateWidth);
33
+ }, []);
34
+
35
+ // Don't render in production
36
+ if (process.env.NODE_ENV === 'production') return null;
37
+
38
+ // Don't render on server
39
+ if (width === 0) return null;
40
+
41
+ return (
42
+ <div className="fixed bottom-4 left-4 z-[9999] flex items-center gap-2 rounded-full bg-black px-3 py-1.5 font-mono text-xs text-cream shadow-lg">
43
+ <span className="font-bold uppercase">{breakpoint}</span>
44
+ <span className="opacity-60">·</span>
45
+ <span className="opacity-60">{width}px</span>
46
+ </div>
47
+ );
48
+ }
49
+
@@ -0,0 +1,33 @@
1
+ 'use client';
2
+
3
+ interface ColorPickerProps {
4
+ value: string;
5
+ onChange: (value: string) => void;
6
+ label?: string;
7
+ }
8
+
9
+ export function ColorPicker({ value, onChange, label }: ColorPickerProps) {
10
+ return (
11
+ <div className="flex items-center gap-2">
12
+ {label && (
13
+ <label className="font-joystix text-xs uppercase text-black/60 min-w-[60px]">{label}</label>
14
+ )}
15
+ <div className="flex items-center gap-2 flex-1">
16
+ <input
17
+ type="color"
18
+ value={value}
19
+ onChange={(e) => onChange(e.target.value)}
20
+ className="w-8 h-8 rounded-sm border border-black cursor-pointer"
21
+ />
22
+ <input
23
+ type="text"
24
+ value={value}
25
+ onChange={(e) => onChange(e.target.value)}
26
+ className="flex-1 px-2 py-1 font-mondwest text-base font-mono bg-warm-cloud border border-black rounded-sm text-black focus:outline-none focus:ring-2 focus:ring-tertiary"
27
+ placeholder="#000000"
28
+ />
29
+ </div>
30
+ </div>
31
+ );
32
+ }
33
+
@@ -0,0 +1,44 @@
1
+ 'use client';
2
+
3
+ import { AddTabButton } from '../tabs/ComponentsTab/AddTabButton';
4
+
5
+ interface ComponentsSecondaryNavProps {
6
+ activeSubTab: string;
7
+ onSubTabChange: (tab: string) => void;
8
+ tabs: Array<{ id: string; label: string }>;
9
+ onAddFolder: (folderName: string) => void;
10
+ }
11
+
12
+ export function ComponentsSecondaryNav({
13
+ activeSubTab,
14
+ onSubTabChange,
15
+ tabs,
16
+ onAddFolder,
17
+ }: ComponentsSecondaryNavProps) {
18
+ return (
19
+ <div className="flex items-center gap-1 px-2 py-2 bg-warm-cloud border-t border-black overflow-x-auto">
20
+ {tabs.map((tab) => (
21
+ <button
22
+ key={tab.id}
23
+ type="button"
24
+ onClick={() => onSubTabChange(tab.id)}
25
+ className={`flex items-center justify-center px-4 py-2 font-joystix text-xs uppercase cursor-pointer select-none text-black transition-all duration-200 ease-out relative border border-black rounded-sm ${
26
+ activeSubTab === tab.id ? 'bg-sun-yellow' : 'bg-transparent hover:bg-black/5'
27
+ }`}
28
+ >
29
+ {tab.label}
30
+ </button>
31
+ ))}
32
+ <AddTabButton
33
+ onAdd={(folderName) => {
34
+ onAddFolder(folderName);
35
+ // Also trigger via ComponentsTab's handler if available
36
+ if ((window as any).__componentsTabAddFolder) {
37
+ (window as any).__componentsTabAddFolder(folderName);
38
+ }
39
+ }}
40
+ />
41
+ </div>
42
+ );
43
+ }
44
+
@@ -0,0 +1,56 @@
1
+ 'use client';
2
+
3
+ import type { Tab } from '../types';
4
+ import { SecondaryNavigation } from './SecondaryNavigation';
5
+ import { PrimaryNavigationFooter } from './PrimaryNavigationFooter';
6
+
7
+ interface ContextualFooterProps {
8
+ activeTab: Tab;
9
+ onTabChange: (tab: Tab) => void;
10
+ // Components tab props
11
+ componentSubTab?: string;
12
+ componentSearchQuery?: string;
13
+ onComponentSubTabChange?: (tab: string) => void;
14
+ onComponentSearchChange?: (query: string) => void;
15
+ componentTabs?: Array<{ id: string; label: string }>;
16
+ onAddComponentFolder?: (folderName: string) => void;
17
+ // Typography tab props
18
+ typographySearchQuery?: string;
19
+ onTypographySearchChange?: (query: string) => void;
20
+ }
21
+
22
+ export function ContextualFooter({
23
+ activeTab,
24
+ onTabChange,
25
+ componentSubTab,
26
+ componentSearchQuery,
27
+ onComponentSubTabChange,
28
+ onComponentSearchChange,
29
+ componentTabs,
30
+ onAddComponentFolder,
31
+ typographySearchQuery,
32
+ onTypographySearchChange,
33
+ }: ContextualFooterProps) {
34
+ return (
35
+ <>
36
+ {/* Secondary Navigation (above footer) */}
37
+ <SecondaryNavigation
38
+ activeTab={activeTab}
39
+ componentSubTab={componentSubTab}
40
+ onComponentSubTabChange={onComponentSubTabChange}
41
+ componentTabs={componentTabs}
42
+ onAddComponentFolder={onAddComponentFolder}
43
+ />
44
+ {/* Primary Navigation Footer (always visible) */}
45
+ <PrimaryNavigationFooter
46
+ activeTab={activeTab}
47
+ onTabChange={onTabChange}
48
+ componentSearchQuery={componentSearchQuery}
49
+ onComponentSearchChange={onComponentSearchChange}
50
+ typographySearchQuery={typographySearchQuery}
51
+ onTypographySearchChange={onTypographySearchChange}
52
+ />
53
+ </>
54
+ );
55
+ }
56
+
@@ -0,0 +1,43 @@
1
+ 'use client';
2
+
3
+ import Draggable, { DraggableEvent, DraggableData } from 'react-draggable';
4
+ import { useRef, ReactNode } from 'react';
5
+
6
+ interface DraggablePanelProps {
7
+ children: ReactNode;
8
+ position: { x: number; y: number };
9
+ onPositionChange: (position: { x: number; y: number }) => void;
10
+ className?: string;
11
+ }
12
+
13
+ export function DraggablePanel({
14
+ children,
15
+ position,
16
+ onPositionChange,
17
+ className = '',
18
+ }: DraggablePanelProps) {
19
+ const nodeRef = useRef<HTMLDivElement>(null);
20
+
21
+ const handleDragStop = (_: DraggableEvent, data: DraggableData) => {
22
+ onPositionChange({ x: data.x, y: data.y });
23
+ };
24
+
25
+ return (
26
+ <Draggable
27
+ nodeRef={nodeRef}
28
+ handle=".drag-handle"
29
+ defaultPosition={position}
30
+ onStop={handleDragStop}
31
+ bounds="parent"
32
+ >
33
+ <div
34
+ ref={nodeRef}
35
+ className={`fixed z-[9999] ${className}`}
36
+ style={{ top: 0, left: 0 }}
37
+ >
38
+ {children}
39
+ </div>
40
+ </Draggable>
41
+ );
42
+ }
43
+