@donotdev/ui 0.0.2 → 0.0.3

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 (104) hide show
  1. package/dist/components/auth/AuthMenu.d.ts.map +1 -1
  2. package/dist/components/auth/AuthMenu.js +15 -28
  3. package/dist/components/common/FeatureCard.js +1 -1
  4. package/dist/components/cookie-consent/CookieConsent.d.ts.map +1 -1
  5. package/dist/components/cookie-consent/CookieConsent.js +2 -41
  6. package/dist/components/cookie-consent/index.d.ts +0 -1
  7. package/dist/components/cookie-consent/index.d.ts.map +1 -1
  8. package/dist/components/cookie-consent/index.js +1 -1
  9. package/dist/components/layout/components/DropdownNavigation.d.ts.map +1 -1
  10. package/dist/components/layout/components/header/ThemeToggle.d.ts +1 -1
  11. package/dist/components/layout/components/header/ThemeToggle.d.ts.map +1 -1
  12. package/dist/components/layout/components/header/ThemeToggle.js +5 -4
  13. package/dist/dndev.css +137 -120
  14. package/dist/index.js +4 -4
  15. package/dist/internal/devtools/DebugTools.d.ts.map +1 -1
  16. package/dist/internal/devtools/DebugTools.js +8 -4
  17. package/dist/internal/devtools/components/ConfigTab.d.ts.map +1 -1
  18. package/dist/internal/devtools/components/ConfigTab.js +31 -133
  19. package/dist/internal/devtools/components/DebugDialog.d.ts.map +1 -1
  20. package/dist/internal/devtools/components/DebugDialog.js +11 -520
  21. package/dist/internal/devtools/components/DesignTab.d.ts +2 -0
  22. package/dist/internal/devtools/components/DesignTab.d.ts.map +1 -0
  23. package/dist/internal/devtools/components/DesignTab.js +220 -0
  24. package/dist/internal/devtools/components/StoresTab.d.ts.map +1 -1
  25. package/dist/internal/devtools/components/StoresTab.js +54 -102
  26. package/dist/internal/devtools/components/index.d.ts +1 -6
  27. package/dist/internal/devtools/components/index.d.ts.map +1 -1
  28. package/dist/internal/devtools/components/index.js +1 -6
  29. package/dist/internal/devtools/utils/index.d.ts +0 -1
  30. package/dist/internal/devtools/utils/index.d.ts.map +1 -1
  31. package/dist/internal/devtools/utils/index.js +0 -1
  32. package/dist/internal/initializers/BaseStoresInitializer.d.ts.map +1 -1
  33. package/dist/internal/initializers/BaseStoresInitializer.js +25 -58
  34. package/dist/internal/layout/DnDevLayout.js +3 -3
  35. package/dist/internal/layout/components/footer/FooterBranding.d.ts.map +1 -1
  36. package/dist/internal/layout/components/footer/FooterBranding.js +3 -1
  37. package/dist/routing/AuthGuard.d.ts +14 -10
  38. package/dist/routing/AuthGuard.d.ts.map +1 -1
  39. package/dist/routing/AuthGuard.js +25 -22
  40. package/dist/routing/Link.d.ts +2 -2
  41. package/dist/routing/Link.js +2 -2
  42. package/dist/routing/hooks/hooks.next.js +1 -1
  43. package/dist/routing/hooks/hooks.vite.js +1 -1
  44. package/dist/routing/hooks/useRedirectGuard.next.d.ts +2 -36
  45. package/dist/routing/hooks/useRedirectGuard.next.d.ts.map +1 -1
  46. package/dist/routing/hooks/useRedirectGuard.next.js +14 -55
  47. package/dist/routing/hooks/useRedirectGuard.vite.d.ts +2 -36
  48. package/dist/routing/hooks/useRedirectGuard.vite.d.ts.map +1 -1
  49. package/dist/routing/hooks/useRedirectGuard.vite.js +14 -55
  50. package/dist/routing/index.d.ts +0 -1
  51. package/dist/routing/index.d.ts.map +1 -1
  52. package/dist/routing/index.js +1 -1
  53. package/dist/styles/index.css +137 -120
  54. package/dist/utils/assetResolver.d.ts +5 -5
  55. package/dist/utils/assetResolver.js +4 -4
  56. package/dist/utils/useAuthSafe.d.ts +25 -12
  57. package/dist/utils/useAuthSafe.d.ts.map +1 -1
  58. package/dist/utils/useAuthSafe.js +3 -1
  59. package/dist/utils/useAuthVisibility.d.ts +3 -3
  60. package/dist/utils/useAuthVisibility.d.ts.map +1 -1
  61. package/dist/utils/useAuthVisibility.js +25 -21
  62. package/dist/utils/useBillingVisibility.d.ts +2 -2
  63. package/dist/utils/useBillingVisibility.d.ts.map +1 -1
  64. package/dist/utils/useBillingVisibility.js +12 -13
  65. package/dist/utils/useCrudSafe.d.ts +1 -1
  66. package/dist/utils/useCrudSafe.d.ts.map +1 -1
  67. package/dist/utils/useCrudSafe.js +26 -13
  68. package/dist/utils/useOAuthSafe.d.ts +25 -12
  69. package/dist/utils/useOAuthSafe.d.ts.map +1 -1
  70. package/dist/utils/useStripeBillingSafe.d.ts +30 -18
  71. package/dist/utils/useStripeBillingSafe.d.ts.map +1 -1
  72. package/dist/utils/useStripeBillingSafe.js +5 -6
  73. package/dist/vite-routing/AppRoutes.d.ts.map +1 -1
  74. package/dist/vite-routing/AppRoutes.js +5 -5
  75. package/dist/vite-routing/RootLayout.d.ts.map +1 -1
  76. package/dist/vite-routing/RootLayout.js +34 -7
  77. package/package.json +9 -9
  78. package/dist/internal/devtools/components/AuthTab.d.ts +0 -2
  79. package/dist/internal/devtools/components/AuthTab.d.ts.map +0 -1
  80. package/dist/internal/devtools/components/AuthTab.js +0 -98
  81. package/dist/internal/devtools/components/ColorRatioTab.d.ts +0 -2
  82. package/dist/internal/devtools/components/ColorRatioTab.d.ts.map +0 -1
  83. package/dist/internal/devtools/components/ColorRatioTab.js +0 -322
  84. package/dist/internal/devtools/components/DebugToggle.d.ts +0 -2
  85. package/dist/internal/devtools/components/DebugToggle.d.ts.map +0 -1
  86. package/dist/internal/devtools/components/DebugToggle.js +0 -57
  87. package/dist/internal/devtools/components/EnvironmentTab.d.ts +0 -2
  88. package/dist/internal/devtools/components/EnvironmentTab.d.ts.map +0 -1
  89. package/dist/internal/devtools/components/EnvironmentTab.js +0 -26
  90. package/dist/internal/devtools/components/I18nTab.d.ts +0 -2
  91. package/dist/internal/devtools/components/I18nTab.d.ts.map +0 -1
  92. package/dist/internal/devtools/components/I18nTab.js +0 -76
  93. package/dist/internal/devtools/components/OAuthGuideButton.d.ts +0 -10
  94. package/dist/internal/devtools/components/OAuthGuideButton.d.ts.map +0 -1
  95. package/dist/internal/devtools/components/OAuthGuideButton.js +0 -71
  96. package/dist/internal/devtools/components/StripeDebugTab.d.ts +0 -2
  97. package/dist/internal/devtools/components/StripeDebugTab.d.ts.map +0 -1
  98. package/dist/internal/devtools/components/StripeDebugTab.js +0 -175
  99. package/dist/internal/devtools/components/ThemesTab.d.ts +0 -2
  100. package/dist/internal/devtools/components/ThemesTab.d.ts.map +0 -1
  101. package/dist/internal/devtools/components/ThemesTab.js +0 -77
  102. package/dist/internal/devtools/utils/spacingAnalyzer.d.ts +0 -15
  103. package/dist/internal/devtools/utils/spacingAnalyzer.d.ts.map +0 -1
  104. package/dist/internal/devtools/utils/spacingAnalyzer.js +0 -88
