@moontra/moonui-pro 2.20.2 → 2.20.4

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 (153) hide show
  1. package/package.json +8 -3
  2. package/plugin/index.d.ts +86 -0
  3. package/plugin/index.js +308 -0
  4. package/scripts/postinstall.js +191 -23
  5. package/src/components/advanced-chart/index.tsx +0 -1246
  6. package/src/components/advanced-forms/index.tsx +0 -585
  7. package/src/components/animated-button/index.tsx +0 -385
  8. package/src/components/calendar/event-dialog.tsx +0 -377
  9. package/src/components/calendar/index.tsx +0 -1220
  10. package/src/components/calendar-pro/index.tsx +0 -1697
  11. package/src/components/color-picker/index.tsx +0 -432
  12. package/src/components/credit-card-input/index.tsx +0 -406
  13. package/src/components/dashboard/dashboard-grid.tsx +0 -480
  14. package/src/components/dashboard/demo.tsx +0 -425
  15. package/src/components/dashboard/index.tsx +0 -1046
  16. package/src/components/dashboard/time-range-picker.tsx +0 -336
  17. package/src/components/dashboard/types.ts +0 -225
  18. package/src/components/dashboard/widgets/activity-feed.tsx +0 -349
  19. package/src/components/dashboard/widgets/chart-widget.tsx +0 -418
  20. package/src/components/dashboard/widgets/comparison-widget.tsx +0 -177
  21. package/src/components/dashboard/widgets/index.ts +0 -5
  22. package/src/components/dashboard/widgets/metric-card.tsx +0 -363
  23. package/src/components/dashboard/widgets/progress-widget.tsx +0 -113
  24. package/src/components/data-table/data-table-bulk-actions.tsx +0 -204
  25. package/src/components/data-table/data-table-column-toggle.tsx +0 -169
  26. package/src/components/data-table/data-table-export.ts +0 -156
  27. package/src/components/data-table/data-table-filter-drawer.tsx +0 -448
  28. package/src/components/data-table/index.tsx +0 -845
  29. package/src/components/draggable-list/index.tsx +0 -100
  30. package/src/components/error-boundary/index.tsx +0 -232
  31. package/src/components/file-upload/index.tsx +0 -1660
  32. package/src/components/floating-action-button/index.tsx +0 -206
  33. package/src/components/form-wizard/form-wizard-context.tsx +0 -335
  34. package/src/components/form-wizard/form-wizard-navigation.tsx +0 -118
  35. package/src/components/form-wizard/form-wizard-progress.tsx +0 -329
  36. package/src/components/form-wizard/form-wizard-step.tsx +0 -111
  37. package/src/components/form-wizard/index.tsx +0 -102
  38. package/src/components/form-wizard/types.ts +0 -77
  39. package/src/components/gesture-drawer/index.tsx +0 -551
  40. package/src/components/github-stars/github-api.ts +0 -426
  41. package/src/components/github-stars/hooks.ts +0 -517
  42. package/src/components/github-stars/index.tsx +0 -375
  43. package/src/components/github-stars/types.ts +0 -148
  44. package/src/components/github-stars/variants.tsx +0 -515
  45. package/src/components/health-check/index.tsx +0 -439
  46. package/src/components/hover-card-3d/index.tsx +0 -529
  47. package/src/components/index.ts +0 -130
  48. package/src/components/internal/index.ts +0 -78
  49. package/src/components/kanban/add-card-modal.tsx +0 -502
  50. package/src/components/kanban/card-detail-modal.tsx +0 -761
  51. package/src/components/kanban/index.ts +0 -13
  52. package/src/components/kanban/kanban.tsx +0 -1689
  53. package/src/components/kanban/types.ts +0 -168
  54. package/src/components/lazy-component/index.tsx +0 -823
  55. package/src/components/license-error/index.tsx +0 -31
  56. package/src/components/magnetic-button/index.tsx +0 -216
  57. package/src/components/memory-efficient-data/index.tsx +0 -1018
  58. package/src/components/moonui-quiz-form/index.tsx +0 -817
  59. package/src/components/navbar/index.tsx +0 -781
  60. package/src/components/optimized-image/index.tsx +0 -425
  61. package/src/components/performance-debugger/index.tsx +0 -613
  62. package/src/components/performance-monitor/index.tsx +0 -808
  63. package/src/components/phone-number-input/index.tsx +0 -343
  64. package/src/components/phone-number-input/phone-number-input-simple.tsx +0 -167
  65. package/src/components/pinch-zoom/index.tsx +0 -566
  66. package/src/components/quiz-form/index.tsx +0 -479
  67. package/src/components/rich-text-editor/index.tsx +0 -2322
  68. package/src/components/rich-text-editor/slash-commands-extension.ts +0 -230
  69. package/src/components/rich-text-editor/slash-commands.css +0 -35
  70. package/src/components/rich-text-editor/table-styles.css +0 -65
  71. package/src/components/sidebar/index.tsx +0 -884
  72. package/src/components/spotlight-card/index.tsx +0 -191
  73. package/src/components/swipeable-card/index.tsx +0 -100
  74. package/src/components/timeline/index.tsx +0 -1183
  75. package/src/components/ui/accordion.tsx +0 -581
  76. package/src/components/ui/alert-dialog.tsx +0 -141
  77. package/src/components/ui/alert.tsx +0 -141
  78. package/src/components/ui/aspect-ratio.tsx +0 -245
  79. package/src/components/ui/avatar.tsx +0 -155
  80. package/src/components/ui/badge.tsx +0 -230
  81. package/src/components/ui/breadcrumb.tsx +0 -216
  82. package/src/components/ui/button.tsx +0 -228
  83. package/src/components/ui/calendar.tsx +0 -387
  84. package/src/components/ui/card.tsx +0 -216
  85. package/src/components/ui/checkbox.tsx +0 -259
  86. package/src/components/ui/collapsible.tsx +0 -631
  87. package/src/components/ui/color-picker.tsx +0 -97
  88. package/src/components/ui/command.tsx +0 -948
  89. package/src/components/ui/dialog.tsx +0 -752
  90. package/src/components/ui/dropdown-menu.tsx +0 -706
  91. package/src/components/ui/gesture-drawer.tsx +0 -11
  92. package/src/components/ui/hover-card.tsx +0 -29
  93. package/src/components/ui/index.ts +0 -222
  94. package/src/components/ui/input.tsx +0 -224
  95. package/src/components/ui/label.tsx +0 -29
  96. package/src/components/ui/lightbox.tsx +0 -606
  97. package/src/components/ui/magnetic-button.tsx +0 -129
  98. package/src/components/ui/media-gallery.tsx +0 -611
  99. package/src/components/ui/navigation-menu.tsx +0 -130
  100. package/src/components/ui/pagination.tsx +0 -125
  101. package/src/components/ui/popover.tsx +0 -185
  102. package/src/components/ui/progress.tsx +0 -30
  103. package/src/components/ui/radio-group.tsx +0 -257
  104. package/src/components/ui/scroll-area.tsx +0 -47
  105. package/src/components/ui/select.tsx +0 -378
  106. package/src/components/ui/separator.tsx +0 -145
  107. package/src/components/ui/sheet.tsx +0 -139
  108. package/src/components/ui/skeleton.tsx +0 -20
  109. package/src/components/ui/slider.tsx +0 -354
  110. package/src/components/ui/spotlight-card.tsx +0 -119
  111. package/src/components/ui/switch.tsx +0 -86
  112. package/src/components/ui/table.tsx +0 -331
  113. package/src/components/ui/tabs-pro.tsx +0 -542
  114. package/src/components/ui/tabs.tsx +0 -54
  115. package/src/components/ui/textarea.tsx +0 -28
  116. package/src/components/ui/toast.tsx +0 -317
  117. package/src/components/ui/toggle.tsx +0 -119
  118. package/src/components/ui/tooltip.tsx +0 -151
  119. package/src/components/virtual-list/index.tsx +0 -668
  120. package/src/hooks/use-chart.ts +0 -205
  121. package/src/hooks/use-data-table.ts +0 -182
  122. package/src/hooks/use-docs-pro-access.ts +0 -13
  123. package/src/hooks/use-license-check.ts +0 -65
  124. package/src/hooks/use-subscription.ts +0 -19
  125. package/src/hooks/use-toast.ts +0 -15
  126. package/src/index.ts +0 -22
  127. package/src/lib/ai-providers.ts +0 -377
  128. package/src/lib/component-metadata.ts +0 -18
  129. package/src/lib/micro-interactions.ts +0 -255
  130. package/src/lib/paddle.ts +0 -17
  131. package/src/lib/utils.ts +0 -6
  132. package/src/patterns/login-form/index.tsx +0 -276
  133. package/src/patterns/login-form/types.ts +0 -67
  134. package/src/setupTests.ts +0 -41
  135. package/src/styles/advanced-chart.css +0 -239
  136. package/src/styles/calendar.css +0 -35
  137. package/src/styles/design-system.css +0 -363
  138. package/src/styles/index.css +0 -681
  139. package/src/styles/tailwind.css +0 -7
  140. package/src/styles/tokens.css +0 -455
  141. package/src/types/next-auth.d.ts +0 -21
  142. package/src/use-intersection-observer.tsx +0 -154
  143. package/src/use-local-storage.tsx +0 -71
  144. package/src/use-paddle.ts +0 -138
  145. package/src/use-performance-optimizer.ts +0 -389
  146. package/src/use-pro-access.ts +0 -141
  147. package/src/use-scroll-animation.ts +0 -219
  148. package/src/use-subscription.ts +0 -37
  149. package/src/use-toast.ts +0 -32
  150. package/src/utils/chart-helpers.ts +0 -357
  151. package/src/utils/cn.ts +0 -6
  152. package/src/utils/data-processing.ts +0 -151
  153. package/src/utils/license-validator.tsx +0 -183
