@moontra/moonui-pro 2.8.11 → 2.8.12

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/dist/index.mjs CHANGED
@@ -3,7 +3,7 @@ import { twMerge } from 'tailwind-merge';
3
3
  import * as t from 'react';
4
4
  import t__default, { useState, useRef, useCallback, forwardRef, createContext, useEffect, useContext, useMemo, useLayoutEffect, useDebugValue, Component } from 'react';
5
5
  import * as AccordionPrimitive from '@radix-ui/react-accordion';
6
- import { ChevronDown, Info, AlertCircle, AlertTriangle, Check, X, MoreHorizontal, Loader2, Minus, Search, ChevronRight, Circle, ChevronUp, Lock, Sparkles, Plus, CreditCard, Globe, CheckCircle2, XCircle, RotateCcw, Download, Clock, HelpCircle, ChevronLeft, Calendar as Calendar$1, Edit, Trash2, MapPin, User, GripVertical, MessageCircle, Paperclip, Bold as Bold$1, Italic as Italic$1, Underline as Underline$1, Strikethrough, Code as Code$1, Type, Heading1, Heading2, Heading3, AlignLeft, AlignCenter, AlignRight, AlignJustify, List, ListOrdered, CheckSquare, Quote, Palette, Highlighter, Link2, Image as Image$1, Table as Table$1, Settings, Undo, Redo, Eye, RefreshCw, Wand2, Maximize, FileText, Briefcase, MessageSquare, Heart, GraduationCap, Zap, Languages, Lightbulb, TrendingUp, TrendingDown, ZoomOut, ZoomIn, FileSpreadsheet, FileJson, Maximize2, Move, Menu, Bell, CheckCheck, CheckCircle, Settings2, LogOut, Edit3, LayoutGrid, Upload, Share2, Save, Filter, FileDown, ArrowUp, ArrowDown, ArrowUpDown, ChevronsLeft, ChevronsRight, Pin, Sun, Moon, Monitor, Star, ExternalLink, CalendarIcon, DollarSign, Users, Github, GitFork, Activity, Server, EyeOff, RotateCw, Timer, Cpu, MemoryStick, HardDrive, Network, BarChart3, Video, Music, Archive, File, Columns, Grip, Unlock, Minimize2, Map as Map$1, Target, MoreVertical, BellOff, ArrowDownRight, ArrowUpRight } from 'lucide-react';
6
+ import { ChevronDown, Info, AlertCircle, AlertTriangle, Check, X, MoreHorizontal, Loader2, Minus, Search, ChevronRight, Circle, ChevronUp, Lock, Sparkles, Plus, CreditCard, Globe, CheckCircle2, XCircle, RotateCcw, Download, Clock, HelpCircle, ChevronLeft, Calendar as Calendar$1, Edit, Trash2, MapPin, User, GripVertical, MessageCircle, Paperclip, Bold as Bold$1, Italic as Italic$1, Underline as Underline$1, Strikethrough, Code as Code$1, Type, Heading1, Heading2, Heading3, AlignLeft, AlignCenter, AlignRight, AlignJustify, List, ListOrdered, CheckSquare, Quote, Palette, Highlighter, Link2, Image as Image$1, Table as Table$1, Settings, Undo, Redo, Eye, RefreshCw, Wand2, Maximize, FileText, Briefcase, MessageSquare, Heart, GraduationCap, Zap, Languages, Lightbulb, Copy, TrendingUp, TrendingDown, ZoomOut, ZoomIn, FileSpreadsheet, FileJson, Maximize2, Move, Menu, Bell, CheckCheck, CheckCircle, Settings2, LogOut, Edit3, LayoutGrid, Upload, Share2, Save, Filter, FileDown, ArrowUp, ArrowDown, ArrowUpDown, ChevronsLeft, ChevronsRight, Pin, Sun, Moon, Monitor, Star, ExternalLink, CalendarIcon, DollarSign, Users, Github, GitFork, Activity, Server, EyeOff, RotateCw, Timer, Cpu, MemoryStick, HardDrive, Network, BarChart3, Video, Music, Archive, File, Columns, Grip, Unlock, Minimize2, Map as Map$1, Target, MoreVertical, BellOff, ArrowDownRight, ArrowUpRight } from 'lucide-react';
7
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
8
  import { cva } from 'class-variance-authority';