@@ -0,0 +1,220 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ // packages/ui/src/internal/devtools/components/DesignTab.tsx
4
+ /**
5
+ * @fileoverview Design Tab Component
6
+ * @description Unified design debugging: themes, color ratio, typography
7
+ *
8
+ * @version 0.0.1
9
+ * @since 0.0.1
10
+ * @author AMBROISE PARK Consulting
11
+ */
12
+ import { Palette, Type } from 'lucide-react';
13
+ import { useState } from 'react';
14
+ import { Card, Button, BUTTON_VARIANT, Stack, Label, Badge, BADGE_VARIANT, Alert, ALERT_VARIANT, DescriptionList, Text, ScrollArea, Accordion, CopyToClipboard, toast, } from '@donotdev/components';
15
+ import { useTheme, useThemeReady } from '@donotdev/core';
16
+ export const DesignTab = () => {
17
+ const currentTheme = useTheme('currentTheme');
18
+ const availableThemes = useTheme('availableThemes');
19
+ const isDarkMode = useTheme('isDarkMode');
20
+ const isReady = useThemeReady();
21
+ const [colorRatio, setColorRatio] = useState(null);
22
+ const [typography, setTypography] = useState(null);
23
+ const [isAnalyzing, setIsAnalyzing] = useState(false);
24
+ // Get CSS variables from :root
25
+ const getCSSVariables = () => {
26
+ const root = document.documentElement;
27
+ const style = getComputedStyle(root);
28
+ const vars = {};
29
+ const relevantVars = [
30
+ '--background',
31
+ '--foreground',
32
+ '--card',
33
+ '--card-foreground',
34
+ '--popover',
35
+ '--popover-foreground',
36
+ '--primary',
37
+ '--primary-foreground',
38
+ '--secondary',
39
+ '--secondary-foreground',
40
+ '--muted',
41
+ '--muted-foreground',
42
+ '--accent',
43
+ '--accent-foreground',
44
+ '--destructive',
45
+ '--destructive-foreground',
46
+ '--border',
47
+ '--input',
48
+ '--ring',
49
+ '--radius',
50
+ ];
51
+ relevantVars.forEach((varName) => {
52
+ const value = style.getPropertyValue(varName).trim();
53
+ if (value)
54
+ vars[varName] = value;
55
+ });
56
+ return vars;
57
+ };
58
+ // Check if element is inside devtools, dialogs, or overlays
59
+ const isOverlayElement = (el) => {
60
+ return !!(el.closest('[data-dndev-devtools]') ||
61
+ el.closest('[data-radix-portal]') ||
62
+ el.closest('[role="dialog"]') ||
63
+ el.closest('[data-state="open"]') ||
64
+ el.closest('.dndev-sheet-content'));
65
+ };
66
+ const analyzeTypography = () => {
67
+ const elements = document.querySelectorAll('*');
68
+ const fontSizeMap = new Map();
69
+ const fontWeightMap = new Map();
70
+ const fontFamilyMap = new Map();
71
+ let analyzedCount = 0;
72
+ elements.forEach((el) => {
73
+ // Skip overlay/devtools elements
74
+ if (isOverlayElement(el))
75
+ return;
76
+ const style = getComputedStyle(el);
77
+ const rect = el.getBoundingClientRect();
78
+ // Skip invisible elements
79
+ if (rect.width === 0 || rect.height === 0)
80
+ return;
81
+ analyzedCount++;
82
+ // Font size
83
+ const fontSize = style.fontSize;
84
+ fontSizeMap.set(fontSize, (fontSizeMap.get(fontSize) || 0) + 1);
85
+ // Font weight
86
+ const fontWeight = style.fontWeight;
87
+ fontWeightMap.set(fontWeight, (fontWeightMap.get(fontWeight) || 0) + 1);
88
+ // Font family (simplified)
89
+ const fontFamily = style.fontFamily.split(',')[0]?.trim().replace(/"/g, '') || 'unknown';
90
+ fontFamilyMap.set(fontFamily, (fontFamilyMap.get(fontFamily) || 0) + 1);
91
+ });
92
+ // Sort by count descending
93
+ const fontSizes = Array.from(fontSizeMap.entries())
94
+ .map(([size, count]) => ({ size, count }))
95
+ .sort((a, b) => b.count - a.count)
96
+ .slice(0, 10);
97
+ const fontWeights = Array.from(fontWeightMap.entries())
98
+ .map(([weight, count]) => ({ weight, count }))
99
+ .sort((a, b) => b.count - a.count);
100
+ const fontFamilies = Array.from(fontFamilyMap.entries())
101
+ .map(([family, count]) => ({ family, count }))
102
+ .sort((a, b) => b.count - a.count)
103
+ .slice(0, 5);
104
+ return {
105
+ fontSizes,
106
+ fontWeights,
107
+ fontFamilies,
108
+ totalElements: analyzedCount,
109
+ };
110
+ };
111
+ const analyzeColorRatio = () => {
112
+ const elements = document.querySelectorAll('*');
113
+ const colorMap = new Map();
114
+ elements.forEach((el) => {
115
+ // Skip overlay/devtools elements
116
+ if (isOverlayElement(el))
117
+ return;
118
+ const style = getComputedStyle(el);
119
+ const bg = style.backgroundColor;
120
+ const rect = el.getBoundingClientRect();
121
+ const area = rect.width * rect.height;
122
+ if (bg && bg !== 'rgba(0, 0, 0, 0)' && bg !== 'transparent' && area > 0) {
123
+ const existing = colorMap.get(bg) || { count: 0, area: 0 };
124
+ colorMap.set(bg, { count: existing.count + 1, area: existing.area + area });
125
+ }
126
+ });
127
+ const totalArea = Array.from(colorMap.values()).reduce((sum, v) => sum + v.area, 0);
128
+ if (totalArea === 0)
129
+ return { dominant: 0, secondary: 0, accent: 0, details: [] };
130
+ const colors = Array.from(colorMap.entries())
131
+ .map(([color, data]) => ({
132
+ color,
133
+ percentage: (data.area / totalArea) * 100,
134
+ }))
135
+ .sort((a, b) => b.percentage - a.percentage);
136
+ // Simplified categorization based on percentage
137
+ const dominant = colors.slice(0, 2).reduce((sum, c) => sum + c.percentage, 0);
138
+ const secondary = colors.slice(2, 5).reduce((sum, c) => sum + c.percentage, 0);
139
+ const accent = colors.slice(5).reduce((sum, c) => sum + c.percentage, 0);
140
+ return {
141
+ dominant,
142
+ secondary,
143
+ accent,
144
+ details: colors.slice(0, 15).map((c) => ({
145
+ ...c,
146
+ category: c.percentage > 30 ? 'dominant' : c.percentage > 10 ? 'secondary' : 'accent',
147
+ })),
148
+ };
149
+ };
150
+ const runAnalysis = () => {
151
+ setIsAnalyzing(true);
152
+ try {
153
+ setColorRatio(analyzeColorRatio());
154
+ setTypography(analyzeTypography());
155
+ toast('success', 'Analysis complete');
156
+ }
157
+ catch (error) {
158
+ console.error('Analysis failed:', error);
159
+ toast('error', 'Analysis failed');
160
+ }
161
+ finally {
162
+ setIsAnalyzing(false);
163
+ }
164
+ };
165
+ // Format analysis results for clipboard
166
+ const getAnalysisJson = () => {
167
+ return JSON.stringify({
168
+ theme: {
169
+ current: currentTheme,
170
+ isDarkMode,
171
+ isReady,
172
+ availableCount: availableThemes.length,
173
+ },
174
+ cssVariables: getCSSVariables(),
175
+ colorRatio: colorRatio || null,
176
+ typography: typography || null,
177
+ timestamp: new Date().toISOString(),
178
+ }, null, 2);
179
+ };
180
+ const copyAnalysis = () => {
181
+ navigator.clipboard.writeText(getAnalysisJson()).then(() => {
182
+ toast('success', 'Analysis copied to clipboard');
183
+ }).catch(() => {
184
+ toast('error', 'Failed to copy');
185
+ });
186
+ };
187
+ const cssVars = getCSSVariables();
188
+ const themeItems = [
189
+ { label: 'Current', value: _jsx(Text, { className: "dndev-font-mono", children: currentTheme }) },
190
+ { label: 'Dark Mode', value: _jsx(Badge, { variant: isDarkMode ? BADGE_VARIANT.DEFAULT : BADGE_VARIANT.SECONDARY, children: isDarkMode ? 'Yes' : 'No' }) },
191
+ { label: 'Ready', value: _jsx(Badge, { variant: isReady ? BADGE_VARIANT.DEFAULT : BADGE_VARIANT.DESTRUCTIVE, children: isReady ? 'Yes' : 'No' }) },
192
+ { label: 'Available', value: _jsxs(Text, { className: "dndev-font-mono", children: [availableThemes.length, " themes"] }) },
193
+ ];
194
+ const isColorCompliant = colorRatio &&
195
+ colorRatio.dominant >= 50 &&
196
+ colorRatio.dominant <= 70 &&
197
+ colorRatio.secondary >= 20 &&
198
+ colorRatio.secondary <= 40 &&
199
+ colorRatio.accent >= 5 &&
200
+ colorRatio.accent <= 20;
201
+ return (_jsxs(Stack, { gap: "medium", style: { padding: 'var(--gap-md)' }, children: [_jsx(Card, { title: "Theme", subtitle: currentTheme, children: _jsx(DescriptionList, { items: themeItems }) }), _jsx(Card, { title: "CSS Variables", subtitle: `${Object.keys(cssVars).length} variables`, children: _jsx(ScrollArea, { className: "dndev-max-h-48", children: _jsx(Stack, { gap: "tight", children: Object.entries(cssVars).map(([name, value]) => (_jsxs(Stack, { direction: "row", align: "center", justify: "between", gap: "tight", children: [_jsx(Text, { className: "dndev-font-mono dndev-text-xs", children: name }), _jsxs(Stack, { direction: "row", align: "center", gap: "tight", children: [_jsx("div", { style: {
202
+ width: '1rem',
203
+ height: '1rem',
204
+ backgroundColor: `var(${name})`,
205
+ border: '1px solid var(--border)',
206
+ borderRadius: 'var(--radius-sm)',
207
+ } }), _jsx(Text, { className: "dndev-font-mono dndev-text-xs dndev-text-muted-foreground", children: value })] })] }, name))) }) }) }), _jsxs(Stack, { direction: "row", gap: "tight", children: [_jsx(Button, { onClick: runAnalysis, variant: BUTTON_VARIANT.DEFAULT, icon: Palette, disabled: isAnalyzing, style: { flex: 1 }, children: isAnalyzing ? 'Analyzing...' : 'Analyze Page' }), _jsx(Button, { onClick: copyAnalysis, variant: BUTTON_VARIANT.OUTLINE, disabled: !colorRatio && !typography, children: "Copy JSON" })] }), colorRatio && (_jsx(Card, { title: _jsxs(Stack, { direction: "row", align: "center", justify: "between", className: "dndev-w-full", children: [_jsx("span", { children: "60/30/10 Color Ratio" }), _jsx(Badge, { variant: isColorCompliant ? BADGE_VARIANT.DEFAULT : BADGE_VARIANT.SECONDARY, children: isColorCompliant ? '✓ Balanced' : '⚠ Review' })] }), children: _jsxs(Stack, { gap: "tight", children: [_jsxs(Stack, { direction: "row", justify: "between", children: [_jsx("span", { children: "Dominant (60%)" }), _jsxs("span", { className: colorRatio.dominant >= 50 && colorRatio.dominant <= 70 ? 'dndev-text-primary' : 'dndev-text-destructive', children: [colorRatio.dominant.toFixed(1), "%"] })] }), _jsxs(Stack, { direction: "row", justify: "between", children: [_jsx("span", { children: "Secondary (30%)" }), _jsxs("span", { className: colorRatio.secondary >= 20 && colorRatio.secondary <= 40 ? 'dndev-text-primary' : 'dndev-text-destructive', children: [colorRatio.secondary.toFixed(1), "%"] })] }), _jsxs(Stack, { direction: "row", justify: "between", children: [_jsx("span", { children: "Accent (10%)" }), _jsxs("span", { className: colorRatio.accent >= 5 && colorRatio.accent <= 20 ? 'dndev-text-primary' : 'dndev-text-destructive', children: [colorRatio.accent.toFixed(1), "%"] })] }), colorRatio.details.length > 0 && (_jsx(Accordion, { type: "single", collapsible: true, items: [
208
+ {
209
+ value: 'colors',
210
+ trigger: _jsxs("span", { className: "dndev-text-sm", children: ["Top Colors (", colorRatio.details.length, ")"] }),
211
+ content: (_jsx(Stack, { gap: "tight", children: colorRatio.details.map((c, i) => (_jsxs(Stack, { direction: "row", align: "center", gap: "tight", children: [_jsx("div", { style: {
212
+ width: '1rem',
213
+ height: '1rem',
214
+ backgroundColor: c.color,
215
+ border: '1px solid var(--border)',
216
+ flexShrink: 0,
217
+ } }), _jsxs(Text, { className: "dndev-text-xs dndev-font-mono", style: { flex: 1 }, children: [c.percentage.toFixed(1), "%"] }), _jsx(Badge, { variant: BADGE_VARIANT.SECONDARY, children: c.category })] }, i))) })),
218
+ },
219
+ ] }))] }) })), typography && (_jsx(Card, { title: _jsxs(Stack, { direction: "row", align: "center", justify: "between", className: "dndev-w-full", children: [_jsx("span", { children: "Typography" }), _jsxs(Badge, { variant: BADGE_VARIANT.SECONDARY, children: [typography.totalElements, " elements"] })] }), children: _jsxs(Stack, { gap: "medium", children: [_jsxs("div", { children: [_jsxs(Label, { className: "dndev-text-sm dndev-font-semibold", children: ["Font Sizes (", typography.fontSizes.length, ")"] }), _jsx(Stack, { gap: "tight", className: "dndev-mt-xs", children: typography.fontSizes.map((f, i) => (_jsxs(Stack, { direction: "row", justify: "between", children: [_jsx(Text, { className: "dndev-font-mono dndev-text-xs", children: f.size }), _jsxs(Text, { className: "dndev-text-xs dndev-text-muted-foreground", children: [f.count, "\u00D7"] })] }, i))) })] }), _jsxs("div", { children: [_jsxs(Label, { className: "dndev-text-sm dndev-font-semibold", children: ["Font Weights (", typography.fontWeights.length, ")"] }), _jsx(Stack, { direction: "row", gap: "tight", wrap: "wrap", className: "dndev-mt-xs", children: typography.fontWeights.map((f, i) => (_jsxs(Badge, { variant: BADGE_VARIANT.SECONDARY, children: [f.weight, " (", f.count, "\u00D7)"] }, i))) })] }), _jsxs("div", { children: [_jsxs(Label, { className: "dndev-text-sm dndev-font-semibold", children: ["Font Families (", typography.fontFamilies.length, ")"] }), _jsx(Stack, { gap: "tight", className: "dndev-mt-xs", children: typography.fontFamilies.map((f, i) => (_jsxs(Stack, { direction: "row", justify: "between", children: [_jsx(Text, { className: "dndev-font-mono dndev-text-xs", children: f.family }), _jsxs(Text, { className: "dndev-text-xs dndev-text-muted-foreground", children: [f.count, "\u00D7"] })] }, i))) })] })] }) })), !colorRatio && !typography && (_jsx(Alert, { variant: ALERT_VARIANT.INFO, children: "Click \"Analyze Page\" to check color ratio and typography" }))] }));
220
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"StoresTab.d.ts","sourceRoot":"","sources":["../../../../src/internal/devtools/components/StoresTab.tsx"],"names":[],"mappings":"AAiCA,eAAO,MAAM,SAAS,+CA8NrB,CAAC"}
1
+ {"version":3,"file":"StoresTab.d.ts","sourceRoot":"","sources":["../../../../src/internal/devtools/components/StoresTab.tsx"],"names":[],"mappings":"AAwCA,eAAO,MAAM,SAAS,+CAoGrB,CAAC"}
@@ -2,120 +2,72 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  // packages/ui/src/internal/devtools/components/StoresTab.tsx
4
4
  /**
5
-
6
5
  * @fileoverview Stores Tab Component
7
-
8
- * @description Displays all registered Zustand stores from globalThis._DNDEV_STORES_
9
-
6
+ * @description Displays registered Zustand stores from globalThis._DNDEV_STORES_
10
7
  *
11
-
12
- * @version 0.0.1
13
-
8
+ * @version 0.0.2
14
9
  * @since 0.0.1
15
-
16
10
  * @author AMBROISE PARK Consulting
17
-
18
11
  */
19
- import { useState, useEffect } from 'react';
20
- import { Stack, Card, Accordion, ScrollArea, JsonViewer, CopyToClipboard, Text, Alert, ALERT_VARIANT, } from '@donotdev/components';
12
+ import { useState, useEffect, useCallback } from 'react';
13
+ import { Stack, Card, Accordion, ScrollArea, JsonViewer, Text, Badge, BADGE_VARIANT, Button, BUTTON_VARIANT, } from '@donotdev/components';
14
+ import { RefreshCw } from 'lucide-react';
15
+ const STORE_NAMES = {
16
+ 'theme-store': 'Theme',
17
+ 'consent-store': 'Consent',
18
+ 'navigation-store': 'Navigation',
19
+ 'loading-store': 'Loading',
20
+ 'overlay-store': 'Overlay',
21
+ 'network-store': 'Network',
22
+ 'abort-store': 'Abort',
23
+ 'i18n-store': 'i18n',
24
+ 'error-store': 'Error',
25
+ };
21
26
  export const StoresTab = () => {
22
- const [stores, setStores] = useState(() => {
23
- if (typeof globalThis !== 'undefined' &&
24
- globalThis._DNDEV_STORES_) {
25
- const currentStores = globalThis._DNDEV_STORES_;
26
- return Object.keys(currentStores).reduce((acc, key) => {
27
- acc[key] = currentStores[key];
28
- return acc;
29
- }, {});
27
+ const [storeStates, setStoreStates] = useState({});
28
+ const [storeNames, setStoreNames] = useState([]);
29
+ const refreshStores = useCallback(() => {
30
+ const registry = globalThis._DNDEV_STORES_;
31
+ if (!registry) {
32
+ setStoreNames([]);
33
+ setStoreStates({});
34
+ return;
30
35
  }
31
- return {};
32
- });
33
- useEffect(() => {
34
- const updateStores = () => {
35
- if (typeof globalThis !== 'undefined' &&
36
- globalThis._DNDEV_STORES_) {
37
- const currentStores = globalThis._DNDEV_STORES_;
38
- const storeKeys = Object.keys(currentStores);
39
- setStores((prev) => {
40
- const prevKeys = Object.keys(prev);
41
- if (prevKeys.length !== storeKeys.length) {
42
- const newStores = {};
43
- storeKeys.forEach((key) => {
44
- newStores[key] = currentStores[key];
45
- });
46
- return newStores;
47
- }
48
- const hasChanged = storeKeys.some((key) => !prev[key] || prev[key] !== currentStores[key]);
49
- if (hasChanged) {
50
- const newStores = {};
51
- storeKeys.forEach((key) => {
52
- newStores[key] = currentStores[key];
53
- });
54
- return newStores;
55
- }
56
- return prev;
57
- });
36
+ const names = Object.keys(registry);
37
+ setStoreNames(names);
38
+ const states = {};
39
+ names.forEach((name) => {
40
+ try {
41
+ const store = registry[name];
42
+ if (store?.getState) {
43
+ states[name] = store.getState();
44
+ }
45
+ else {
46
+ states[name] = { _error: 'No getState method' };
47
+ }
58
48
  }
59
- else {
60
- setStores({});
49
+ catch (e) {
50
+ states[name] = { _error: String(e) };
61
51
  }
62
- };
63
- updateStores();
64
- const interval = setInterval(updateStores, 500);
65
- return () => clearInterval(interval);
52
+ });
53
+ setStoreStates(states);
66
54
  }, []);
67
- const getStoreDisplayName = (storeName) => {
68
- const nameMap = {
69
- 'theme-store': 'Theme Store',
70
- 'consent-store': 'Consent Store',
71
- 'navigation-store': 'Navigation Store',
72
- loading: 'Loading Store',
73
- overlay: 'Overlay Store',
74
- network: 'Network Store',
75
- abort: 'Abort Controller Store',
76
- 'i18n-store': 'i18n Store',
77
- 'error-store': 'Error Store',
78
- 'dndev-theme-store': 'Theme Store (Legacy)',
79
- };
80
- return (nameMap[storeName] ||
81
- storeName
82
- .replace(/-store$/, ' Store')
83
- .replace(/-/g, ' ')
84
- .replace(/\b\w/g, (l) => l.toUpperCase()));
85
- };
86
- const getStoreState = (store) => {
87
- try {
88
- if (store && typeof store.getState === 'function') {
89
- return store.getState();
90
- }
91
- return null;
92
- }
93
- catch (error) {
94
- return {
95
- error: error instanceof Error ? error.message : 'Unknown error',
96
- };
97
- }
98
- };
99
- const formatJSON = (data) => {
100
- try {
101
- return JSON.stringify(data, null, 2);
102
- }
103
- catch (error) {
104
- return 'Error formatting data';
105
- }
106
- };
107
- const storeEntries = Object.entries(stores);
108
- if (storeEntries.length === 0) {
109
- return (_jsx(Stack, { gap: "medium", children: _jsx(Alert, { variant: ALERT_VARIANT.WARNING, children: _jsx(Text, { as: "span", children: "No stores registered in globalThis._DNDEV_STORES_" }) }) }));
55
+ useEffect(() => {
56
+ refreshStores();
57
+ const interval = setInterval(refreshStores, 1000);
58
+ return () => clearInterval(interval);
59
+ }, [refreshStores]);
60
+ const getDisplayName = (name) => STORE_NAMES[name] || name.replace(/-store$/, '').replace(/-/g, ' ');
61
+ if (storeNames.length === 0) {
62
+ return (_jsx(Stack, { gap: "medium", style: { padding: 'var(--gap-md)' }, children: _jsx(Card, { title: "Stores", subtitle: "No stores registered", children: _jsx(Text, { className: "dndev-text-muted-foreground", children: "globalThis._DNDEV_STORES_ is empty or not initialized." }) }) }));
110
63
  }
111
- return (_jsx(Stack, { gap: "medium", children: _jsx(Card, { title: "Stores", subtitle: `${storeEntries.length} store${storeEntries.length !== 1 ? 's' : ''} registered`, children: _jsx(Accordion, { items: storeEntries.map(([storeName, store]) => {
112
- const state = getStoreState(store);
113
- const hasError = state && 'error' in state;
114
- const displayName = getStoreDisplayName(storeName);
64
+ return (_jsx(Stack, { gap: "medium", style: { padding: 'var(--gap-md)' }, children: _jsx(Card, { title: _jsxs(Stack, { direction: "row", align: "center", justify: "between", className: "dndev-w-full", children: [_jsx("span", { children: "Stores" }), _jsxs(Stack, { direction: "row", align: "center", gap: "tight", children: [_jsx(Badge, { variant: BADGE_VARIANT.SECONDARY, children: storeNames.length }), _jsx(Button, { variant: BUTTON_VARIANT.GHOST, icon: RefreshCw, onClick: refreshStores, title: "Refresh stores" })] })] }), children: _jsx(Accordion, { type: "single", collapsible: true, items: storeNames.map((name) => {
65
+ const state = storeStates[name];
66
+ const hasError = state?._error;
115
67
  return {
116
- value: storeName,
117
- trigger: (_jsxs(Stack, { direction: "row", gap: "tight", align: "center", justify: "between", children: [_jsx(Text, { as: "span", children: displayName }), _jsxs(Text, { as: "span", className: "dndev-text-xs dndev-text-muted-foreground dndev-font-mono", children: ["(", storeName, ")"] })] })),
118
- content: (_jsx(Stack, { gap: "medium", children: hasError ? (_jsx(Alert, { variant: ALERT_VARIANT.ERROR, children: _jsxs(Text, { as: "span", children: ["Failed to get state: ", state.error] }) })) : state === null ? (_jsx(Alert, { variant: ALERT_VARIANT.WARNING, children: _jsx(Text, { as: "span", children: "Store does not have getState() method or is not initialized" }) })) : (_jsx(Card, { title: "Store State", subtitle: "Current state from getState()", footer: _jsx(Stack, { direction: "row", gap: "tight", justify: "end", children: _jsx(CopyToClipboard, { text: formatJSON(state), tooltipText: "Copy store state", copiedTooltipText: "Copied!" }) }), children: _jsx(ScrollArea, { className: "dndev-max-h-96", children: _jsx(JsonViewer, { data: state, defaultDepth: 2, showCopyButton: true }) }) })) })),
68
+ value: name,
69
+ trigger: (_jsxs(Stack, { direction: "row", align: "center", justify: "between", className: "dndev-w-full", children: [_jsx(Text, { className: "dndev-font-medium", children: getDisplayName(name) }), _jsx(Text, { className: "dndev-font-mono dndev-text-xs dndev-text-muted-foreground", children: name })] })),
70
+ content: hasError ? (_jsx(Text, { className: "dndev-text-destructive dndev-text-sm", children: state._error })) : (_jsx(ScrollArea, { className: "dndev-max-h-64", children: _jsx(JsonViewer, { data: state, defaultDepth: 2, showCopyButton: true }) })),
119
71
  };
120
72
  }) }) }) }));
121
73
  };
@@ -6,16 +6,11 @@
6
6
  * @since 0.0.1
7
7
  * @author AMBROISE PARK Consulting
8
8
  */
9
- export * from './AuthTab';
10
9
  export * from './ConfigTab';
11
10
  export * from './CookieTab';
12
11
  export * from './DebugDialog';
13
- export * from './DebugToggle';
14
- export * from './EnvironmentTab';
15
- export * from './I18nTab';
16
12
  export * from './LayoutReset';
17
13
  export * from './MaskedValue';
18
14
  export * from './StoresTab';
19
- export * from './StripeDebugTab';
20
- export * from './ThemesTab';
15
+ export * from './DesignTab';
21
16
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/internal/devtools/components/index.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/internal/devtools/components/index.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC"}
@@ -7,15 +7,10 @@
7
7
  * @since 0.0.1
8
8
  * @author AMBROISE PARK Consulting
9
9
  */
10
- export * from './AuthTab';
11
10
  export * from './ConfigTab';
12
11
  export * from './CookieTab';
13
12
  export * from './DebugDialog';
14
- export * from './DebugToggle';
15
- export * from './EnvironmentTab';
16
- export * from './I18nTab';
17
13
  export * from './LayoutReset';
18
14
  export * from './MaskedValue';
19
15
  export * from './StoresTab';
20
- export * from './StripeDebugTab';
21
- export * from './ThemesTab';
16
+ export * from './DesignTab';
@@ -7,7 +7,6 @@
7
7
  * @author AMBROISE PARK Consulting
8
8
  */
9
9
  export * from './envVarDiscovery';
10
- export * from './spacingAnalyzer';
11
10
  export * from './virtualModuleInspector';
12
11
  export * from './maskSensitive';
13
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/internal/devtools/utils/index.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AACzC,cAAc,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/internal/devtools/utils/index.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AACzC,cAAc,iBAAiB,CAAC"}
@@ -8,6 +8,5 @@
8
8
  * @author AMBROISE PARK Consulting
9
9
  */
10
10
  export * from './envVarDiscovery';
11
- export * from './spacingAnalyzer';
12
11
  export * from './virtualModuleInspector';
13
12
  export * from './maskSensitive';
@@ -1 +1 @@
1
- {"version":3,"file":"BaseStoresInitializer.d.ts","sourceRoot":"","sources":["../../../src/internal/initializers/BaseStoresInitializer.tsx"],"names":[],"mappings":"AAkCA,OAAO,KAAK,EAAa,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AA6DvC;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,EAAE,aAAa,CAAC;IACxB,YAAY,CAAC,EAAE,iBAAiB,EAAE,CAAC;IACnC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAyGD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,QAAQ,EACR,QAAQ,EACR,YAAiB,EACjB,aAAqB,EACrB,aAAa,GACd,EAAE,0BAA0B,kDA0K5B"}
1
+ {"version":3,"file":"BaseStoresInitializer.d.ts","sourceRoot":"","sources":["../../../src/internal/initializers/BaseStoresInitializer.tsx"],"names":[],"mappings":"AAkCA,OAAO,KAAK,EAAa,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AA6DvC;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,EAAE,aAAa,CAAC;IACxB,YAAY,CAAC,EAAE,iBAAiB,EAAE,CAAC;IACnC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAyGD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,QAAQ,EACR,QAAQ,EACR,YAAiB,EACjB,aAAqB,EACrB,aAAa,GACd,EAAE,0BAA0B,kDAqI5B"}
@@ -232,77 +232,44 @@ export function BaseStoresInitializer({ children, handlers, customStores = [], s
232
232
  const allCustomReady = criticalCustomStores.length === 0 ||
233
233
  customReadiness.every((ready) => ready === true);
234
234
  const criticalReady = consentReady && themeReady && i18nReady && allCustomReady;
235
+ // Two-phase loader: wait for BOTH framework AND route to be ready
235
236
  useLayoutEffect(() => {
236
237
  if (!isClient() || !criticalReady)
237
238
  return;
238
239
  let removed = false;
239
- const removeLoader = (fade = false) => {
240
+ let frameworkReady = false;
241
+ let routeReady = false;
242
+ const removeLoader = () => {
240
243
  if (removed)
241
244
  return;
242
245
  removed = true;
243
- removeShellLoader({ fade });
246
+ removeShellLoader({ fade: true }); // Always fade
244
247
  };
245
- const handleFrameworkReady = () => {
246
- if (removed)
248
+ const checkBothReady = () => {
249
+ if (!frameworkReady || !routeReady || removed)
247
250
  return;
248
- removeLoader(false);
251
+ removeLoader();
249
252
  };
250
- const unsubscribe = globalEmitter.on('DNDEV_FRAMEWORK_READY', handleFrameworkReady);
251
- const root = document.getElementById('root');
252
- if (!root) {
253
- setTimeout(() => removeLoader(true), 300);
254
- return () => {
255
- unsubscribe();
256
- };
257
- }
258
- const hasReactContent = () => {
259
- // Zero reflow content detection - only uses properties that don't trigger layout
260
- const children = Array.from(root.children);
261
- // Check for non-shell-loader children without forcing reflow
262
- // React content will have:
263
- // 1. Child elements (components mount as DOM nodes)
264
- // 2. Data attributes (React adds data-reactroot, etc.)
265
- // NOTE: We avoid textContent checks as they force layout recalculation
266
- const hasContent = children.some((child) => {
267
- if (child.id === 'shell-loader')
268
- return false;
269
- // Zero-reflow detection strategies:
270
- // - Child elements exist (no layout needed)
271
- // - React data attributes (no layout needed)
272
- // - Custom data attributes from framework (no layout needed)
273
- return (child.children.length > 0 ||
274
- child.hasAttribute('data-reactroot') ||
275
- child.hasAttribute('data-dndev-mounted'));
276
- });
277
- return hasContent;
253
+ const handleFrameworkReady = () => {
254
+ frameworkReady = true;
255
+ checkBothReady();
278
256
  };
279
- const checkAndRemove = () => {
280
- if (hasReactContent()) {
281
- removeLoader(false);
282
- return true;
283
- }
284
- return false;
257
+ const handleRouteReady = () => {
258
+ routeReady = true;
259
+ checkBothReady();
285
260
  };
286
- let fallbackTimeout = null;
287
- requestAnimationFrame(() => {
288
- requestAnimationFrame(() => {
289
- if (!checkAndRemove()) {
290
- fallbackTimeout = setTimeout(() => {
291
- if (hasReactContent()) {
292
- removeLoader(false);
293
- }
294
- else {
295
- removeLoader(true);
296
- }
297
- }, 1000);
298
- }
299
- });
300
- });
301
- return () => {
302
- unsubscribe();
303
- if (fallbackTimeout) {
304
- clearTimeout(fallbackTimeout);
261
+ const unsubFramework = globalEmitter.on('DNDEV_FRAMEWORK_READY', handleFrameworkReady);
262
+ const unsubRoute = globalEmitter.on('DNDEV_ROUTE_READY', handleRouteReady);
263
+ // Fallback timeout: if route doesn't signal within 2s, remove anyway
264
+ const fallbackTimeout = setTimeout(() => {
265
+ if (!removed) {
266
+ removeLoader();
305
267
  }
268
+ }, 2000);
269
+ return () => {
270
+ unsubFramework();
271
+ unsubRoute();
272
+ clearTimeout(fallbackTimeout);
306
273
  };
307
274
  }, [criticalReady]);
308
275
  if (!criticalReady)
@@ -226,13 +226,13 @@ export const DnDevLayout = ({ children, layout, className, }) => {
226
226
  DEFAULT_SLOTS.sidebar.bottom;
227
227
  return (_jsx(DnDevMergedBar, { position: mergedBarConfig.position, height: mergedBarConfig.height, trigger: triggerSlot(), top: topSlot(), content: contentSlot(), bottom: bottomSlot() }));
228
228
  }, [layout?.mergedbar, config]);
229
- // Apply routing animation data attribute
230
- useEffect(() => {
229
+ // Apply routing animation data attribute (useLayoutEffect = before paint)
230
+ useLayoutEffect(() => {
231
231
  if (!isClient() || !mainRef.current)
232
232
  return;
233
233
  const routingAnimation = getComputedStyle(document.documentElement)
234
234
  .getPropertyValue('--routing-animation')
235
- .trim() || 'fade';
235
+ .trim() || 'none';
236
236
  mainRef.current.setAttribute('data-routing-animation', routingAnimation);
237
237
  }, [pathname]);
238
238
  // Apply data attributes
@@ -1 +1 @@
1
- {"version":3,"file":"FooterBranding.d.ts","sourceRoot":"","sources":["../../../../../src/internal/layout/components/footer/FooterBranding.tsx"],"names":[],"mappings":"AAeA,MAAM,WAAW,mBAAmB;IAClC,6BAA6B;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,gBAAgB;IAChB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,aAAa,CAAC,mBAAmB,CAgCnE,CAAC;AAEF,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"FooterBranding.d.ts","sourceRoot":"","sources":["../../../../../src/internal/layout/components/footer/FooterBranding.tsx"],"names":[],"mappings":"AAeA,MAAM,WAAW,mBAAmB;IAClC,6BAA6B;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,gBAAgB;IAChB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,aAAa,CAAC,mBAAmB,CAoCnE,CAAC;AAEF,eAAe,cAAc,CAAC"}
@@ -27,7 +27,9 @@ export const FooterBranding = ({ className, size = 'xs', }) => {
27
27
  xs: { fontSize: 'var(--font-size-xs)' },
28
28
  sm: { fontSize: 'var(--font-size-sm)' },
29
29
  };
30
- return (_jsxs("a", { href: isFrameworkSite ? 'https://www.ambroise-park.com' : 'https://donotdev.com', target: "_blank", rel: "noopener noreferrer", className: className, style: {
30
+ return (_jsxs("a", { href: isFrameworkSite
31
+ ? 'https://www.ambroise-park.com'
32
+ : 'https://donotdev.com', target: "_blank", rel: "noopener noreferrer", className: className, style: {
31
33
  display: 'inline-flex',
32
34
  alignItems: 'center',
33
35
  gap: 'var(--gap-sm)',