@contenify/chatbot 2.0.0 → 3.0.0

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
@@ -167,7 +167,7 @@ var savePreferencesApi = async ({
167
167
  import { jsx } from "react/jsx-runtime";
168
168
  var defaultPreferences = {
169
169
  chatbot: {
170
- name: "AI News Assistant",
170
+ name: "ContenifyAI Assistant",
171
171
  primaryColor: "#10b981",
172
172
  secondaryColor: "#064e3b",
173
173
  theme: "system"
@@ -266,42 +266,83 @@ function usePreferences() {
266
266
  // components/chatbot/ChatBot.tsx
267
267
  import { useState as useState7 } from "react";
268
268
 
269
- // util/util.ts
270
- function extractArticleContent(raw) {
271
- if (!raw) {
272
- return { article: "", metaKeywords: [] };
273
- }
274
- if (!raw.trim().startsWith("{")) {
275
- return {
276
- article: raw,
277
- metaKeywords: []
278
- };
269
+ // src/utils/ aiJsonParser.ts
270
+ function extractJSON(raw) {
271
+ if (!raw) return null;
272
+ let cleaned = raw.replace(/```json/gi, "").replace(/```/g, "").trim();
273
+ const first = cleaned.indexOf("{");
274
+ const last = cleaned.lastIndexOf("}");
275
+ if (first === -1 || last === -1 || last <= first) return null;
276
+ const possibleJson = cleaned.substring(first, last + 1);
277
+ try {
278
+ return JSON.parse(possibleJson);
279
+ } catch (err) {
280
+ console.warn("JSON parse failed:", err);
281
+ return null;
279
282
  }
283
+ }
284
+
285
+ // src/utils/decodeUnicode.ts
286
+ function decodeUnicode(text) {
287
+ if (!text) return "";
280
288
  try {
281
- const parsed = JSON.parse(raw);
282
- return {
283
- title: parsed.title || "",
284
- article: parsed.article || parsed.content || "",
285
- metaKeywords: Array.isArray(parsed.metaKeywords) ? parsed.metaKeywords : []
286
- };
289
+ return JSON.parse(`"${text.replace(/"/g, '\\"')}"`);
287
290
  } catch (e) {
288
- const article = raw.replace(/^[\s\S]*?"article"\s*:\s*"/, "").replace(/"\s*,\s*"metaKeywords"[\s\S]*$/, "").replace(/"}\s*$/, "") || raw;
291
+ return text;
292
+ }
293
+ }
294
+
295
+ // src/utils/articleTextToHtml.ts
296
+ function articleTextToHtml(text) {
297
+ if (!text) return "";
298
+ text = text.replace(/\\n/g, "\n");
299
+ const lines = text.split("\n").map((line) => line.trim()).filter(Boolean);
300
+ let html = "";
301
+ for (const line of lines) {
302
+ if (line.length < 80 && !line.endsWith("\u0964") && !line.endsWith(".")) {
303
+ html += `<h2>${line}</h2>`;
304
+ continue;
305
+ }
306
+ html += `<p>${line}</p>`;
307
+ }
308
+ return html;
309
+ }
310
+
311
+ // src/utils/formatAIContent.ts
312
+ function formatAIContent(raw) {
313
+ const empty = {
314
+ isArticle: false,
315
+ title: "",
316
+ subtitle: "",
317
+ articleHtml: "",
318
+ article: "",
319
+ metaDescription: "",
320
+ metaKeywords: [],
321
+ plainText: ""
322
+ };
323
+ if (!raw) return empty;
324
+ const parsed = extractJSON(raw);
325
+ if (parsed && (parsed.title || parsed.article)) {
326
+ const rawArticle = (parsed.article || "").trim();
327
+ const safeArticle = decodeUnicode(rawArticle);
328
+ const isHtml = /<[a-z][\s\S]*>/i.test(safeArticle);
329
+ const articleHtml = isHtml ? safeArticle : articleTextToHtml(safeArticle);
289
330
  return {
290
- article,
291
- metaKeywords: []
331
+ isArticle: true,
332
+ title: parsed.title || "",
333
+ subtitle: parsed.subtitle || "",
334
+ articleHtml,
335
+ article: rawArticle,
336
+ metaDescription: parsed.metaDescription || "",
337
+ metaKeywords: Array.isArray(parsed.metaKeywords) ? parsed.metaKeywords : [],
338
+ plainText: raw
292
339
  };
293
340
  }
341
+ return __spreadProps(__spreadValues({}, empty), { plainText: raw });
294
342
  }
295
- var hexToRgba = (hex, opacity) => {
296
- const sanitizedHex = hex.replace("#", "");
297
- const r = parseInt(sanitizedHex.substring(0, 2), 16);
298
- const g = parseInt(sanitizedHex.substring(2, 4), 16);
299
- const b = parseInt(sanitizedHex.substring(4, 6), 16);
300
- return `rgba(${r}, ${g}, ${b}, ${opacity})`;
301
- };
302
343
 
303
344
  // components/chatbot/ChatWindow.tsx
304
- import { Check, Copy, Edit3, Send, SendHorizontal, Zap, X as X2, RefreshCcw as RefreshCcw2, FileText, ListChecks, Share2, BookOpen, Mail, Key } from "lucide-react";
345
+ import { Check, Copy, Edit3, Send, SendHorizontal, Zap, X as X2, RefreshCcw as RefreshCcw2, FileText, ListChecks, Share2, BookOpen, Mail, Key, Loader2 } from "lucide-react";
305
346
  import { useLayoutEffect, useRef, useState as useState4, useEffect as useEffect4 } from "react";
306
347
 
307
348
  // components/chatbot/EditModal.tsx
@@ -490,38 +531,38 @@ function useTheme() {
490
531
 
491
532
  // components/chatbot/EditModal.tsx
492
533
  import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
534
+ function toSlug(text) {
535
+ return text.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-");
536
+ }
493
537
  function EditModal({
494
538
  isOpen,
539
+ initialTitle = "",
540
+ initialSubtitle = "",
495
541
  initialContent,
542
+ initialMetaDescription = "",
496
543
  metaKeywords,
497
544
  onClose,
498
545
  onSaveDraft,
499
546
  onPost
500
547
  }) {
501
548
  const { primaryColor } = useTheme();
549
+ const [title, setTitle] = useState2("");
550
+ const [subtitle, setSubtitle] = useState2("");
551
+ const [slug, setSlug] = useState2("");
552
+ const [slugEdited, setSlugEdited] = useState2(false);
553
+ const [metaDescription, setMetaDescription] = useState2("");
502
554
  const [content, setContent] = useState2("");
503
555
  const [keywords, setKeywords] = useState2(metaKeywords);
504
556
  const [newKeyword, setNewKeyword] = useState2("");
505
- const markdownToHtml = (text) => {
506
- const lines = text.split("\n").filter((line) => line.trim());
507
- let html = "";
508
- lines.forEach((line) => {
509
- const trimmed = line.trim();
510
- if (trimmed.startsWith("# ")) {
511
- html += `<h1>${trimmed.replace(/^#\s+/, "")}</h1>`;
512
- } else if (trimmed.startsWith("## ")) {
513
- html += `<h2>${trimmed.replace(/^##\s+/, "")}</h2>`;
514
- } else {
515
- html += `<p>${trimmed}</p>`;
516
- }
517
- });
518
- return html;
519
- };
520
557
  useEffect3(() => {
521
- const htmlContent = markdownToHtml(initialContent);
522
- setContent(htmlContent);
558
+ setTitle(initialTitle);
559
+ setSubtitle(initialSubtitle);
560
+ setMetaDescription(initialMetaDescription);
561
+ setSlug(toSlug(initialTitle));
562
+ setSlugEdited(false);
563
+ setContent(initialContent);
523
564
  setKeywords(metaKeywords);
524
- }, [initialContent, metaKeywords]);
565
+ }, [initialTitle, initialSubtitle, initialContent, initialMetaDescription, metaKeywords]);
525
566
  useEffect3(() => {
526
567
  if (isOpen) {
527
568
  document.body.style.overflow = "hidden";
@@ -532,6 +573,16 @@ function EditModal({
532
573
  document.body.style.overflow = "";
533
574
  };
534
575
  }, [isOpen]);
576
+ const handleTitleChange = (value) => {
577
+ setTitle(value);
578
+ if (!slugEdited) {
579
+ setSlug(toSlug(value));
580
+ }
581
+ };
582
+ const handleSlugChange = (value) => {
583
+ setSlug(toSlug(value));
584
+ setSlugEdited(true);
585
+ };
535
586
  const handleAddKeyword = () => {
536
587
  if (newKeyword.trim() && !keywords.includes(newKeyword.trim())) {
537
588
  setKeywords([...keywords, newKeyword.trim()]);
@@ -547,28 +598,62 @@ function EditModal({
547
598
  handleAddKeyword();
548
599
  }
549
600
  };
601
+ const getEditData = () => ({
602
+ title,
603
+ subtitle,
604
+ article: content,
605
+ metaDescription,
606
+ slug,
607
+ keywords
608
+ });
550
609
  if (!isOpen) return null;
551
610
  return /* @__PURE__ */ jsxs2("div", { className: "cnfy-edit-modal-overlay", children: [
552
- /* @__PURE__ */ jsx3(
553
- "div",
554
- {
555
- className: "cnfy-edit-modal-backdrop",
556
- onClick: onClose
557
- }
558
- ),
611
+ /* @__PURE__ */ jsx3("div", { className: "cnfy-edit-modal-backdrop", onClick: onClose }),
559
612
  /* @__PURE__ */ jsxs2("div", { className: "cnfy-edit-modal", children: [
560
613
  /* @__PURE__ */ jsxs2("div", { className: "cnfy-edit-modal-header", children: [
561
614
  /* @__PURE__ */ jsx3("h2", { className: "cnfy-edit-modal-title", children: "Edit Article" }),
562
- /* @__PURE__ */ jsx3(
563
- "button",
564
- {
565
- onClick: onClose,
566
- className: "cnfy-edit-modal-close-btn",
567
- children: /* @__PURE__ */ jsx3(X, { size: 20, className: "cnfy-edit-modal-close-icon" })
568
- }
569
- )
615
+ /* @__PURE__ */ jsx3("button", { onClick: onClose, className: "cnfy-edit-modal-close-btn", children: /* @__PURE__ */ jsx3(X, { size: 20, className: "cnfy-edit-modal-close-icon" }) })
570
616
  ] }),
571
617
  /* @__PURE__ */ jsxs2("div", { className: "cnfy-edit-modal-body", children: [
618
+ /* @__PURE__ */ jsxs2("div", { children: [
619
+ /* @__PURE__ */ jsx3("label", { className: "cnfy-edit-label", children: "Title" }),
620
+ /* @__PURE__ */ jsx3(
621
+ "input",
622
+ {
623
+ type: "text",
624
+ value: title,
625
+ onChange: (e) => handleTitleChange(e.target.value),
626
+ placeholder: "Article title...",
627
+ className: "cnfy-edit-input"
628
+ }
629
+ )
630
+ ] }),
631
+ /* @__PURE__ */ jsxs2("div", { children: [
632
+ /* @__PURE__ */ jsx3("label", { className: "cnfy-edit-label", children: "Subtitle" }),
633
+ /* @__PURE__ */ jsx3(
634
+ "input",
635
+ {
636
+ type: "text",
637
+ value: subtitle,
638
+ onChange: (e) => setSubtitle(e.target.value),
639
+ placeholder: "Article subtitle...",
640
+ className: "cnfy-edit-input"
641
+ }
642
+ )
643
+ ] }),
644
+ /* @__PURE__ */ jsxs2("div", { children: [
645
+ /* @__PURE__ */ jsx3("label", { className: "cnfy-edit-label", children: "Slug" }),
646
+ /* @__PURE__ */ jsx3(
647
+ "input",
648
+ {
649
+ type: "text",
650
+ value: slug,
651
+ onChange: (e) => handleSlugChange(e.target.value),
652
+ placeholder: "article-url-slug",
653
+ className: "cnfy-edit-input cnfy-edit-input--mono"
654
+ }
655
+ )
656
+ ] }),
572
657
  /* @__PURE__ */ jsxs2("div", { children: [
573
658
  /* @__PURE__ */ jsx3("label", { className: "cnfy-edit-label", children: "Article Content" }),
574
659
  /* @__PURE__ */ jsx3(
@@ -581,26 +666,32 @@ function EditModal({
581
666
  )
582
667
  ] }),
583
668
  /* @__PURE__ */ jsxs2("div", { children: [
584
- /* @__PURE__ */ jsx3("label", { className: "cnfy-edit-label", children: "Meta Keywords" }),
585
- /* @__PURE__ */ jsx3("div", { className: "cnfy-keyword-list", children: keywords.map((keyword, index) => /* @__PURE__ */ jsxs2(
586
- "span",
669
+ /* @__PURE__ */ jsx3("label", { className: "cnfy-edit-label", children: "Meta Description" }),
670
+ /* @__PURE__ */ jsx3(
671
+ "textarea",
587
672
  {
588
- className: "cnfy-keyword-tag",
589
- children: [
590
- "#",
591
- keyword,
592
- /* @__PURE__ */ jsx3(
593
- "button",
594
- {
595
- onClick: () => handleRemoveKeyword(index),
596
- className: "cnfy-keyword-remove-btn",
597
- children: /* @__PURE__ */ jsx3(X, { size: 14 })
598
- }
599
- )
600
- ]
601
- },
602
- index
603
- )) }),
673
+ value: metaDescription,
674
+ onChange: (e) => setMetaDescription(e.target.value),
675
+ placeholder: "Brief description for search engines...",
676
+ className: "cnfy-edit-textarea",
677
+ rows: 3
678
+ }
679
+ )
680
+ ] }),
681
+ /* @__PURE__ */ jsxs2("div", { children: [
682
+ /* @__PURE__ */ jsx3("label", { className: "cnfy-edit-label", children: "Meta Keywords" }),
683
+ /* @__PURE__ */ jsx3("div", { className: "cnfy-keyword-list", children: keywords.map((keyword, index) => /* @__PURE__ */ jsxs2("span", { className: "cnfy-keyword-tag", children: [
684
+ "#",
685
+ keyword,
686
+ /* @__PURE__ */ jsx3(
687
+ "button",
688
+ {
689
+ onClick: () => handleRemoveKeyword(index),
690
+ className: "cnfy-keyword-remove-btn",
691
+ children: /* @__PURE__ */ jsx3(X, { size: 14 })
692
+ }
693
+ )
694
+ ] }, index)) }),
604
695
  /* @__PURE__ */ jsxs2("div", { className: "cnfy-keyword-input-row", children: [
605
696
  /* @__PURE__ */ jsx3(
606
697
  "input",
@@ -613,30 +704,16 @@ function EditModal({
613
704
  className: "cnfy-keyword-input"
614
705
  }
615
706
  ),
616
- /* @__PURE__ */ jsx3(
617
- "button",
618
- {
619
- onClick: handleAddKeyword,
620
- className: "cnfy-btn-add-keyword",
621
- children: "Add"
622
- }
623
- )
707
+ /* @__PURE__ */ jsx3("button", { onClick: handleAddKeyword, className: "cnfy-btn-add-keyword", children: "Add" })
624
708
  ] })
625
709
  ] })
626
710
  ] }),
627
711
  /* @__PURE__ */ jsxs2("div", { className: "cnfy-edit-modal-footer", children: [
712
+ /* @__PURE__ */ jsx3("button", { onClick: onClose, className: "cnfy-btn-footer-cancel", children: "Cancel" }),
628
713
  /* @__PURE__ */ jsx3(
629
714
  "button",
630
715
  {
631
- onClick: onClose,
632
- className: "cnfy-btn-footer-cancel",
633
- children: "Cancel"
634
- }
635
- ),
636
- /* @__PURE__ */ jsx3(
637
- "button",
638
- {
639
- onClick: () => onSaveDraft(content, keywords),
716
+ onClick: () => onSaveDraft(getEditData()),
640
717
  className: "cnfy-btn-save-draft",
641
718
  children: "Save Draft"
642
719
  }
@@ -644,7 +721,7 @@ function EditModal({
644
721
  /* @__PURE__ */ jsx3(
645
722
  "button",
646
723
  {
647
- onClick: () => onPost(content, keywords),
724
+ onClick: () => onPost(getEditData()),
648
725
  className: "cnfy-btn-post-article",
649
726
  style: { backgroundColor: primaryColor, color: "#fff" },
650
727
  children: "Post"
@@ -844,7 +921,7 @@ var getNewsBySource = async (sourceId) => {
844
921
 
845
922
  // components/chatbot/ChatWindow.tsx
846
923
  import Select from "react-select";
847
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
924
+ import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
848
925
  var ACTION_ICONS = {
849
926
  recreate_article: RefreshCcw2,
850
927
  create_summary: ListChecks,
@@ -858,12 +935,13 @@ function ChatWindow({
858
935
  onSend,
859
936
  onSelectNews,
860
937
  isStreaming = false,
938
+ activeField,
861
939
  analyzedData,
862
940
  onSelectAction,
863
941
  onPost: onPostCallback
864
942
  }) {
865
943
  var _a, _b;
866
- const { loading, showNewsPanel } = useTheme();
944
+ const { showNewsPanel } = useTheme();
867
945
  const bottomRef = useRef(null);
868
946
  const textareaRef = useRef(null);
869
947
  const dropdownRef = useRef(null);
@@ -876,40 +954,48 @@ function ChatWindow({
876
954
  const [selectedSource, setSelectedSource] = useState4(null);
877
955
  const [loadingSources, setLoadingSources] = useState4(false);
878
956
  const [scraping, setScraping] = useState4(false);
879
- const [editModal, setEditModal] = useState4({ isOpen: false, content: "", metaKeywords: [], messageId: "" });
957
+ const [editModal, setEditModal] = useState4({ isOpen: false, title: "", subtitle: "", content: "", metaDescription: "", metaKeywords: [], messageId: "" });
880
958
  const { primaryColor } = useTheme();
881
959
  const { preferences } = usePreferences();
882
960
  const preferredLanguage = (_a = preferences == null ? void 0 : preferences.localization) == null ? void 0 : _a.language;
883
- const handleCopy = async (blocks, messageId) => {
961
+ const handleCopy = async (parsed, messageId) => {
884
962
  try {
885
- const formattedText = blocksToFormattedText(blocks);
886
- await navigator.clipboard.writeText(formattedText);
963
+ const tempDiv = document.createElement("div");
964
+ let textContent = "";
965
+ if (parsed.title) textContent += parsed.title + "\n\n";
966
+ if (parsed.subtitle) textContent += parsed.subtitle + "\n\n";
967
+ if (parsed.articleHtml) {
968
+ tempDiv.innerHTML = parsed.articleHtml;
969
+ textContent += tempDiv.textContent || tempDiv.innerText || "";
970
+ }
971
+ if (parsed.metaDescription) textContent += "\n\nMeta Description:\n" + parsed.metaDescription;
972
+ await navigator.clipboard.writeText(textContent);
887
973
  setCopiedId(messageId);
888
974
  setTimeout(() => setCopiedId(null), 2e3);
889
975
  } catch (err) {
890
976
  console.error("Failed to copy:", err);
891
977
  }
892
978
  };
893
- const blocksToFormattedText = (blocks) => {
894
- return blocks.map((block) => {
895
- if (block.type === "h1") return `# ${block.text}`;
896
- if (block.type === "h2") return `## ${block.text}`;
897
- return block.text;
898
- }).join("\n\n");
899
- };
900
- const handleEdit = (blocks, metaKeywords, messageId) => {
901
- const formattedContent = blocksToFormattedText(blocks);
902
- setEditModal({ isOpen: true, content: formattedContent, metaKeywords, messageId });
979
+ const handleEdit = (parsed, messageId) => {
980
+ setEditModal({
981
+ isOpen: true,
982
+ title: parsed.title || "",
983
+ subtitle: parsed.subtitle || "",
984
+ content: parsed.articleHtml || "",
985
+ metaDescription: parsed.metaDescription || "",
986
+ metaKeywords: parsed.metaKeywords,
987
+ messageId
988
+ });
903
989
  };
904
990
  const handleCloseModal = () => {
905
- setEditModal({ isOpen: false, content: "", metaKeywords: [], messageId: "" });
991
+ setEditModal({ isOpen: false, title: "", subtitle: "", content: "", metaDescription: "", metaKeywords: [], messageId: "" });
906
992
  };
907
- const handleSaveDraft = (content, keywords) => {
908
- console.log("Saving draft:", { content, keywords });
993
+ const handleSaveDraft = (data) => {
994
+ console.log("Saving draft:", data);
909
995
  handleCloseModal();
910
996
  };
911
- const handlePost = (content, keywords) => {
912
- onPostCallback == null ? void 0 : onPostCallback(content, keywords);
997
+ const handlePost = (data) => {
998
+ onPostCallback == null ? void 0 : onPostCallback(data.article, data.keywords);
913
999
  handleCloseModal();
914
1000
  };
915
1001
  useLayoutEffect(() => {
@@ -998,77 +1084,62 @@ function ChatWindow({
998
1084
  document.removeEventListener("mousedown", handleClickOutside);
999
1085
  };
1000
1086
  }, [showNewsDropdown]);
1001
- function formatAIContent(raw) {
1002
- const extracted = extractArticleContent(raw);
1003
- if (!extracted || !extracted.article) {
1004
- return { blocks: [], metaKeywords: [] };
1005
- }
1006
- const { article, metaKeywords } = extracted;
1007
- const normalized = article.replace(/^H1:\s*/gm, "").replace(/^H2:\s*/gm, "").replace(/<\/h1>/gi, "\n").replace(/<\/h2>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<br\s*\/?>/gi, "\n");
1008
- const lines = normalized.split("\n").map((line) => line.replace(/<[^>]+>/g, "").trim()).filter(Boolean);
1009
- const blocks = [];
1010
- lines.forEach((line, index) => {
1011
- if (index === 0 && line.length < 120) {
1012
- blocks.push({ type: "h1", text: line });
1013
- return;
1014
- }
1015
- if (line.length < 90 && !line.endsWith("\u0964") && !line.endsWith(".")) {
1016
- blocks.push({ type: "h2", text: line });
1017
- return;
1018
- }
1019
- blocks.push({ type: "p", text: line });
1020
- });
1021
- return { blocks, metaKeywords };
1022
- }
1023
1087
  return /* @__PURE__ */ jsxs4("div", { className: "cnfy-chat", children: [
1024
1088
  /* @__PURE__ */ jsx5("div", { className: "cnfy-chat-area", children: /* @__PURE__ */ jsxs4("div", { className: "cnfy-chat-scroll", children: [
1025
1089
  messages.map((msg) => {
1026
- var _a2;
1090
+ var _a2, _b2;
1027
1091
  const parsed = formatAIContent(msg.content);
1028
1092
  return /* @__PURE__ */ jsx5("div", { className: "cnfy-msg", children: /* @__PURE__ */ jsxs4("div", { className: msg.role === "assistant" ? `cnfy-msg-body` : `cnfy-msg-body you`, children: [
1029
- msg.role === "assistant" && parsed.blocks.length > 0 && /* @__PURE__ */ jsx5("div", { className: "cnfy-msg-copy-row", children: /* @__PURE__ */ jsx5(
1093
+ msg.role === "assistant" && parsed.isArticle && /* @__PURE__ */ jsx5("div", { className: "cnfy-msg-copy-row", children: /* @__PURE__ */ jsx5(
1030
1094
  "button",
1031
1095
  {
1032
- onClick: () => handleCopy(parsed.blocks, msg.id),
1096
+ onClick: () => handleCopy(parsed, msg.id),
1033
1097
  className: "cnfy-copy-btn",
1034
1098
  title: "Copy to clipboard",
1035
1099
  children: copiedId === msg.id ? /* @__PURE__ */ jsx5(Check, { size: 16, className: "cnfy-copy-icon--copied" }) : /* @__PURE__ */ jsx5(Copy, { size: 16 })
1036
1100
  }
1037
1101
  ) }),
1038
- parsed.blocks.map((block, idx) => {
1039
- if (block.type === "h1") {
1040
- return /* @__PURE__ */ jsx5(
1041
- "h1",
1042
- {
1043
- className: "cnfy-block-h1",
1044
- children: block.text
1045
- },
1046
- idx
1047
- );
1048
- }
1049
- if (block.type === "h2") {
1050
- return /* @__PURE__ */ jsx5(
1051
- "h2",
1052
- {
1053
- className: "cnfy-block-h2",
1054
- children: block.text
1055
- },
1056
- idx
1057
- );
1058
- }
1059
- return /* @__PURE__ */ jsx5("p", { className: "cnfy-block-p", children: block.text }, idx);
1060
- }),
1061
- parsed.metaKeywords.length > 0 && /* @__PURE__ */ jsx5("div", { className: "cnfy-msg-keywords", children: /* @__PURE__ */ jsx5("div", { className: "cnfy-msg-keywords-list", children: parsed.metaKeywords.map((tag, i) => /* @__PURE__ */ jsxs4(
1062
- "span",
1063
- {
1064
- className: "cnfy-msg-keyword-tag",
1065
- children: [
1066
- "#",
1067
- tag
1068
- ]
1069
- },
1070
- i
1071
- )) }) }),
1102
+ msg.role === "assistant" && isStreaming && msg.id === ((_a2 = messages[messages.length - 1]) == null ? void 0 : _a2.id) && !parsed.isArticle && !parsed.plainText && /* @__PURE__ */ jsxs4("div", { className: "cnfy-creating-indicator", children: [
1103
+ /* @__PURE__ */ jsx5(Loader2, { size: 16, className: "cnfy-animate-spin" }),
1104
+ /* @__PURE__ */ jsx5("span", { children: "Creating article..." })
1105
+ ] }),
1106
+ parsed.isArticle ? /* @__PURE__ */ jsxs4(Fragment, { children: [
1107
+ parsed.title && /* @__PURE__ */ jsxs4("h1", { className: "cnfy-block-h1", children: [
1108
+ parsed.title,
1109
+ activeField === "title" && /* @__PURE__ */ jsx5("span", { className: "cnfy-cursor-blink", children: "|" })
1110
+ ] }),
1111
+ parsed.subtitle && /* @__PURE__ */ jsxs4("h1", { className: "cnfy-block-h2", children: [
1112
+ parsed.subtitle,
1113
+ activeField === "subtitle" && /* @__PURE__ */ jsx5("span", { className: "cnfy-cursor-blink", children: "|" })
1114
+ ] }),
1115
+ /* @__PURE__ */ jsx5("hr", { className: "cnfy-divider" }),
1116
+ parsed.articleHtml && /* @__PURE__ */ jsx5(
1117
+ "div",
1118
+ {
1119
+ className: "cnfy-article-content",
1120
+ dangerouslySetInnerHTML: { __html: parsed.articleHtml }
1121
+ }
1122
+ ),
1123
+ activeField === "article" && /* @__PURE__ */ jsx5("span", { className: "cnfy-cursor-blink", children: "|" })
1124
+ ] }) : (
1125
+ /* Plain text messages (status, error, options, etc.) */
1126
+ parsed.plainText && /* @__PURE__ */ jsx5("p", { className: "cnfy-block-p", children: parsed.plainText })
1127
+ ),
1128
+ parsed.metaDescription && /* @__PURE__ */ jsxs4("div", { className: "cnfy-msg-keywords", children: [
1129
+ /* @__PURE__ */ jsx5("div", { className: "cnfy-msg-keywords-label", children: "Meta Description" }),
1130
+ /* @__PURE__ */ jsx5("div", { className: "cnfy-block-p", children: parsed.metaDescription })
1131
+ ] }),
1132
+ parsed.metaKeywords.length > 0 && /* @__PURE__ */ jsxs4("div", { className: "cnfy-msg-keywords", children: [
1133
+ /* @__PURE__ */ jsx5("div", { className: "cnfy-msg-keywords-label", children: "Keywords" }),
1134
+ /* @__PURE__ */ jsx5("div", { className: "cnfy-msg-keywords-list", children: parsed.metaKeywords.map((tag, i) => /* @__PURE__ */ jsx5(
1135
+ "span",
1136
+ {
1137
+ className: "cnfy-msg-keyword-tag",
1138
+ children: tag
1139
+ },
1140
+ i
1141
+ )) })
1142
+ ] }),
1072
1143
  msg.role === "assistant" && (analyzedData == null ? void 0 : analyzedData.messageId) === msg.id && analyzedData.options.length > 0 && /* @__PURE__ */ jsx5("div", { className: "cnfy-action-options", children: analyzedData.options.map((option) => {
1073
1144
  const IconComponent = ACTION_ICONS[option.id] || FileText;
1074
1145
  return /* @__PURE__ */ jsxs4(
@@ -1084,11 +1155,11 @@ function ChatWindow({
1084
1155
  option.id
1085
1156
  );
1086
1157
  }) }),
1087
- msg.role === "assistant" && parsed.blocks.length > 0 && !(analyzedData == null ? void 0 : analyzedData.messageId) && (!isStreaming || msg.id !== ((_a2 = messages[messages.length - 1]) == null ? void 0 : _a2.id)) && /* @__PURE__ */ jsxs4("div", { className: "cnfy-msg-actions", children: [
1158
+ msg.role === "assistant" && parsed.isArticle && !(analyzedData == null ? void 0 : analyzedData.messageId) && (!isStreaming || msg.id !== ((_b2 = messages[messages.length - 1]) == null ? void 0 : _b2.id)) && /* @__PURE__ */ jsxs4("div", { className: "cnfy-msg-actions", children: [
1088
1159
  /* @__PURE__ */ jsxs4(
1089
1160
  "button",
1090
1161
  {
1091
- onClick: () => handleEdit(parsed.blocks, parsed.metaKeywords, msg.id),
1162
+ onClick: () => handleEdit(parsed, msg.id),
1092
1163
  className: "cnfy-btn-edit",
1093
1164
  children: [
1094
1165
  /* @__PURE__ */ jsx5(Edit3, { size: 16 }),
@@ -1099,7 +1170,7 @@ function ChatWindow({
1099
1170
  /* @__PURE__ */ jsxs4(
1100
1171
  "button",
1101
1172
  {
1102
- onClick: () => handlePost(msg.content, parsed.metaKeywords),
1173
+ onClick: () => handlePost({ article: parsed.articleHtml, keywords: parsed.metaKeywords }),
1103
1174
  className: "cnfy-btn-post",
1104
1175
  style: { backgroundColor: primaryColor, color: "#fff" },
1105
1176
  children: [
@@ -1241,7 +1312,8 @@ function ChatWindow({
1241
1312
  rows: 1,
1242
1313
  placeholder: "Ask AI something\u2026",
1243
1314
  className: `cnfy-chat-textarea ${!showNewsPanel ? "cnfy-chat-textarea--with-pulse" : ""}`,
1244
- style: { maxHeight: "200px", overflowY: input.split("\n").length > 6 ? "auto" : "hidden" }
1315
+ style: { maxHeight: "200px", overflowY: input.split("\n").length > 6 ? "auto" : "hidden" },
1316
+ disabled: true
1245
1317
  }
1246
1318
  ),
1247
1319
  /* @__PURE__ */ jsx5(
@@ -1258,11 +1330,14 @@ function ChatWindow({
1258
1330
  EditModal,
1259
1331
  {
1260
1332
  isOpen: editModal.isOpen,
1333
+ initialTitle: editModal.title,
1334
+ initialSubtitle: editModal.subtitle,
1261
1335
  initialContent: editModal.content,
1336
+ initialMetaDescription: editModal.metaDescription,
1262
1337
  metaKeywords: editModal.metaKeywords,
1263
1338
  onClose: handleCloseModal,
1264
- onSaveDraft: handleSaveDraft,
1265
- onPost: handlePost
1339
+ onSaveDraft: (data) => handleSaveDraft({ article: data.article, keywords: data.keywords }),
1340
+ onPost: (data) => handlePost({ article: data.article, keywords: data.keywords })
1266
1341
  }
1267
1342
  )
1268
1343
  ] });
@@ -1377,8 +1452,17 @@ var ClientSelect = (props) => {
1377
1452
  };
1378
1453
  var ClientSelect_default = ClientSelect;
1379
1454
 
1455
+ // src/utils/util.ts
1456
+ var hexToRgba = (hex, opacity) => {
1457
+ const sanitizedHex = hex.replace("#", "");
1458
+ const r = parseInt(sanitizedHex.substring(0, 2), 16);
1459
+ const g = parseInt(sanitizedHex.substring(2, 4), 16);
1460
+ const b = parseInt(sanitizedHex.substring(4, 6), 16);
1461
+ return `rgba(${r}, ${g}, ${b}, ${opacity})`;
1462
+ };
1463
+
1380
1464
  // components/preferences/Preferences.tsx
1381
- import { Fragment, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
1465
+ import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
1382
1466
  var STATE_OPTIONS = [
1383
1467
  { value: "Uttar Pradesh", label: "Uttar Pradesh" }
1384
1468
  ];
@@ -1623,7 +1707,7 @@ function PreferencesPage() {
1623
1707
  /* @__PURE__ */ jsxs7("p", { className: "cnfy-dash-info-text", children: [
1624
1708
  "Current configuration: ",
1625
1709
  /* @__PURE__ */ jsx10("strong", { children: (_b = preferences == null ? void 0 : preferences.localization) == null ? void 0 : _b.state }),
1626
- ((_c = preferences == null ? void 0 : preferences.localization) == null ? void 0 : _c.cities) && preferences.localization.cities.length > 0 && /* @__PURE__ */ jsxs7(Fragment, { children: [
1710
+ ((_c = preferences == null ? void 0 : preferences.localization) == null ? void 0 : _c.cities) && preferences.localization.cities.length > 0 && /* @__PURE__ */ jsxs7(Fragment2, { children: [
1627
1711
  " - ",
1628
1712
  preferences.localization.cities.join(", ")
1629
1713
  ] })
@@ -1695,12 +1779,72 @@ function PreferencesPage() {
1695
1779
  }
1696
1780
 
1697
1781
  // services/chat.service.ts
1782
+ function getHeaders() {
1783
+ const apiKey = getApiKey();
1784
+ const headers = {
1785
+ "Content-Type": "application/json"
1786
+ };
1787
+ if (apiKey) {
1788
+ headers["x-api-key"] = apiKey;
1789
+ }
1790
+ return headers;
1791
+ }
1792
+ async function processSSEStream(reader, callbacks) {
1793
+ const decoder = new TextDecoder();
1794
+ let buffer = "";
1795
+ const processLines = (lines) => {
1796
+ var _a, _b, _c, _d, _e;
1797
+ for (const line of lines) {
1798
+ const trimmed = line.trim();
1799
+ if (!trimmed.startsWith("data: ")) continue;
1800
+ const data = trimmed.slice(6).trim();
1801
+ try {
1802
+ const event = JSON.parse(data);
1803
+ switch (event.type) {
1804
+ case "start":
1805
+ break;
1806
+ case "field_start":
1807
+ (_a = callbacks.onFieldStart) == null ? void 0 : _a.call(callbacks, event.field);
1808
+ break;
1809
+ case "content":
1810
+ callbacks.onContent(event.field, event.content);
1811
+ break;
1812
+ case "keywords":
1813
+ (_b = callbacks.onKeywords) == null ? void 0 : _b.call(callbacks, event.content);
1814
+ break;
1815
+ case "field_end":
1816
+ (_c = callbacks.onFieldEnd) == null ? void 0 : _c.call(callbacks, event.field);
1817
+ break;
1818
+ case "done":
1819
+ (_d = callbacks.onComplete) == null ? void 0 : _d.call(callbacks, event.data);
1820
+ return true;
1821
+ case "error":
1822
+ (_e = callbacks.onError) == null ? void 0 : _e.call(callbacks, new Error(event.message));
1823
+ return true;
1824
+ }
1825
+ } catch (e) {
1826
+ }
1827
+ }
1828
+ return false;
1829
+ };
1830
+ while (true) {
1831
+ const { done, value } = await reader.read();
1832
+ if (done) break;
1833
+ buffer += decoder.decode(value, { stream: true });
1834
+ const lines = buffer.split("\n");
1835
+ buffer = lines.pop() || "";
1836
+ if (processLines(lines)) return;
1837
+ }
1838
+ if (buffer.trim()) {
1839
+ processLines([buffer]);
1840
+ }
1841
+ }
1698
1842
  var analyzeInputApi = async (input) => {
1699
- const { data } = await api_default.post("/chat/analyze-input", { input });
1843
+ const { data } = await api_default.post("/chatboat/analyze-input", { input });
1700
1844
  return data.data;
1701
1845
  };
1702
1846
  var createContentApi = async (payload) => {
1703
- const { data } = await api_default.post("/chat/create-content", payload);
1847
+ const { data } = await api_default.post("/chatboat/create-content", payload);
1704
1848
  return data.data;
1705
1849
  };
1706
1850
  var createContentStreamApi = async ({
@@ -1708,22 +1852,18 @@ var createContentStreamApi = async ({
1708
1852
  content,
1709
1853
  actionId,
1710
1854
  settings,
1711
- onChunk,
1855
+ onFieldStart,
1856
+ onContent,
1857
+ onKeywords,
1858
+ onFieldEnd,
1712
1859
  onComplete,
1713
1860
  onError
1714
1861
  }) => {
1715
1862
  var _a;
1716
1863
  const API_BASE_URL = getApiBaseUrl();
1717
- const apiKey = getApiKey();
1718
- const headers = {
1719
- "Content-Type": "application/json"
1720
- };
1721
- if (apiKey) {
1722
- headers["x-api-key"] = apiKey;
1723
- }
1724
1864
  const response = await fetch(`${API_BASE_URL}/chat/create-content/stream`, {
1725
1865
  method: "POST",
1726
- headers,
1866
+ headers: getHeaders(),
1727
1867
  credentials: "include",
1728
1868
  body: JSON.stringify({ url, content, actionId, settings })
1729
1869
  });
@@ -1732,50 +1872,17 @@ var createContentStreamApi = async ({
1732
1872
  throw new Error(`HTTP error! status: ${response.status} - ${errorText}`);
1733
1873
  }
1734
1874
  const reader = (_a = response.body) == null ? void 0 : _a.getReader();
1735
- const decoder = new TextDecoder();
1736
1875
  if (!reader) {
1737
1876
  throw new Error("No reader available");
1738
1877
  }
1739
- let buffer = "";
1740
- let hasReceivedData = false;
1741
- while (true) {
1742
- const { done, value } = await reader.read();
1743
- if (done) {
1744
- if (hasReceivedData) onComplete == null ? void 0 : onComplete();
1745
- break;
1746
- }
1747
- const chunk = decoder.decode(value, { stream: true });
1748
- buffer += chunk;
1749
- const lines = buffer.split("\n");
1750
- buffer = lines.pop() || "";
1751
- for (const line of lines) {
1752
- const trimmedLine = line.trim();
1753
- if (trimmedLine === "") continue;
1754
- hasReceivedData = true;
1755
- if (trimmedLine.startsWith("data: ")) {
1756
- const data = trimmedLine.slice(6).trim();
1757
- if (data === "[DONE]") {
1758
- onComplete == null ? void 0 : onComplete();
1759
- return;
1760
- }
1761
- try {
1762
- const parsed = JSON.parse(data);
1763
- if (parsed.content) onChunk(parsed.content);
1764
- else if (parsed.delta) onChunk(parsed.delta);
1765
- else if (parsed.text) onChunk(parsed.text);
1766
- else if (parsed.chunk) onChunk(parsed.chunk);
1767
- else if (typeof parsed === "string") onChunk(parsed);
1768
- } catch (e) {
1769
- if (data) onChunk(data);
1770
- }
1771
- } else if (trimmedLine) {
1772
- onChunk(trimmedLine + "\n");
1773
- }
1774
- }
1775
- }
1776
- if (!hasReceivedData) {
1777
- throw new Error("No data received from stream");
1778
- }
1878
+ await processSSEStream(reader, {
1879
+ onFieldStart,
1880
+ onContent,
1881
+ onKeywords,
1882
+ onFieldEnd,
1883
+ onComplete,
1884
+ onError
1885
+ });
1779
1886
  };
1780
1887
  var rewriteNewsStreamApi = async ({
1781
1888
  article,
@@ -1787,7 +1894,10 @@ var rewriteNewsStreamApi = async ({
1787
1894
  includeQuotes,
1788
1895
  includeFAQ,
1789
1896
  targetAudience,
1790
- onChunk,
1897
+ onFieldStart,
1898
+ onContent,
1899
+ onKeywords,
1900
+ onFieldEnd,
1791
1901
  onComplete,
1792
1902
  onError
1793
1903
  }) => {
@@ -1796,9 +1906,7 @@ var rewriteNewsStreamApi = async ({
1796
1906
  article,
1797
1907
  language
1798
1908
  };
1799
- if (articleId) {
1800
- payload.articleId = articleId;
1801
- }
1909
+ if (articleId) payload.articleId = articleId;
1802
1910
  if (tone) payload.tone = tone;
1803
1911
  if (style) payload.style = style;
1804
1912
  if (wordCount) payload.wordCount = wordCount;
@@ -1807,99 +1915,42 @@ var rewriteNewsStreamApi = async ({
1807
1915
  if (targetAudience) payload.targetAudience = targetAudience;
1808
1916
  try {
1809
1917
  const API_BASE_URL = getApiBaseUrl();
1810
- const apiKey = getApiKey();
1811
- const headers = {
1812
- "Content-Type": "application/json"
1813
- };
1814
- if (apiKey) {
1815
- headers["x-api-key"] = apiKey;
1816
- }
1817
1918
  const response = await fetch(`${API_BASE_URL}/chatboat/rewrite/stream`, {
1818
1919
  method: "POST",
1819
- headers,
1920
+ headers: getHeaders(),
1820
1921
  credentials: "include",
1821
1922
  body: JSON.stringify(payload)
1822
1923
  });
1823
- console.log("\u{1F4E1} Response status:", response.status);
1824
- console.log("\u{1F4E1} Response headers:", Object.fromEntries(response.headers.entries()));
1825
1924
  if (!response.ok) {
1826
1925
  const errorText = await response.text();
1827
- console.error("\u274C API Error:", response.status, errorText);
1828
1926
  throw new Error(`HTTP error! status: ${response.status} - ${errorText}`);
1829
1927
  }
1928
+ const contentType = response.headers.get("content-type") || "";
1929
+ if (contentType.includes("application/json")) {
1930
+ const json = await response.json();
1931
+ const data = json.data || json;
1932
+ onComplete == null ? void 0 : onComplete({
1933
+ title: data.title || "",
1934
+ subtitle: data.subtitle || "",
1935
+ article: data.article || "",
1936
+ metaDescription: data.metaDescription || "",
1937
+ metaKeywords: Array.isArray(data.metaKeywords) ? data.metaKeywords : []
1938
+ });
1939
+ return;
1940
+ }
1830
1941
  const reader = (_a = response.body) == null ? void 0 : _a.getReader();
1831
- const decoder = new TextDecoder();
1832
1942
  if (!reader) {
1833
1943
  throw new Error("No reader available");
1834
1944
  }
1835
- let buffer = "";
1836
- let hasReceivedData = false;
1837
- while (true) {
1838
- const { done, value } = await reader.read();
1839
- if (done) {
1840
- if (hasReceivedData) {
1841
- onComplete == null ? void 0 : onComplete();
1842
- }
1843
- break;
1844
- }
1845
- const chunk = decoder.decode(value, { stream: true });
1846
- buffer += chunk;
1847
- const lines = buffer.split("\n");
1848
- buffer = lines.pop() || "";
1849
- for (const line of lines) {
1850
- const trimmedLine = line.trim();
1851
- if (trimmedLine === "") continue;
1852
- hasReceivedData = true;
1853
- console.log("\u{1F4E8} Received line:", trimmedLine.substring(0, 100) + (trimmedLine.length > 100 ? "..." : ""));
1854
- if (trimmedLine.startsWith("data: ")) {
1855
- const data = trimmedLine.slice(6).trim();
1856
- if (data === "[DONE]") {
1857
- console.log("\u2705 Stream completed with [DONE] signal");
1858
- onComplete == null ? void 0 : onComplete();
1859
- return;
1860
- }
1861
- try {
1862
- const parsed = JSON.parse(data);
1863
- console.log("\u{1F4CB} Parsed JSON:", parsed);
1864
- if (parsed.content) {
1865
- onChunk(parsed.content);
1866
- } else if (parsed.delta) {
1867
- onChunk(parsed.delta);
1868
- } else if (parsed.text) {
1869
- onChunk(parsed.text);
1870
- } else if (parsed.chunk) {
1871
- onChunk(parsed.chunk);
1872
- } else if (typeof parsed === "string") {
1873
- onChunk(parsed);
1874
- } else {
1875
- console.warn("\u26A0\uFE0F Unknown JSON format:", parsed);
1876
- }
1877
- } catch (e) {
1878
- console.log("\u{1F4DD} Non-JSON data, treating as plain text");
1879
- if (data) {
1880
- onChunk(data);
1881
- }
1882
- }
1883
- } else {
1884
- console.log("\u{1F4C4} Plain text chunk");
1885
- if (trimmedLine) {
1886
- onChunk(trimmedLine + "\n");
1887
- }
1888
- }
1889
- }
1890
- }
1891
- if (!hasReceivedData) {
1892
- console.error("\u274C No data received from stream");
1893
- throw new Error("No data received from stream");
1894
- }
1895
- console.log("\u2705 Stream completed successfully");
1896
- } catch (error) {
1897
- console.error("\u274C Streaming error:", error);
1898
- console.error("Error details:", {
1899
- message: error.message,
1900
- name: error.name,
1901
- stack: error.stack
1945
+ await processSSEStream(reader, {
1946
+ onFieldStart,
1947
+ onContent,
1948
+ onKeywords,
1949
+ onFieldEnd,
1950
+ onComplete,
1951
+ onError
1902
1952
  });
1953
+ } catch (error) {
1903
1954
  onError == null ? void 0 : onError(error);
1904
1955
  throw error;
1905
1956
  }
@@ -1919,29 +1970,31 @@ var rewriteNewsApi = async ({
1919
1970
  article,
1920
1971
  language
1921
1972
  };
1922
- if (articleId) {
1923
- payload.articleId = articleId;
1924
- }
1973
+ if (articleId) payload.articleId = articleId;
1925
1974
  if (tone) payload.tone = tone;
1926
1975
  if (style) payload.style = style;
1927
1976
  if (wordCount) payload.wordCount = wordCount;
1928
1977
  if (includeQuotes !== void 0) payload.includeQuotes = includeQuotes;
1929
1978
  if (includeFAQ !== void 0) payload.includeFAQ = includeFAQ;
1930
1979
  if (targetAudience) payload.targetAudience = targetAudience;
1931
- const { data } = await api_default.post("/chat/rewrite", payload);
1980
+ const { data } = await api_default.post("/chatboat/rewrite", payload);
1932
1981
  return data.data;
1933
1982
  };
1934
1983
 
1935
1984
  // components/chatbot/ChatBot.tsx
1936
1985
  import toast2 from "react-hot-toast";
1937
- import { Loader2 } from "lucide-react";
1986
+ import { Loader2 as Loader22 } from "lucide-react";
1938
1987
  import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
1988
+ function buildStructuredContent(title, article, metaKeywords, subtitle = "", metaDescription = "") {
1989
+ return JSON.stringify({ title, subtitle, article, metaDescription, metaKeywords });
1990
+ }
1939
1991
  function ChatBot({ onPost }) {
1940
1992
  const { loading } = useTheme();
1941
1993
  const { preferences } = usePreferences();
1942
1994
  const [preferencesOpen, setPreferencesOpen] = useState7(false);
1943
1995
  const [messages, setMessages] = useState7([]);
1944
1996
  const [isStreaming, setIsStreaming] = useState7(false);
1997
+ const [activeField, setActiveField] = useState7(null);
1945
1998
  const [analyzedData, setAnalyzedData] = useState7(null);
1946
1999
  const handleRecreate = async ({ title, content, id }) => {
1947
2000
  var _a;
@@ -1952,17 +2005,28 @@ function ChatBot({ onPost }) {
1952
2005
  id: crypto.randomUUID(),
1953
2006
  role: "user",
1954
2007
  content: `Recreate this news:
1955
- ${title}`
2008
+ ${title}`
1956
2009
  },
1957
2010
  {
1958
2011
  id: assistantId,
1959
2012
  role: "assistant",
1960
- content: "\u23F3 Creating article..."
2013
+ content: ""
1961
2014
  }
1962
2015
  ]);
2016
+ setIsStreaming(true);
1963
2017
  try {
1964
- let accumulatedContent = "";
1965
2018
  let hasStartedStreaming = false;
2019
+ let titleBuffer = "";
2020
+ let articleBuffer = "";
2021
+ let keywordsBuffer = [];
2022
+ const updateMessage = () => {
2023
+ const structured = buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
2024
+ setMessages(
2025
+ (prev) => prev.map(
2026
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: structured }) : m
2027
+ )
2028
+ );
2029
+ };
1966
2030
  const contentPrefs = preferences == null ? void 0 : preferences.content;
1967
2031
  const lang = ((_a = preferences == null ? void 0 : preferences.localization) == null ? void 0 : _a.language) === "hi" ? "Hindi" : "English";
1968
2032
  try {
@@ -1976,39 +2040,66 @@ ${title}`
1976
2040
  includeQuotes: contentPrefs == null ? void 0 : contentPrefs.includeQuotes,
1977
2041
  includeFAQ: contentPrefs == null ? void 0 : contentPrefs.includeFAQ,
1978
2042
  targetAudience: contentPrefs == null ? void 0 : contentPrefs.targetAudience,
1979
- onChunk: (chunk) => {
2043
+ onFieldStart: (field) => {
2044
+ setActiveField(field);
2045
+ },
2046
+ onContent: (field, chunk) => {
1980
2047
  hasStartedStreaming = true;
1981
- accumulatedContent += chunk;
2048
+ if (field === "title") titleBuffer += chunk;
2049
+ else articleBuffer += chunk;
2050
+ updateMessage();
2051
+ },
2052
+ onKeywords: (keywords) => {
2053
+ keywordsBuffer = keywords;
2054
+ updateMessage();
2055
+ },
2056
+ onFieldEnd: () => {
2057
+ setActiveField(null);
2058
+ },
2059
+ onComplete: (data) => {
2060
+ const finalContent = data && (data.title || data.article) ? JSON.stringify(data) : buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
1982
2061
  setMessages(
1983
2062
  (prev) => prev.map(
1984
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: accumulatedContent }) : m
2063
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: finalContent }) : m
1985
2064
  )
1986
2065
  );
1987
- },
1988
- onComplete: () => {
1989
- console.log("Streaming completed successfully");
2066
+ setIsStreaming(false);
2067
+ setActiveField(null);
1990
2068
  toast2.success("Article created successfully!");
1991
2069
  },
1992
2070
  onError: async (error) => {
1993
2071
  console.error("Streaming error:", error);
1994
2072
  if (!hasStartedStreaming) {
1995
- console.log("Falling back to regular API...");
1996
2073
  throw error;
1997
2074
  } else {
2075
+ setIsStreaming(false);
2076
+ setActiveField(null);
1998
2077
  toast2.error("Failed to complete article generation");
1999
2078
  setMessages(
2000
2079
  (prev) => prev.map(
2001
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: accumulatedContent || "\u274C Failed to create article. Please try again." }) : m
2080
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: titleBuffer || articleBuffer ? buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer) : "Failed to create article. Please try again." }) : m
2002
2081
  )
2003
2082
  );
2004
2083
  }
2005
2084
  }
2006
2085
  });
2086
+ if (isStreaming) {
2087
+ if (titleBuffer || articleBuffer) {
2088
+ const structured = buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
2089
+ setMessages(
2090
+ (prev) => prev.map(
2091
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: structured }) : m
2092
+ )
2093
+ );
2094
+ }
2095
+ setIsStreaming(false);
2096
+ setActiveField(null);
2097
+ }
2007
2098
  } catch (streamError) {
2008
2099
  console.log("Streaming failed, using regular API...", streamError);
2009
2100
  setMessages(
2010
2101
  (prev) => prev.map(
2011
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "\u23F3 Generating with regular API..." }) : m
2102
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "" }) : m
2012
2103
  )
2013
2104
  );
2014
2105
  const result = await rewriteNewsApi({
@@ -2024,17 +2115,20 @@ ${title}`
2024
2115
  });
2025
2116
  setMessages(
2026
2117
  (prev) => prev.map(
2027
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: result.article || result }) : m
2118
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: typeof result === "string" ? result : JSON.stringify(result) }) : m
2028
2119
  )
2029
2120
  );
2121
+ setIsStreaming(false);
2030
2122
  toast2.success("Article created successfully!");
2031
2123
  }
2032
2124
  } catch (err) {
2033
2125
  console.error("Complete Error:", err);
2126
+ setIsStreaming(false);
2127
+ setActiveField(null);
2034
2128
  toast2.error((err == null ? void 0 : err.message) || "An error occurred while creating the article");
2035
2129
  setMessages(
2036
2130
  (prev) => prev.map(
2037
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "\u274C Failed to create article. Please try again." }) : m
2131
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "Failed to create article. Please try again." }) : m
2038
2132
  )
2039
2133
  );
2040
2134
  }
@@ -2052,9 +2146,10 @@ ${title}`
2052
2146
  if (hasUrl) {
2053
2147
  setMessages((prev) => [
2054
2148
  ...prev,
2055
- { id: assistantId, role: "assistant", content: "\u{1F50D} Analyzing your link..." }
2149
+ { id: assistantId, role: "assistant", content: "Analyzing your link..." }
2056
2150
  ]);
2057
2151
  try {
2152
+ return;
2058
2153
  const result = await analyzeInputApi(text);
2059
2154
  if (result.hasUrl && ((_a = result.options) == null ? void 0 : _a.length) > 0) {
2060
2155
  setAnalyzedData({
@@ -2085,20 +2180,30 @@ ${optionsList}`
2085
2180
  toast2.error("Failed to analyze the link");
2086
2181
  setMessages(
2087
2182
  (prev) => prev.map(
2088
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "\u274C Failed to analyze the link. Please try again." }) : m
2183
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "Failed to analyze the link. Please try again." }) : m
2089
2184
  )
2090
2185
  );
2091
2186
  }
2092
2187
  } else {
2093
2188
  setMessages((prev) => [
2094
2189
  ...prev,
2095
- { id: assistantId, role: "assistant", content: "\u23F3 Generating content..." }
2190
+ { id: assistantId, role: "assistant", content: "" }
2096
2191
  ]);
2097
2192
  setIsStreaming(true);
2098
2193
  try {
2099
2194
  const contentPrefs = preferences == null ? void 0 : preferences.content;
2100
2195
  const lang = ((_b = preferences == null ? void 0 : preferences.localization) == null ? void 0 : _b.language) === "hi" ? "Hindi" : "English";
2101
- let accumulatedContent = "";
2196
+ let titleBuffer = "";
2197
+ let articleBuffer = "";
2198
+ let keywordsBuffer = [];
2199
+ const updateMessage = () => {
2200
+ const structured = buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
2201
+ setMessages(
2202
+ (prev) => prev.map(
2203
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: structured }) : m
2204
+ )
2205
+ );
2206
+ };
2102
2207
  await rewriteNewsStreamApi({
2103
2208
  article: text,
2104
2209
  language: lang,
@@ -2108,30 +2213,58 @@ ${optionsList}`
2108
2213
  includeQuotes: contentPrefs == null ? void 0 : contentPrefs.includeQuotes,
2109
2214
  includeFAQ: contentPrefs == null ? void 0 : contentPrefs.includeFAQ,
2110
2215
  targetAudience: contentPrefs == null ? void 0 : contentPrefs.targetAudience,
2111
- onChunk: (chunk) => {
2112
- accumulatedContent += chunk;
2216
+ onFieldStart: (field) => {
2217
+ setActiveField(field);
2218
+ },
2219
+ onContent: (field, chunk) => {
2220
+ if (field === "title") titleBuffer += chunk;
2221
+ else articleBuffer += chunk;
2222
+ updateMessage();
2223
+ },
2224
+ onKeywords: (keywords) => {
2225
+ keywordsBuffer = keywords;
2226
+ updateMessage();
2227
+ },
2228
+ onFieldEnd: () => {
2229
+ setActiveField(null);
2230
+ },
2231
+ onComplete: (data) => {
2232
+ const finalContent = data && (data.title || data.article) ? JSON.stringify(data) : buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
2113
2233
  setMessages(
2114
2234
  (prev) => prev.map(
2115
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: accumulatedContent }) : m
2235
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: finalContent }) : m
2116
2236
  )
2117
2237
  );
2118
- },
2119
- onComplete: () => {
2120
2238
  setIsStreaming(false);
2239
+ setActiveField(null);
2121
2240
  toast2.success("Content generated!");
2122
2241
  },
2123
2242
  onError: (error) => {
2124
2243
  console.error("Stream error:", error);
2125
2244
  setIsStreaming(false);
2245
+ setActiveField(null);
2126
2246
  toast2.error("Failed to generate content");
2127
2247
  }
2128
2248
  });
2249
+ if (isStreaming) {
2250
+ if (titleBuffer || articleBuffer) {
2251
+ const structured = buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
2252
+ setMessages(
2253
+ (prev) => prev.map(
2254
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: structured }) : m
2255
+ )
2256
+ );
2257
+ }
2258
+ setIsStreaming(false);
2259
+ setActiveField(null);
2260
+ }
2129
2261
  } catch (err) {
2130
2262
  console.error("Send message error:", err);
2131
2263
  setIsStreaming(false);
2264
+ setActiveField(null);
2132
2265
  setMessages(
2133
2266
  (prev) => prev.map(
2134
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "\u274C Failed to generate content. Please try again." }) : m
2267
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "Failed to generate content. Please try again." }) : m
2135
2268
  )
2136
2269
  );
2137
2270
  }
@@ -2145,12 +2278,22 @@ ${optionsList}`
2145
2278
  setMessages((prev) => [
2146
2279
  ...prev,
2147
2280
  { id: crypto.randomUUID(), role: "user", content: `Action: ${actionName}` },
2148
- { id: assistantId, role: "assistant", content: "\u23F3 Creating content..." }
2281
+ { id: assistantId, role: "assistant", content: "" }
2149
2282
  ]);
2150
2283
  setIsStreaming(true);
2151
2284
  try {
2152
2285
  const contentPrefs = preferences == null ? void 0 : preferences.content;
2153
- let accumulatedContent = "";
2286
+ let titleBuffer = "";
2287
+ let articleBuffer = "";
2288
+ let keywordsBuffer = [];
2289
+ const updateMessage = () => {
2290
+ const structured = buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
2291
+ setMessages(
2292
+ (prev) => prev.map(
2293
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: structured }) : m
2294
+ )
2295
+ );
2296
+ };
2154
2297
  try {
2155
2298
  await createContentStreamApi({
2156
2299
  url,
@@ -2161,24 +2304,51 @@ ${optionsList}`
2161
2304
  style: contentPrefs == null ? void 0 : contentPrefs.style,
2162
2305
  wordCount: contentPrefs == null ? void 0 : contentPrefs.wordCount
2163
2306
  },
2164
- onChunk: (chunk) => {
2165
- accumulatedContent += chunk;
2307
+ onFieldStart: (field) => {
2308
+ setActiveField(field);
2309
+ },
2310
+ onContent: (field, chunk) => {
2311
+ if (field === "title") titleBuffer += chunk;
2312
+ else articleBuffer += chunk;
2313
+ updateMessage();
2314
+ },
2315
+ onKeywords: (keywords) => {
2316
+ keywordsBuffer = keywords;
2317
+ updateMessage();
2318
+ },
2319
+ onFieldEnd: () => {
2320
+ setActiveField(null);
2321
+ },
2322
+ onComplete: (data) => {
2323
+ const finalContent = data && (data.title || data.article) ? JSON.stringify(data) : buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
2166
2324
  setMessages(
2167
2325
  (prev) => prev.map(
2168
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: accumulatedContent }) : m
2326
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: finalContent }) : m
2169
2327
  )
2170
2328
  );
2171
- },
2172
- onComplete: () => {
2173
2329
  setIsStreaming(false);
2330
+ setActiveField(null);
2174
2331
  toast2.success("Content created!");
2175
2332
  },
2176
2333
  onError: (error) => {
2177
2334
  console.error("Stream error:", error);
2178
2335
  setIsStreaming(false);
2336
+ setActiveField(null);
2179
2337
  toast2.error("Failed to create content");
2180
2338
  }
2181
2339
  });
2340
+ if (isStreaming) {
2341
+ if (titleBuffer || articleBuffer) {
2342
+ const structured = buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
2343
+ setMessages(
2344
+ (prev) => prev.map(
2345
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: structured }) : m
2346
+ )
2347
+ );
2348
+ }
2349
+ setIsStreaming(false);
2350
+ setActiveField(null);
2351
+ }
2182
2352
  } catch (e) {
2183
2353
  const result = await createContentApi({ url, content, actionId, settings: {
2184
2354
  tone: contentPrefs == null ? void 0 : contentPrefs.tone,
@@ -2187,25 +2357,27 @@ ${optionsList}`
2187
2357
  } });
2188
2358
  setMessages(
2189
2359
  (prev) => prev.map(
2190
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: result.article || result.content || JSON.stringify(result) }) : m
2360
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: typeof result === "string" ? result : JSON.stringify(result) }) : m
2191
2361
  )
2192
2362
  );
2193
2363
  setIsStreaming(false);
2364
+ setActiveField(null);
2194
2365
  toast2.success("Content created!");
2195
2366
  }
2196
2367
  } catch (err) {
2197
2368
  console.error("Create content error:", err);
2198
2369
  setIsStreaming(false);
2370
+ setActiveField(null);
2199
2371
  setMessages(
2200
2372
  (prev) => prev.map(
2201
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "\u274C Failed to create content. Please try again." }) : m
2373
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "Failed to create content. Please try again." }) : m
2202
2374
  )
2203
2375
  );
2204
2376
  }
2205
2377
  };
2206
2378
  if (loading) {
2207
2379
  return /* @__PURE__ */ jsx11("div", { className: "cnfy-loading", children: /* @__PURE__ */ jsxs8("div", { className: "cnfy-loading-inner", children: [
2208
- /* @__PURE__ */ jsx11(Loader2, { className: "cnfy-loading-spinner cnfy-animate-spin" }),
2380
+ /* @__PURE__ */ jsx11(Loader22, { className: "cnfy-loading-spinner cnfy-animate-spin" }),
2209
2381
  /* @__PURE__ */ jsx11("p", { className: "cnfy-loading-text", children: "Loading..." })
2210
2382
  ] }) });
2211
2383
  }
@@ -2219,6 +2391,7 @@ ${optionsList}`
2219
2391
  onSend: handleSendMessage,
2220
2392
  onSelectNews: handleRecreate,
2221
2393
  isStreaming,
2394
+ activeField,
2222
2395
  analyzedData,
2223
2396
  onSelectAction: handleSelectAction,
2224
2397
  onPost