@moontra/moonui-pro 2.8.3 → 2.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moontra/moonui-pro",
3
- "version": "2.8.3",
3
+ "version": "2.8.5",
4
4
  "description": "Premium React components for MoonUI - Advanced UI library with 50+ pro components including performance, interactive, and gesture components",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
@@ -59,6 +59,7 @@ export interface PhoneNumberInputProps extends Omit<React.InputHTMLAttributes<HT
59
59
 
60
60
  // Telefon numarası formatlama
61
61
  function formatPhoneNumber(number: string, format: string): string {
62
+ if (!number || !format) return ''
62
63
  const cleaned = number.replace(/\D/g, '')
63
64
  let formatted = ''
64
65
  let digitIndex = 0
@@ -77,6 +78,7 @@ function formatPhoneNumber(number: string, format: string): string {
77
78
 
78
79
  // Telefon numarası doğrulama
79
80
  function validatePhoneNumber(number: string, countryCode: string): boolean {
81
+ if (!number || !countryCode) return false
80
82
  const cleaned = number.replace(/\D/g, '')
81
83
  const country = countries.find(c => c.code === countryCode)
82
84
 
@@ -90,6 +92,7 @@ function validatePhoneNumber(number: string, countryCode: string): boolean {
90
92
 
91
93
  // Uluslararası format dönüştürme
92
94
  function toInternationalFormat(countryCode: string, phoneNumber: string): string {
95
+ if (!countryCode || !phoneNumber) return ''
93
96
  const country = countries.find(c => c.code === countryCode)
94
97
  if (!country) return phoneNumber
95
98
 
@@ -247,7 +250,7 @@ export const MoonUIPhoneNumberInputPro = React.forwardRef<HTMLDivElement, PhoneN
247
250
  onPaste={handlePaste}
248
251
  onFocus={() => setIsFocused(true)}
249
252
  onBlur={() => setIsFocused(false)}
250
- placeholder={selectedCountry.format.replace(/x/g, '•')}
253
+ placeholder={selectedCountry?.format?.replace(/x/g, '•') || 'Enter phone number'}
251
254
  className={cn(
252
255
  "pr-10",
253
256
  error && "border-destructive",
@@ -22,6 +22,7 @@ import Typography from '@tiptap/extension-typography';
22
22
  import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight';
23
23
  import { common, createLowlight } from 'lowlight';
24
24
  import { SlashCommandsExtension } from './slash-commands-extension';
25
+ import { motion } from 'framer-motion';
25
26
 
26
27
  // Import pro access hooks
27
28
  // Note: DocsProAccess should be handled by consuming application
@@ -66,7 +67,13 @@ import {
66
67
  Palette,
67
68
  Eye,
68
69
  Edit,
69
- Lock
70
+ Lock,
71
+ Briefcase,
72
+ MessageSquare,
73
+ Heart,
74
+ GraduationCap,
75
+ Zap,
76
+ Lightbulb
70
77
  } from 'lucide-react';
71
78
  import { cn } from '../../lib/utils';
72
79
  import { Button } from '../ui/button';
@@ -94,7 +101,7 @@ import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';
94
101
  import { MoonUIColorPickerPro as ColorPicker } from '../ui/color-picker';
95
102
  import { Slider } from '../ui/slider';
96
103
  import { toast } from '../ui/toast';
97
- // Note: AI providers should be handled by consuming application
104
+ import { createAIProvider, type AIProvider as AIProviderInterface } from '../../lib/ai-providers';
98
105
 
99
106
  // Type definitions for AI functionality
100
107
  type AIProvider = 'openai' | 'anthropic' | 'gemini' | 'claude' | 'cohere'
@@ -116,59 +123,31 @@ interface SlashCommand {
116
123
  action: (text: string) => Promise<{ text: string; error?: string }> | void
117
124
  }
118
125
 
119
- // Mock AI provider function
120
- const getAIProvider = (settings: AISettingsType) => {
121
- const generateWithPrompt = async (systemPrompt: string, userText: string) => {
122
- // Mock implementation - consuming app should provide real AI integration
123
- const result = `[${systemPrompt}] ${userText}`
124
- return { text: result, error: undefined }
125
- }
126
-
127
- return {
128
- generateText: async (prompt: string) => {
129
- const result = await generateWithPrompt('Generate text', prompt)
130
- return result.text
131
- },
132
- rewrite: async (text: string) => {
133
- const result = await generateWithPrompt('Rewrite this text', text)
134
- return result
135
- },
136
- expand: async (text: string) => {
137
- const result = await generateWithPrompt('Expand this text', text)
138
- return result
139
- },
140
- summarize: async (text: string) => {
141
- const result = await generateWithPrompt('Summarize this text', text)
142
- return result
143
- },
144
- fixGrammar: async (text: string) => {
145
- const result = await generateWithPrompt('Fix grammar and spelling', text)
146
- return result
147
- },
148
- translate: async (text: string, targetLang: string) => {
149
- const result = await generateWithPrompt(`Translate to ${targetLang}`, text)
150
- return result
151
- },
152
- changeTone: async (text: string, tone: string) => {
153
- const result = await generateWithPrompt(`Change tone to ${tone}`, text)
154
- return result
155
- },
156
- continueWriting: async (text: string) => {
157
- const result = await generateWithPrompt('Continue writing', text)
158
- return result
159
- },
160
- improveWriting: async (text: string) => {
161
- const result = await generateWithPrompt('Improve writing quality', text)
162
- return result
163
- },
164
- generateIdeas: async (text: string) => {
165
- const result = await generateWithPrompt('Generate writing ideas', text)
166
- return result
167
- },
168
- complete: async (text: string) => {
169
- const result = await generateWithPrompt('Complete this text', text)
170
- return result
171
- }
126
+ // Get AI provider instance
127
+ const getAIProvider = (settings: AISettingsType): AIProviderInterface | null => {
128
+ if (!settings.apiKey) return null;
129
+
130
+ try {
131
+ // Map provider names to supported ones
132
+ const providerMap: Record<string, 'openai' | 'gemini' | 'claude'> = {
133
+ 'openai': 'openai',
134
+ 'gemini': 'gemini',
135
+ 'claude': 'claude',
136
+ 'anthropic': 'claude', // Map anthropic to claude
137
+ 'cohere': 'openai' // Use OpenAI as fallback for unsupported providers
138
+ };
139
+
140
+ const mappedProvider = providerMap[settings.provider] || 'openai';
141
+
142
+ return createAIProvider(mappedProvider, {
143
+ apiKey: settings.apiKey,
144
+ model: settings.model,
145
+ temperature: settings.temperature,
146
+ maxTokens: settings.maxTokens
147
+ });
148
+ } catch (error) {
149
+ console.error('Failed to create AI provider:', error);
150
+ return null;
172
151
  }
173
152
  }
174
153
  import './slash-commands.css';
@@ -298,7 +277,7 @@ export function RichTextEditor({
298
277
  aiConfig = {
299
278
  provider: 'openai',
300
279
  apiKey: '',
301
- model: 'gpt-4',
280
+ model: 'gpt-3.5-turbo',
302
281
  temperature: 0.7,
303
282
  maxTokens: 1000,
304
283
  },
@@ -557,7 +536,11 @@ export function RichTextEditor({
557
536
  setIsProcessing(true);
558
537
  try {
559
538
  const provider = getAIProvider(aiSettings);
560
- let response;
539
+ if (!provider) {
540
+ throw new Error('Failed to initialize AI provider');
541
+ }
542
+
543
+ let response: string;
561
544
 
562
545
  switch (action) {
563
546
  case 'rewrite':
@@ -573,7 +556,6 @@ export function RichTextEditor({
573
556
  response = await provider.fixGrammar(text);
574
557
  break;
575
558
  case 'translate':
576
- // TODO: Kullanıcıdan hedef dil seçmesini iste
577
559
  response = await provider.translate(text, 'Turkish');
578
560
  break;
579
561
  case 'tone_professional':
@@ -601,20 +583,11 @@ export function RichTextEditor({
601
583
  response = await provider.complete(text);
602
584
  }
603
585
 
604
- if (response.error) {
605
- toast({
606
- title: "AI Error",
607
- description: response.error,
608
- variant: "destructive",
609
- });
610
- return null;
611
- }
612
-
613
- return response.text;
614
- } catch {
586
+ return response;
587
+ } catch (error) {
615
588
  toast({
616
589
  title: "AI Error",
617
- description: "Failed to process with AI. Please check your settings.",
590
+ description: error instanceof Error ? error.message : "Failed to process with AI",
618
591
  variant: "destructive",
619
592
  });
620
593
  return null;
@@ -638,17 +611,52 @@ export function RichTextEditor({
638
611
  return;
639
612
  }
640
613
 
614
+ // Show processing toast
615
+ const processingToast = toast({
616
+ title: "Processing with AI...",
617
+ description: getActionDescription(action),
618
+ duration: 60000, // Long duration
619
+ });
620
+
641
621
  const result = await callAI(action, selectedText || editor.getText());
642
622
 
623
+ // Dismiss processing toast
624
+ processingToast.dismiss();
625
+
643
626
  if (result) {
644
627
  if (selectedText) {
645
628
  editor.chain().focus().deleteSelection().insertContent(result).run();
646
629
  } else {
647
630
  editor.chain().focus().insertContent(result).run();
648
631
  }
632
+
633
+ // Success toast
634
+ toast({
635
+ title: "AI action completed",
636
+ description: "Your text has been updated successfully.",
637
+ });
649
638
  }
650
639
  };
651
640
 
641
+ const getActionDescription = (action: string): string => {
642
+ const descriptions: Record<string, string> = {
643
+ rewrite: "Rewriting your text...",
644
+ improve: "Improving your writing...",
645
+ expand: "Expanding your text...",
646
+ summarize: "Creating a summary...",
647
+ fix: "Fixing grammar and spelling...",
648
+ translate: "Translating to Turkish...",
649
+ tone_professional: "Making text professional...",
650
+ tone_casual: "Making text casual...",
651
+ tone_friendly: "Making text friendly...",
652
+ tone_formal: "Making text formal...",
653
+ continue: "Continuing your writing...",
654
+ ideas: "Generating ideas...",
655
+ complete: "Completing your text..."
656
+ };
657
+ return descriptions[action] || "Processing...";
658
+ };
659
+
652
660
  const [linkUrl, setLinkUrl] = useState('');
653
661
  const [imageUrl, setImageUrl] = useState('');
654
662
  const [isLinkDialogOpen, setIsLinkDialogOpen] = useState(false);
@@ -1250,7 +1258,7 @@ export function RichTextEditor({
1250
1258
  <Button
1251
1259
  variant="ghost"
1252
1260
  size="sm"
1253
- className="h-8 px-3 bg-purple-100 hover:bg-purple-200 dark:bg-purple-900 dark:hover:bg-purple-800"
1261
+ className="h-8 px-3 bg-purple-100 hover:bg-purple-200 dark:bg-purple-900 dark:hover:bg-purple-800 transition-colors"
1254
1262
  disabled={isProcessing}
1255
1263
  >
1256
1264
  {isProcessing ? (
@@ -1261,58 +1269,134 @@ export function RichTextEditor({
1261
1269
  AI Tools
1262
1270
  </Button>
1263
1271
  </DropdownMenuTrigger>
1264
- <DropdownMenuContent className="w-56">
1265
- <DropdownMenuItem onClick={() => handleAIAction('rewrite')}>
1272
+ <DropdownMenuContent className="w-64">
1273
+ <div className="px-2 py-1.5 text-xs font-semibold text-muted-foreground flex items-center gap-2">
1274
+ <Wand2 className="w-3 h-3" />
1275
+ Writing Improvements
1276
+ </div>
1277
+ <DropdownMenuItem
1278
+ onClick={() => handleAIAction('rewrite')}
1279
+ disabled={isProcessing}
1280
+ >
1266
1281
  <RefreshCw className="w-4 h-4 mr-2" />
1267
1282
  Rewrite Selection
1283
+ <span className="ml-auto text-xs text-muted-foreground">Alt+R</span>
1268
1284
  </DropdownMenuItem>
1269
- <DropdownMenuItem onClick={() => handleAIAction('improve')}>
1270
- <Wand2 className="w-4 h-4 mr-2" />
1285
+ <DropdownMenuItem
1286
+ onClick={() => handleAIAction('improve')}
1287
+ disabled={isProcessing}
1288
+ >
1289
+ <Sparkles className="w-4 h-4 mr-2" />
1271
1290
  Improve Writing
1272
1291
  </DropdownMenuItem>
1273
- <DropdownMenuItem onClick={() => handleAIAction('expand')}>
1292
+ <DropdownMenuItem
1293
+ onClick={() => handleAIAction('expand')}
1294
+ disabled={isProcessing}
1295
+ >
1274
1296
  <Maximize className="w-4 h-4 mr-2" />
1275
1297
  Expand Text
1276
1298
  </DropdownMenuItem>
1277
- <DropdownMenuItem onClick={() => handleAIAction('summarize')}>
1299
+ <DropdownMenuItem
1300
+ onClick={() => handleAIAction('summarize')}
1301
+ disabled={isProcessing}
1302
+ >
1278
1303
  <FileText className="w-4 h-4 mr-2" />
1279
1304
  Summarize
1280
1305
  </DropdownMenuItem>
1281
- <DropdownMenuItem onClick={() => handleAIAction('continue')}>
1306
+ <DropdownMenuItem
1307
+ onClick={() => handleAIAction('continue')}
1308
+ disabled={isProcessing}
1309
+ >
1282
1310
  <Plus className="w-4 h-4 mr-2" />
1283
1311
  Continue Writing
1284
1312
  </DropdownMenuItem>
1313
+
1285
1314
  <DropdownMenuSeparator />
1286
- <DropdownMenuItem onClick={() => handleAIAction('fix')}>
1287
- <Check className="w-4 h-4 mr-2" />
1288
- Fix Grammar & Spelling
1289
- </DropdownMenuItem>
1290
- <DropdownMenuSeparator />
1291
- <DropdownMenuItem onClick={() => handleAIAction('tone_professional')}>
1292
- <Sparkles className="w-4 h-4 mr-2" />
1315
+ <div className="px-2 py-1.5 text-xs font-semibold text-muted-foreground flex items-center gap-2">
1316
+ <Palette className="w-3 h-3" />
1317
+ Tone Adjustments
1318
+ </div>
1319
+ <DropdownMenuItem
1320
+ onClick={() => handleAIAction('tone_professional')}
1321
+ disabled={isProcessing}
1322
+ >
1323
+ <Briefcase className="w-4 h-4 mr-2" />
1293
1324
  Make Professional
1294
1325
  </DropdownMenuItem>
1295
- <DropdownMenuItem onClick={() => handleAIAction('tone_casual')}>
1296
- <Sparkles className="w-4 h-4 mr-2" />
1326
+ <DropdownMenuItem
1327
+ onClick={() => handleAIAction('tone_casual')}
1328
+ disabled={isProcessing}
1329
+ >
1330
+ <MessageSquare className="w-4 h-4 mr-2" />
1297
1331
  Make Casual
1298
1332
  </DropdownMenuItem>
1299
- <DropdownMenuItem onClick={() => handleAIAction('tone_friendly')}>
1300
- <Sparkles className="w-4 h-4 mr-2" />
1333
+ <DropdownMenuItem
1334
+ onClick={() => handleAIAction('tone_friendly')}
1335
+ disabled={isProcessing}
1336
+ >
1337
+ <Heart className="w-4 h-4 mr-2" />
1301
1338
  Make Friendly
1302
1339
  </DropdownMenuItem>
1303
- <DropdownMenuItem onClick={() => handleAIAction('tone_formal')}>
1304
- <Sparkles className="w-4 h-4 mr-2" />
1340
+ <DropdownMenuItem
1341
+ onClick={() => handleAIAction('tone_formal')}
1342
+ disabled={isProcessing}
1343
+ >
1344
+ <GraduationCap className="w-4 h-4 mr-2" />
1305
1345
  Make Formal
1306
1346
  </DropdownMenuItem>
1347
+
1307
1348
  <DropdownMenuSeparator />
1308
- <DropdownMenuItem onClick={() => handleAIAction('translate')}>
1349
+ <div className="px-2 py-1.5 text-xs font-semibold text-muted-foreground flex items-center gap-2">
1350
+ <Zap className="w-3 h-3" />
1351
+ Other Actions
1352
+ </div>
1353
+ <DropdownMenuItem
1354
+ onClick={() => handleAIAction('fix')}
1355
+ disabled={isProcessing}
1356
+ >
1357
+ <Check className="w-4 h-4 mr-2" />
1358
+ Fix Grammar & Spelling
1359
+ <span className="ml-auto text-xs text-muted-foreground">F7</span>
1360
+ </DropdownMenuItem>
1361
+ <DropdownMenuItem
1362
+ onClick={() => handleAIAction('translate')}
1363
+ disabled={isProcessing}
1364
+ >
1309
1365
  <Languages className="w-4 h-4 mr-2" />
1310
1366
  Translate to Turkish
1311
1367
  </DropdownMenuItem>
1312
- <DropdownMenuItem onClick={() => handleAIAction('ideas')}>
1313
- <Sparkles className="w-4 h-4 mr-2" />
1368
+ <DropdownMenuItem
1369
+ onClick={() => handleAIAction('ideas')}
1370
+ disabled={isProcessing}
1371
+ >
1372
+ <Lightbulb className="w-4 h-4 mr-2" />
1314
1373
  Generate Ideas
1315
1374
  </DropdownMenuItem>
1375
+
1376
+ {!aiSettings.apiKey && (
1377
+ <>
1378
+ <DropdownMenuSeparator />
1379
+ <div className="px-2 py-2">
1380
+ <motion.div
1381
+ initial={{ opacity: 0, y: -10 }}
1382
+ animate={{ opacity: 1, y: 0 }}
1383
+ className="text-xs text-muted-foreground bg-yellow-100 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-md p-3"
1384
+ >
1385
+ <div className="flex items-start gap-2">
1386
+ <Settings className="w-3 h-3 mt-0.5 text-yellow-600 dark:text-yellow-400" />
1387
+ <div>
1388
+ <div className="font-medium text-yellow-900 dark:text-yellow-200 mb-1">
1389
+ API Key Required
1390
+ </div>
1391
+ <div className="text-yellow-800 dark:text-yellow-300">
1392
+ Click the settings icon to configure your AI provider and API key.
1393
+ </div>
1394
+ </div>
1395
+ </div>
1396
+ </motion.div>
1397
+ </div>
1398
+ </>
1399
+ )}
1316
1400
  </DropdownMenuContent>
1317
1401
  </DropdownMenu>
1318
1402
 
@@ -1338,7 +1422,20 @@ export function RichTextEditor({
1338
1422
  <Label htmlFor="provider">Provider</Label>
1339
1423
  <Select
1340
1424
  value={aiSettings.provider}
1341
- onValueChange={(value: 'openai' | 'claude' | 'gemini' | 'cohere') => setAiSettings({ ...aiSettings, provider: value })}
1425
+ onValueChange={(value: 'openai' | 'claude' | 'gemini' | 'cohere') => {
1426
+ // Update model when provider changes
1427
+ const defaultModels = {
1428
+ openai: 'gpt-3.5-turbo',
1429
+ claude: 'claude-3-sonnet-20240229',
1430
+ gemini: 'gemini-2.0-flash',
1431
+ cohere: 'command'
1432
+ };
1433
+ setAiSettings({
1434
+ ...aiSettings,
1435
+ provider: value,
1436
+ model: defaultModels[value] || 'gpt-3.5-turbo'
1437
+ });
1438
+ }}
1342
1439
  >
1343
1440
  <SelectTrigger>
1344
1441
  <SelectValue />
@@ -1386,8 +1483,9 @@ export function RichTextEditor({
1386
1483
  )}
1387
1484
  {aiSettings.provider === 'gemini' && (
1388
1485
  <>
1389
- <SelectItem value="gemini-pro">Gemini Pro</SelectItem>
1390
- <SelectItem value="gemini-pro-vision">Gemini Pro Vision</SelectItem>
1486
+ <SelectItem value="gemini-2.0-flash">Gemini 2.0 Flash</SelectItem>
1487
+ <SelectItem value="gemini-1.5-flash">Gemini 1.5 Flash</SelectItem>
1488
+ <SelectItem value="gemini-1.5-pro">Gemini 1.5 Pro</SelectItem>
1391
1489
  </>
1392
1490
  )}
1393
1491
  {aiSettings.provider === 'cohere' && (