@moontra/moonui-pro 2.20.1 → 2.20.2

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 (76) hide show
  1. package/dist/index.d.ts +691 -261
  2. package/dist/index.mjs +7418 -4934
  3. package/package.json +4 -3
  4. package/scripts/postbuild.js +27 -0
  5. package/src/components/advanced-chart/index.tsx +5 -1
  6. package/src/components/advanced-forms/index.tsx +175 -16
  7. package/src/components/calendar/event-dialog.tsx +18 -13
  8. package/src/components/calendar/index.tsx +197 -50
  9. package/src/components/dashboard/dashboard-grid.tsx +21 -3
  10. package/src/components/dashboard/types.ts +3 -0
  11. package/src/components/dashboard/widgets/activity-feed.tsx +6 -1
  12. package/src/components/dashboard/widgets/comparison-widget.tsx +177 -0
  13. package/src/components/dashboard/widgets/index.ts +5 -0
  14. package/src/components/dashboard/widgets/metric-card.tsx +21 -1
  15. package/src/components/dashboard/widgets/progress-widget.tsx +113 -0
  16. package/src/components/error-boundary/index.tsx +160 -37
  17. package/src/components/form-wizard/form-wizard-context.tsx +54 -26
  18. package/src/components/form-wizard/form-wizard-progress.tsx +33 -2
  19. package/src/components/form-wizard/types.ts +2 -1
  20. package/src/components/github-stars/hooks.ts +1 -0
  21. package/src/components/github-stars/variants.tsx +3 -1
  22. package/src/components/health-check/index.tsx +14 -14
  23. package/src/components/hover-card-3d/index.tsx +2 -3
  24. package/src/components/index.ts +5 -3
  25. package/src/components/kanban/kanban.tsx +23 -18
  26. package/src/components/license-error/index.tsx +2 -0
  27. package/src/components/magnetic-button/index.tsx +56 -7
  28. package/src/components/memory-efficient-data/index.tsx +117 -115
  29. package/src/components/navbar/index.tsx +781 -0
  30. package/src/components/performance-debugger/index.tsx +62 -38
  31. package/src/components/performance-monitor/index.tsx +47 -33
  32. package/src/components/phone-number-input/index.tsx +32 -27
  33. package/src/components/phone-number-input/phone-number-input-simple.tsx +167 -0
  34. package/src/components/rich-text-editor/index.tsx +26 -28
  35. package/src/components/rich-text-editor/slash-commands-extension.ts +15 -5
  36. package/src/components/sidebar/index.tsx +32 -13
  37. package/src/components/timeline/index.tsx +84 -49
  38. package/src/components/ui/accordion.tsx +550 -42
  39. package/src/components/ui/avatar.tsx +2 -0
  40. package/src/components/ui/badge.tsx +2 -0
  41. package/src/components/ui/breadcrumb.tsx +2 -0
  42. package/src/components/ui/button.tsx +39 -33
  43. package/src/components/ui/card.tsx +2 -0
  44. package/src/components/ui/collapsible.tsx +546 -50
  45. package/src/components/ui/command.tsx +790 -67
  46. package/src/components/ui/dialog.tsx +510 -92
  47. package/src/components/ui/dropdown-menu.tsx +540 -52
  48. package/src/components/ui/index.ts +37 -5
  49. package/src/components/ui/input.tsx +2 -0
  50. package/src/components/ui/magnetic-button.tsx +1 -1
  51. package/src/components/ui/media-gallery.tsx +1 -2
  52. package/src/components/ui/navigation-menu.tsx +130 -0
  53. package/src/components/ui/pagination.tsx +2 -0
  54. package/src/components/ui/select.tsx +6 -2
  55. package/src/components/ui/spotlight-card.tsx +1 -1
  56. package/src/components/ui/table.tsx +2 -0
  57. package/src/components/ui/tabs-pro.tsx +542 -0
  58. package/src/components/ui/tabs.tsx +23 -167
  59. package/src/components/ui/toggle.tsx +12 -12
  60. package/src/index.ts +11 -3
  61. package/src/styles/index.css +596 -0
  62. package/src/use-performance-optimizer.ts +1 -1
  63. package/src/utils/chart-helpers.ts +1 -1
  64. package/src/__tests__/use-intersection-observer.test.tsx +0 -216
  65. package/src/__tests__/use-local-storage.test.tsx +0 -174
  66. package/src/__tests__/use-pro-access.test.tsx +0 -183
  67. package/src/components/advanced-chart/advanced-chart.test.tsx +0 -281
  68. package/src/components/data-table/data-table.test.tsx +0 -187
  69. package/src/components/enhanced/badge.tsx +0 -191
  70. package/src/components/enhanced/button.tsx +0 -362
  71. package/src/components/enhanced/card.tsx +0 -266
  72. package/src/components/enhanced/dialog.tsx +0 -246
  73. package/src/components/enhanced/index.ts +0 -4
  74. package/src/components/file-upload/file-upload.test.tsx +0 -243
  75. package/src/components/rich-text-editor/index-old-backup.tsx +0 -437
  76. package/src/types/moonui.d.ts +0 -22