@@ -1,948 +0,0 @@
1
- "use client"
2
-
3
- import * as React from "react"
4
- import { useState, useRef, useEffect, useCallback, useMemo } from "react"
5
- import {
6
- Search,
7
- Mic,
8
- MicOff,
9
- Clock,
10
- Sparkles,
11
- ChevronRight,
12
- Zap,
13
- Brain,
14
- Settings,
15
- Wand2,
16
- Bot,
17
- RefreshCw,
18
- X,
19
- Check
20
- } from "lucide-react"
21
- import { Command as CommandPrimitive } from "cmdk"
22
- import { cva, type VariantProps } from "class-variance-authority"
23
- import { motion, AnimatePresence } from "framer-motion"
24
- import Fuse from "fuse.js"
25
-
26
- import { cn } from "../../lib/utils"
27
- import { Dialog, DialogContent, DialogTitle, DialogHeader, DialogDescription } from "./dialog"
28
- import { Badge } from "./badge"
29
- import { Button } from "./button"
30
- import {
31
- Tooltip,
32
- TooltipContent,
33
- TooltipProvider,
34
- TooltipTrigger
35
- } from "./tooltip"
36
- import {
37
- DropdownMenu,
38
- DropdownMenuContent,
39
- DropdownMenuItem,
40
- DropdownMenuSeparator,
41
- DropdownMenuTrigger,
42
- } from "./dropdown-menu"
43
- import { Input } from "./input"
44
- import { Label } from "./label"
45
- import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./select"
46
- import { createAIProvider, type AIProvider } from "../../lib/ai-providers"
47
-
48
- /**
49
- * AI-Enhanced Command Palette Component
50
- *
51
- * Features:
52
- * - Fuzzy search with typo tolerance
53
- * - AI-powered natural language processing
54
- * - Voice commands with Web Speech API
55
- * - Smart suggestions based on usage patterns
56
- * - Intelligent history tracking
57
- * - Real AI service integration
58
- */
59
-
60
- // AI Configuration Types
61
- export interface CommandAIConfig {
62
- provider?: 'openai' | 'claude' | 'gemini';
63
- apiKey?: string;
64
- model?: string;
65
- temperature?: number;
66
- maxTokens?: number;
67
- }
68
-
69
- interface AISettingsType {
70
- provider: 'openai' | 'claude' | 'gemini'
71
- apiKey: string
72
- model: string
73
- temperature: number
74
- maxTokens: number
75
- }
76
-
77
- // Command history interface
78
- interface CommandHistoryItem {
79
- id: string
80
- command: string
81
- category?: string
82
- timestamp: number
83
- frequency: number
84
- }
85
-
86
- // Enhanced command item interface
87
- export interface CommandItemData {
88
- id: string
89
- label: string
90
- value: string
91
- category?: string
92
- keywords?: string[]
93
- action?: () => void
94
- shortcut?: string[]
95
- icon?: React.ReactNode
96
- priority?: number
97
- context?: string[]
98
- }
99
-
100
- // Command variants with new AI-focused styles
101
- export const commandVariantsPro = cva(
102
- "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
103
- {
104
- variants: {
105
- variant: {
106
- default: "bg-popover text-popover-foreground",
107
- glass: "bg-background/80 text-foreground backdrop-blur-sm border border-white/10",
108
- bordered: "bg-popover text-popover-foreground border-2 border-border",
109
- gradient: "bg-gradient-to-br from-primary/10 via-background to-secondary/10 border border-primary/20",
110
- neon: "bg-background border-2 border-primary shadow-[0_0_20px_rgba(var(--primary),0.3)]",
111
- },
112
- size: {
113
- sm: "min-h-[300px] max-h-[400px]",
114
- default: "min-h-[400px] max-h-[500px]",
115
- lg: "min-h-[500px] max-h-[600px]",
116
- xl: "min-h-[600px] max-h-[700px]",
117
- },
118
- },
119
- defaultVariants: {
120
- variant: "default",
121
- size: "default",
122
- },
123
- }
124
- )
125
-
126
- // Fuzzy search hook
127
- const useFuzzySearch = (items: CommandItemData[], query: string, enabled: boolean) => {
128
- return useMemo(() => {
129
- if (!enabled || !query) return items
130
-
131
- const fuse = new Fuse(items, {
132
- keys: ['label', 'keywords', 'category'],
133
- threshold: 0.3,
134
- includeScore: true,
135
- })
136
-
137
- return fuse.search(query).map(result => result.item)
138
- }, [items, query, enabled])
139
- }
140
-
141
- // Command history hook
142
- const useCommandHistory = (enabled: boolean, maxItems: number = 10) => {
143
- const [history, setHistory] = useState<CommandHistoryItem[]>([])
144
-
145
- useEffect(() => {
146
- if (!enabled) return
147
-
148
- const stored = localStorage.getItem('moonui-command-history')
149
- if (stored) {
150
- setHistory(JSON.parse(stored))
151
- }
152
- }, [enabled])
153
-
154
- const addToHistory = useCallback((item: CommandItemData) => {
155
- if (!enabled) return
156
-
157
- setHistory(prev => {
158
- const existing = prev.find(h => h.id === item.id)
159
- let updated: CommandHistoryItem[]
160
-
161
- if (existing) {
162
- updated = prev.map(h =>
163
- h.id === item.id
164
- ? { ...h, timestamp: Date.now(), frequency: h.frequency + 1 }
165
- : h
166
- )
167
- } else {
168
- updated = [...prev, {
169
- id: item.id,
170
- command: item.label,
171
- category: item.category,
172
- timestamp: Date.now(),
173
- frequency: 1,
174
- }]
175
- }
176
-
177
- // Keep only maxItems
178
- updated = updated
179
- .sort((a, b) => b.frequency - a.frequency)
180
- .slice(0, maxItems)
181
-
182
- localStorage.setItem('moonui-command-history', JSON.stringify(updated))
183
- return updated
184
- })
185
- }, [enabled, maxItems])
186
-
187
- return { history, addToHistory }
188
- }
189
-
190
- // Voice command hook
191
- const useVoiceCommand = (enabled: boolean, onTranscript?: (text: string) => void) => {
192
- const [isListening, setIsListening] = useState(false)
193
- const [transcript, setTranscript] = useState('')
194
- const recognitionRef = useRef<any>(null)
195
-
196
- useEffect(() => {
197
- if (!enabled || typeof window === 'undefined') return
198
-
199
- const SpeechRecognition = (window as any).SpeechRecognition || (window as any).webkitSpeechRecognition
200
- if (!SpeechRecognition) return
201
-
202
- const recognition = new SpeechRecognition()
203
- recognition.continuous = false
204
- recognition.interimResults = false
205
- recognition.lang = 'en-US'
206
-
207
- recognition.onresult = (event: any) => {
208
- const text = event.results[0][0].transcript
209
- setTranscript(text)
210
- onTranscript?.(text)
211
- setIsListening(false)
212
- }
213
-
214
- recognition.onerror = () => {
215
- setIsListening(false)
216
- }
217
-
218
- recognition.onend = () => {
219
- setIsListening(false)
220
- }
221
-
222
- recognitionRef.current = recognition
223
- }, [enabled, onTranscript])
224
-
225
- const toggleListening = useCallback(() => {
226
- if (!recognitionRef.current) return
227
-
228
- if (isListening) {
229
- recognitionRef.current.stop()
230
- setIsListening(false)
231
- } else {
232
- recognitionRef.current.start()
233
- setIsListening(true)
234
- }
235
- }, [isListening])
236
-
237
- return { isListening, transcript, toggleListening }
238
- }
239
-
240
- // AI Suggestions component
241
- const AISuggestions: React.FC<{
242
- items: CommandItemData[]
243
- onSelect: (item: CommandItemData) => void
244
- history: CommandHistoryItem[]
245
- }> = ({ items, onSelect, history }) => {
246
- const suggestions = useMemo(() => {
247
- // Combine history frequency with item priority
248
- const scored = items.map(item => {
249
- const historyItem = history.find(h => h.id === item.id)
250
- const score = (item.priority || 0) + (historyItem?.frequency || 0) * 2
251
- return { ...item, score }
252
- })
253
-
254
- return scored
255
- .sort((a, b) => b.score - a.score)
256
- .slice(0, 3)
257
- }, [items, history])
258
-
259
- if (suggestions.length === 0) return null
260
-
261
- return (
262
- <div className="px-3 py-2 border-b">
263
- <div className="flex items-center gap-2 mb-2">
264
- <Sparkles className="h-3 w-3 text-primary" />
265
- <span className="text-xs font-medium text-muted-foreground">AI Suggestions</span>
266
- </div>
267
- <div className="flex gap-2">
268
- {suggestions.map(item => (
269
- <motion.button
270
- key={item.id}
271
- initial={{ opacity: 0, scale: 0.9 }}
272
- animate={{ opacity: 1, scale: 1 }}
273
- whileHover={{ scale: 1.05 }}
274
- whileTap={{ scale: 0.95 }}
275
- onClick={() => onSelect(item)}
276
- className="flex items-center gap-1 px-2 py-1 text-xs bg-primary/10 hover:bg-primary/20 rounded-md transition-colors"
277
- >
278
- {item.icon}
279
- <span>{item.label}</span>
280
- </motion.button>
281
- ))}
282
- </div>
283
- </div>
284
- )
285
- }
286
-
287
- // Enhanced Command Component Props
288
- interface CommandProProps
289
- extends Omit<React.ComponentProps<typeof CommandPrimitive>, 'onSelect'>,
290
- VariantProps<typeof commandVariantsPro> {
291
- items?: CommandItemData[]
292
- onItemSelect?: (item: CommandItemData) => void
293
- enableAI?: boolean
294
- enableVoice?: boolean
295
- enableFuzzySearch?: boolean
296
- enableAISuggestions?: boolean
297
- enableHistory?: boolean
298
- maxHistoryItems?: number
299
- placeholder?: string
300
- emptyMessage?: string
301
- showShortcuts?: boolean
302
- categories?: string[]
303
- onVoiceCommand?: (transcript: string) => void
304
- aiConfig?: CommandAIConfig
305
- persistAISettings?: boolean
306
- onAICommand?: (command: string, result: string) => void
307
- }
308
-
309
- // Get AI provider instance
310
- const getAIProvider = (settings: AISettingsType): AIProvider | null => {
311
- if (!settings.apiKey) return null;
312
-
313
- try {
314
- return createAIProvider(settings.provider, {
315
- apiKey: settings.apiKey,
316
- model: settings.model,
317
- temperature: settings.temperature,
318
- maxTokens: settings.maxTokens
319
- });
320
- } catch (error) {
321
- console.error('Failed to create AI provider:', error);
322
- return null;
323
- }
324
- }
325
-
326
- // Main AI-Enhanced Command Component
327
- export const MoonUICommandPro = React.forwardRef<
328
- React.ElementRef<typeof CommandPrimitive>,
329
- CommandProProps
330
- >(({
331
- className,
332
- variant,
333
- size,
334
- items = [],
335
- onItemSelect,
336
- enableAI = false,
337
- enableVoice = true,
338
- enableFuzzySearch = true,
339
- enableAISuggestions = true,
340
- enableHistory = true,
341
- maxHistoryItems = 10,
342
- placeholder = "Search commands or type...",
343
- emptyMessage = "No results found.",
344
- showShortcuts = true,
345
- categories,
346
- onVoiceCommand,
347
- aiConfig = {},
348
- persistAISettings = true,
349
- onAICommand,
350
- ...props
351
- }, ref) => {
352
- const [search, setSearch] = useState('')
353
- const [selectedCategory, setSelectedCategory] = useState<string | null>(null)
354
- const [isAISettingsOpen, setIsAISettingsOpen] = useState(false)
355
- const [isProcessing, setIsProcessing] = useState(false)
356
-
357
- // AI Settings
358
- const [aiSettings, setAiSettings] = useState<AISettingsType>(() => {
359
- if (persistAISettings && typeof window !== 'undefined') {
360
- try {
361
- const stored = localStorage.getItem('moonui-ai-settings');
362
- if (stored) {
363
- const parsed = JSON.parse(stored);
364
- return {
365
- provider: (aiConfig.provider !== undefined ? aiConfig.provider : parsed.provider || 'openai') as 'openai' | 'claude' | 'gemini',
366
- apiKey: aiConfig.apiKey !== undefined ? aiConfig.apiKey : parsed.apiKey || '',
367
- model: aiConfig.model !== undefined ? aiConfig.model : parsed.model || 'gpt-3.5-turbo',
368
- temperature: aiConfig.temperature !== undefined ? aiConfig.temperature : parsed.temperature ?? 0.7,
369
- maxTokens: aiConfig.maxTokens !== undefined ? aiConfig.maxTokens : parsed.maxTokens ?? 1000,
370
- };
371
- }
372
- } catch (e) {
373
- console.error('Failed to load AI settings from localStorage:', e);
374
- }
375
- }
376
-
377
- return {
378
- provider: (aiConfig.provider || 'openai') as 'openai' | 'claude' | 'gemini',
379
- apiKey: aiConfig.apiKey || '',
380
- model: aiConfig.model || 'gpt-3.5-turbo',
381
- temperature: aiConfig.temperature ?? 0.7,
382
- maxTokens: aiConfig.maxTokens ?? 1000,
383
- };
384
- });
385
-
386
- const { history, addToHistory } = useCommandHistory(enableHistory, maxHistoryItems)
387
- const { isListening, transcript, toggleListening } = useVoiceCommand(enableVoice && enableAI, (text) => {
388
- setSearch(text)
389
- onVoiceCommand?.(text)
390
- if (enableAI) {
391
- handleNaturalLanguageCommand(text)
392
- }
393
- })
394
-
395
- // Filter items by category
396
- const filteredByCategory = useMemo(() => {
397
- if (!selectedCategory) return items
398
- return items.filter(item => item.category === selectedCategory)
399
- }, [items, selectedCategory])
400
-
401
- // Apply fuzzy search
402
- const searchResults = useFuzzySearch(filteredByCategory, search, enableFuzzySearch)
403
-
404
- // Natural language command processing
405
- const handleNaturalLanguageCommand = async (text: string) => {
406
- if (!enableAI || !aiSettings.apiKey || isProcessing) return
407
-
408
- setIsProcessing(true)
409
- try {
410
- const provider = getAIProvider(aiSettings)
411
- if (!provider) {
412
- throw new Error('Failed to initialize AI provider')
413
- }
414
-
415
- // Process natural language command
416
- const prompt = `Convert this natural language command to a specific action: "${text}".
417
- Available commands: ${items.map(item => item.label).join(', ')}.
418
- Respond with just the closest matching command name, nothing else.`
419
-
420
- const result = await provider.generateText(prompt)
421
-
422
- // Find matching command
423
- const matchingCommand = items.find(item =>
424
- item.label.toLowerCase().includes(result.toLowerCase()) ||
425
- result.toLowerCase().includes(item.label.toLowerCase())
426
- )
427
-
428
- if (matchingCommand) {
429
- handleSelect(matchingCommand)
430
- onAICommand?.(text, matchingCommand.label)
431
- }
432
-
433
- } catch (error) {
434
- console.error('Natural language processing error:', error)
435
- } finally {
436
- setIsProcessing(false)
437
- }
438
- }
439
-
440
- // Handle item selection
441
- const handleSelect = useCallback((item: CommandItemData) => {
442
- addToHistory(item)
443
- item.action?.()
444
- onItemSelect?.(item)
445
- }, [addToHistory, onItemSelect])
446
-
447
- // Group items by category
448
- const groupedItems = useMemo(() => {
449
- const groups: Record<string, CommandItemData[]> = {}
450
-
451
- searchResults.forEach(item => {
452
- const category = item.category || 'Other'
453
- if (!groups[category]) groups[category] = []
454
- groups[category].push(item)
455
- })
456
-
457
- return groups
458
- }, [searchResults])
459
-
460
- return (
461
- <>
462
- <CommandPrimitive
463
- ref={ref}
464
- className={cn(commandVariantsPro({ variant, size }), className)}
465
- {...props}
466
- >
467
- {/* Search Input with Voice and AI */}
468
- <div className="flex items-center border-b px-3">
469
- <Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
470
- <CommandPrimitive.Input
471
- value={search}
472
- onValueChange={setSearch}
473
- onKeyDown={(e) => {
474
- if (e.key === 'Enter' && enableAI && search) {
475
- e.preventDefault()
476
- handleNaturalLanguageCommand(search)
477
- }
478
- }}
479
- className="flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50"
480
- placeholder={placeholder}
481
- />
482
-
483
- {/* Voice Command Button */}
484
- {enableVoice && enableAI && (
485
- <TooltipProvider>
486
- <Tooltip>
487
- <TooltipTrigger asChild>
488
- <Button
489
- variant="ghost"
490
- size="sm"
491
- onClick={toggleListening}
492
- className={cn(
493
- "ml-2",
494
- isListening && "text-primary animate-pulse"
495
- )}
496
- >
497
- {isListening ? (
498
- <Mic className="h-4 w-4" />
499
- ) : (
500
- <MicOff className="h-4 w-4" />
501
- )}
502
- </Button>
503
- </TooltipTrigger>
504
- <TooltipContent>
505
- {isListening ? "Listening..." : "Voice command"}
506
- </TooltipContent>
507
- </Tooltip>
508
- </TooltipProvider>
509
- )}
510
-
511
- {/* AI Settings Button */}
512
- {enableAI && (
513
- <TooltipProvider>
514
- <Tooltip>
515
- <TooltipTrigger asChild>
516
- <Button
517
- variant="ghost"
518
- size="sm"
519
- onClick={() => setIsAISettingsOpen(true)}
520
- className="ml-1"
521
- >
522
- <Settings className="h-4 w-4" />
523
- </Button>
524
- </TooltipTrigger>
525
- <TooltipContent>
526
- AI Settings
527
- </TooltipContent>
528
- </Tooltip>
529
- </TooltipProvider>
530
- )}
531
- </div>
532
-
533
- {/* Category Filters */}
534
- {categories && categories.length > 0 && (
535
- <div className="flex gap-2 px-3 py-2 border-b">
536
- <Button
537
- variant={selectedCategory === null ? "primary" : "outline"}
538
- size="sm"
539
- onClick={() => setSelectedCategory(null)}
540
- className="h-7 text-xs"
541
- >
542
- All
543
- </Button>
544
- {categories.map(cat => (
545
- <Button
546
- key={cat}
547
- variant={selectedCategory === cat ? "primary" : "outline"}
548
- size="sm"
549
- onClick={() => setSelectedCategory(cat)}
550
- className="h-7 text-xs"
551
- >
552
- {cat}
553
- </Button>
554
- ))}
555
- </div>
556
- )}
557
-
558
- {/* AI Suggestions */}
559
- {enableAI && enableAISuggestions && !search && (
560
- <AISuggestions
561
- items={items}
562
- onSelect={handleSelect}
563
- history={history}
564
- />
565
- )}
566
-
567
- {/* Recent Commands */}
568
- {enableHistory && history.length > 0 && !search && (
569
- <div className="px-3 py-2 border-b">
570
- <div className="flex items-center gap-2 mb-2">
571
- <Clock className="h-3 w-3 text-muted-foreground" />
572
- <span className="text-xs font-medium text-muted-foreground">Recent Commands</span>
573
- </div>
574
- <div className="space-y-1">
575
- {history.slice(0, 3).map(item => (
576
- <button
577
- key={item.id}
578
- onClick={() => {
579
- const cmd = items.find(i => i.id === item.id)
580
- if (cmd) handleSelect(cmd)
581
- }}
582
- className="flex items-center justify-between w-full px-2 py-1 text-xs hover:bg-accent rounded-sm transition-colors"
583
- >
584
- <span>{item.command}</span>
585
- {item.category && (
586
- <Badge variant="outline" className="text-[10px] h-4">
587
- {item.category}
588
- </Badge>
589
- )}
590
- </button>
591
- ))}
592
- </div>
593
- </div>
594
- )}
595
-
596
- {/* Command List */}
597
- <CommandPrimitive.List className="max-h-[300px] overflow-y-auto overflow-x-hidden p-2">
598
- {searchResults.length === 0 ? (
599
- <CommandPrimitive.Empty className="py-6 text-center text-sm text-muted-foreground">
600
- {emptyMessage}
601
- </CommandPrimitive.Empty>
602
- ) : (
603
- <AnimatePresence>
604
- {Object.entries(groupedItems).map(([category, categoryItems]) => (
605
- <motion.div
606
- key={category}
607
- initial={{ opacity: 0, y: 10 }}
608
- animate={{ opacity: 1, y: 0 }}
609
- exit={{ opacity: 0, y: -10 }}
610
- >
611
- <CommandPrimitive.Group
612
- heading={category}
613
- className="overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground"
614
- >
615
- {categoryItems.map(item => (
616
- <CommandPrimitive.Item
617
- key={item.id}
618
- value={item.value}
619
- onSelect={() => handleSelect(item)}
620
- className="relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50"
621
- >
622
- <div className="flex items-center gap-2 flex-1">
623
- {item.icon && (
624
- <span className="shrink-0">{item.icon}</span>
625
- )}
626
- <span>{item.label}</span>
627
- </div>
628
- {showShortcuts && item.shortcut && (
629
- <div className="ml-auto flex gap-0.5">
630
- {item.shortcut.map((key, i) => (
631
- <kbd
632
- key={i}
633
- className="pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground opacity-100"
634
- >
635
- {key}
636
- </kbd>
637
- ))}
638
- </div>
639
- )}
640
- </CommandPrimitive.Item>
641
- ))}
642
- </CommandPrimitive.Group>
643
- </motion.div>
644
- ))}
645
- </AnimatePresence>
646
- )}
647
- </CommandPrimitive.List>
648
-
649
- {/* AI Processing Indicator */}
650
- {isProcessing && (
651
- <div className="px-3 py-2 border-t">
652
- <div className="flex items-center gap-2 text-xs text-muted-foreground">
653
- <RefreshCw className="h-3 w-3 animate-spin" />
654
- <span>Processing natural language command...</span>
655
- </div>
656
- </div>
657
- )}
658
-
659
- {/* Natural Language Hint */}
660
- {enableAI && (
661
- <div className="px-3 py-2 border-t">
662
- <div className="flex items-center gap-2 text-xs text-muted-foreground">
663
- <Zap className="h-3 w-3" />
664
- <span>Tip: You can use natural language commands like "open settings"</span>
665
- </div>
666
- </div>
667
- )}
668
- </CommandPrimitive>
669
-
670
- {/* AI Settings Modal */}
671
- {enableAI && (
672
- <Dialog open={isAISettingsOpen} onOpenChange={setIsAISettingsOpen}>
673
- <DialogContent className="sm:max-w-[425px]">
674
- <DialogHeader>
675
- <DialogTitle>AI Settings</DialogTitle>
676
- <DialogDescription>
677
- Configure your AI provider and API settings for enhanced command processing.
678
- </DialogDescription>
679
- </DialogHeader>
680
- <div className="grid gap-4 py-4">
681
- <div className="grid gap-2">
682
- <Label htmlFor="provider">Provider</Label>
683
- <Select
684
- value={aiSettings.provider}
685
- onValueChange={(value: 'openai' | 'claude' | 'gemini') => {
686
- const defaultModels = {
687
- openai: 'gpt-3.5-turbo',
688
- claude: 'claude-3-sonnet-20240229',
689
- gemini: 'gemini-2.0-flash'
690
- };
691
- const newSettings = {
692
- ...aiSettings,
693
- provider: value,
694
- model: defaultModels[value] || 'gpt-3.5-turbo'
695
- };
696
- setAiSettings(newSettings);
697
- if (persistAISettings) {
698
- localStorage.setItem('moonui-ai-settings', JSON.stringify(newSettings));
699
- }
700
- }}
701
- >
702
- <SelectTrigger>
703
- <SelectValue />
704
- </SelectTrigger>
705
- <SelectContent>
706
- <SelectItem value="openai">OpenAI</SelectItem>
707
- <SelectItem value="claude">Claude (Anthropic)</SelectItem>
708
- <SelectItem value="gemini">Gemini (Google)</SelectItem>
709
- </SelectContent>
710
- </Select>
711
- </div>
712
- <div className="grid gap-2">
713
- <Label htmlFor="apiKey">API Key</Label>
714
- <Input
715
- id="apiKey"
716
- type="password"
717
- value={aiSettings.apiKey}
718
- onChange={(e) => {
719
- const newSettings = { ...aiSettings, apiKey: e.target.value };
720
- setAiSettings(newSettings);
721
- if (persistAISettings) {
722
- localStorage.setItem('moonui-ai-settings', JSON.stringify(newSettings));
723
- }
724
- }}
725
- placeholder="sk-..."
726
- />
727
- </div>
728
- <div className="grid gap-2">
729
- <Label htmlFor="model">Model</Label>
730
- <Select
731
- value={aiSettings.model}
732
- onValueChange={(value) => {
733
- const newSettings = { ...aiSettings, model: value };
734
- setAiSettings(newSettings);
735
- if (persistAISettings) {
736
- localStorage.setItem('moonui-ai-settings', JSON.stringify(newSettings));
737
- }
738
- }}
739
- >
740
- <SelectTrigger>
741
- <SelectValue />
742
- </SelectTrigger>
743
- <SelectContent>
744
- {aiSettings.provider === 'openai' && (
745
- <>
746
- <SelectItem value="gpt-4">GPT-4</SelectItem>
747
- <SelectItem value="gpt-3.5-turbo">GPT-3.5 Turbo</SelectItem>
748
- </>
749
- )}
750
- {aiSettings.provider === 'claude' && (
751
- <>
752
- <SelectItem value="claude-3-opus">Claude 3 Opus</SelectItem>
753
- <SelectItem value="claude-3-sonnet">Claude 3 Sonnet</SelectItem>
754
- <SelectItem value="claude-3-haiku">Claude 3 Haiku</SelectItem>
755
- </>
756
- )}
757
- {aiSettings.provider === 'gemini' && (
758
- <>
759
- <SelectItem value="gemini-2.0-flash">Gemini 2.0 Flash</SelectItem>
760
- <SelectItem value="gemini-1.5-flash">Gemini 1.5 Flash</SelectItem>
761
- <SelectItem value="gemini-1.5-pro">Gemini 1.5 Pro</SelectItem>
762
- </>
763
- )}
764
- {false && (
765
- <>
766
- <SelectItem value="command">Command</SelectItem>
767
- <SelectItem value="command-light">Command Light</SelectItem>
768
- </>
769
- )}
770
- </SelectContent>
771
- </Select>
772
- </div>
773
- </div>
774
- <div className="flex justify-end gap-2">
775
- <Button
776
- variant="outline"
777
- onClick={() => setIsAISettingsOpen(false)}
778
- >
779
- Cancel
780
- </Button>
781
- <Button onClick={() => setIsAISettingsOpen(false)}>
782
- Save Settings
783
- </Button>
784
- </div>
785
- </DialogContent>
786
- </Dialog>
787
- )}
788
- </>
789
- )
790
- })
791
- MoonUICommandPro.displayName = CommandPrimitive.displayName
792
-
793
- // Enhanced Dialog wrapper for AI Command
794
- interface CommandDialogProProps extends React.ComponentProps<typeof Dialog> {
795
- commandProps?: CommandProProps
796
- children?: React.ReactNode
797
- }
798
-
799
- export const MoonUICommandDialogPro = ({
800
- children,
801
- commandProps,
802
- ...props
803
- }: CommandDialogProProps) => {
804
- return (
805
- <Dialog {...props}>
806
- <DialogContent className="overflow-hidden p-0 shadow-2xl" hideCloseButton>
807
- <DialogTitle className="sr-only">AI Command Menu</DialogTitle>
808
- <MoonUICommandPro {...commandProps}>
809
- {children}
810
- </MoonUICommandPro>
811
- </DialogContent>
812
- </Dialog>
813
- )
814
- }
815
-
816
- // Basic Command Components (for backward compatibility)
817
- interface CommandInputProps
818
- extends React.ComponentProps<typeof CommandPrimitive.Input> {}
819
-
820
- export const MoonUICommandInputPro = React.forwardRef<
821
- React.ElementRef<typeof CommandPrimitive.Input>,
822
- CommandInputProps
823
- >(({ className, ...props }, ref) => (
824
- <div className="flex items-center border-b px-3" cmdk-input-wrapper="">
825
- <Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
826
- <CommandPrimitive.Input
827
- ref={ref}
828
- className={cn(
829
- "flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
830
- className
831
- )}
832
- {...props}
833
- />
834
- </div>
835
- ))
836
- MoonUICommandInputPro.displayName = CommandPrimitive.Input.displayName
837
-
838
- interface CommandListProps
839
- extends React.ComponentProps<typeof CommandPrimitive.List> {}
840
-
841
- export const MoonUICommandListPro = React.forwardRef<
842
- React.ElementRef<typeof CommandPrimitive.List>,
843
- CommandListProps
844
- >(({ className, ...props }, ref) => (
845
- <CommandPrimitive.List
846
- ref={ref}
847
- className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)}
848
- {...props}
849
- />
850
- ))
851
- MoonUICommandListPro.displayName = CommandPrimitive.List.displayName
852
-
853
- interface CommandEmptyProps
854
- extends React.ComponentProps<typeof CommandPrimitive.Empty> {}
855
-
856
- export const MoonUICommandEmptyPro = React.forwardRef<
857
- React.ElementRef<typeof CommandPrimitive.Empty>,
858
- CommandEmptyProps
859
- >(({ className, ...props }, ref) => (
860
- <CommandPrimitive.Empty
861
- ref={ref}
862
- className={cn("py-6 text-center text-sm text-muted-foreground", className)}
863
- {...props}
864
- />
865
- ))
866
- MoonUICommandEmptyPro.displayName = CommandPrimitive.Empty.displayName
867
-
868
- interface CommandGroupProps
869
- extends React.ComponentProps<typeof CommandPrimitive.Group> {}
870
-
871
- export const MoonUICommandGroupPro = React.forwardRef<
872
- React.ElementRef<typeof CommandPrimitive.Group>,
873
- CommandGroupProps
874
- >(({ className, ...props }, ref) => (
875
- <CommandPrimitive.Group
876
- ref={ref}
877
- className={cn(
878
- "overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
879
- className
880
- )}
881
- {...props}
882
- />
883
- ))
884
- MoonUICommandGroupPro.displayName = CommandPrimitive.Group.displayName
885
-
886
- interface CommandSeparatorProps
887
- extends React.ComponentProps<typeof CommandPrimitive.Separator> {}
888
-
889
- export const MoonUICommandSeparatorPro = React.forwardRef<
890
- React.ElementRef<typeof CommandPrimitive.Separator>,
891
- CommandSeparatorProps
892
- >(({ className, ...props }, ref) => (
893
- <CommandPrimitive.Separator
894
- ref={ref}
895
- className={cn("-mx-1 h-px bg-border", className)}
896
- {...props}
897
- />
898
- ))
899
- MoonUICommandSeparatorPro.displayName = CommandPrimitive.Separator.displayName
900
-
901
- interface CommandItemProps
902
- extends React.ComponentProps<typeof CommandPrimitive.Item> {}
903
-
904
- export const MoonUICommandItemPro = React.forwardRef<
905
- React.ElementRef<typeof CommandPrimitive.Item>,
906
- CommandItemProps
907
- >(({ className, ...props }, ref) => (
908
- <CommandPrimitive.Item
909
- ref={ref}
910
- className={cn(
911
- "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50",
912
- className
913
- )}
914
- {...props}
915
- />
916
- ))
917
- MoonUICommandItemPro.displayName = CommandPrimitive.Item.displayName
918
-
919
- export const MoonUICommandShortcutPro = ({
920
- className,
921
- ...props
922
- }: React.HTMLAttributes<HTMLSpanElement>) => {
923
- return (
924
- <span
925
- className={cn(
926
- "ml-auto text-xs tracking-widest text-muted-foreground",
927
- className
928
- )}
929
- {...props}
930
- />
931
- )
932
- }
933
- MoonUICommandShortcutPro.displayName = "CommandShortcutPro"
934
-
935
- // Additional exports for convenience
936
- export {
937
- commandVariantsPro as commandVariants,
938
- MoonUICommandPro as Command,
939
- MoonUICommandDialogPro as CommandDialog,
940
- MoonUICommandInputPro as CommandInput,
941
- MoonUICommandListPro as CommandList,
942
- MoonUICommandEmptyPro as CommandEmpty,
943
- MoonUICommandGroupPro as CommandGroup,
944
- MoonUICommandSeparatorPro as CommandSeparator,
945
- MoonUICommandItemPro as CommandItem,
946
- MoonUICommandShortcutPro as CommandShortcut
947
- }
948
-