@moontra/moonui-pro 2.20.2 → 2.20.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.
- package/package.json +8 -3
- package/plugin/index.d.ts +86 -0
- package/plugin/index.js +308 -0
- package/scripts/postinstall.js +176 -23
- package/src/components/advanced-chart/index.tsx +0 -1246
- package/src/components/advanced-forms/index.tsx +0 -585
- package/src/components/animated-button/index.tsx +0 -385
- package/src/components/calendar/event-dialog.tsx +0 -377
- package/src/components/calendar/index.tsx +0 -1220
- package/src/components/calendar-pro/index.tsx +0 -1697
- package/src/components/color-picker/index.tsx +0 -432
- package/src/components/credit-card-input/index.tsx +0 -406
- package/src/components/dashboard/dashboard-grid.tsx +0 -480
- package/src/components/dashboard/demo.tsx +0 -425
- package/src/components/dashboard/index.tsx +0 -1046
- package/src/components/dashboard/time-range-picker.tsx +0 -336
- package/src/components/dashboard/types.ts +0 -225
- package/src/components/dashboard/widgets/activity-feed.tsx +0 -349
- package/src/components/dashboard/widgets/chart-widget.tsx +0 -418
- package/src/components/dashboard/widgets/comparison-widget.tsx +0 -177
- package/src/components/dashboard/widgets/index.ts +0 -5
- package/src/components/dashboard/widgets/metric-card.tsx +0 -363
- package/src/components/dashboard/widgets/progress-widget.tsx +0 -113
- package/src/components/data-table/data-table-bulk-actions.tsx +0 -204
- package/src/components/data-table/data-table-column-toggle.tsx +0 -169
- package/src/components/data-table/data-table-export.ts +0 -156
- package/src/components/data-table/data-table-filter-drawer.tsx +0 -448
- package/src/components/data-table/index.tsx +0 -845
- package/src/components/draggable-list/index.tsx +0 -100
- package/src/components/error-boundary/index.tsx +0 -232
- package/src/components/file-upload/index.tsx +0 -1660
- package/src/components/floating-action-button/index.tsx +0 -206
- package/src/components/form-wizard/form-wizard-context.tsx +0 -335
- package/src/components/form-wizard/form-wizard-navigation.tsx +0 -118
- package/src/components/form-wizard/form-wizard-progress.tsx +0 -329
- package/src/components/form-wizard/form-wizard-step.tsx +0 -111
- package/src/components/form-wizard/index.tsx +0 -102
- package/src/components/form-wizard/types.ts +0 -77
- package/src/components/gesture-drawer/index.tsx +0 -551
- package/src/components/github-stars/github-api.ts +0 -426
- package/src/components/github-stars/hooks.ts +0 -517
- package/src/components/github-stars/index.tsx +0 -375
- package/src/components/github-stars/types.ts +0 -148
- package/src/components/github-stars/variants.tsx +0 -515
- package/src/components/health-check/index.tsx +0 -439
- package/src/components/hover-card-3d/index.tsx +0 -529
- package/src/components/index.ts +0 -130
- package/src/components/internal/index.ts +0 -78
- package/src/components/kanban/add-card-modal.tsx +0 -502
- package/src/components/kanban/card-detail-modal.tsx +0 -761
- package/src/components/kanban/index.ts +0 -13
- package/src/components/kanban/kanban.tsx +0 -1689
- package/src/components/kanban/types.ts +0 -168
- package/src/components/lazy-component/index.tsx +0 -823
- package/src/components/license-error/index.tsx +0 -31
- package/src/components/magnetic-button/index.tsx +0 -216
- package/src/components/memory-efficient-data/index.tsx +0 -1018
- package/src/components/moonui-quiz-form/index.tsx +0 -817
- package/src/components/navbar/index.tsx +0 -781
- package/src/components/optimized-image/index.tsx +0 -425
- package/src/components/performance-debugger/index.tsx +0 -613
- package/src/components/performance-monitor/index.tsx +0 -808
- package/src/components/phone-number-input/index.tsx +0 -343
- package/src/components/phone-number-input/phone-number-input-simple.tsx +0 -167
- package/src/components/pinch-zoom/index.tsx +0 -566
- package/src/components/quiz-form/index.tsx +0 -479
- package/src/components/rich-text-editor/index.tsx +0 -2322
- package/src/components/rich-text-editor/slash-commands-extension.ts +0 -230
- package/src/components/rich-text-editor/slash-commands.css +0 -35
- package/src/components/rich-text-editor/table-styles.css +0 -65
- package/src/components/sidebar/index.tsx +0 -884
- package/src/components/spotlight-card/index.tsx +0 -191
- package/src/components/swipeable-card/index.tsx +0 -100
- package/src/components/timeline/index.tsx +0 -1183
- package/src/components/ui/accordion.tsx +0 -581
- package/src/components/ui/alert-dialog.tsx +0 -141
- package/src/components/ui/alert.tsx +0 -141
- package/src/components/ui/aspect-ratio.tsx +0 -245
- package/src/components/ui/avatar.tsx +0 -155
- package/src/components/ui/badge.tsx +0 -230
- package/src/components/ui/breadcrumb.tsx +0 -216
- package/src/components/ui/button.tsx +0 -228
- package/src/components/ui/calendar.tsx +0 -387
- package/src/components/ui/card.tsx +0 -216
- package/src/components/ui/checkbox.tsx +0 -259
- package/src/components/ui/collapsible.tsx +0 -631
- package/src/components/ui/color-picker.tsx +0 -97
- package/src/components/ui/command.tsx +0 -948
- package/src/components/ui/dialog.tsx +0 -752
- package/src/components/ui/dropdown-menu.tsx +0 -706
- package/src/components/ui/gesture-drawer.tsx +0 -11
- package/src/components/ui/hover-card.tsx +0 -29
- package/src/components/ui/index.ts +0 -222
- package/src/components/ui/input.tsx +0 -224
- package/src/components/ui/label.tsx +0 -29
- package/src/components/ui/lightbox.tsx +0 -606
- package/src/components/ui/magnetic-button.tsx +0 -129
- package/src/components/ui/media-gallery.tsx +0 -611
- package/src/components/ui/navigation-menu.tsx +0 -130
- package/src/components/ui/pagination.tsx +0 -125
- package/src/components/ui/popover.tsx +0 -185
- package/src/components/ui/progress.tsx +0 -30
- package/src/components/ui/radio-group.tsx +0 -257
- package/src/components/ui/scroll-area.tsx +0 -47
- package/src/components/ui/select.tsx +0 -378
- package/src/components/ui/separator.tsx +0 -145
- package/src/components/ui/sheet.tsx +0 -139
- package/src/components/ui/skeleton.tsx +0 -20
- package/src/components/ui/slider.tsx +0 -354
- package/src/components/ui/spotlight-card.tsx +0 -119
- package/src/components/ui/switch.tsx +0 -86
- package/src/components/ui/table.tsx +0 -331
- package/src/components/ui/tabs-pro.tsx +0 -542
- package/src/components/ui/tabs.tsx +0 -54
- package/src/components/ui/textarea.tsx +0 -28
- package/src/components/ui/toast.tsx +0 -317
- package/src/components/ui/toggle.tsx +0 -119
- package/src/components/ui/tooltip.tsx +0 -151
- package/src/components/virtual-list/index.tsx +0 -668
- package/src/hooks/use-chart.ts +0 -205
- package/src/hooks/use-data-table.ts +0 -182
- package/src/hooks/use-docs-pro-access.ts +0 -13
- package/src/hooks/use-license-check.ts +0 -65
- package/src/hooks/use-subscription.ts +0 -19
- package/src/hooks/use-toast.ts +0 -15
- package/src/index.ts +0 -22
- package/src/lib/ai-providers.ts +0 -377
- package/src/lib/component-metadata.ts +0 -18
- package/src/lib/micro-interactions.ts +0 -255
- package/src/lib/paddle.ts +0 -17
- package/src/lib/utils.ts +0 -6
- package/src/patterns/login-form/index.tsx +0 -276
- package/src/patterns/login-form/types.ts +0 -67
- package/src/setupTests.ts +0 -41
- package/src/styles/advanced-chart.css +0 -239
- package/src/styles/calendar.css +0 -35
- package/src/styles/design-system.css +0 -363
- package/src/styles/index.css +0 -681
- package/src/styles/tailwind.css +0 -7
- package/src/styles/tokens.css +0 -455
- package/src/types/next-auth.d.ts +0 -21
- package/src/use-intersection-observer.tsx +0 -154
- package/src/use-local-storage.tsx +0 -71
- package/src/use-paddle.ts +0 -138
- package/src/use-performance-optimizer.ts +0 -389
- package/src/use-pro-access.ts +0 -141
- package/src/use-scroll-animation.ts +0 -219
- package/src/use-subscription.ts +0 -37
- package/src/use-toast.ts +0 -32
- package/src/utils/chart-helpers.ts +0 -357
- package/src/utils/cn.ts +0 -6
- package/src/utils/data-processing.ts +0 -151
- package/src/utils/license-validator.tsx +0 -183
|
@@ -1,613 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import React, { useState, useEffect, useRef, useCallback } from "react"
|
|
4
|
-
import { motion, AnimatePresence } from "framer-motion"
|
|
5
|
-
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card"
|
|
6
|
-
import { Button } from "../ui/button"
|
|
7
|
-
import { MoonUIBadgePro as Badge } from "../ui/badge"
|
|
8
|
-
import { Progress } from "../ui/progress"
|
|
9
|
-
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs"
|
|
10
|
-
import { cn } from "../../lib/utils"
|
|
11
|
-
import {
|
|
12
|
-
Activity,
|
|
13
|
-
Zap,
|
|
14
|
-
Clock,
|
|
15
|
-
Eye,
|
|
16
|
-
EyeOff,
|
|
17
|
-
Download,
|
|
18
|
-
RefreshCw,
|
|
19
|
-
AlertTriangle,
|
|
20
|
-
CheckCircle,
|
|
21
|
-
TrendingUp,
|
|
22
|
-
TrendingDown,
|
|
23
|
-
Lock,
|
|
24
|
-
Sparkles,
|
|
25
|
-
Cpu,
|
|
26
|
-
MemoryStick,
|
|
27
|
-
Network,
|
|
28
|
-
HardDrive
|
|
29
|
-
} from "lucide-react"
|
|
30
|
-
import { useSubscription } from "../../hooks/use-subscription"
|
|
31
|
-
|
|
32
|
-
interface PerformanceMetric {
|
|
33
|
-
name: string
|
|
34
|
-
value: number
|
|
35
|
-
unit: string
|
|
36
|
-
threshold?: {
|
|
37
|
-
good: number
|
|
38
|
-
needs_improvement: number
|
|
39
|
-
}
|
|
40
|
-
description?: string
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
interface PerformanceEntry {
|
|
44
|
-
timestamp: number
|
|
45
|
-
metrics: PerformanceMetric[]
|
|
46
|
-
url: string
|
|
47
|
-
userAgent: string
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
interface PerformanceDebuggerProps {
|
|
51
|
-
autoCapture?: boolean
|
|
52
|
-
captureInterval?: number
|
|
53
|
-
maxEntries?: number
|
|
54
|
-
showRealTime?: boolean
|
|
55
|
-
showWebVitals?: boolean
|
|
56
|
-
showResourceTiming?: boolean
|
|
57
|
-
showNavigationTiming?: boolean
|
|
58
|
-
onMetricChange?: (metrics: PerformanceMetric[]) => void
|
|
59
|
-
className?: string
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const PerformanceDebuggerInternal: React.FC<PerformanceDebuggerProps> = ({
|
|
63
|
-
autoCapture = true,
|
|
64
|
-
captureInterval = 5000,
|
|
65
|
-
maxEntries = 50,
|
|
66
|
-
showRealTime = true,
|
|
67
|
-
showWebVitals = true,
|
|
68
|
-
showResourceTiming = true,
|
|
69
|
-
showNavigationTiming = true,
|
|
70
|
-
onMetricChange,
|
|
71
|
-
className
|
|
72
|
-
}) => {
|
|
73
|
-
const [entries, setEntries] = useState<PerformanceEntry[]>([])
|
|
74
|
-
const [currentMetrics, setCurrentMetrics] = useState<PerformanceMetric[]>([])
|
|
75
|
-
const [isCapturing, setIsCapturing] = useState(autoCapture)
|
|
76
|
-
const [isVisible, setIsVisible] = useState(true)
|
|
77
|
-
const intervalRef = useRef<NodeJS.Timeout | undefined>(undefined)
|
|
78
|
-
|
|
79
|
-
// Core Web Vitals thresholds
|
|
80
|
-
const webVitalsThresholds = {
|
|
81
|
-
FCP: { good: 1800, needs_improvement: 3000 },
|
|
82
|
-
LCP: { good: 2500, needs_improvement: 4000 },
|
|
83
|
-
FID: { good: 100, needs_improvement: 300 },
|
|
84
|
-
CLS: { good: 0.1, needs_improvement: 0.25 },
|
|
85
|
-
INP: { good: 200, needs_improvement: 500 },
|
|
86
|
-
TTFB: { good: 800, needs_improvement: 1800 }
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Get performance metrics
|
|
90
|
-
const captureMetrics = useCallback(() => {
|
|
91
|
-
const metrics: PerformanceMetric[] = []
|
|
92
|
-
|
|
93
|
-
// Navigation Timing
|
|
94
|
-
if (showNavigationTiming && 'performance' in window) {
|
|
95
|
-
const nav = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming
|
|
96
|
-
if (nav) {
|
|
97
|
-
metrics.push(
|
|
98
|
-
{
|
|
99
|
-
name: 'DNS Lookup',
|
|
100
|
-
value: nav.domainLookupEnd - nav.domainLookupStart,
|
|
101
|
-
unit: 'ms',
|
|
102
|
-
description: 'Time to resolve domain name'
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
name: 'TCP Connection',
|
|
106
|
-
value: nav.connectEnd - nav.connectStart,
|
|
107
|
-
unit: 'ms',
|
|
108
|
-
description: 'Time to establish TCP connection'
|
|
109
|
-
},
|
|
110
|
-
{
|
|
111
|
-
name: 'Request/Response',
|
|
112
|
-
value: nav.responseEnd - nav.requestStart,
|
|
113
|
-
unit: 'ms',
|
|
114
|
-
description: 'Time to get first byte and complete response'
|
|
115
|
-
},
|
|
116
|
-
{
|
|
117
|
-
name: 'DOM Processing',
|
|
118
|
-
value: nav.domComplete - (nav.domInteractive || nav.fetchStart),
|
|
119
|
-
unit: 'ms',
|
|
120
|
-
description: 'Time to process DOM'
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
name: 'Load Complete',
|
|
124
|
-
value: nav.loadEventEnd - nav.loadEventStart,
|
|
125
|
-
unit: 'ms',
|
|
126
|
-
description: 'Time to fire load event'
|
|
127
|
-
}
|
|
128
|
-
)
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Web Vitals (simulated - in real app you'd use web-vitals library)
|
|
133
|
-
if (showWebVitals) {
|
|
134
|
-
const paintEntries = performance.getEntriesByType('paint')
|
|
135
|
-
const fcpEntry = paintEntries.find(entry => entry.name === 'first-contentful-paint')
|
|
136
|
-
|
|
137
|
-
if (fcpEntry) {
|
|
138
|
-
metrics.push({
|
|
139
|
-
name: 'FCP',
|
|
140
|
-
value: fcpEntry.startTime,
|
|
141
|
-
unit: 'ms',
|
|
142
|
-
threshold: webVitalsThresholds.FCP,
|
|
143
|
-
description: 'First Contentful Paint'
|
|
144
|
-
})
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Simulated LCP (in real app, use PerformanceObserver)
|
|
148
|
-
metrics.push({
|
|
149
|
-
name: 'LCP',
|
|
150
|
-
value: Math.random() * 4000 + 1000,
|
|
151
|
-
unit: 'ms',
|
|
152
|
-
threshold: webVitalsThresholds.LCP,
|
|
153
|
-
description: 'Largest Contentful Paint'
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
// Simulated CLS
|
|
157
|
-
metrics.push({
|
|
158
|
-
name: 'CLS',
|
|
159
|
-
value: Math.random() * 0.3,
|
|
160
|
-
unit: '',
|
|
161
|
-
threshold: webVitalsThresholds.CLS,
|
|
162
|
-
description: 'Cumulative Layout Shift'
|
|
163
|
-
})
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Memory usage (if available)
|
|
167
|
-
if ('memory' in performance) {
|
|
168
|
-
const memory = (performance as any).memory
|
|
169
|
-
metrics.push(
|
|
170
|
-
{
|
|
171
|
-
name: 'JS Heap Used',
|
|
172
|
-
value: memory.usedJSHeapSize / 1024 / 1024,
|
|
173
|
-
unit: 'MB',
|
|
174
|
-
description: 'JavaScript heap memory in use'
|
|
175
|
-
},
|
|
176
|
-
{
|
|
177
|
-
name: 'JS Heap Total',
|
|
178
|
-
value: memory.totalJSHeapSize / 1024 / 1024,
|
|
179
|
-
unit: 'MB',
|
|
180
|
-
description: 'Total JavaScript heap memory'
|
|
181
|
-
},
|
|
182
|
-
{
|
|
183
|
-
name: 'JS Heap Limit',
|
|
184
|
-
value: memory.jsHeapSizeLimit / 1024 / 1024,
|
|
185
|
-
unit: 'MB',
|
|
186
|
-
description: 'JavaScript heap memory limit'
|
|
187
|
-
}
|
|
188
|
-
)
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Resource timing
|
|
192
|
-
if (showResourceTiming) {
|
|
193
|
-
const resources = performance.getEntriesByType('resource')
|
|
194
|
-
const totalSize = resources.reduce((acc, resource) => {
|
|
195
|
-
return acc + ((resource as any).transferSize || 0)
|
|
196
|
-
}, 0)
|
|
197
|
-
|
|
198
|
-
metrics.push({
|
|
199
|
-
name: 'Resources Loaded',
|
|
200
|
-
value: resources.length,
|
|
201
|
-
unit: 'count',
|
|
202
|
-
description: 'Total number of resources loaded'
|
|
203
|
-
})
|
|
204
|
-
|
|
205
|
-
if (totalSize > 0) {
|
|
206
|
-
metrics.push({
|
|
207
|
-
name: 'Total Transfer Size',
|
|
208
|
-
value: totalSize / 1024,
|
|
209
|
-
unit: 'KB',
|
|
210
|
-
description: 'Total size of transferred resources'
|
|
211
|
-
})
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Current performance scores (simulated)
|
|
216
|
-
metrics.push(
|
|
217
|
-
{
|
|
218
|
-
name: 'Performance Score',
|
|
219
|
-
value: Math.random() * 40 + 60,
|
|
220
|
-
unit: '/100',
|
|
221
|
-
threshold: { good: 90, needs_improvement: 50 },
|
|
222
|
-
description: 'Overall performance score'
|
|
223
|
-
},
|
|
224
|
-
{
|
|
225
|
-
name: 'CPU Usage',
|
|
226
|
-
value: Math.random() * 50 + 20,
|
|
227
|
-
unit: '%',
|
|
228
|
-
threshold: { good: 30, needs_improvement: 70 },
|
|
229
|
-
description: 'Current CPU usage'
|
|
230
|
-
}
|
|
231
|
-
)
|
|
232
|
-
|
|
233
|
-
return metrics
|
|
234
|
-
}, [showNavigationTiming, showWebVitals, showResourceTiming])
|
|
235
|
-
|
|
236
|
-
// Capture performance data
|
|
237
|
-
const capture = useCallback(() => {
|
|
238
|
-
const metrics = captureMetrics()
|
|
239
|
-
const entry: PerformanceEntry = {
|
|
240
|
-
timestamp: Date.now(),
|
|
241
|
-
metrics,
|
|
242
|
-
url: window.location.href,
|
|
243
|
-
userAgent: navigator.userAgent
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
setEntries(prev => [entry, ...prev.slice(0, maxEntries - 1)])
|
|
247
|
-
setCurrentMetrics(metrics)
|
|
248
|
-
onMetricChange?.(metrics)
|
|
249
|
-
}, [captureMetrics, maxEntries, onMetricChange])
|
|
250
|
-
|
|
251
|
-
// Auto capture
|
|
252
|
-
useEffect(() => {
|
|
253
|
-
if (isCapturing && showRealTime) {
|
|
254
|
-
capture() // Initial capture
|
|
255
|
-
intervalRef.current = setInterval(capture, captureInterval)
|
|
256
|
-
} else {
|
|
257
|
-
if (intervalRef.current) {
|
|
258
|
-
clearInterval(intervalRef.current)
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return () => {
|
|
263
|
-
if (intervalRef.current) {
|
|
264
|
-
clearInterval(intervalRef.current)
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
}, [isCapturing, showRealTime, capture, captureInterval])
|
|
268
|
-
|
|
269
|
-
// Initial capture on mount
|
|
270
|
-
useEffect(() => {
|
|
271
|
-
capture()
|
|
272
|
-
}, [])
|
|
273
|
-
|
|
274
|
-
const getMetricStatus = (metric: PerformanceMetric): 'good' | 'needs_improvement' | 'poor' => {
|
|
275
|
-
if (!metric.threshold) return 'good'
|
|
276
|
-
|
|
277
|
-
if (metric.value <= metric.threshold.good) return 'good'
|
|
278
|
-
if (metric.value <= metric.threshold.needs_improvement) return 'needs_improvement'
|
|
279
|
-
return 'poor'
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
const getStatusColor = (status: string) => {
|
|
283
|
-
switch (status) {
|
|
284
|
-
case 'good': return 'text-green-500'
|
|
285
|
-
case 'needs_improvement': return 'text-yellow-500'
|
|
286
|
-
case 'poor': return 'text-red-500'
|
|
287
|
-
default: return 'text-muted-foreground'
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
const getStatusIcon = (status: string) => {
|
|
292
|
-
switch (status) {
|
|
293
|
-
case 'good': return <CheckCircle className="h-4 w-4 text-green-500" />
|
|
294
|
-
case 'needs_improvement': return <AlertTriangle className="h-4 w-4 text-yellow-500" />
|
|
295
|
-
case 'poor': return <AlertTriangle className="h-4 w-4 text-red-500" />
|
|
296
|
-
default: return <Activity className="h-4 w-4 text-muted-foreground" />
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
const exportData = () => {
|
|
301
|
-
const data = {
|
|
302
|
-
timestamp: new Date().toISOString(),
|
|
303
|
-
entries,
|
|
304
|
-
currentMetrics
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
const blob = new Blob([JSON.stringify(data, null, 2)], {
|
|
308
|
-
type: 'application/json'
|
|
309
|
-
})
|
|
310
|
-
|
|
311
|
-
const url = URL.createObjectURL(blob)
|
|
312
|
-
const link = document.createElement('a')
|
|
313
|
-
link.href = url
|
|
314
|
-
link.download = `performance-debug-${Date.now()}.json`
|
|
315
|
-
document.body.appendChild(link)
|
|
316
|
-
link.click()
|
|
317
|
-
document.body.removeChild(link)
|
|
318
|
-
URL.revokeObjectURL(url)
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
const clearData = () => {
|
|
322
|
-
setEntries([])
|
|
323
|
-
setCurrentMetrics([])
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
const formatValue = (value: number, unit: string) => {
|
|
327
|
-
const formatted = unit === 'ms' || unit === 'MB' || unit === 'KB'
|
|
328
|
-
? value.toFixed(1)
|
|
329
|
-
: unit === '%' || unit === '/100'
|
|
330
|
-
? Math.round(value)
|
|
331
|
-
: value.toString()
|
|
332
|
-
|
|
333
|
-
return `${formatted}${unit}`
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
if (!isVisible) {
|
|
337
|
-
return (
|
|
338
|
-
<Button
|
|
339
|
-
variant="outline"
|
|
340
|
-
size="sm"
|
|
341
|
-
onClick={() => setIsVisible(true)}
|
|
342
|
-
className="fixed bottom-4 right-4 z-50"
|
|
343
|
-
>
|
|
344
|
-
<Activity className="h-4 w-4 mr-2" />
|
|
345
|
-
Performance
|
|
346
|
-
</Button>
|
|
347
|
-
)
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
return (
|
|
351
|
-
<Card className={cn("w-full max-w-6xl", className)}>
|
|
352
|
-
<CardHeader className="pb-6">
|
|
353
|
-
<div className="flex items-center justify-between">
|
|
354
|
-
<div>
|
|
355
|
-
<CardTitle className="flex items-center gap-2">
|
|
356
|
-
<Zap className="h-5 w-5" />
|
|
357
|
-
Performance Debugger
|
|
358
|
-
</CardTitle>
|
|
359
|
-
<CardDescription>
|
|
360
|
-
Real-time performance monitoring and web vitals tracking
|
|
361
|
-
</CardDescription>
|
|
362
|
-
</div>
|
|
363
|
-
|
|
364
|
-
<div className="flex items-center gap-2">
|
|
365
|
-
<div className={cn(
|
|
366
|
-
"inline-flex items-center gap-1 rounded-md px-2 py-0.5 text-xs font-medium",
|
|
367
|
-
isCapturing
|
|
368
|
-
? "border border-border bg-background text-foreground"
|
|
369
|
-
: "bg-secondary text-secondary-foreground"
|
|
370
|
-
)}>
|
|
371
|
-
{isCapturing ? (
|
|
372
|
-
<Activity className="h-3 w-3 animate-pulse" />
|
|
373
|
-
) : (
|
|
374
|
-
<Clock className="h-3 w-3" />
|
|
375
|
-
)}
|
|
376
|
-
<span>{isCapturing ? "Live" : "Paused"}</span>
|
|
377
|
-
</div>
|
|
378
|
-
|
|
379
|
-
<div className="flex items-center gap-1">
|
|
380
|
-
<Button
|
|
381
|
-
variant="outline"
|
|
382
|
-
size="icon"
|
|
383
|
-
className="h-7 w-7"
|
|
384
|
-
onClick={() => setIsCapturing(!isCapturing)}
|
|
385
|
-
>
|
|
386
|
-
{isCapturing ? (
|
|
387
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
388
|
-
<rect x="6" y="4" width="4" height="16"></rect>
|
|
389
|
-
<rect x="14" y="4" width="4" height="16"></rect>
|
|
390
|
-
</svg>
|
|
391
|
-
) : (
|
|
392
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
393
|
-
<polygon points="5 3 19 12 5 21 5 3"></polygon>
|
|
394
|
-
</svg>
|
|
395
|
-
)}
|
|
396
|
-
</Button>
|
|
397
|
-
|
|
398
|
-
<Button
|
|
399
|
-
variant="outline"
|
|
400
|
-
size="icon"
|
|
401
|
-
className="h-7 w-7"
|
|
402
|
-
onClick={capture}
|
|
403
|
-
>
|
|
404
|
-
<RefreshCw className="h-3.5 w-3.5" />
|
|
405
|
-
</Button>
|
|
406
|
-
|
|
407
|
-
<Button
|
|
408
|
-
variant="outline"
|
|
409
|
-
size="icon"
|
|
410
|
-
className="h-7 w-7"
|
|
411
|
-
onClick={exportData}
|
|
412
|
-
>
|
|
413
|
-
<Download className="h-3.5 w-3.5" />
|
|
414
|
-
</Button>
|
|
415
|
-
|
|
416
|
-
<Button
|
|
417
|
-
variant="ghost"
|
|
418
|
-
size="icon"
|
|
419
|
-
className="h-7 w-7"
|
|
420
|
-
onClick={() => setIsVisible(false)}
|
|
421
|
-
>
|
|
422
|
-
<EyeOff className="h-3.5 w-3.5" />
|
|
423
|
-
</Button>
|
|
424
|
-
</div>
|
|
425
|
-
</div>
|
|
426
|
-
</div>
|
|
427
|
-
</CardHeader>
|
|
428
|
-
|
|
429
|
-
<CardContent>
|
|
430
|
-
<Tabs defaultValue="current" className="w-full mt-2">
|
|
431
|
-
<TabsList className="grid w-full grid-cols-3">
|
|
432
|
-
<TabsTrigger value="current">Current Metrics</TabsTrigger>
|
|
433
|
-
<TabsTrigger value="history">History</TabsTrigger>
|
|
434
|
-
<TabsTrigger value="vitals">Web Vitals</TabsTrigger>
|
|
435
|
-
</TabsList>
|
|
436
|
-
|
|
437
|
-
<TabsContent value="current" className="space-y-4">
|
|
438
|
-
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
439
|
-
<AnimatePresence>
|
|
440
|
-
{currentMetrics.map((metric, index) => {
|
|
441
|
-
const status = getMetricStatus(metric)
|
|
442
|
-
return (
|
|
443
|
-
<motion.div
|
|
444
|
-
key={metric.name}
|
|
445
|
-
initial={{ opacity: 0, y: 20 }}
|
|
446
|
-
animate={{ opacity: 1, y: 0 }}
|
|
447
|
-
exit={{ opacity: 0, y: -20 }}
|
|
448
|
-
transition={{ delay: index * 0.05 }}
|
|
449
|
-
>
|
|
450
|
-
<Card>
|
|
451
|
-
<CardContent className="p-4">
|
|
452
|
-
<div className="flex items-center justify-between mb-2">
|
|
453
|
-
<div className="flex items-center gap-2">
|
|
454
|
-
{getStatusIcon(status)}
|
|
455
|
-
<span className="font-medium text-sm">{metric.name}</span>
|
|
456
|
-
</div>
|
|
457
|
-
<span className={cn("font-bold", getStatusColor(status))}>
|
|
458
|
-
{formatValue(metric.value, metric.unit)}
|
|
459
|
-
</span>
|
|
460
|
-
</div>
|
|
461
|
-
|
|
462
|
-
{metric.threshold && (
|
|
463
|
-
<div className="space-y-1">
|
|
464
|
-
<Progress
|
|
465
|
-
value={Math.min((metric.value / (metric.threshold.needs_improvement * 2)) * 100, 100)}
|
|
466
|
-
className="h-2"
|
|
467
|
-
/>
|
|
468
|
-
<div className="text-xs text-muted-foreground">
|
|
469
|
-
Good: <{metric.threshold.good}{metric.unit} |
|
|
470
|
-
Needs improvement: <{metric.threshold.needs_improvement}{metric.unit}
|
|
471
|
-
</div>
|
|
472
|
-
</div>
|
|
473
|
-
)}
|
|
474
|
-
|
|
475
|
-
{metric.description && (
|
|
476
|
-
<p className="text-xs text-muted-foreground mt-2">
|
|
477
|
-
{metric.description}
|
|
478
|
-
</p>
|
|
479
|
-
)}
|
|
480
|
-
</CardContent>
|
|
481
|
-
</Card>
|
|
482
|
-
</motion.div>
|
|
483
|
-
)
|
|
484
|
-
})}
|
|
485
|
-
</AnimatePresence>
|
|
486
|
-
</div>
|
|
487
|
-
</TabsContent>
|
|
488
|
-
|
|
489
|
-
<TabsContent value="history" className="space-y-4">
|
|
490
|
-
<div className="flex items-center justify-between">
|
|
491
|
-
<div className="text-sm text-muted-foreground">
|
|
492
|
-
{entries.length} entries captured
|
|
493
|
-
</div>
|
|
494
|
-
<Button variant="outline" size="sm" onClick={clearData}>
|
|
495
|
-
Clear History
|
|
496
|
-
</Button>
|
|
497
|
-
</div>
|
|
498
|
-
|
|
499
|
-
<div className="space-y-2 max-h-96 overflow-y-auto">
|
|
500
|
-
{entries.map((entry, index) => (
|
|
501
|
-
<Card key={entry.timestamp}>
|
|
502
|
-
<CardContent className="p-4">
|
|
503
|
-
<div className="flex items-center justify-between mb-2">
|
|
504
|
-
<span className="text-sm font-medium">
|
|
505
|
-
{new Date(entry.timestamp).toLocaleTimeString()}
|
|
506
|
-
</span>
|
|
507
|
-
<Badge variant="outline" className="text-xs">
|
|
508
|
-
{entry.metrics.length} metrics
|
|
509
|
-
</Badge>
|
|
510
|
-
</div>
|
|
511
|
-
|
|
512
|
-
<div className="grid grid-cols-2 md:grid-cols-4 gap-2 text-xs">
|
|
513
|
-
{entry.metrics.slice(0, 4).map((metric) => (
|
|
514
|
-
<div key={metric.name} className="flex justify-between">
|
|
515
|
-
<span className="text-muted-foreground">{metric.name}:</span>
|
|
516
|
-
<span className={getStatusColor(getMetricStatus(metric))}>
|
|
517
|
-
{formatValue(metric.value, metric.unit)}
|
|
518
|
-
</span>
|
|
519
|
-
</div>
|
|
520
|
-
))}
|
|
521
|
-
</div>
|
|
522
|
-
</CardContent>
|
|
523
|
-
</Card>
|
|
524
|
-
))}
|
|
525
|
-
</div>
|
|
526
|
-
</TabsContent>
|
|
527
|
-
|
|
528
|
-
<TabsContent value="vitals" className="space-y-4">
|
|
529
|
-
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
530
|
-
{currentMetrics
|
|
531
|
-
.filter(metric => ['FCP', 'LCP', 'CLS', 'FID', 'INP', 'TTFB'].includes(metric.name))
|
|
532
|
-
.map((metric) => {
|
|
533
|
-
const status = getMetricStatus(metric)
|
|
534
|
-
return (
|
|
535
|
-
<Card key={metric.name}>
|
|
536
|
-
<CardContent className="p-6">
|
|
537
|
-
<div className="flex items-center justify-between mb-4">
|
|
538
|
-
<div>
|
|
539
|
-
<h3 className="font-semibold">{metric.name}</h3>
|
|
540
|
-
<p className="text-sm text-muted-foreground">
|
|
541
|
-
{metric.description}
|
|
542
|
-
</p>
|
|
543
|
-
</div>
|
|
544
|
-
{getStatusIcon(status)}
|
|
545
|
-
</div>
|
|
546
|
-
|
|
547
|
-
<div className="text-2xl font-bold mb-2 text-center">
|
|
548
|
-
<span className={getStatusColor(status)}>
|
|
549
|
-
{formatValue(metric.value, metric.unit)}
|
|
550
|
-
</span>
|
|
551
|
-
</div>
|
|
552
|
-
|
|
553
|
-
{metric.threshold && (
|
|
554
|
-
<div className="space-y-2">
|
|
555
|
-
<Progress
|
|
556
|
-
value={Math.min((metric.value / (metric.threshold.needs_improvement * 2)) * 100, 100)}
|
|
557
|
-
className="h-3"
|
|
558
|
-
/>
|
|
559
|
-
<div className="flex justify-between text-xs text-muted-foreground">
|
|
560
|
-
<span>Good: <{metric.threshold.good}{metric.unit}</span>
|
|
561
|
-
<span>Poor: >{metric.threshold.needs_improvement}{metric.unit}</span>
|
|
562
|
-
</div>
|
|
563
|
-
</div>
|
|
564
|
-
)}
|
|
565
|
-
</CardContent>
|
|
566
|
-
</Card>
|
|
567
|
-
)
|
|
568
|
-
})}
|
|
569
|
-
</div>
|
|
570
|
-
</TabsContent>
|
|
571
|
-
</Tabs>
|
|
572
|
-
</CardContent>
|
|
573
|
-
</Card>
|
|
574
|
-
)
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
export const PerformanceDebugger: React.FC<PerformanceDebuggerProps> = ({ className, ...props }) => {
|
|
578
|
-
// Check if we're in docs mode or have pro access
|
|
579
|
-
const { hasProAccess, isLoading } = useSubscription()
|
|
580
|
-
|
|
581
|
-
// In docs mode, always show the component
|
|
582
|
-
|
|
583
|
-
// If not in docs mode and no pro access, show upgrade prompt
|
|
584
|
-
if (!isLoading && !hasProAccess) {
|
|
585
|
-
return (
|
|
586
|
-
<Card className={cn("w-fit", className)}>
|
|
587
|
-
<CardContent className="py-6 text-center">
|
|
588
|
-
<div className="space-y-4">
|
|
589
|
-
<div className="rounded-full bg-purple-100 dark:bg-purple-900/30 p-3 w-fit mx-auto">
|
|
590
|
-
<Lock className="h-6 w-6 text-purple-600 dark:text-purple-400" />
|
|
591
|
-
</div>
|
|
592
|
-
<div>
|
|
593
|
-
<h3 className="font-semibold text-sm mb-2">Pro Feature</h3>
|
|
594
|
-
<p className="text-muted-foreground text-xs mb-4">
|
|
595
|
-
Performance Debugger is available exclusively to MoonUI Pro subscribers.
|
|
596
|
-
</p>
|
|
597
|
-
<a href="/pricing">
|
|
598
|
-
<Button size="sm">
|
|
599
|
-
<Sparkles className="mr-2 h-4 w-4" />
|
|
600
|
-
Upgrade to Pro
|
|
601
|
-
</Button>
|
|
602
|
-
</a>
|
|
603
|
-
</div>
|
|
604
|
-
</div>
|
|
605
|
-
</CardContent>
|
|
606
|
-
</Card>
|
|
607
|
-
)
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
return <PerformanceDebuggerInternal className={className} {...props} />
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
export type { PerformanceMetric, PerformanceEntry, PerformanceDebuggerProps }
|