@@ -1,33 +1,119 @@
1
1
  "use client"
2
2
 
3
3
  import * as React from "react"
4
- import { Search } from "lucide-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"
5
21
  import { Command as CommandPrimitive } from "cmdk"
6
22
  import { cva, type VariantProps } from "class-variance-authority"
23
+ import { motion, AnimatePresence } from "framer-motion"
24
+ import Fuse from "fuse.js"
7
25
 
8
26
  import { cn } from "../../lib/utils"
9
- import { Dialog, DialogContent, DialogTitle } from "./dialog"
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"
10
47
 
11
48
  /**
12
- * Command (Komut Paleti) Bileşeni
49
+ * AI-Enhanced Command Palette Component
13
50
  *
14
- * Gerçek command palette işlevselliği ile klavye navigasyonu, arama ve filtreleme.
15
- * cmdk kütüphanesi üzerine inşa edilmiş, tam özellikli command interface.
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
16
58
  */
17
59
 
18
- const MoonUIcommandVariantsPro = cva(
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(
19
102
  "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
20
103
  {
21
104
  variants: {
22
105
  variant: {
23
106
  default: "bg-popover text-popover-foreground",
24
- glass: "bg-background/80 text-foreground backdrop-blur-sm",
25
- bordered: "bg-popover text-popover-foreground border border-border",
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)]",
26
111
  },
27
112
  size: {
28
- sm: "min-h-[200px]",
29
- default: "min-h-[300px]",
30
- lg: "min-h-[400px]",
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]",
31
117
  },
32
118
  },
33
119
  defaultVariants: {
@@ -37,42 +123,689 @@ const MoonUIcommandVariantsPro = cva(
37
123
  }
38
124
  )
39
125
 
