@moontra/moonui-pro 2.0.22 → 2.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 (99) hide show
  1. package/dist/index.mjs +215 -214
  2. package/package.json +4 -2
  3. package/src/__tests__/use-intersection-observer.test.tsx +216 -0
  4. package/src/__tests__/use-local-storage.test.tsx +174 -0
  5. package/src/__tests__/use-pro-access.test.tsx +183 -0
  6. package/src/components/advanced-chart/advanced-chart.test.tsx +281 -0
  7. package/src/components/advanced-chart/index.tsx +412 -0
  8. package/src/components/advanced-forms/index.tsx +431 -0
  9. package/src/components/animated-button/index.tsx +202 -0
  10. package/src/components/calendar/event-dialog.tsx +372 -0
  11. package/src/components/calendar/index.tsx +557 -0
  12. package/src/components/color-picker/index.tsx +434 -0
  13. package/src/components/dashboard/index.tsx +334 -0
  14. package/src/components/data-table/data-table.test.tsx +187 -0
  15. package/src/components/data-table/index.tsx +368 -0
  16. package/src/components/draggable-list/index.tsx +100 -0
  17. package/src/components/enhanced/button.tsx +360 -0
  18. package/src/components/enhanced/card.tsx +272 -0
  19. package/src/components/enhanced/dialog.tsx +248 -0
  20. package/src/components/enhanced/index.ts +3 -0
  21. package/src/components/error-boundary/index.tsx +111 -0
  22. package/src/components/file-upload/file-upload.test.tsx +242 -0
  23. package/src/components/file-upload/index.tsx +362 -0
  24. package/src/components/floating-action-button/index.tsx +209 -0
  25. package/src/components/github-stars/index.tsx +414 -0
  26. package/src/components/health-check/index.tsx +441 -0
  27. package/src/components/hover-card-3d/index.tsx +170 -0
  28. package/src/components/index.ts +76 -0
  29. package/src/components/kanban/index.tsx +436 -0
  30. package/src/components/lazy-component/index.tsx +342 -0
  31. package/src/components/magnetic-button/index.tsx +170 -0
  32. package/src/components/memory-efficient-data/index.tsx +352 -0
  33. package/src/components/optimized-image/index.tsx +427 -0
  34. package/src/components/performance-debugger/index.tsx +591 -0
  35. package/src/components/performance-monitor/index.tsx +775 -0
  36. package/src/components/pinch-zoom/index.tsx +172 -0
  37. package/src/components/rich-text-editor/index-old-backup.tsx +443 -0
  38. package/src/components/rich-text-editor/index.tsx +1537 -0
  39. package/src/components/rich-text-editor/slash-commands-extension.ts +220 -0
  40. package/src/components/rich-text-editor/slash-commands.css +35 -0
  41. package/src/components/rich-text-editor/table-styles.css +65 -0
  42. package/src/components/spotlight-card/index.tsx +194 -0
  43. package/src/components/swipeable-card/index.tsx +100 -0
  44. package/src/components/timeline/index.tsx +333 -0
  45. package/src/components/ui/animated-button.tsx +185 -0
  46. package/src/components/ui/avatar.tsx +135 -0
  47. package/src/components/ui/badge.tsx +225 -0
  48. package/src/components/ui/button.tsx +221 -0
  49. package/src/components/ui/card.tsx +141 -0
  50. package/src/components/ui/checkbox.tsx +256 -0
  51. package/src/components/ui/color-picker.tsx +95 -0
  52. package/src/components/ui/dialog.tsx +332 -0
  53. package/src/components/ui/dropdown-menu.tsx +200 -0
  54. package/src/components/ui/hover-card-3d.tsx +103 -0
  55. package/src/components/ui/index.ts +33 -0
  56. package/src/components/ui/input.tsx +219 -0
  57. package/src/components/ui/label.tsx +26 -0
  58. package/src/components/ui/magnetic-button.tsx +129 -0
  59. package/src/components/ui/popover.tsx +183 -0
  60. package/src/components/ui/select.tsx +273 -0
  61. package/src/components/ui/separator.tsx +140 -0
  62. package/src/components/ui/slider.tsx +351 -0
  63. package/src/components/ui/spotlight-card.tsx +119 -0
  64. package/src/components/ui/switch.tsx +83 -0
  65. package/src/components/ui/tabs.tsx +195 -0
  66. package/src/components/ui/textarea.tsx +25 -0
  67. package/src/components/ui/toast.tsx +313 -0
  68. package/src/components/ui/tooltip.tsx +152 -0
  69. package/src/components/virtual-list/index.tsx +369 -0
  70. package/src/hooks/use-chart.ts +205 -0
  71. package/src/hooks/use-data-table.ts +182 -0
  72. package/src/hooks/use-docs-pro-access.ts +13 -0
  73. package/src/hooks/use-license-check.ts +65 -0
  74. package/src/hooks/use-subscription.ts +19 -0
  75. package/src/index.ts +14 -0
  76. package/src/lib/micro-interactions.ts +255 -0
  77. package/src/lib/utils.ts +6 -0
  78. package/src/patterns/login-form/index.tsx +276 -0
  79. package/src/patterns/login-form/types.ts +67 -0
  80. package/src/setupTests.ts +41 -0
  81. package/src/styles/design-system.css +365 -0
  82. package/src/styles/index.css +4 -0
  83. package/src/styles/tailwind.css +6 -0
  84. package/src/styles/tokens.css +453 -0
  85. package/src/types/moonui.d.ts +22 -0
  86. package/src/use-intersection-observer.tsx +154 -0
  87. package/src/use-local-storage.tsx +71 -0
  88. package/src/use-paddle.ts +138 -0
  89. package/src/use-performance-optimizer.ts +379 -0
  90. package/src/use-pro-access.ts +141 -0
  91. package/src/use-scroll-animation.ts +221 -0
  92. package/src/use-subscription.ts +37 -0
  93. package/src/use-toast.ts +32 -0
  94. package/src/utils/chart-helpers.ts +257 -0
  95. package/src/utils/cn.ts +69 -0
  96. package/src/utils/data-processing.ts +151 -0
  97. package/src/utils/license-guard.tsx +177 -0
  98. package/src/utils/license-validator.tsx +183 -0
  99. package/src/utils/package-guard.ts +60 -0
