@moontra/moonui-pro 2.8.9 → 2.8.11

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
@@ -4044,7 +4044,7 @@ var me = t.forwardRef((r2, o) => {
4044
4044
  var e;
4045
4045
  return Array.from(((e = I.current) == null ? void 0 : e.querySelectorAll(ce)) || []);
4046
4046
  }
4047
- function X19(e) {
4047
+ function X20(e) {
4048
4048
  let s = V()[e];
4049
4049
  s && E.setState("value", s.getAttribute(T));
4050
4050
  }
@@ -4059,10 +4059,10 @@ var me = t.forwardRef((r2, o) => {
4059
4059
  s = e > 0 ? we(s, N) : De(s, N), i = s == null ? void 0 : s.querySelector(ce);
4060
4060
  i ? E.setState("value", i.getAttribute(T)) : Q(e);
4061
4061
  }
4062
- let oe = () => X19(V().length - 1), ie3 = (e) => {
4062
+ let oe = () => X20(V().length - 1), ie3 = (e) => {
4063
4063
  e.preventDefault(), e.metaKey ? oe() : e.altKey ? re(1) : Q(1);
4064
4064
  }, se = (e) => {
4065
- e.preventDefault(), e.metaKey ? X19(0) : e.altKey ? re(-1) : Q(-1);
4065
+ e.preventDefault(), e.metaKey ? X20(0) : e.altKey ? re(-1) : Q(-1);
4066
4066
  };
4067
4067
  return t.createElement(Primitive2.div, { ref: o, tabIndex: -1, ...O, "cmdk-root": "", onKeyDown: (e) => {
4068
4068
  var s;
@@ -4089,7 +4089,7 @@ var me = t.forwardRef((r2, o) => {
4089
4089
  break;
4090
4090
  }
4091
4091
  case "Home": {
4092
- e.preventDefault(), X19(0);
4092
+ e.preventDefault(), X20(0);
4093
4093
  break;
4094
4094
  }
4095
4095
  case "End": {
@@ -50283,53 +50283,78 @@ var EditorColorPicker = ({
50283
50283
  onColorSelect,
50284
50284
  currentColor = "#000000"
50285
50285
  }) => {
50286
- return /* @__PURE__ */ jsx(
50287
- MoonUIColorPickerPro,
50288
- {
50289
- value: currentColor,
50290
- onChange: onColorSelect,
50291
- showInput: true,
50292
- showPresets: true,
50293
- size: "sm",
50294
- presets: [
50295
- "#000000",
50296
- "#ffffff",
50297
- "#ff0000",
50298
- "#00ff00",
50299
- "#0000ff",
50300
- "#ffff00",
50301
- "#ff00ff",
50302
- "#00ffff",
50303
- "#ff8800",
50304
- "#8800ff",
50305
- "#88ff00",
50306
- "#0088ff",
50307
- "#ff0088",
50308
- "#00ff88",
50309
- "#888888",
50310
- "#f87171",
50311
- "#fb923c",
50312
- "#fbbf24",
50313
- "#facc15",
50314
- "#a3e635",
50315
- "#4ade80",
50316
- "#34d399",
50317
- "#2dd4bf",
50318
- "#22d3ee",
50319
- "#38bdf8",
50320
- "#60a5fa",
50321
- "#818cf8",
50322
- "#a78bfa",
50323
- "#c084fc",
50324
- "#e879f9",
50325
- "#f472b6",
50326
- "#fb7185",
50327
- "#f43f5e",
50328
- "#ef4444",
50329
- "#dc2626"
50330
- ]
50331
- }
50332
- );
50286
+ const [showAdvanced, setShowAdvanced] = t__default.useState(false);
50287
+ const quickColors = [
50288
+ "#000000",
50289
+ // Black
50290
+ "#ef4444",
50291
+ // Red
50292
+ "#f59e0b",
50293
+ // Orange
50294
+ "#10b981",
50295
+ // Green
50296
+ "#3b82f6",
50297
+ // Blue
50298
+ "#8b5cf6",
50299
+ // Purple
50300
+ "#6b7280",
50301
+ // Gray
50302
+ "#ec4899"
50303
+ // Pink
50304
+ ];
50305
+ if (showAdvanced) {
50306
+ return /* @__PURE__ */ jsxs("div", { className: "p-4 w-64", children: [
50307
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-3", children: [
50308
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Choose Color" }),
50309
+ /* @__PURE__ */ jsx(
50310
+ MoonUIButtonPro,
50311
+ {
50312
+ size: "sm",
50313
+ variant: "ghost",
50314
+ onClick: () => setShowAdvanced(false),
50315
+ children: "Back"
50316
+ }
50317
+ )
50318
+ ] }),
50319
+ /* @__PURE__ */ jsx(
50320
+ MoonUIColorPickerPro,
50321
+ {
50322
+ value: currentColor,
50323
+ onChange: (color) => {
50324
+ onColorSelect(color);
50325
+ setShowAdvanced(false);
50326
+ },
50327
+ showInput: true,
50328
+ showPresets: false,
50329
+ size: "sm"
50330
+ }
50331
+ )
50332
+ ] });
50333
+ }
50334
+ return /* @__PURE__ */ jsx("div", { className: "p-3 w-36", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-2", children: [
50335
+ quickColors.map((color) => /* @__PURE__ */ jsx(
50336
+ "button",
50337
+ {
50338
+ onClick: () => onColorSelect(color),
50339
+ className: cn(
50340
+ "w-10 h-10 rounded-md border-2 hover:scale-110 transition-transform",
50341
+ currentColor === color ? "border-primary" : "border-transparent"
50342
+ ),
50343
+ style: { backgroundColor: color },
50344
+ title: color
50345
+ },
50346
+ color
50347
+ )),
50348
+ /* @__PURE__ */ jsx(
50349
+ "button",
50350
+ {
50351
+ onClick: () => setShowAdvanced(true),
50352
+ className: "w-10 h-10 rounded-md border-2 border-dashed border-muted-foreground/50 hover:border-primary transition-colors flex items-center justify-center",
50353
+ title: "More colors",
50354
+ children: /* @__PURE__ */ jsx(Palette, { className: "w-5 h-5 text-muted-foreground" })
50355
+ }
50356
+ )
50357
+ ] }) });
50333
50358
  };
50334
50359
  var ToolbarButton = ({
50335
50360
  active,
@@ -50379,13 +50404,7 @@ function RichTextEditor({
50379
50404
  color: true,
50380
50405
  ai: true
50381
50406
  },
50382
- aiConfig = {
50383
- provider: "openai",
50384
- apiKey: "",
50385
- model: "gpt-3.5-turbo",
50386
- temperature: 0.7,
50387
- maxTokens: 1e3
50388
- },
50407
+ aiConfig = {},
50389
50408
  persistAISettings = true
50390
50409
  }) {
50391
50410
  const { hasProAccess, isLoading } = useSubscription();
@@ -50406,30 +50425,42 @@ function RichTextEditor({
50406
50425
  if (persistAISettings && typeof window !== "undefined") {
50407
50426
  try {
50408
50427
  const stored = localStorage.getItem("moonui-ai-settings");
50428
+ console.log("[RichTextEditor] Loading AI settings from localStorage:", stored);
50409
50429
  if (stored) {
50410
50430
  const parsed = JSON.parse(stored);
50411
- return {
50412
- provider: aiConfig.provider || parsed.provider || "openai",
50413
- apiKey: aiConfig.apiKey || parsed.apiKey || "",
50414
- model: aiConfig.model || parsed.model || "gpt-3.5-turbo",
50415
- temperature: aiConfig.temperature ?? parsed.temperature ?? 0.7,
50416
- maxTokens: aiConfig.maxTokens ?? parsed.maxTokens ?? 1e3
50431
+ console.log("[RichTextEditor] Parsed AI settings:", parsed);
50432
+ const settings = {
50433
+ provider: aiConfig.provider !== void 0 ? aiConfig.provider : parsed.provider || "openai",
50434
+ apiKey: aiConfig.apiKey !== void 0 ? aiConfig.apiKey : parsed.apiKey || "",
50435
+ model: aiConfig.model !== void 0 ? aiConfig.model : parsed.model || "gpt-3.5-turbo",
50436
+ temperature: aiConfig.temperature !== void 0 ? aiConfig.temperature : parsed.temperature ?? 0.7,
50437
+ maxTokens: aiConfig.maxTokens !== void 0 ? aiConfig.maxTokens : parsed.maxTokens ?? 1e3
50417
50438
  };
50439
+ console.log("[RichTextEditor] Final settings with props override:", settings);
50440
+ return settings;
50418
50441
  }
50419
50442
  } catch (e) {
50420
50443
  console.error("Failed to load AI settings from localStorage:", e);
50421
50444
  }
50422
50445
  }
50423
- return {
50446
+ const defaultSettings = {
50424
50447
  provider: aiConfig.provider || "openai",
50425
50448
  apiKey: aiConfig.apiKey || "",
50426
50449
  model: aiConfig.model || "gpt-3.5-turbo",
50427
50450
  temperature: aiConfig.temperature ?? 0.7,
50428
50451
  maxTokens: aiConfig.maxTokens ?? 1e3
50429
50452
  };
50453
+ console.log("[RichTextEditor] Using default settings:", defaultSettings);
50454
+ return defaultSettings;
50430
50455
  });
50431
50456
  const [isAiSettingsOpen, setIsAiSettingsOpen] = useState(false);
50432
50457
  const [isProcessing, setIsProcessing] = useState(false);
50458
+ const [typingText, setTypingText] = useState("");
50459
+ const [isTyping, setIsTyping] = useState(false);
50460
+ const typingIntervalRef = useRef(null);
50461
+ const [currentAction, setCurrentAction] = useState("");
50462
+ const [remainingText, setRemainingText] = useState("");
50463
+ const isTypingRef = useRef(false);
50433
50464
  const [isSourceView, setIsSourceView] = useState(false);
50434
50465
  const [sourceContent, setSourceContent] = useState("");
50435
50466
  const [currentTextColor, setCurrentTextColor] = useState("#000000");
@@ -50440,6 +50471,27 @@ function RichTextEditor({
50440
50471
  }
50441
50472
  return "en";
50442
50473
  });
50474
+ useEffect(() => {
50475
+ return () => {
50476
+ if (typingIntervalRef.current) {
50477
+ clearTimeout(typingIntervalRef.current);
50478
+ }
50479
+ };
50480
+ }, []);
50481
+ const stopTyping = () => {
50482
+ if (typingIntervalRef.current) {
50483
+ clearTimeout(typingIntervalRef.current);
50484
+ typingIntervalRef.current = null;
50485
+ }
50486
+ setIsTyping(false);
50487
+ isTypingRef.current = false;
50488
+ setTypingText("");
50489
+ setRemainingText("");
50490
+ toast({
50491
+ title: "AI typing stopped",
50492
+ description: "The AI response was interrupted."
50493
+ });
50494
+ };
50443
50495
  const slashCommands = [
50444
50496
  {
50445
50497
  id: "rewrite",
@@ -50710,6 +50762,7 @@ function RichTextEditor({
50710
50762
  });
50711
50763
  return;
50712
50764
  }
50765
+ setCurrentAction(action);
50713
50766
  const processingToast = toast({
50714
50767
  title: "Processing with AI...",
50715
50768
  description: getActionDescription(action, targetLanguage),
@@ -50719,15 +50772,36 @@ function RichTextEditor({
50719
50772
  const result = await callAI(action, selectedText || editor.getText(), targetLanguage);
50720
50773
  processingToast.dismiss();
50721
50774
  if (result) {
50775
+ setIsTyping(true);
50776
+ isTypingRef.current = true;
50777
+ setTypingText("");
50722
50778
  if (selectedText) {
50723
- editor.chain().focus().deleteSelection().insertContent(result).run();
50724
- } else {
50725
- editor.chain().focus().insertContent(result).run();
50726
- }
50727
- toast({
50728
- title: "AI action completed",
50729
- description: "Your text has been updated successfully."
50730
- });
50779
+ editor.chain().focus().deleteSelection().run();
50780
+ }
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();
50731
50805
  }
50732
50806
  };
50733
50807
  const getActionDescription = (action, targetLanguage) => {
@@ -51267,13 +51341,24 @@ function RichTextEditor({
51267
51341
  variant: "ghost",
51268
51342
  size: "sm",
51269
51343
  className: "h-8 px-3 bg-purple-100 hover:bg-purple-200 dark:bg-purple-900 dark:hover:bg-purple-800 transition-colors",
51270
- disabled: isProcessing,
51344
+ disabled: isProcessing || isTyping,
51271
51345
  children: [
51272
- isProcessing ? /* @__PURE__ */ jsx(RefreshCw, { className: "w-4 h-4 mr-1 animate-spin" }) : /* @__PURE__ */ jsx(Wand2, { className: "w-4 h-4 mr-1" }),
51273
- "AI Tools"
51346
+ isProcessing || isTyping ? /* @__PURE__ */ jsx(RefreshCw, { className: "w-4 h-4 mr-1 animate-spin" }) : /* @__PURE__ */ jsx(Wand2, { className: "w-4 h-4 mr-1" }),
51347
+ isTyping ? "Typing..." : "AI Tools"
51274
51348
  ]
51275
51349
  }
51276
51350
  ) }),
51351
+ isTyping && /* @__PURE__ */ jsx(
51352
+ MoonUIButtonPro,
51353
+ {
51354
+ variant: "destructive",
51355
+ size: "sm",
51356
+ className: "h-8 px-2",
51357
+ onClick: stopTyping,
51358
+ title: "Stop AI typing",
51359
+ children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4" })
51360
+ }
51361
+ ),
51277
51362
  /* @__PURE__ */ jsxs(MoonUIDropdownMenuContentPro, { className: "w-64", children: [
51278
51363
  /* @__PURE__ */ jsxs("div", { className: "px-2 py-1.5 text-xs font-semibold text-muted-foreground flex items-center gap-2", children: [
51279
51364
  /* @__PURE__ */ jsx(Wand2, { className: "w-3 h-3" }),
@@ -51283,7 +51368,7 @@ function RichTextEditor({
51283
51368
  MoonUIDropdownMenuItemPro,
51284
51369
  {
51285
51370
  onClick: () => handleAIAction("rewrite"),
51286
- disabled: isProcessing,
51371
+ disabled: isProcessing || isTyping,
51287
51372
  children: [
51288
51373
  /* @__PURE__ */ jsx(RefreshCw, { className: "w-4 h-4 mr-2" }),
51289
51374
  "Rewrite Selection",
@@ -51295,7 +51380,7 @@ function RichTextEditor({
51295
51380
  MoonUIDropdownMenuItemPro,
51296
51381
  {
51297
51382
  onClick: () => handleAIAction("improve"),
51298
- disabled: isProcessing,
51383
+ disabled: isProcessing || isTyping,
51299
51384
  children: [
51300
51385
  /* @__PURE__ */ jsx(Sparkles, { className: "w-4 h-4 mr-2" }),
51301
51386
  "Improve Writing"
@@ -51306,7 +51391,7 @@ function RichTextEditor({
51306
51391
  MoonUIDropdownMenuItemPro,
51307
51392
  {
51308
51393
  onClick: () => handleAIAction("expand"),
51309
- disabled: isProcessing,
51394
+ disabled: isProcessing || isTyping,
51310
51395
  children: [
51311
51396
  /* @__PURE__ */ jsx(Maximize, { className: "w-4 h-4 mr-2" }),
51312
51397
  "Expand Text"
@@ -51317,7 +51402,7 @@ function RichTextEditor({
51317
51402
  MoonUIDropdownMenuItemPro,
51318
51403
  {
51319
51404
  onClick: () => handleAIAction("summarize"),
51320
- disabled: isProcessing,
51405
+ disabled: isProcessing || isTyping,
51321
51406
  children: [
51322
51407
  /* @__PURE__ */ jsx(FileText, { className: "w-4 h-4 mr-2" }),
51323
51408
  "Summarize"
@@ -51328,7 +51413,7 @@ function RichTextEditor({
51328
51413
  MoonUIDropdownMenuItemPro,
51329
51414
  {
51330
51415
  onClick: () => handleAIAction("continue"),
51331
- disabled: isProcessing,
51416
+ disabled: isProcessing || isTyping,
51332
51417
  children: [
51333
51418
  /* @__PURE__ */ jsx(Plus, { className: "w-4 h-4 mr-2" }),
51334
51419
  "Continue Writing"
@@ -51344,7 +51429,7 @@ function RichTextEditor({
51344
51429
  MoonUIDropdownMenuItemPro,
51345
51430
  {
51346
51431
  onClick: () => handleAIAction("tone_professional"),
51347
- disabled: isProcessing,
51432
+ disabled: isProcessing || isTyping,
51348
51433
  children: [
51349
51434
  /* @__PURE__ */ jsx(Briefcase, { className: "w-4 h-4 mr-2" }),
51350
51435
  "Make Professional"
@@ -51355,7 +51440,7 @@ function RichTextEditor({
51355
51440
  MoonUIDropdownMenuItemPro,
51356
51441
  {
51357
51442
  onClick: () => handleAIAction("tone_casual"),
51358
- disabled: isProcessing,
51443
+ disabled: isProcessing || isTyping,
51359
51444
  children: [
51360
51445
  /* @__PURE__ */ jsx(MessageSquare, { className: "w-4 h-4 mr-2" }),
51361
51446
  "Make Casual"
@@ -51366,7 +51451,7 @@ function RichTextEditor({
51366
51451
  MoonUIDropdownMenuItemPro,
51367
51452
  {
51368
51453
  onClick: () => handleAIAction("tone_friendly"),
51369
- disabled: isProcessing,
51454
+ disabled: isProcessing || isTyping,
51370
51455
  children: [
51371
51456
  /* @__PURE__ */ jsx(Heart, { className: "w-4 h-4 mr-2" }),
51372
51457
  "Make Friendly"
@@ -51377,7 +51462,7 @@ function RichTextEditor({
51377
51462
  MoonUIDropdownMenuItemPro,
51378
51463
  {
51379
51464
  onClick: () => handleAIAction("tone_formal"),
51380
- disabled: isProcessing,
51465
+ disabled: isProcessing || isTyping,
51381
51466
  children: [
51382
51467
  /* @__PURE__ */ jsx(GraduationCap, { className: "w-4 h-4 mr-2" }),
51383
51468
  "Make Formal"
@@ -51393,7 +51478,7 @@ function RichTextEditor({
51393
51478
  MoonUIDropdownMenuItemPro,
51394
51479
  {
51395
51480
  onClick: () => handleAIAction("fix"),
51396
- disabled: isProcessing,
51481
+ disabled: isProcessing || isTyping,
51397
51482
  children: [
51398
51483
  /* @__PURE__ */ jsx(Check, { className: "w-4 h-4 mr-2" }),
51399
51484
  "Fix Grammar & Spelling",
@@ -51402,7 +51487,7 @@ function RichTextEditor({
51402
51487
  }
51403
51488
  ),
51404
51489
  /* @__PURE__ */ jsxs(MoonUIDropdownMenuSubPro, { children: [
51405
- /* @__PURE__ */ jsxs(MoonUIDropdownMenuSubTriggerPro, { disabled: isProcessing, children: [
51490
+ /* @__PURE__ */ jsxs(MoonUIDropdownMenuSubTriggerPro, { disabled: isProcessing || isTyping, children: [
51406
51491
  /* @__PURE__ */ jsx(Languages, { className: "w-4 h-4 mr-2" }),
51407
51492
  "Translate",
51408
51493
  lastTranslateLanguage && /* @__PURE__ */ jsx("span", { className: "ml-auto text-xs text-muted-foreground", children: SUPPORTED_LANGUAGES.find((l) => l.code === lastTranslateLanguage)?.nativeName || "English" })
@@ -51415,7 +51500,7 @@ function RichTextEditor({
51415
51500
  localStorage.setItem("moonui-last-translate-language", language.code);
51416
51501
  handleAIAction("translate", language.name);
51417
51502
  },
51418
- disabled: isProcessing,
51503
+ disabled: isProcessing || isTyping,
51419
51504
  children: [
51420
51505
  /* @__PURE__ */ jsx("span", { className: "text-sm", children: language.nativeName }),
51421
51506
  /* @__PURE__ */ jsx("span", { className: "ml-auto text-xs text-muted-foreground", children: language.name }),
@@ -51429,7 +51514,7 @@ function RichTextEditor({
51429
51514
  MoonUIDropdownMenuItemPro,
51430
51515
  {
51431
51516
  onClick: () => handleAIAction("ideas"),
51432
- disabled: isProcessing,
51517
+ disabled: isProcessing || isTyping,
51433
51518
  children: [
51434
51519
  /* @__PURE__ */ jsx(Lightbulb, { className: "w-4 h-4 mr-2" }),
51435
51520
  "Generate Ideas"
@@ -51805,7 +51890,7 @@ function RichTextEditor({
51805
51890
  /* @__PURE__ */ jsx(
51806
51891
  "div",
51807
51892
  {
51808
- className: "overflow-auto",
51893
+ className: "overflow-auto relative",
51809
51894
  style: { height: typeof height === "number" ? `${height}px` : height },
51810
51895
  children: isSourceView ? /* @__PURE__ */ jsx(
51811
51896
  "textarea",
@@ -51815,7 +51900,57 @@ function RichTextEditor({
51815
51900
  className: "w-full h-full p-4 font-mono text-sm resize-none focus:outline-none bg-gray-50 dark:bg-gray-900",
51816
51901
  placeholder: "HTML source code..."
51817
51902
  }
51818
- ) : /* @__PURE__ */ jsx(EditorContent, { editor })
51903
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
51904
+ /* @__PURE__ */ jsx(EditorContent, { editor }),
51905
+ isProcessing && /* @__PURE__ */ jsx(
51906
+ motion.div,
51907
+ {
51908
+ initial: { opacity: 0 },
51909
+ animate: { opacity: 1 },
51910
+ exit: { opacity: 0 },
51911
+ className: "absolute inset-0 bg-background/80 backdrop-blur-sm flex items-center justify-center z-50",
51912
+ children: /* @__PURE__ */ jsx("div", { className: "bg-card border rounded-lg p-6 shadow-lg max-w-sm w-full mx-4", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center space-y-4", children: [
51913
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
51914
+ /* @__PURE__ */ jsx("div", { className: "w-16 h-16 border-4 border-purple-200 dark:border-purple-800 rounded-full animate-pulse" }),
51915
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx(Wand2, { className: "w-8 h-8 text-purple-600 dark:text-purple-400 animate-bounce" }) })
51916
+ ] }),
51917
+ /* @__PURE__ */ jsxs("div", { className: "text-center space-y-2", children: [
51918
+ /* @__PURE__ */ jsx("h3", { className: "font-semibold text-lg", children: "AI is thinking..." }),
51919
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: getActionDescription(
51920
+ currentAction,
51921
+ lastTranslateLanguage
51922
+ ) })
51923
+ ] }),
51924
+ /* @__PURE__ */ jsxs("div", { className: "flex space-x-1", children: [
51925
+ /* @__PURE__ */ jsx(
51926
+ motion.div,
51927
+ {
51928
+ animate: { scale: [1, 1.5, 1] },
51929
+ transition: { duration: 0.6, repeat: Infinity, delay: 0 },
51930
+ className: "w-2 h-2 bg-purple-600 dark:bg-purple-400 rounded-full"
51931
+ }
51932
+ ),
51933
+ /* @__PURE__ */ jsx(
51934
+ motion.div,
51935
+ {
51936
+ animate: { scale: [1, 1.5, 1] },
51937
+ transition: { duration: 0.6, repeat: Infinity, delay: 0.2 },
51938
+ className: "w-2 h-2 bg-purple-600 dark:bg-purple-400 rounded-full"
51939
+ }
51940
+ ),
51941
+ /* @__PURE__ */ jsx(
51942
+ motion.div,
51943
+ {
51944
+ animate: { scale: [1, 1.5, 1] },
51945
+ transition: { duration: 0.6, repeat: Infinity, delay: 0.4 },
51946
+ className: "w-2 h-2 bg-purple-600 dark:bg-purple-400 rounded-full"
51947
+ }
51948
+ )
51949
+ ] })
51950
+ ] }) })
51951
+ }
51952
+ )
51953
+ ] })
51819
51954
  }
51820
51955
  )
51821
51956
  ] });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moontra/moonui-pro",
3
- "version": "2.8.9",
3
+ "version": "2.8.11",
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",
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import React, { useState } from 'react';
3
+ import React, { useState, useRef, useEffect } from 'react';
4
4
  import { useEditor, EditorContent } from '@tiptap/react';
5
5
  import StarterKit from '@tiptap/starter-kit';
6
6
  import Placeholder from '@tiptap/extension-placeholder';
@@ -73,7 +73,8 @@ import {
73
73
  Heart,
74
74
  GraduationCap,
75
75
  Zap,
76
- Lightbulb
76
+ Lightbulb,
77
+ X
77
78
  } from 'lucide-react';
78
79
  import { cn } from '../../lib/utils';
79
80
  import { Button } from '../ui/button';
@@ -218,23 +219,71 @@ const EditorColorPicker = ({
218
219
  onColorSelect: (color: string) => void;
219
220
  currentColor?: string;
220
221
  }) => {
222
+ const [showAdvanced, setShowAdvanced] = React.useState(false);
223
+
224
+ // Quick colors - 3x3 grid of 8 colors + 1 advanced button
225
+ const quickColors = [
226
+ '#000000', // Black
227
+ '#ef4444', // Red
228
+ '#f59e0b', // Orange
229
+ '#10b981', // Green
230
+ '#3b82f6', // Blue
231
+ '#8b5cf6', // Purple
232
+ '#6b7280', // Gray
233
+ '#ec4899', // Pink
234
+ ];
235
+
236
+ if (showAdvanced) {
237
+ return (
238
+ <div className="p-4 w-64">
239
+ <div className="flex items-center justify-between mb-3">
240
+ <span className="text-sm font-medium">Choose Color</span>
241
+ <Button
242
+ size="sm"
243
+ variant="ghost"
244
+ onClick={() => setShowAdvanced(false)}
245
+ >
246
+ Back
247
+ </Button>
248
+ </div>
249
+ <ColorPicker
250
+ value={currentColor}
251
+ onChange={(color) => {
252
+ onColorSelect(color);
253
+ setShowAdvanced(false);
254
+ }}
255
+ showInput={true}
256
+ showPresets={false}
257
+ size="sm"
258
+ />
259
+ </div>
260
+ );
261
+ }
262
+
221
263
  return (
222
- <ColorPicker
223
- value={currentColor}
224
- onChange={onColorSelect}
225
- showInput={true}
226
- showPresets={true}
227
- size="sm"
228
- presets={[
229
- '#000000', '#ffffff', '#ff0000', '#00ff00', '#0000ff',
230
- '#ffff00', '#ff00ff', '#00ffff', '#ff8800', '#8800ff',
231
- '#88ff00', '#0088ff', '#ff0088', '#00ff88', '#888888',
232
- '#f87171', '#fb923c', '#fbbf24', '#facc15', '#a3e635',
233
- '#4ade80', '#34d399', '#2dd4bf', '#22d3ee', '#38bdf8',
234
- '#60a5fa', '#818cf8', '#a78bfa', '#c084fc', '#e879f9',
235
- '#f472b6', '#fb7185', '#f43f5e', '#ef4444', '#dc2626',
236
- ]}
237
- />
264
+ <div className="p-3 w-36">
265
+ <div className="grid grid-cols-3 gap-2">
266
+ {quickColors.map((color) => (
267
+ <button
268
+ key={color}
269
+ onClick={() => onColorSelect(color)}
270
+ className={cn(
271
+ "w-10 h-10 rounded-md border-2 hover:scale-110 transition-transform",
272
+ currentColor === color ? "border-primary" : "border-transparent"
273
+ )}
274
+ style={{ backgroundColor: color }}
275
+ title={color}
276
+ />
277
+ ))}
278
+ <button
279
+ onClick={() => setShowAdvanced(true)}
280
+ className="w-10 h-10 rounded-md border-2 border-dashed border-muted-foreground/50 hover:border-primary transition-colors flex items-center justify-center"
281
+ title="More colors"
282
+ >
283
+ <Palette className="w-5 h-5 text-muted-foreground" />
284
+ </button>
285
+ </div>
286
+ </div>
238
287
  );
239
288
  };
240
289
 
@@ -300,13 +349,7 @@ export function RichTextEditor({
300
349
  color: true,
301
350
  ai: true,
302
351
  },
303
- aiConfig = {
304
- provider: 'openai',
305
- apiKey: '',
306
- model: 'gpt-3.5-turbo',
307
- temperature: 0.7,
308
- maxTokens: 1000,
309
- },
352
+ aiConfig = {},
310
353
  persistAISettings = true,
311
354
  }: RichTextEditorProps) {
312
355
  // Pro access kontrolü
@@ -348,16 +391,20 @@ export function RichTextEditor({
348
391
  if (persistAISettings && typeof window !== 'undefined') {
349
392
  try {
350
393
  const stored = localStorage.getItem('moonui-ai-settings');
394
+ console.log('[RichTextEditor] Loading AI settings from localStorage:', stored);
351
395
  if (stored) {
352
396
  const parsed = JSON.parse(stored);
397
+ console.log('[RichTextEditor] Parsed AI settings:', parsed);
353
398
  // Props'tan gelen değerler her zaman öncelikli
354
- return {
355
- provider: (aiConfig.provider || parsed.provider || 'openai') as 'openai' | 'claude' | 'gemini' | 'cohere',
356
- apiKey: aiConfig.apiKey || parsed.apiKey || '',
357
- model: aiConfig.model || parsed.model || 'gpt-3.5-turbo',
358
- temperature: aiConfig.temperature ?? parsed.temperature ?? 0.7,
359
- maxTokens: aiConfig.maxTokens ?? parsed.maxTokens ?? 1000,
399
+ const settings = {
400
+ provider: (aiConfig.provider !== undefined ? aiConfig.provider : parsed.provider || 'openai') as 'openai' | 'claude' | 'gemini' | 'cohere',
401
+ apiKey: aiConfig.apiKey !== undefined ? aiConfig.apiKey : parsed.apiKey || '',
402
+ model: aiConfig.model !== undefined ? aiConfig.model : parsed.model || 'gpt-3.5-turbo',
403
+ temperature: aiConfig.temperature !== undefined ? aiConfig.temperature : parsed.temperature ?? 0.7,
404
+ maxTokens: aiConfig.maxTokens !== undefined ? aiConfig.maxTokens : parsed.maxTokens ?? 1000,
360
405
  };
406
+ console.log('[RichTextEditor] Final settings with props override:', settings);
407
+ return settings;
361
408
  }
362
409
  } catch (e) {
363
410
  console.error('Failed to load AI settings from localStorage:', e);
@@ -365,16 +412,24 @@ export function RichTextEditor({
365
412
  }
366
413
 
367
414
  // LocalStorage yoksa veya persist kapalıysa props/varsayılan değerleri kullan
368
- return {
415
+ const defaultSettings = {
369
416
  provider: (aiConfig.provider || 'openai') as 'openai' | 'claude' | 'gemini' | 'cohere',
370
417
  apiKey: aiConfig.apiKey || '',
371
418
  model: aiConfig.model || 'gpt-3.5-turbo',
372
419
  temperature: aiConfig.temperature ?? 0.7,
373
420
  maxTokens: aiConfig.maxTokens ?? 1000,
374
421
  };
422
+ console.log('[RichTextEditor] Using default settings:', defaultSettings);
423
+ return defaultSettings;
375
424
  });
376
425
  const [isAiSettingsOpen, setIsAiSettingsOpen] = useState(false);
377
426
  const [isProcessing, setIsProcessing] = useState(false);
427
+ const [typingText, setTypingText] = useState('');
428
+ const [isTyping, setIsTyping] = useState(false);
429
+ const typingIntervalRef = useRef<NodeJS.Timeout | null>(null);
430
+ const [currentAction, setCurrentAction] = useState('');
431
+ const [remainingText, setRemainingText] = useState(''); // To store remaining text when stopped
432
+ const isTypingRef = useRef(false); // Ref to track typing state in closure
378
433
  const [isSourceView, setIsSourceView] = useState(false);
379
434
  const [sourceContent, setSourceContent] = useState('');
380
435
  const [currentTextColor, setCurrentTextColor] = useState('#000000');
@@ -386,6 +441,31 @@ export function RichTextEditor({
386
441
  }
387
442
  return 'en';
388
443
  });
444
+
445
+ // Clean up typewriter effect on unmount
446
+ useEffect(() => {
447
+ return () => {
448
+ if (typingIntervalRef.current) {
449
+ clearTimeout(typingIntervalRef.current);
450
+ }
451
+ };
452
+ }, []);
453
+
454
+ // Stop typing function
455
+ const stopTyping = () => {
456
+ if (typingIntervalRef.current) {
457
+ clearTimeout(typingIntervalRef.current);
458
+ typingIntervalRef.current = null;
459
+ }
460
+ setIsTyping(false);
461
+ isTypingRef.current = false;
462
+ setTypingText('');
463
+ setRemainingText('');
464
+ toast({
465
+ title: "AI typing stopped",
466
+ description: "The AI response was interrupted.",
467
+ });
468
+ };
389
469
 
390
470
  // Slash commands tanımları
391
471
  const slashCommands: SlashCommand[] = [
@@ -668,6 +748,9 @@ export function RichTextEditor({
668
748
  return;
669
749
  }
670
750
 
751
+ // Set current action for UI
752
+ setCurrentAction(action);
753
+
671
754
  // Show processing toast
672
755
  const processingToast = toast({
673
756
  title: "Processing with AI...",
@@ -681,17 +764,45 @@ export function RichTextEditor({
681
764
  processingToast.dismiss();
682
765
 
683
766
  if (result) {
767
+ // Start typewriter effect
768
+ setIsTyping(true);
769
+ isTypingRef.current = true;
770
+ setTypingText('');
771
+
684
772
  if (selectedText) {
685
- editor.chain().focus().deleteSelection().insertContent(result).run();
686
- } else {
687
- editor.chain().focus().insertContent(result).run();
773
+ editor.chain().focus().deleteSelection().run();
688
774
  }
689
775
 
690
- // Success toast
691
- toast({
692
- title: "AI action completed",
693
- description: "Your text has been updated successfully.",
694
- });
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();
695
806
  }
696
807
  };
697
808
 
@@ -1316,16 +1427,27 @@ export function RichTextEditor({
1316
1427
  variant="ghost"
1317
1428
  size="sm"
1318
1429
  className="h-8 px-3 bg-purple-100 hover:bg-purple-200 dark:bg-purple-900 dark:hover:bg-purple-800 transition-colors"
1319
- disabled={isProcessing}
1430
+ disabled={isProcessing || isTyping}
1320
1431
  >
1321
- {isProcessing ? (
1432
+ {isProcessing || isTyping ? (
1322
1433
  <RefreshCw className="w-4 h-4 mr-1 animate-spin" />
1323
1434
  ) : (
1324
1435
  <Wand2 className="w-4 h-4 mr-1" />
1325
1436
  )}
1326
- AI Tools
1437
+ {isTyping ? 'Typing...' : 'AI Tools'}
1327
1438
  </Button>
1328
1439
  </DropdownMenuTrigger>
1440
+ {isTyping && (
1441
+ <Button
1442
+ variant="destructive"
1443
+ size="sm"
1444
+ className="h-8 px-2"
1445
+ onClick={stopTyping}
1446
+ title="Stop AI typing"
1447
+ >
1448
+ <X className="w-4 h-4" />
1449
+ </Button>
1450
+ )}
1329
1451
  <DropdownMenuContent className="w-64">
1330
1452
  <div className="px-2 py-1.5 text-xs font-semibold text-muted-foreground flex items-center gap-2">
1331
1453
  <Wand2 className="w-3 h-3" />
@@ -1333,7 +1455,7 @@ export function RichTextEditor({
1333
1455
  </div>
1334
1456
  <DropdownMenuItem
1335
1457
  onClick={() => handleAIAction('rewrite')}
1336
- disabled={isProcessing}
1458
+ disabled={isProcessing || isTyping}
1337
1459
  >
1338
1460
  <RefreshCw className="w-4 h-4 mr-2" />
1339
1461
  Rewrite Selection
@@ -1341,28 +1463,28 @@ export function RichTextEditor({
1341
1463
  </DropdownMenuItem>
1342
1464
  <DropdownMenuItem
1343
1465
  onClick={() => handleAIAction('improve')}
1344
- disabled={isProcessing}
1466
+ disabled={isProcessing || isTyping}
1345
1467
  >
1346
1468
  <Sparkles className="w-4 h-4 mr-2" />
1347
1469
  Improve Writing
1348
1470
  </DropdownMenuItem>
1349
1471
  <DropdownMenuItem
1350
1472
  onClick={() => handleAIAction('expand')}
1351
- disabled={isProcessing}
1473
+ disabled={isProcessing || isTyping}
1352
1474
  >
1353
1475
  <Maximize className="w-4 h-4 mr-2" />
1354
1476
  Expand Text
1355
1477
  </DropdownMenuItem>
1356
1478
  <DropdownMenuItem
1357
1479
  onClick={() => handleAIAction('summarize')}
1358
- disabled={isProcessing}
1480
+ disabled={isProcessing || isTyping}
1359
1481
  >
1360
1482
  <FileText className="w-4 h-4 mr-2" />
1361
1483
  Summarize
1362
1484
  </DropdownMenuItem>
1363
1485
  <DropdownMenuItem
1364
1486
  onClick={() => handleAIAction('continue')}
1365
- disabled={isProcessing}
1487
+ disabled={isProcessing || isTyping}
1366
1488
  >
1367
1489
  <Plus className="w-4 h-4 mr-2" />
1368
1490
  Continue Writing
@@ -1375,28 +1497,28 @@ export function RichTextEditor({
1375
1497
  </div>
1376
1498
  <DropdownMenuItem
1377
1499
  onClick={() => handleAIAction('tone_professional')}
1378
- disabled={isProcessing}
1500
+ disabled={isProcessing || isTyping}
1379
1501
  >
1380
1502
  <Briefcase className="w-4 h-4 mr-2" />
1381
1503
  Make Professional
1382
1504
  </DropdownMenuItem>
1383
1505
  <DropdownMenuItem
1384
1506
  onClick={() => handleAIAction('tone_casual')}
1385
- disabled={isProcessing}
1507
+ disabled={isProcessing || isTyping}
1386
1508
  >
1387
1509
  <MessageSquare className="w-4 h-4 mr-2" />
1388
1510
  Make Casual
1389
1511
  </DropdownMenuItem>
1390
1512
  <DropdownMenuItem
1391
1513
  onClick={() => handleAIAction('tone_friendly')}
1392
- disabled={isProcessing}
1514
+ disabled={isProcessing || isTyping}
1393
1515
  >
1394
1516
  <Heart className="w-4 h-4 mr-2" />
1395
1517
  Make Friendly
1396
1518
  </DropdownMenuItem>
1397
1519
  <DropdownMenuItem
1398
1520
  onClick={() => handleAIAction('tone_formal')}
1399
- disabled={isProcessing}
1521
+ disabled={isProcessing || isTyping}
1400
1522
  >
1401
1523
  <GraduationCap className="w-4 h-4 mr-2" />
1402
1524
  Make Formal
@@ -1409,14 +1531,14 @@ export function RichTextEditor({
1409
1531
  </div>
1410
1532
  <DropdownMenuItem
1411
1533
  onClick={() => handleAIAction('fix')}
1412
- disabled={isProcessing}
1534
+ disabled={isProcessing || isTyping}
1413
1535
  >
1414
1536
  <Check className="w-4 h-4 mr-2" />
1415
1537
  Fix Grammar & Spelling
1416
1538
  <span className="ml-auto text-xs text-muted-foreground">F7</span>
1417
1539
  </DropdownMenuItem>
1418
1540
  <DropdownMenuSub>
1419
- <DropdownMenuSubTrigger disabled={isProcessing}>
1541
+ <DropdownMenuSubTrigger disabled={isProcessing || isTyping}>
1420
1542
  <Languages className="w-4 h-4 mr-2" />
1421
1543
  Translate
1422
1544
  {lastTranslateLanguage && (
@@ -1436,7 +1558,7 @@ export function RichTextEditor({
1436
1558
  // Çeviriyi yap
1437
1559
  handleAIAction('translate', language.name);
1438
1560
  }}
1439
- disabled={isProcessing}
1561
+ disabled={isProcessing || isTyping}
1440
1562
  >
1441
1563
  <span className="text-sm">{language.nativeName}</span>
1442
1564
  <span className="ml-auto text-xs text-muted-foreground">{language.name}</span>
@@ -1449,7 +1571,7 @@ export function RichTextEditor({
1449
1571
  </DropdownMenuSub>
1450
1572
  <DropdownMenuItem
1451
1573
  onClick={() => handleAIAction('ideas')}
1452
- disabled={isProcessing}
1574
+ disabled={isProcessing || isTyping}
1453
1575
  >
1454
1576
  <Lightbulb className="w-4 h-4 mr-2" />
1455
1577
  Generate Ideas
@@ -1850,7 +1972,7 @@ export function RichTextEditor({
1850
1972
 
1851
1973
  {/* Editor */}
1852
1974
  <div
1853
- className="overflow-auto"
1975
+ className="overflow-auto relative"
1854
1976
  style={{ height: typeof height === 'number' ? `${height}px` : height }}
1855
1977
  >
1856
1978
  {isSourceView ? (
@@ -1861,7 +1983,55 @@ export function RichTextEditor({
1861
1983
  placeholder="HTML source code..."
1862
1984
  />
1863
1985
  ) : (
1864
- <EditorContent editor={editor} />
1986
+ <>
1987
+ <EditorContent editor={editor} />
1988
+ {/* AI Processing Overlay */}
1989
+ {isProcessing && (
1990
+ <motion.div
1991
+ initial={{ opacity: 0 }}
1992
+ animate={{ opacity: 1 }}
1993
+ exit={{ opacity: 0 }}
1994
+ className="absolute inset-0 bg-background/80 backdrop-blur-sm flex items-center justify-center z-50"
1995
+ >
1996
+ <div className="bg-card border rounded-lg p-6 shadow-lg max-w-sm w-full mx-4">
1997
+ <div className="flex flex-col items-center space-y-4">
1998
+ <div className="relative">
1999
+ <div className="w-16 h-16 border-4 border-purple-200 dark:border-purple-800 rounded-full animate-pulse"></div>
2000
+ <div className="absolute inset-0 flex items-center justify-center">
2001
+ <Wand2 className="w-8 h-8 text-purple-600 dark:text-purple-400 animate-bounce" />
2002
+ </div>
2003
+ </div>
2004
+ <div className="text-center space-y-2">
2005
+ <h3 className="font-semibold text-lg">AI is thinking...</h3>
2006
+ <p className="text-sm text-muted-foreground">
2007
+ {getActionDescription(
2008
+ currentAction,
2009
+ lastTranslateLanguage
2010
+ )}
2011
+ </p>
2012
+ </div>
2013
+ <div className="flex space-x-1">
2014
+ <motion.div
2015
+ animate={{ scale: [1, 1.5, 1] }}
2016
+ transition={{ duration: 0.6, repeat: Infinity, delay: 0 }}
2017
+ className="w-2 h-2 bg-purple-600 dark:bg-purple-400 rounded-full"
2018
+ />
2019
+ <motion.div
2020
+ animate={{ scale: [1, 1.5, 1] }}
2021
+ transition={{ duration: 0.6, repeat: Infinity, delay: 0.2 }}
2022
+ className="w-2 h-2 bg-purple-600 dark:bg-purple-400 rounded-full"
2023
+ />
2024
+ <motion.div
2025
+ animate={{ scale: [1, 1.5, 1] }}
2026
+ transition={{ duration: 0.6, repeat: Infinity, delay: 0.4 }}
2027
+ className="w-2 h-2 bg-purple-600 dark:bg-purple-400 rounded-full"
2028
+ />
2029
+ </div>
2030
+ </div>
2031
+ </div>
2032
+ </motion.div>
2033
+ )}
2034
+ </>
1865
2035
  )}
1866
2036
  </div>
1867
2037
  </div>