40
- // Command bileşeni - cmdk primitive wrapper
41
- interface CommandProps
42
- extends React.ComponentProps<typeof CommandPrimitive>,
43
- VariantProps<typeof MoonUIcommandVariantsPro> {}
126
+ // Fuzzy search hook
127
+ const useFuzzySearch = (items: CommandItemData[], query: string, enabled: boolean) => {
128
+ return useMemo(() => {
129
+ if (!enabled || !query) return items
44
130
 
45
- const MoonUICommandPro = React.forwardRef<
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<
46
328
  React.ElementRef<typeof CommandPrimitive>,
47
- CommandProps
48
- >(({ className, variant, size, ...props }, ref) => (
49
- <CommandPrimitive
50
- ref={ref}
51
- className={cn(MoonUIcommandVariantsPro({ variant, size }), className)}
52
- {...props}
53
- />
54
- ))
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
+ })
55
791
  MoonUICommandPro.displayName = CommandPrimitive.displayName
56
792
 
57
- // CommandDialog bileşeni
58
- interface CommandDialogProps extends React.ComponentProps<typeof Dialog> {
59
- commandClassName?: string
793
+ // Enhanced Dialog wrapper for AI Command
794
+ interface CommandDialogProProps extends React.ComponentProps<typeof Dialog> {
795
+ commandProps?: CommandProProps
60
796
  children?: React.ReactNode
61
797
  }
62
798
 
63
- const MoonUICommandDialogPro = ({
799
+ export const MoonUICommandDialogPro = ({
64
800
  children,
65
- commandClassName,
801
+ commandProps,
66
802
  ...props
67
- }: CommandDialogProps) => {
803
+ }: CommandDialogProProps) => {
68
804
  return (
69
805
  <Dialog {...props}>
70
- <DialogContent className="overflow-hidden p-0 shadow-lg" hideCloseButton>
71
- <DialogTitle className="sr-only">Command Menu</DialogTitle>
72
- <MoonUICommandPro className={cn(
73
- "[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5",
74
- commandClassName
75
- )}>
806
+ <DialogContent className="overflow-hidden p-0 shadow-2xl" hideCloseButton>
807
+ <DialogTitle className="sr-only">AI Command Menu</DialogTitle>
808
+ <MoonUICommandPro {...commandProps}>
76
809
  {children}
77
810
  </MoonUICommandPro>
78
811
  </DialogContent>
@@ -80,11 +813,11 @@ const MoonUICommandDialogPro = ({
80
813
  )
81
814
  }
82
815
 
83
- // CommandInput bileşeni
816
+ // Basic Command Components (for backward compatibility)
84
817
  interface CommandInputProps
85
818
  extends React.ComponentProps<typeof CommandPrimitive.Input> {}
86
819
 
87
- const MoonUICommandInputPro = React.forwardRef<
820
+ export const MoonUICommandInputPro = React.forwardRef<
88
821
  React.ElementRef<typeof CommandPrimitive.Input>,
89
822
  CommandInputProps
90
823
  >(({ className, ...props }, ref) => (
@@ -102,11 +835,10 @@ const MoonUICommandInputPro = React.forwardRef<
102
835
  ))
103
836
  MoonUICommandInputPro.displayName = CommandPrimitive.Input.displayName
104
837
 
105
- // CommandList bileşeni
106
838
  interface CommandListProps
107
839
  extends React.ComponentProps<typeof CommandPrimitive.List> {}
108
840
 
109
- const MoonUICommandListPro = React.forwardRef<
841
+ export const MoonUICommandListPro = React.forwardRef<
110
842
  React.ElementRef<typeof CommandPrimitive.List>,
111
843
  CommandListProps
112
844
  >(({ className, ...props }, ref) => (
@@ -118,11 +850,10 @@ const MoonUICommandListPro = React.forwardRef<
118
850
  ))
119
851
  MoonUICommandListPro.displayName = CommandPrimitive.List.displayName
120
852
 
121
- // CommandEmpty bileşeni
122
853
  interface CommandEmptyProps
123
854
  extends React.ComponentProps<typeof CommandPrimitive.Empty> {}
124
855
 
125
- const MoonUICommandEmptyPro = React.forwardRef<
856
+ export const MoonUICommandEmptyPro = React.forwardRef<
126
857
  React.ElementRef<typeof CommandPrimitive.Empty>,
127
858
  CommandEmptyProps
128
859
  >(({ className, ...props }, ref) => (
@@ -134,11 +865,10 @@ const MoonUICommandEmptyPro = React.forwardRef<
134
865
  ))
135
866
  MoonUICommandEmptyPro.displayName = CommandPrimitive.Empty.displayName
136
867
 
137
- // CommandGroup bileşeni
138
868
  interface CommandGroupProps
139
869
  extends React.ComponentProps<typeof CommandPrimitive.Group> {}
140
870
 
141
- const MoonUICommandGroupPro = React.forwardRef<
871
+ export const MoonUICommandGroupPro = React.forwardRef<
142
872
  React.ElementRef<typeof CommandPrimitive.Group>,
143
873
  CommandGroupProps
144
874
  >(({ className, ...props }, ref) => (
@@ -153,11 +883,10 @@ const MoonUICommandGroupPro = React.forwardRef<
153
883
  ))
154
884
  MoonUICommandGroupPro.displayName = CommandPrimitive.Group.displayName
155
885
 
156
- // CommandSeparator bileşeni
157
886
  interface CommandSeparatorProps
158
887
  extends React.ComponentProps<typeof CommandPrimitive.Separator> {}
159
888
 
160
- const MoonUICommandSeparatorPro = React.forwardRef<
889
+ export const MoonUICommandSeparatorPro = React.forwardRef<
161
890
  React.ElementRef<typeof CommandPrimitive.Separator>,
162
891
  CommandSeparatorProps
163
892
  >(({ className, ...props }, ref) => (
@@ -169,11 +898,10 @@ const MoonUICommandSeparatorPro = React.forwardRef<
169
898
  ))
170
899
  MoonUICommandSeparatorPro.displayName = CommandPrimitive.Separator.displayName
171
900
 
172
- // CommandItem bileşeni
173
901
  interface CommandItemProps
174
902
  extends React.ComponentProps<typeof CommandPrimitive.Item> {}
175
903
 
176
- const MoonUICommandItemPro = React.forwardRef<
904
+ export const MoonUICommandItemPro = React.forwardRef<
177
905
  React.ElementRef<typeof CommandPrimitive.Item>,
178
906
  CommandItemProps
179
907
  >(({ className, ...props }, ref) => (
@@ -188,8 +916,7 @@ const MoonUICommandItemPro = React.forwardRef<
188
916
  ))
189
917
  MoonUICommandItemPro.displayName = CommandPrimitive.Item.displayName
190
918
 
191
- // CommandShortcut bileşeni - utility component
192
- const MoonUICommandShortcutPro = ({
919
+ export const MoonUICommandShortcutPro = ({
193
920
  className,
194
921
  ...props
195
922
  }: React.HTMLAttributes<HTMLSpanElement>) => {
@@ -205,21 +932,17 @@ const MoonUICommandShortcutPro = ({
205
932
  }
206
933
  MoonUICommandShortcutPro.displayName = "CommandShortcutPro"
207
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
+ }
208
948
 
209
- // Internal aliases for Pro component usage
210
- export const commandVariantsInternal = MoonUIcommandVariantsPro
211
- export const CommandInternal = MoonUICommandPro
212
- export const CommandDialogInternal = MoonUICommandDialogPro
213
- export const CommandInputInternal = MoonUICommandInputPro
214
- export const CommandListInternal = MoonUICommandListPro
215
- export const CommandEmptyInternal = MoonUICommandEmptyPro
216
- export const CommandGroupInternal = MoonUICommandGroupPro
217
- export const CommandSeparatorInternal = MoonUICommandSeparatorPro
218
- export const CommandItemInternal = MoonUICommandItemPro
219
- export const CommandShortcutInternal = MoonUICommandShortcutPro
220
-
221
- // Pro exports
222
- export { MoonUIcommandVariantsPro, MoonUICommandPro, MoonUICommandDialogPro, MoonUICommandInputPro, MoonUICommandListPro, MoonUICommandEmptyPro, MoonUICommandGroupPro, MoonUICommandSeparatorPro, MoonUICommandItemPro, MoonUICommandShortcutPro }
223
-
224
- // Clean exports (without MoonUI prefix for easier usage)
225
- export { MoonUIcommandVariantsPro as commandVariants, MoonUICommandPro as Command, MoonUICommandDialogPro as CommandDialog, MoonUICommandInputPro as CommandInput, MoonUICommandListPro as CommandList, MoonUICommandEmptyPro as CommandEmpty, MoonUICommandGroupPro as CommandGroup, MoonUICommandSeparatorPro as CommandSeparator, MoonUICommandItemPro as CommandItem, MoonUICommandShortcutPro as CommandShortcut }