@@ -0,0 +1,775 @@
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 { Badge } from "../ui/badge"
8
+ import { Progress } from "@moontra/moonui"
9
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs"
10
+ import { cn } from "../../lib/utils"
11
+ import {
12
+ Activity,
13
+ Monitor,
14
+ Cpu,
15
+ MemoryStick,
16
+ Network,
17
+ HardDrive,
18
+ Zap,
19
+ Eye,
20
+ EyeOff,
21
+ BarChart3,
22
+ TrendingUp,
23
+ TrendingDown,
24
+ AlertTriangle,
25
+ CheckCircle,
26
+ RefreshCw,
27
+ Download,
28
+ Lock,
29
+ Sparkles,
30
+ Timer,
31
+ Gauge
32
+ } from "lucide-react"
33
+ import { useSubscription } from "../../hooks/use-subscription"
34
+
35
+ export interface PerformanceMetrics {
36
+ cpu: {
37
+ usage: number
38
+ cores: number
39
+ temperature?: number
40
+ }
41
+ memory: {
42
+ used: number
43
+ total: number
44
+ available: number
45
+ percentage: number
46
+ }
47
+ disk: {
48
+ used: number
49
+ total: number
50
+ free: number
51
+ percentage: number
52
+ }
53
+ network: {
54
+ download: number
55
+ upload: number
56
+ latency: number
57
+ }
58
+ system: {
59
+ uptime: number
60
+ loadAverage: number[]
61
+ processes: number
62
+ }
63
+ browser?: {
64
+ jsHeapUsed: number
65
+ jsHeapTotal: number
66
+ jsHeapLimit: number
67
+ }
68
+ }
69
+
70
+ export interface PerformanceAlert {
71
+ id: string
72
+ type: "warning" | "critical" | "info"
73
+ metric: string
74
+ message: string
75
+ value: number
76
+ threshold: number
77
+ timestamp: Date
78
+ }
79
+
80
+ export interface PerformanceMonitorProps {
81
+ autoRefresh?: boolean
82
+ refreshInterval?: number
83
+ showAlerts?: boolean
84
+ alertThresholds?: {
85
+ cpu: number
86
+ memory: number
87
+ disk: number
88
+ network: number
89
+ }
90
+ showCharts?: boolean
91
+ chartDuration?: number
92
+ maxDataPoints?: number
93
+ onAlert?: (alert: PerformanceAlert) => void
94
+ onMetricsUpdate?: (metrics: PerformanceMetrics) => void
95
+ className?: string
96
+ }
97
+
98
+ const PerformanceMonitorInternal: React.FC<PerformanceMonitorProps> = ({
99
+ autoRefresh = true,
100
+ refreshInterval = 5000,
101
+ showAlerts = true,
102
+ alertThresholds = {
103
+ cpu: 80,
104
+ memory: 85,
105
+ disk: 90,
106
+ network: 1000
107
+ },
108
+ showCharts = true,
109
+ chartDuration = 300000, // 5 minutes
110
+ maxDataPoints = 60,
111
+ onAlert,
112
+ onMetricsUpdate,
113
+ className
114
+ }) => {
115
+ const [metrics, setMetrics] = useState<PerformanceMetrics | null>(null)
116
+ const [history, setHistory] = useState<{ timestamp: number; metrics: PerformanceMetrics }[]>([])
117
+ const [alerts, setAlerts] = useState<PerformanceAlert[]>([])
118
+ const [isVisible, setIsVisible] = useState(true)
119
+ const [isRefreshing, setIsRefreshing] = useState(false)
120
+ const intervalRef = useRef<NodeJS.Timeout>()
121
+
122
+ const generateMockMetrics = useCallback((): PerformanceMetrics => {
123
+ const baseMetrics = metrics || {
124
+ cpu: { usage: 25, cores: 8 },
125
+ memory: { used: 4.2, total: 16, available: 11.8, percentage: 26 },
126
+ disk: { used: 250, total: 500, free: 250, percentage: 50 },
127
+ network: { download: 50, upload: 20, latency: 25 },
128
+ system: { uptime: 86400, loadAverage: [1.2, 1.5, 1.8], processes: 120 }
129
+ }
130
+
131
+ // Simulate realistic variations
132
+ const variance = 0.1
133
+ return {
134
+ cpu: {
135
+ usage: Math.max(0, Math.min(100, baseMetrics.cpu.usage + (Math.random() - 0.5) * 20)),
136
+ cores: baseMetrics.cpu.cores,
137
+ temperature: 45 + Math.random() * 15
138
+ },
139
+ memory: {
140
+ used: Math.max(0, baseMetrics.memory.used + (Math.random() - 0.5) * 2),
141
+ total: baseMetrics.memory.total,
142
+ available: baseMetrics.memory.total - baseMetrics.memory.used,
143
+ percentage: Math.max(0, Math.min(100, baseMetrics.memory.percentage + (Math.random() - 0.5) * 10))
144
+ },
145
+ disk: {
146
+ used: baseMetrics.disk.used + (Math.random() - 0.5) * 10,
147
+ total: baseMetrics.disk.total,
148
+ free: baseMetrics.disk.total - baseMetrics.disk.used,
149
+ percentage: Math.max(0, Math.min(100, baseMetrics.disk.percentage + (Math.random() - 0.5) * 5))
150
+ },
151
+ network: {
152
+ download: Math.max(0, baseMetrics.network.download + (Math.random() - 0.5) * 30),
153
+ upload: Math.max(0, baseMetrics.network.upload + (Math.random() - 0.5) * 15),
154
+ latency: Math.max(1, baseMetrics.network.latency + (Math.random() - 0.5) * 10)
155
+ },
156
+ system: {
157
+ uptime: baseMetrics.system.uptime + refreshInterval / 1000,
158
+ loadAverage: baseMetrics.system.loadAverage.map(load =>
159
+ Math.max(0, load + (Math.random() - 0.5) * 0.2)
160
+ ),
161
+ processes: Math.max(50, baseMetrics.system.processes + Math.floor((Math.random() - 0.5) * 10))
162
+ },
163
+ browser: typeof window !== 'undefined' && 'memory' in performance ? {
164
+ jsHeapUsed: (performance as any).memory.usedJSHeapSize / 1024 / 1024,
165
+ jsHeapTotal: (performance as any).memory.totalJSHeapSize / 1024 / 1024,
166
+ jsHeapLimit: (performance as any).memory.jsHeapSizeLimit / 1024 / 1024
167
+ } : undefined
168
+ }
169
+ }, [metrics, refreshInterval])
170
+
171
+ const checkAlerts = useCallback((newMetrics: PerformanceMetrics) => {
172
+ const newAlerts: PerformanceAlert[] = []
173
+ const now = new Date()
174
+
175
+ // CPU Alert
176
+ if (newMetrics.cpu.usage > alertThresholds.cpu) {
177
+ newAlerts.push({
178
+ id: `cpu-${Date.now()}`,
179
+ type: newMetrics.cpu.usage > 95 ? "critical" : "warning",
180
+ metric: "CPU",
181
+ message: `CPU usage is ${newMetrics.cpu.usage.toFixed(1)}%`,
182
+ value: newMetrics.cpu.usage,
183
+ threshold: alertThresholds.cpu,
184
+ timestamp: now
185
+ })
186
+ }
187
+
188
+ // Memory Alert
189
+ if (newMetrics.memory.percentage > alertThresholds.memory) {
190
+ newAlerts.push({
191
+ id: `memory-${Date.now()}`,
192
+ type: newMetrics.memory.percentage > 95 ? "critical" : "warning",
193
+ metric: "Memory",
194
+ message: `Memory usage is ${newMetrics.memory.percentage.toFixed(1)}%`,
195
+ value: newMetrics.memory.percentage,
196
+ threshold: alertThresholds.memory,
197
+ timestamp: now
198
+ })
199
+ }
200
+
201
+ // Disk Alert
202
+ if (newMetrics.disk.percentage > alertThresholds.disk) {
203
+ newAlerts.push({
204
+ id: `disk-${Date.now()}`,
205
+ type: newMetrics.disk.percentage > 98 ? "critical" : "warning",
206
+ metric: "Disk",
207
+ message: `Disk usage is ${newMetrics.disk.percentage.toFixed(1)}%`,
208
+ value: newMetrics.disk.percentage,
209
+ threshold: alertThresholds.disk,
210
+ timestamp: now
211
+ })
212
+ }
213
+
214
+ // Network Latency Alert
215
+ if (newMetrics.network.latency > alertThresholds.network) {
216
+ newAlerts.push({
217
+ id: `network-${Date.now()}`,
218
+ type: "warning",
219
+ metric: "Network",
220
+ message: `High network latency: ${newMetrics.network.latency.toFixed(0)}ms`,
221
+ value: newMetrics.network.latency,
222
+ threshold: alertThresholds.network,
223
+ timestamp: now
224
+ })
225
+ }
226
+
227
+ if (newAlerts.length > 0) {
228
+ setAlerts(prev => [...newAlerts, ...prev].slice(0, 20))
229
+ newAlerts.forEach(alert => onAlert?.(alert))
230
+ }
231
+ }, [alertThresholds, onAlert])
232
+
233
+ const updateMetrics = useCallback(async () => {
234
+ setIsRefreshing(true)
235
+
236
+ try {
237
+ const newMetrics = generateMockMetrics()
238
+ setMetrics(newMetrics)
239
+
240
+ // Update history
241
+ setHistory(prev => {
242
+ const newHistory = [
243
+ { timestamp: Date.now(), metrics: newMetrics },
244
+ ...prev.slice(0, maxDataPoints - 1)
245
+ ]
246
+ return newHistory
247
+ })
248
+
249
+ // Check for alerts
250
+ if (showAlerts) {
251
+ checkAlerts(newMetrics)
252
+ }
253
+
254
+ onMetricsUpdate?.(newMetrics)
255
+ } catch (error) {
256
+ console.error("Failed to update metrics:", error)
257
+ } finally {
258
+ setIsRefreshing(false)
259
+ }
260
+ }, [generateMockMetrics, maxDataPoints, showAlerts, checkAlerts, onMetricsUpdate])
261
+
262
+ // Initial load
263
+ useEffect(() => {
264
+ updateMetrics()
265
+ }, [])
266
+
267
+ // Auto refresh
268
+ useEffect(() => {
269
+ if (!autoRefresh) return
270
+
271
+ intervalRef.current = setInterval(updateMetrics, refreshInterval)
272
+ return () => {
273
+ if (intervalRef.current) {
274
+ clearInterval(intervalRef.current)
275
+ }
276
+ }
277
+ }, [autoRefresh, refreshInterval, updateMetrics])
278
+
279
+ const formatBytes = (bytes: number, decimals = 1): string => {
280
+ if (bytes === 0) return '0 B'
281
+ const k = 1024
282
+ const dm = decimals < 0 ? 0 : decimals
283
+ const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
284
+ const i = Math.floor(Math.log(bytes) / Math.log(k))
285
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]
286
+ }
287
+
288
+ const formatUptime = (seconds: number): string => {
289
+ const days = Math.floor(seconds / 86400)
290
+ const hours = Math.floor((seconds % 86400) / 3600)
291
+ const minutes = Math.floor((seconds % 3600) / 60)
292
+
293
+ if (days > 0) return `${days}d ${hours}h ${minutes}m`
294
+ if (hours > 0) return `${hours}h ${minutes}m`
295
+ return `${minutes}m`
296
+ }
297
+
298
+ const getStatusColor = (percentage: number, thresholds = [70, 85]): string => {
299
+ if (percentage >= thresholds[1]) return "text-red-500"
300
+ if (percentage >= thresholds[0]) return "text-yellow-500"
301
+ return "text-green-500"
302
+ }
303
+
304
+ const exportMetrics = () => {
305
+ const data = {
306
+ timestamp: new Date().toISOString(),
307
+ currentMetrics: metrics,
308
+ history: history.slice(0, 20),
309
+ alerts: alerts.slice(0, 10)
310
+ }
311
+
312
+ const blob = new Blob([JSON.stringify(data, null, 2)], {
313
+ type: 'application/json'
314
+ })
315
+
316
+ const url = URL.createObjectURL(blob)
317
+ const link = document.createElement('a')
318
+ link.href = url
319
+ link.download = `performance-metrics-${Date.now()}.json`
320
+ document.body.appendChild(link)
321
+ link.click()
322
+ document.body.removeChild(link)
323
+ URL.revokeObjectURL(url)
324
+ }
325
+
326
+ if (!isVisible) {
327
+ return (
328
+ <Button
329
+ variant="outline"
330
+ size="sm"
331
+ onClick={() => setIsVisible(true)}
332
+ className="fixed bottom-4 left-4 z-50"
333
+ >
334
+ <Monitor className="h-4 w-4 mr-2" />
335
+ Performance
336
+ </Button>
337
+ )
338
+ }
339
+
340
+ if (!metrics) {
341
+ return (
342
+ <Card className={cn("w-full max-w-6xl", className)}>
343
+ <CardContent className="p-6">
344
+ <div className="text-center">
345
+ <Activity className="h-12 w-12 animate-pulse mx-auto mb-4 text-muted-foreground" />
346
+ <p className="text-muted-foreground">Loading performance metrics...</p>
347
+ </div>
348
+ </CardContent>
349
+ </Card>
350
+ )
351
+ }
352
+
353
+ return (
354
+ <Card className={cn("w-full max-w-6xl", className)}>
355
+ <CardHeader>
356
+ <div className="flex items-center justify-between">
357
+ <div>
358
+ <CardTitle className="flex items-center gap-2">
359
+ <Monitor className="h-5 w-5" />
360
+ Performance Monitor
361
+ </CardTitle>
362
+ <CardDescription>
363
+ Real-time system performance monitoring and alerts
364
+ </CardDescription>
365
+ </div>
366
+
367
+ <div className="flex items-center gap-2">
368
+ <Badge variant={autoRefresh ? "default" : "secondary"} className="gap-1">
369
+ {autoRefresh ? <Activity className="h-3 w-3 animate-pulse" /> : <Timer className="h-3 w-3" />}
370
+ {autoRefresh ? "Live" : "Paused"}
371
+ </Badge>
372
+
373
+ {showAlerts && alerts.length > 0 && (
374
+ <Badge variant="destructive" className="gap-1">
375
+ <AlertTriangle className="h-3 w-3" />
376
+ {alerts.length}
377
+ </Badge>
378
+ )}
379
+
380
+ <Button
381
+ variant="outline"
382
+ size="sm"
383
+ onClick={updateMetrics}
384
+ disabled={isRefreshing}
385
+ >
386
+ <RefreshCw className={cn("h-4 w-4", isRefreshing && "animate-spin")} />
387
+ </Button>
388
+
389
+ <Button
390
+ variant="outline"
391
+ size="sm"
392
+ onClick={exportMetrics}
393
+ >
394
+ <Download className="h-4 w-4" />
395
+ </Button>
396
+
397
+ <Button
398
+ variant="ghost"
399
+ size="sm"
400
+ onClick={() => setIsVisible(false)}
401
+ >
402
+ <EyeOff className="h-4 w-4" />
403
+ </Button>
404
+ </div>
405
+ </div>
406
+ </CardHeader>
407
+
408
+ <CardContent>
409
+ <Tabs defaultValue="overview" className="w-full">
410
+ <TabsList className="grid w-full grid-cols-4">
411
+ <TabsTrigger value="overview">Overview</TabsTrigger>
412
+ <TabsTrigger value="details">Details</TabsTrigger>
413
+ <TabsTrigger value="alerts">Alerts</TabsTrigger>
414
+ <TabsTrigger value="history">History</TabsTrigger>
415
+ </TabsList>
416
+
417
+ <TabsContent value="overview" className="space-y-6">
418
+ {/* Key Metrics Grid */}
419
+ <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
420
+ <Card>
421
+ <CardContent className="p-4">
422
+ <div className="flex items-center justify-between">
423
+ <div className="flex items-center gap-2">
424
+ <Cpu className="h-4 w-4 text-blue-500" />
425
+ <span className="text-sm font-medium">CPU</span>
426
+ </div>
427
+ <span className={cn("font-bold", getStatusColor(metrics.cpu.usage))}>
428
+ {metrics.cpu.usage.toFixed(1)}%
429
+ </span>
430
+ </div>
431
+ <Progress value={metrics.cpu.usage} className="h-2 mt-2" />
432
+ <div className="text-xs text-muted-foreground mt-1">
433
+ {metrics.cpu.cores} cores {metrics.cpu.temperature && `• ${metrics.cpu.temperature.toFixed(0)}°C`}
434
+ </div>
435
+ </CardContent>
436
+ </Card>
437
+
438
+ <Card>
439
+ <CardContent className="p-4">
440
+ <div className="flex items-center justify-between">
441
+ <div className="flex items-center gap-2">
442
+ <MemoryStick className="h-4 w-4 text-green-500" />
443
+ <span className="text-sm font-medium">Memory</span>
444
+ </div>
445
+ <span className={cn("font-bold", getStatusColor(metrics.memory.percentage))}>
446
+ {metrics.memory.percentage.toFixed(1)}%
447
+ </span>
448
+ </div>
449
+ <Progress value={metrics.memory.percentage} className="h-2 mt-2" />
450
+ <div className="text-xs text-muted-foreground mt-1">
451
+ {formatBytes(metrics.memory.used * 1024 * 1024 * 1024)} / {formatBytes(metrics.memory.total * 1024 * 1024 * 1024)}
452
+ </div>
453
+ </CardContent>
454
+ </Card>
455
+
456
+ <Card>
457
+ <CardContent className="p-4">
458
+ <div className="flex items-center justify-between">
459
+ <div className="flex items-center gap-2">
460
+ <HardDrive className="h-4 w-4 text-purple-500" />
461
+ <span className="text-sm font-medium">Disk</span>
462
+ </div>
463
+ <span className={cn("font-bold", getStatusColor(metrics.disk.percentage, [80, 90]))}>
464
+ {metrics.disk.percentage.toFixed(1)}%
465
+ </span>
466
+ </div>
467
+ <Progress value={metrics.disk.percentage} className="h-2 mt-2" />
468
+ <div className="text-xs text-muted-foreground mt-1">
469
+ {formatBytes(metrics.disk.used * 1024 * 1024 * 1024)} / {formatBytes(metrics.disk.total * 1024 * 1024 * 1024)}
470
+ </div>
471
+ </CardContent>
472
+ </Card>
473
+
474
+ <Card>
475
+ <CardContent className="p-4">
476
+ <div className="flex items-center justify-between">
477
+ <div className="flex items-center gap-2">
478
+ <Network className="h-4 w-4 text-orange-500" />
479
+ <span className="text-sm font-medium">Network</span>
480
+ </div>
481
+ <span className={cn("font-bold", getStatusColor(metrics.network.latency, [50, 100]))}>
482
+ {metrics.network.latency.toFixed(0)}ms
483
+ </span>
484
+ </div>
485
+ <div className="text-xs text-muted-foreground mt-2">
486
+ ↓ {metrics.network.download.toFixed(0)} MB/s • ↑ {metrics.network.upload.toFixed(0)} MB/s
487
+ </div>
488
+ </CardContent>
489
+ </Card>
490
+ </div>
491
+
492
+ {/* System Info */}
493
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
494
+ <Card>
495
+ <CardHeader>
496
+ <CardTitle className="text-base">System Information</CardTitle>
497
+ </CardHeader>
498
+ <CardContent className="space-y-3">
499
+ <div className="flex justify-between text-sm">
500
+ <span className="text-muted-foreground">Uptime:</span>
501
+ <span className="font-medium">{formatUptime(metrics.system.uptime)}</span>
502
+ </div>
503
+ <div className="flex justify-between text-sm">
504
+ <span className="text-muted-foreground">Processes:</span>
505
+ <span className="font-medium">{metrics.system.processes}</span>
506
+ </div>
507
+ <div className="flex justify-between text-sm">
508
+ <span className="text-muted-foreground">Load Average:</span>
509
+ <span className="font-medium">
510
+ {metrics.system.loadAverage.map(load => load.toFixed(2)).join(", ")}
511
+ </span>
512
+ </div>
513
+ </CardContent>
514
+ </Card>
515
+
516
+ {metrics.browser && (
517
+ <Card>
518
+ <CardHeader>
519
+ <CardTitle className="text-base">Browser Memory</CardTitle>
520
+ </CardHeader>
521
+ <CardContent className="space-y-3">
522
+ <div className="flex justify-between text-sm">
523
+ <span className="text-muted-foreground">JS Heap Used:</span>
524
+ <span className="font-medium">{formatBytes(metrics.browser.jsHeapUsed * 1024 * 1024)}</span>
525
+ </div>
526
+ <div className="flex justify-between text-sm">
527
+ <span className="text-muted-foreground">JS Heap Total:</span>
528
+ <span className="font-medium">{formatBytes(metrics.browser.jsHeapTotal * 1024 * 1024)}</span>
529
+ </div>
530
+ <div className="flex justify-between text-sm">
531
+ <span className="text-muted-foreground">JS Heap Limit:</span>
532
+ <span className="font-medium">{formatBytes(metrics.browser.jsHeapLimit * 1024 * 1024)}</span>
533
+ </div>
534
+ </CardContent>
535
+ </Card>
536
+ )}
537
+ </div>
538
+ </TabsContent>
539
+
540
+ <TabsContent value="details" className="space-y-4">
541
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
542
+ {/* Detailed CPU Info */}
543
+ <Card>
544
+ <CardHeader>
545
+ <CardTitle className="text-base flex items-center gap-2">
546
+ <Cpu className="h-4 w-4" />
547
+ CPU Details
548
+ </CardTitle>
549
+ </CardHeader>
550
+ <CardContent className="space-y-4">
551
+ <div>
552
+ <div className="flex justify-between text-sm mb-2">
553
+ <span>Usage</span>
554
+ <span className={getStatusColor(metrics.cpu.usage)}>{metrics.cpu.usage.toFixed(1)}%</span>
555
+ </div>
556
+ <Progress value={metrics.cpu.usage} className="h-2" />
557
+ </div>
558
+ <div className="grid grid-cols-2 gap-4 text-sm">
559
+ <div>
560
+ <span className="text-muted-foreground">Cores:</span>
561
+ <span className="ml-2 font-medium">{metrics.cpu.cores}</span>
562
+ </div>
563
+ {metrics.cpu.temperature && (
564
+ <div>
565
+ <span className="text-muted-foreground">Temperature:</span>
566
+ <span className="ml-2 font-medium">{metrics.cpu.temperature.toFixed(0)}°C</span>
567
+ </div>
568
+ )}
569
+ </div>
570
+ </CardContent>
571
+ </Card>
572
+
573
+ {/* Detailed Memory Info */}
574
+ <Card>
575
+ <CardHeader>
576
+ <CardTitle className="text-base flex items-center gap-2">
577
+ <MemoryStick className="h-4 w-4" />
578
+ Memory Details
579
+ </CardTitle>
580
+ </CardHeader>
581
+ <CardContent className="space-y-4">
582
+ <div>
583
+ <div className="flex justify-between text-sm mb-2">
584
+ <span>Usage</span>
585
+ <span className={getStatusColor(metrics.memory.percentage)}>{metrics.memory.percentage.toFixed(1)}%</span>
586
+ </div>
587
+ <Progress value={metrics.memory.percentage} className="h-2" />
588
+ </div>
589
+ <div className="space-y-2 text-sm">
590
+ <div className="flex justify-between">
591
+ <span className="text-muted-foreground">Used:</span>
592
+ <span className="font-medium">{formatBytes(metrics.memory.used * 1024 * 1024 * 1024)}</span>
593
+ </div>
594
+ <div className="flex justify-between">
595
+ <span className="text-muted-foreground">Available:</span>
596
+ <span className="font-medium">{formatBytes(metrics.memory.available * 1024 * 1024 * 1024)}</span>
597
+ </div>
598
+ <div className="flex justify-between">
599
+ <span className="text-muted-foreground">Total:</span>
600
+ <span className="font-medium">{formatBytes(metrics.memory.total * 1024 * 1024 * 1024)}</span>
601
+ </div>
602
+ </div>
603
+ </CardContent>
604
+ </Card>
605
+ </div>
606
+ </TabsContent>
607
+
608
+ <TabsContent value="alerts" className="space-y-4">
609
+ <div className="flex items-center justify-between">
610
+ <div className="text-sm text-muted-foreground">
611
+ {alerts.length} alerts
612
+ </div>
613
+ <Button
614
+ variant="outline"
615
+ size="sm"
616
+ onClick={() => setAlerts([])}
617
+ disabled={alerts.length === 0}
618
+ >
619
+ Clear All
620
+ </Button>
621
+ </div>
622
+
623
+ <div className="space-y-2 max-h-96 overflow-y-auto">
624
+ <AnimatePresence>
625
+ {alerts.map((alert, index) => (
626
+ <motion.div
627
+ key={alert.id}
628
+ initial={{ opacity: 0, x: -20 }}
629
+ animate={{ opacity: 1, x: 0 }}
630
+ exit={{ opacity: 0, x: 20 }}
631
+ transition={{ delay: index * 0.05 }}
632
+ >
633
+ <Card>
634
+ <CardContent className="p-4">
635
+ <div className="flex items-center justify-between">
636
+ <div className="flex items-center gap-3">
637
+ {alert.type === "critical" ? (
638
+ <AlertTriangle className="h-4 w-4 text-red-500" />
639
+ ) : alert.type === "warning" ? (
640
+ <AlertTriangle className="h-4 w-4 text-yellow-500" />
641
+ ) : (
642
+ <CheckCircle className="h-4 w-4 text-blue-500" />
643
+ )}
644
+ <div>
645
+ <div className="font-medium text-sm">{alert.metric}</div>
646
+ <div className="text-sm text-muted-foreground">{alert.message}</div>
647
+ </div>
648
+ </div>
649
+ <div className="text-right text-sm">
650
+ <div className="font-medium">
651
+ {alert.value.toFixed(1)}{alert.metric === "Network" ? "ms" : "%"}
652
+ </div>
653
+ <div className="text-xs text-muted-foreground">
654
+ {alert.timestamp.toLocaleTimeString()}
655
+ </div>
656
+ </div>
657
+ </div>
658
+ </CardContent>
659
+ </Card>
660
+ </motion.div>
661
+ ))}
662
+ </AnimatePresence>
663
+ </div>
664
+
665
+ {alerts.length === 0 && (
666
+ <div className="text-center py-8">
667
+ <CheckCircle className="h-12 w-12 mx-auto mb-4 text-green-500" />
668
+ <p className="text-muted-foreground">No alerts - System is running smoothly</p>
669
+ </div>
670
+ )}
671
+ </TabsContent>
672
+
673
+ <TabsContent value="history" className="space-y-4">
674
+ <div className="text-sm text-muted-foreground">
675
+ Last {history.length} data points ({Math.round(chartDuration / 60000)} minutes)
676
+ </div>
677
+
678
+ {showCharts && history.length > 1 && (
679
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
680
+ {/* Simple CPU History Chart */}
681
+ <Card>
682
+ <CardHeader>
683
+ <CardTitle className="text-base">CPU Usage Trend</CardTitle>
684
+ </CardHeader>
685
+ <CardContent>
686
+ <div className="h-32 flex items-end justify-between gap-1">
687
+ {history.slice(-20).reverse().map((point, index) => (
688
+ <div
689
+ key={index}
690
+ className="bg-blue-500 rounded-t min-w-[4px] flex-1 transition-all"
691
+ style={{ height: `${(point.metrics.cpu.usage / 100) * 128}px` }}
692
+ />
693
+ ))}
694
+ </div>
695
+ <div className="text-xs text-muted-foreground mt-2 text-center">
696
+ Last 20 readings
697
+ </div>
698
+ </CardContent>
699
+ </Card>
700
+
701
+ {/* Simple Memory History Chart */}
702
+ <Card>
703
+ <CardHeader>
704
+ <CardTitle className="text-base">Memory Usage Trend</CardTitle>
705
+ </CardHeader>
706
+ <CardContent>
707
+ <div className="h-32 flex items-end justify-between gap-1">
708
+ {history.slice(-20).reverse().map((point, index) => (
709
+ <div
710
+ key={index}
711
+ className="bg-green-500 rounded-t min-w-[4px] flex-1 transition-all"
712
+ style={{ height: `${(point.metrics.memory.percentage / 100) * 128}px` }}
713
+ />
714
+ ))}
715
+ </div>
716
+ <div className="text-xs text-muted-foreground mt-2 text-center">
717
+ Last 20 readings
718
+ </div>
719
+ </CardContent>
720
+ </Card>
721
+ </div>
722
+ )}
723
+
724
+ {history.length === 0 && (
725
+ <div className="text-center py-8">
726
+ <BarChart3 className="h-12 w-12 mx-auto mb-4 text-muted-foreground" />
727
+ <p className="text-muted-foreground">No historical data available</p>
728
+ </div>
729
+ )}
730
+ </TabsContent>
731
+ </Tabs>
732
+ </CardContent>
733
+ </Card>
734
+ )
735
+ }
736
+
737
+ export const PerformanceMonitor: React.FC<PerformanceMonitorProps> = ({ className, ...props }) => {
738
+ // Check if we're in docs mode or have pro access
739
+ const docsProAccess = { hasAccess: true } // Pro access assumed in package
740
+ const { hasProAccess, isLoading } = useSubscription()
741
+
742
+ // In docs mode, always show the component
743
+ const canShowComponent = docsProAccess.isDocsMode || hasProAccess
744
+
745
+ // If not in docs mode and no pro access, show upgrade prompt
746
+ if (!docsProAccess.isDocsMode && !isLoading && !hasProAccess) {
747
+ return (
748
+ <Card className={cn("w-fit", className)}>
749
+ <CardContent className="py-6 text-center">
750
+ <div className="space-y-4">
751
+ <div className="rounded-full bg-purple-100 dark:bg-purple-900/30 p-3 w-fit mx-auto">
752
+ <Lock className="h-6 w-6 text-purple-600 dark:text-purple-400" />
753
+ </div>
754
+ <div>
755
+ <h3 className="font-semibold text-sm mb-2">Pro Feature</h3>
756
+ <p className="text-muted-foreground text-xs mb-4">
757
+ Performance Monitor is available exclusively to MoonUI Pro subscribers.
758
+ </p>
759
+ <a href="/pricing">
760
+ <Button size="sm">
761
+ <Sparkles className="mr-2 h-4 w-4" />
762
+ Upgrade to Pro
763
+ </Button>
764
+ </a>
765
+ </div>
766
+ </div>
767
+ </CardContent>
768
+ </Card>
769
+ )
770
+ }
771
+
772
+ return <PerformanceMonitorInternal className={className} {...props} />
773
+ }
774
+
775
+ export type { PerformanceMetrics, PerformanceAlert, PerformanceMonitorProps }