9
9
  import * as AvatarPrimitive from '@radix-ui/react-avatar';
@@ -50471,6 +50471,10 @@ function RichTextEditor({
50471
50471
  }
50472
50472
  return "en";
50473
50473
  });
50474
+ const [isAiPreviewOpen, setIsAiPreviewOpen] = useState(false);
50475
+ const [previewContent, setPreviewContent] = useState("");
50476
+ const [previewAction, setPreviewAction] = useState("");
50477
+ const [previewOriginalText, setPreviewOriginalText] = useState("");
50474
50478
  useEffect(() => {
50475
50479
  return () => {
50476
50480
  if (typingIntervalRef.current) {
@@ -50492,6 +50496,48 @@ function RichTextEditor({
50492
50496
  description: "The AI response was interrupted."
50493
50497
  });
50494
50498
  };
50499
+ const shouldUseModal = (action) => {
50500
+ const modalActions = ["expand", "summarize", "ideas", "continue"];
50501
+ return modalActions.includes(action);
50502
+ };
50503
+ const applyAIContentToEditor = (content, replaceSelection = true) => {
50504
+ if (!editor)
50505
+ return;
50506
+ setIsTyping(true);
50507
+ isTypingRef.current = true;
50508
+ setTypingText("");
50509
+ if (replaceSelection) {
50510
+ const selection = editor.state.selection;
50511
+ const selectedText = editor.state.doc.textBetween(selection.from, selection.to, " ");
50512
+ if (selectedText) {
50513
+ editor.chain().focus().deleteSelection().run();
50514
+ }
50515
+ }
50516
+ let currentIndex = 0;
50517
+ const typeSpeed = 30;
50518
+ const typeNextChar = () => {
50519
+ if (currentIndex < content.length && isTypingRef.current) {
50520
+ const nextChar = content[currentIndex];
50521
+ setTypingText((prev) => prev + nextChar);
50522
+ editor.chain().focus().insertContent(nextChar).run();
50523
+ currentIndex++;
50524
+ setRemainingText(content.substring(currentIndex));
50525
+ typingIntervalRef.current = setTimeout(typeNextChar, typeSpeed);
50526
+ } else {
50527
+ setIsTyping(false);
50528
+ isTypingRef.current = false;
50529
+ setTypingText("");
50530
+ setRemainingText("");
50531
+ if (currentIndex >= content.length) {
50532
+ toast({
50533
+ title: "AI action completed",
50534
+ description: "Your text has been updated successfully."
50535
+ });
50536
+ }
50537
+ }
50538
+ };
50539
+ typeNextChar();
50540
+ };
50495
50541
  const slashCommands = [
50496
50542
  {
50497
50543
  id: "rewrite",
@@ -50629,10 +50675,18 @@ function RichTextEditor({
50629
50675
  const { from: from2, to } = editor.state.selection;
50630
50676
  const selectedText = editor.state.doc.textBetween(from2, to, " ");
50631
50677
  setIsProcessing(true);
50678
+ setCurrentAction(command2.id);
50632
50679
  try {
50633
50680
  const response = await command2.action(selectedText || editor.getText());
50634
50681
  if (response.text) {
50635
- editor.chain().focus().insertContent(response.text).run();
50682
+ if (shouldUseModal(command2.id)) {
50683
+ setPreviewContent(response.text);
50684
+ setPreviewAction(command2.id);
50685
+ setPreviewOriginalText(selectedText || editor.getText());
50686
+ setIsAiPreviewOpen(true);
50687
+ } else {
50688
+ applyAIContentToEditor(response.text, !!selectedText);
50689
+ }
50636
50690
  } else if (response.error) {
50637
50691
  toast({
50638
50692
  title: "AI Error",
@@ -50772,36 +50826,14 @@ function RichTextEditor({
50772
50826
  const result = await callAI(action, selectedText || editor.getText(), targetLanguage);
50773
50827
  processingToast.dismiss();
50774
50828
  if (result) {
50775
- setIsTyping(true);
50776
- isTypingRef.current = true;
50777
- setTypingText("");
50778
- if (selectedText) {
50779
- editor.chain().focus().deleteSelection().run();
50829
+ if (shouldUseModal(action)) {
50830
+ setPreviewContent(result);
50831
+ setPreviewAction(action);
50832
+ setPreviewOriginalText(selectedText || editor.getText());
50833
+ setIsAiPreviewOpen(true);
50834
+ } else {
50835
+ applyAIContentToEditor(result, !!selectedText);
50780
50836
  }
50781
- let currentIndex = 0;
50782
- const typeSpeed = 30;
50783
- const typeNextChar = () => {
50784
- if (currentIndex < result.length && isTypingRef.current) {
50785
- const nextChar = result[currentIndex];
50786
- setTypingText((prev) => prev + nextChar);
50787
- editor.chain().focus().insertContent(nextChar).run();
50788
- currentIndex++;
50789
- setRemainingText(result.substring(currentIndex));
50790
- typingIntervalRef.current = setTimeout(typeNextChar, typeSpeed);
50791
- } else {
50792
- setIsTyping(false);
50793
- isTypingRef.current = false;
50794
- setTypingText("");
50795
- setRemainingText("");
50796
- if (currentIndex >= result.length) {
50797
- toast({
50798
- title: "AI action completed",
50799
- description: "Your text has been updated successfully."
50800
- });
50801
- }
50802
- }
50803
- };
50804
- typeNextChar();
50805
50837
  }
50806
50838
  };
50807
50839
  const getActionDescription = (action, targetLanguage) => {
@@ -51887,6 +51919,91 @@ function RichTextEditor({
51887
51919
  }, children: "Apply Borders" })
51888
51920
  ] })
51889
51921
  ] }) }),
51922
+ /* @__PURE__ */ jsx(MoonUIDialogPro, { open: isAiPreviewOpen, onOpenChange: setIsAiPreviewOpen, children: /* @__PURE__ */ jsxs(MoonUIDialogContentPro, { className: "max-w-4xl max-h-[80vh] overflow-hidden flex flex-col", children: [
51923
+ /* @__PURE__ */ jsxs(MoonUIDialogHeaderPro, { children: [
51924
+ /* @__PURE__ */ jsxs(MoonUIDialogTitlePro, { className: "flex items-center gap-2", children: [
51925
+ /* @__PURE__ */ jsx(Wand2, { className: "w-5 h-5 text-purple-600 dark:text-purple-400" }),
51926
+ "AI Preview - ",
51927
+ getActionDescription(previewAction).replace("...", "")
51928
+ ] }),
51929
+ /* @__PURE__ */ jsx(MoonUIDialogDescriptionPro, { children: "Review the AI-generated content before applying it to your editor." })
51930
+ ] }),
51931
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-hidden flex flex-col gap-4", children: [
51932
+ /* @__PURE__ */ jsxs("div", { className: "flex-shrink-0", children: [
51933
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-muted-foreground mb-2", children: "Original Text:" }),
51934
+ /* @__PURE__ */ jsx("div", { className: "p-3 bg-muted/50 rounded-md border max-h-32 overflow-auto", children: /* @__PURE__ */ jsx("p", { className: "text-sm whitespace-pre-wrap", children: previewOriginalText }) })
51935
+ ] }),
51936
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-hidden flex flex-col", children: [
51937
+ /* @__PURE__ */ jsxs("div", { className: "text-sm font-medium text-muted-foreground mb-2 flex items-center gap-2", children: [
51938
+ /* @__PURE__ */ jsx(Sparkles, { className: "w-4 h-4" }),
51939
+ "AI Generated Content:"
51940
+ ] }),
51941
+ /* @__PURE__ */ jsx("div", { className: "flex-1 p-4 bg-gradient-to-br from-purple-50 to-blue-50 dark:from-purple-950/50 dark:to-blue-950/50 rounded-md border-2 border-purple-200 dark:border-purple-800 overflow-auto", children: /* @__PURE__ */ jsx("div", { className: "prose prose-sm max-w-none dark:prose-invert", children: /* @__PURE__ */ jsx("div", { className: "whitespace-pre-wrap text-sm leading-relaxed", children: previewContent }) }) })
51942
+ ] })
51943
+ ] }),
51944
+ /* @__PURE__ */ jsxs("div", { className: "flex-shrink-0 flex justify-between items-center pt-4 border-t", children: [
51945
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
51946
+ /* @__PURE__ */ jsxs(
51947
+ MoonUIButtonPro,
51948
+ {
51949
+ variant: "outline",
51950
+ size: "sm",
51951
+ onClick: () => {
51952
+ navigator.clipboard.writeText(previewContent);
51953
+ toast({
51954
+ title: "Copied to clipboard",
51955
+ description: "The AI-generated content has been copied."
51956
+ });
51957
+ },
51958
+ children: [
51959
+ /* @__PURE__ */ jsx(Copy, { className: "w-4 h-4 mr-2" }),
51960
+ "Copy"
51961
+ ]
51962
+ }
51963
+ ),
51964
+ /* @__PURE__ */ jsxs(
51965
+ MoonUIButtonPro,
51966
+ {
51967
+ variant: "outline",
51968
+ size: "sm",
51969
+ onClick: () => {
51970
+ setIsAiPreviewOpen(false);
51971
+ handleAIAction(previewAction);
51972
+ },
51973
+ children: [
51974
+ /* @__PURE__ */ jsx(RefreshCw, { className: "w-4 h-4 mr-2" }),
51975
+ "Regenerate"
51976
+ ]
51977
+ }
51978
+ )
51979
+ ] }),
51980
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
51981
+ /* @__PURE__ */ jsx(
51982
+ MoonUIButtonPro,
51983
+ {
51984
+ variant: "outline",
51985
+ onClick: () => setIsAiPreviewOpen(false),
51986
+ children: "Cancel"
51987
+ }
51988
+ ),
51989
+ /* @__PURE__ */ jsxs(
51990
+ MoonUIButtonPro,
51991
+ {
51992
+ onClick: () => {
51993
+ setIsAiPreviewOpen(false);
51994
+ const replaceSelection = previewOriginalText !== editor?.getText();
51995
+ applyAIContentToEditor(previewContent, replaceSelection);
51996
+ },
51997
+ className: "bg-gradient-to-r from-purple-600 to-blue-600 hover:from-purple-700 hover:to-blue-700",
51998
+ children: [
51999
+ /* @__PURE__ */ jsx(Check, { className: "w-4 h-4 mr-2" }),
52000
+ "Apply to Editor"
52001
+ ]
52002
+ }
52003
+ )
52004
+ ] })
52005
+ ] })
52006
+ ] }) }),
51890
52007
  /* @__PURE__ */ jsx(
51891
52008
  "div",
51892
52009
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moontra/moonui-pro",
3
- "version": "2.8.11",
3
+ "version": "2.8.12",
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",
@@ -74,7 +74,8 @@ import {
74
74
  GraduationCap,
75
75
  Zap,
76
76
  Lightbulb,
77
- X
77
+ X,
78
+ Copy
78
79
  } from 'lucide-react';
79
80
  import { cn } from '../../lib/utils';
80
81
  import { Button } from '../ui/button';
@@ -442,6 +443,12 @@ export function RichTextEditor({
442
443
  return 'en';
443
444
  });
444
445
 
446
+ // AI Preview Modal states
447
+ const [isAiPreviewOpen, setIsAiPreviewOpen] = useState(false);
448
+ const [previewContent, setPreviewContent] = useState('');
449
+ const [previewAction, setPreviewAction] = useState('');
450
+ const [previewOriginalText, setPreviewOriginalText] = useState('');
451
+
445
452
  // Clean up typewriter effect on unmount
446
453
  useEffect(() => {
447
454
  return () => {
@@ -466,6 +473,61 @@ export function RichTextEditor({
466
473
  description: "The AI response was interrupted.",
467
474
  });
468
475
  };
476
+
477
+ // Check if action should use modal preview
478
+ const shouldUseModal = (action: string): boolean => {
479
+ const modalActions = ['expand', 'summarize', 'ideas', 'continue'];
480
+ return modalActions.includes(action);
481
+ };
482
+
483
+ // Apply AI content to editor with typewriter effect
484
+ const applyAIContentToEditor = (content: string, replaceSelection: boolean = true) => {
485
+ if (!editor) return;
486
+
487
+ // Start typewriter effect
488
+ setIsTyping(true);
489
+ isTypingRef.current = true;
490
+ setTypingText('');
491
+
492
+ if (replaceSelection) {
493
+ const selection = editor.state.selection;
494
+ const selectedText = editor.state.doc.textBetween(selection.from, selection.to, ' ');
495
+ if (selectedText) {
496
+ editor.chain().focus().deleteSelection().run();
497
+ }
498
+ }
499
+
500
+ let currentIndex = 0;
501
+ const typeSpeed = 30; // ms per character
502
+
503
+ const typeNextChar = () => {
504
+ if (currentIndex < content.length && isTypingRef.current) {
505
+ const nextChar = content[currentIndex];
506
+ setTypingText(prev => prev + nextChar);
507
+ editor.chain().focus().insertContent(nextChar).run();
508
+ currentIndex++;
509
+ setRemainingText(content.substring(currentIndex));
510
+ typingIntervalRef.current = setTimeout(typeNextChar, typeSpeed);
511
+ } else {
512
+ // Typing complete or stopped
513
+ setIsTyping(false);
514
+ isTypingRef.current = false;
515
+ setTypingText('');
516
+ setRemainingText('');
517
+
518
+ if (currentIndex >= content.length) {
519
+ // Success toast only if completed
520
+ toast({
521
+ title: "AI action completed",
522
+ description: "Your text has been updated successfully.",
523
+ });
524
+ }
525
+ }
526
+ };
527
+
528
+ // Start typing
529
+ typeNextChar();
530
+ };
469
531
 
470
532
  // Slash commands tanımları
471
533
  const slashCommands: SlashCommand[] = [
@@ -606,10 +668,22 @@ export function RichTextEditor({
606
668
  const selectedText = editor.state.doc.textBetween(from, to, ' ');
607
669
 
608
670
  setIsProcessing(true);
671
+ setCurrentAction(command.id);
672
+
609
673
  try {
610
674
  const response = await command.action(selectedText || editor.getText());
611
675
  if (response.text) {
612
- editor.chain().focus().insertContent(response.text).run();
676
+ // Check if this command should use modal preview
677
+ if (shouldUseModal(command.id)) {
678
+ // Open preview modal
679
+ setPreviewContent(response.text);
680
+ setPreviewAction(command.id);
681
+ setPreviewOriginalText(selectedText || editor.getText());
682
+ setIsAiPreviewOpen(true);
683
+ } else {
684
+ // Apply directly with typewriter effect
685
+ applyAIContentToEditor(response.text, !!selectedText);
686
+ }
613
687
  } else if (response.error) {
614
688
  toast({
615
689
  title: "AI Error",
@@ -764,45 +838,17 @@ export function RichTextEditor({
764
838
  processingToast.dismiss();
765
839
 
766
840
  if (result) {
767
- // Start typewriter effect
768
- setIsTyping(true);
769
- isTypingRef.current = true;
770
- setTypingText('');
771
-
772
- if (selectedText) {
773
- editor.chain().focus().deleteSelection().run();
841
+ // Check if this action should use modal preview
842
+ if (shouldUseModal(action)) {
843
+ // Open preview modal instead of directly applying
844
+ setPreviewContent(result);
845
+ setPreviewAction(action);
846
+ setPreviewOriginalText(selectedText || editor.getText());
847
+ setIsAiPreviewOpen(true);
848
+ } else {
849
+ // Apply directly with typewriter effect (for rewrite, fix grammar, tone changes, etc.)
850
+ applyAIContentToEditor(result, !!selectedText);
774
851
  }
775
-
776
- let currentIndex = 0;
777
- const typeSpeed = 30; // ms per character
778
-
779
- const typeNextChar = () => {
780
- if (currentIndex < result.length && isTypingRef.current) {
781
- const nextChar = result[currentIndex];
782
- setTypingText(prev => prev + nextChar);
783
- editor.chain().focus().insertContent(nextChar).run();
784
- currentIndex++;
785
- setRemainingText(result.substring(currentIndex));
786
- typingIntervalRef.current = setTimeout(typeNextChar, typeSpeed);
787
- } else {
788
- // Typing complete or stopped
789
- setIsTyping(false);
790
- isTypingRef.current = false;
791
- setTypingText('');
792
- setRemainingText('');
793
-
794
- if (currentIndex >= result.length) {
795
- // Success toast only if completed
796
- toast({
797
- title: "AI action completed",
798
- description: "Your text has been updated successfully.",
799
- });
800
- }
801
- }
802
- };
803
-
804
- // Start typing
805
- typeNextChar();
806
852
  }
807
853
  };
808
854
 
@@ -1970,6 +2016,99 @@ export function RichTextEditor({
1970
2016
  </DialogContent>
1971
2017
  </Dialog>
1972
2018
 
2019
+ {/* AI Preview Modal */}
2020
+ <Dialog open={isAiPreviewOpen} onOpenChange={setIsAiPreviewOpen}>
2021
+ <DialogContent className="max-w-4xl max-h-[80vh] overflow-hidden flex flex-col">
2022
+ <DialogHeader>
2023
+ <DialogTitle className="flex items-center gap-2">
2024
+ <Wand2 className="w-5 h-5 text-purple-600 dark:text-purple-400" />
2025
+ AI Preview - {getActionDescription(previewAction).replace('...', '')}
2026
+ </DialogTitle>
2027
+ <DialogDescription>
2028
+ Review the AI-generated content before applying it to your editor.
2029
+ </DialogDescription>
2030
+ </DialogHeader>
2031
+
2032
+ <div className="flex-1 overflow-hidden flex flex-col gap-4">
2033
+ {/* Original Text Section */}
2034
+ <div className="flex-shrink-0">
2035
+ <div className="text-sm font-medium text-muted-foreground mb-2">Original Text:</div>
2036
+ <div className="p-3 bg-muted/50 rounded-md border max-h-32 overflow-auto">
2037
+ <p className="text-sm whitespace-pre-wrap">{previewOriginalText}</p>
2038
+ </div>
2039
+ </div>
2040
+
2041
+ {/* AI Generated Content */}
2042
+ <div className="flex-1 overflow-hidden flex flex-col">
2043
+ <div className="text-sm font-medium text-muted-foreground mb-2 flex items-center gap-2">
2044
+ <Sparkles className="w-4 h-4" />
2045
+ AI Generated Content:
2046
+ </div>
2047
+ <div className="flex-1 p-4 bg-gradient-to-br from-purple-50 to-blue-50 dark:from-purple-950/50 dark:to-blue-950/50 rounded-md border-2 border-purple-200 dark:border-purple-800 overflow-auto">
2048
+ <div className="prose prose-sm max-w-none dark:prose-invert">
2049
+ <div className="whitespace-pre-wrap text-sm leading-relaxed">
2050
+ {previewContent}
2051
+ </div>
2052
+ </div>
2053
+ </div>
2054
+ </div>
2055
+ </div>
2056
+
2057
+ {/* Action Buttons */}
2058
+ <div className="flex-shrink-0 flex justify-between items-center pt-4 border-t">
2059
+ <div className="flex gap-2">
2060
+ <Button
2061
+ variant="outline"
2062
+ size="sm"
2063
+ onClick={() => {
2064
+ navigator.clipboard.writeText(previewContent);
2065
+ toast({
2066
+ title: "Copied to clipboard",
2067
+ description: "The AI-generated content has been copied.",
2068
+ });
2069
+ }}
2070
+ >
2071
+ <Copy className="w-4 h-4 mr-2" />
2072
+ Copy
2073
+ </Button>
2074
+ <Button
2075
+ variant="outline"
2076
+ size="sm"
2077
+ onClick={() => {
2078
+ // Regenerate content
2079
+ setIsAiPreviewOpen(false);
2080
+ handleAIAction(previewAction);
2081
+ }}
2082
+ >
2083
+ <RefreshCw className="w-4 h-4 mr-2" />
2084
+ Regenerate
2085
+ </Button>
2086
+ </div>
2087
+
2088
+ <div className="flex gap-2">
2089
+ <Button
2090
+ variant="outline"
2091
+ onClick={() => setIsAiPreviewOpen(false)}
2092
+ >
2093
+ Cancel
2094
+ </Button>
2095
+ <Button
2096
+ onClick={() => {
2097
+ setIsAiPreviewOpen(false);
2098
+ // Apply with typewriter effect
2099
+ const replaceSelection = previewOriginalText !== editor?.getText();
2100
+ applyAIContentToEditor(previewContent, replaceSelection);
2101
+ }}
2102
+ className="bg-gradient-to-r from-purple-600 to-blue-600 hover:from-purple-700 hover:to-blue-700"
2103
+ >
2104
+ <Check className="w-4 h-4 mr-2" />
2105
+ Apply to Editor
2106
+ </Button>
2107
+ </div>
2108
+ </div>
2109
+ </DialogContent>
2110
+ </Dialog>
2111
+
1973
2112
  {/* Editor */}
1974
2113
  <div
1975
2114
  className="overflow-auto relative"