@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.js CHANGED
@@ -200,7 +200,7 @@ var savePreferencesApi = async ({
200
200
  var import_jsx_runtime = require("react/jsx-runtime");
201
201
  var defaultPreferences = {
202
202
  chatbot: {
203
- name: "AI News Assistant",
203
+ name: "ContenifyAI Assistant",
204
204
  primaryColor: "#10b981",
205
205
  secondaryColor: "#064e3b",
206
206
  theme: "system"
@@ -299,39 +299,80 @@ function usePreferences() {
299
299
  // components/chatbot/ChatBot.tsx
300
300
  var import_react10 = require("react");
301
301
 
302
- // util/util.ts
303
- function extractArticleContent(raw) {
304
- if (!raw) {
305
- return { article: "", metaKeywords: [] };
306
- }
307
- if (!raw.trim().startsWith("{")) {
308
- return {
309
- article: raw,
310
- metaKeywords: []
311
- };
302
+ // src/utils/ aiJsonParser.ts
303
+ function extractJSON(raw) {
304
+ if (!raw) return null;
305
+ let cleaned = raw.replace(/```json/gi, "").replace(/```/g, "").trim();
306
+ const first = cleaned.indexOf("{");
307
+ const last = cleaned.lastIndexOf("}");
308
+ if (first === -1 || last === -1 || last <= first) return null;
309
+ const possibleJson = cleaned.substring(first, last + 1);
310
+ try {
311
+ return JSON.parse(possibleJson);
312
+ } catch (err) {
313
+ console.warn("JSON parse failed:", err);
314
+ return null;
312
315
  }
316
+ }
317
+
318
+ // src/utils/decodeUnicode.ts
319
+ function decodeUnicode(text) {
320
+ if (!text) return "";
313
321
  try {
314
- const parsed = JSON.parse(raw);
315
- return {
316
- title: parsed.title || "",
317
- article: parsed.article || parsed.content || "",
318
- metaKeywords: Array.isArray(parsed.metaKeywords) ? parsed.metaKeywords : []
319
- };
322
+ return JSON.parse(`"${text.replace(/"/g, '\\"')}"`);
320
323
  } catch (e) {
321
- const article = raw.replace(/^[\s\S]*?"article"\s*:\s*"/, "").replace(/"\s*,\s*"metaKeywords"[\s\S]*$/, "").replace(/"}\s*$/, "") || raw;
324
+ return text;
325
+ }
326
+ }
327
+
328
+ // src/utils/articleTextToHtml.ts
329
+ function articleTextToHtml(text) {
330
+ if (!text) return "";
331
+ text = text.replace(/\\n/g, "\n");
332
+ const lines = text.split("\n").map((line) => line.trim()).filter(Boolean);
333
+ let html = "";
334
+ for (const line of lines) {
335
+ if (line.length < 80 && !line.endsWith("\u0964") && !line.endsWith(".")) {
336
+ html += `<h2>${line}</h2>`;
337
+ continue;
338
+ }
339
+ html += `<p>${line}</p>`;
340
+ }
341
+ return html;
342
+ }
343
+
344
+ // src/utils/formatAIContent.ts
345
+ function formatAIContent(raw) {
346
+ const empty = {
347
+ isArticle: false,
348
+ title: "",
349
+ subtitle: "",
350
+ articleHtml: "",
351
+ article: "",
352
+ metaDescription: "",
353
+ metaKeywords: [],
354
+ plainText: ""
355
+ };
356
+ if (!raw) return empty;
357
+ const parsed = extractJSON(raw);
358
+ if (parsed && (parsed.title || parsed.article)) {
359
+ const rawArticle = (parsed.article || "").trim();
360
+ const safeArticle = decodeUnicode(rawArticle);
361
+ const isHtml = /<[a-z][\s\S]*>/i.test(safeArticle);
362
+ const articleHtml = isHtml ? safeArticle : articleTextToHtml(safeArticle);
322
363
  return {
323
- article,
324
- metaKeywords: []
364
+ isArticle: true,
365
+ title: parsed.title || "",
366
+ subtitle: parsed.subtitle || "",
367
+ articleHtml,
368
+ article: rawArticle,
369
+ metaDescription: parsed.metaDescription || "",
370
+ metaKeywords: Array.isArray(parsed.metaKeywords) ? parsed.metaKeywords : [],
371
+ plainText: raw
325
372
  };
326
373
  }
374
+ return __spreadProps(__spreadValues({}, empty), { plainText: raw });
327
375
  }
328
- var hexToRgba = (hex, opacity) => {
329
- const sanitizedHex = hex.replace("#", "");
330
- const r = parseInt(sanitizedHex.substring(0, 2), 16);
331
- const g = parseInt(sanitizedHex.substring(2, 4), 16);
332
- const b = parseInt(sanitizedHex.substring(4, 6), 16);
333
- return `rgba(${r}, ${g}, ${b}, ${opacity})`;
334
- };
335
376
 
336
377
  // components/chatbot/ChatWindow.tsx
337
378
  var import_lucide_react4 = require("lucide-react");
@@ -513,38 +554,38 @@ function useTheme() {
513
554
 
514
555
  // components/chatbot/EditModal.tsx
515
556
  var import_jsx_runtime3 = require("react/jsx-runtime");
557
+ function toSlug(text) {
558
+ return text.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-");
559
+ }
516
560
  function EditModal({
517
561
  isOpen,
562
+ initialTitle = "",
563
+ initialSubtitle = "",
518
564
  initialContent,
565
+ initialMetaDescription = "",
519
566
  metaKeywords,
520
567
  onClose,
521
568
  onSaveDraft,
522
569
  onPost
523
570
  }) {
524
571
  const { primaryColor } = useTheme();
572
+ const [title, setTitle] = (0, import_react4.useState)("");
573
+ const [subtitle, setSubtitle] = (0, import_react4.useState)("");
574
+ const [slug, setSlug] = (0, import_react4.useState)("");
575
+ const [slugEdited, setSlugEdited] = (0, import_react4.useState)(false);
576
+ const [metaDescription, setMetaDescription] = (0, import_react4.useState)("");
525
577
  const [content, setContent] = (0, import_react4.useState)("");
526
578
  const [keywords, setKeywords] = (0, import_react4.useState)(metaKeywords);
527
579
  const [newKeyword, setNewKeyword] = (0, import_react4.useState)("");
528
- const markdownToHtml = (text) => {
529
- const lines = text.split("\n").filter((line) => line.trim());
530
- let html = "";
531
- lines.forEach((line) => {
532
- const trimmed = line.trim();
533
- if (trimmed.startsWith("# ")) {
534
- html += `<h1>${trimmed.replace(/^#\s+/, "")}</h1>`;
535
- } else if (trimmed.startsWith("## ")) {
536
- html += `<h2>${trimmed.replace(/^##\s+/, "")}</h2>`;
537
- } else {
538
- html += `<p>${trimmed}</p>`;
539
- }
540
- });
541
- return html;
542
- };
543
580
  (0, import_react4.useEffect)(() => {
544
- const htmlContent = markdownToHtml(initialContent);
545
- setContent(htmlContent);
581
+ setTitle(initialTitle);
582
+ setSubtitle(initialSubtitle);
583
+ setMetaDescription(initialMetaDescription);
584
+ setSlug(toSlug(initialTitle));
585
+ setSlugEdited(false);
586
+ setContent(initialContent);
546
587
  setKeywords(metaKeywords);
547
- }, [initialContent, metaKeywords]);
588
+ }, [initialTitle, initialSubtitle, initialContent, initialMetaDescription, metaKeywords]);
548
589
  (0, import_react4.useEffect)(() => {
549
590
  if (isOpen) {
550
591
  document.body.style.overflow = "hidden";
@@ -555,6 +596,16 @@ function EditModal({
555
596
  document.body.style.overflow = "";
556
597
  };
557
598
  }, [isOpen]);
599
+ const handleTitleChange = (value) => {
600
+ setTitle(value);
601
+ if (!slugEdited) {
602
+ setSlug(toSlug(value));
603
+ }
604
+ };
605
+ const handleSlugChange = (value) => {
606
+ setSlug(toSlug(value));
607
+ setSlugEdited(true);
608
+ };
558
609
  const handleAddKeyword = () => {
559
610
  if (newKeyword.trim() && !keywords.includes(newKeyword.trim())) {
560
611
  setKeywords([...keywords, newKeyword.trim()]);
@@ -570,28 +621,62 @@ function EditModal({
570
621
  handleAddKeyword();
571
622
  }
572
623
  };
624
+ const getEditData = () => ({
625
+ title,
626
+ subtitle,
627
+ article: content,
628
+ metaDescription,
629
+ slug,
630
+ keywords
631
+ });
573
632
  if (!isOpen) return null;
574
633
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cnfy-edit-modal-overlay", children: [
575
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
576
- "div",
577
- {
578
- className: "cnfy-edit-modal-backdrop",
579
- onClick: onClose
580
- }
581
- ),
634
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cnfy-edit-modal-backdrop", onClick: onClose }),
582
635
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cnfy-edit-modal", children: [
583
636
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cnfy-edit-modal-header", children: [
584
637
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { className: "cnfy-edit-modal-title", children: "Edit Article" }),
585
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
586
- "button",
587
- {
588
- onClick: onClose,
589
- className: "cnfy-edit-modal-close-btn",
590
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react2.X, { size: 20, className: "cnfy-edit-modal-close-icon" })
591
- }
592
- )
638
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { onClick: onClose, className: "cnfy-edit-modal-close-btn", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react2.X, { size: 20, className: "cnfy-edit-modal-close-icon" }) })
593
639
  ] }),
594
640
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cnfy-edit-modal-body", children: [
641
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
642
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { className: "cnfy-edit-label", children: "Title" }),
643
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
644
+ "input",
645
+ {
646
+ type: "text",
647
+ value: title,
648
+ onChange: (e) => handleTitleChange(e.target.value),
649
+ placeholder: "Article title...",
650
+ className: "cnfy-edit-input"
651
+ }
652
+ )
653
+ ] }),
654
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
655
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { className: "cnfy-edit-label", children: "Subtitle" }),
656
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
657
+ "input",
658
+ {
659
+ type: "text",
660
+ value: subtitle,
661
+ onChange: (e) => setSubtitle(e.target.value),
662
+ placeholder: "Article subtitle...",
663
+ className: "cnfy-edit-input"
664
+ }
665
+ )
666
+ ] }),
667
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
668
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { className: "cnfy-edit-label", children: "Slug" }),
669
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
670
+ "input",
671
+ {
672
+ type: "text",
673
+ value: slug,
674
+ onChange: (e) => handleSlugChange(e.target.value),
675
+ placeholder: "article-url-slug",
676
+ className: "cnfy-edit-input cnfy-edit-input--mono"
677
+ }
678
+ )
679
+ ] }),
595
680
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
596
681
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { className: "cnfy-edit-label", children: "Article Content" }),
597
682
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
@@ -604,26 +689,32 @@ function EditModal({
604
689
  )
605
690
  ] }),
606
691
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
607
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { className: "cnfy-edit-label", children: "Meta Keywords" }),
608
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cnfy-keyword-list", children: keywords.map((keyword, index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
609
- "span",
692
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { className: "cnfy-edit-label", children: "Meta Description" }),
693
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
694
+ "textarea",
610
695
  {
611
- className: "cnfy-keyword-tag",
612
- children: [
613
- "#",
614
- keyword,
615
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
616
- "button",
617
- {
618
- onClick: () => handleRemoveKeyword(index),
619
- className: "cnfy-keyword-remove-btn",
620
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react2.X, { size: 14 })
621
- }
622
- )
623
- ]
624
- },
625
- index
626
- )) }),
696
+ value: metaDescription,
697
+ onChange: (e) => setMetaDescription(e.target.value),
698
+ placeholder: "Brief description for search engines...",
699
+ className: "cnfy-edit-textarea",
700
+ rows: 3
701
+ }
702
+ )
703
+ ] }),
704
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
705
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { className: "cnfy-edit-label", children: "Meta Keywords" }),
706
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cnfy-keyword-list", children: keywords.map((keyword, index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "cnfy-keyword-tag", children: [
707
+ "#",
708
+ keyword,
709
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
710
+ "button",
711
+ {
712
+ onClick: () => handleRemoveKeyword(index),
713
+ className: "cnfy-keyword-remove-btn",
714
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react2.X, { size: 14 })
715
+ }
716
+ )
717
+ ] }, index)) }),
627
718
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cnfy-keyword-input-row", children: [
628
719
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
629
720
  "input",
@@ -636,30 +727,16 @@ function EditModal({
636
727
  className: "cnfy-keyword-input"
637
728
  }
638
729
  ),
639
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
640
- "button",
641
- {
642
- onClick: handleAddKeyword,
643
- className: "cnfy-btn-add-keyword",
644
- children: "Add"
645
- }
646
- )
730
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { onClick: handleAddKeyword, className: "cnfy-btn-add-keyword", children: "Add" })
647
731
  ] })
648
732
  ] })
649
733
  ] }),
650
734
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cnfy-edit-modal-footer", children: [
735
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { onClick: onClose, className: "cnfy-btn-footer-cancel", children: "Cancel" }),
651
736
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
652
737
  "button",
653
738
  {
654
- onClick: onClose,
655
- className: "cnfy-btn-footer-cancel",
656
- children: "Cancel"
657
- }
658
- ),
659
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
660
- "button",
661
- {
662
- onClick: () => onSaveDraft(content, keywords),
739
+ onClick: () => onSaveDraft(getEditData()),
663
740
  className: "cnfy-btn-save-draft",
664
741
  children: "Save Draft"
665
742
  }
@@ -667,7 +744,7 @@ function EditModal({
667
744
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
668
745
  "button",
669
746
  {
670
- onClick: () => onPost(content, keywords),
747
+ onClick: () => onPost(getEditData()),
671
748
  className: "cnfy-btn-post-article",
672
749
  style: { backgroundColor: primaryColor, color: "#fff" },
673
750
  children: "Post"
@@ -881,12 +958,13 @@ function ChatWindow({
881
958
  onSend,
882
959
  onSelectNews,
883
960
  isStreaming = false,
961
+ activeField,
884
962
  analyzedData,
885
963
  onSelectAction,
886
964
  onPost: onPostCallback
887
965
  }) {
888
966
  var _a, _b;
889
- const { loading, showNewsPanel } = useTheme();
967
+ const { showNewsPanel } = useTheme();
890
968
  const bottomRef = (0, import_react6.useRef)(null);
891
969
  const textareaRef = (0, import_react6.useRef)(null);
892
970
  const dropdownRef = (0, import_react6.useRef)(null);
@@ -899,40 +977,48 @@ function ChatWindow({
899
977
  const [selectedSource, setSelectedSource] = (0, import_react6.useState)(null);
900
978
  const [loadingSources, setLoadingSources] = (0, import_react6.useState)(false);
901
979
  const [scraping, setScraping] = (0, import_react6.useState)(false);
902
- const [editModal, setEditModal] = (0, import_react6.useState)({ isOpen: false, content: "", metaKeywords: [], messageId: "" });
980
+ const [editModal, setEditModal] = (0, import_react6.useState)({ isOpen: false, title: "", subtitle: "", content: "", metaDescription: "", metaKeywords: [], messageId: "" });
903
981
  const { primaryColor } = useTheme();
904
982
  const { preferences } = usePreferences();
905
983
  const preferredLanguage = (_a = preferences == null ? void 0 : preferences.localization) == null ? void 0 : _a.language;
906
- const handleCopy = async (blocks, messageId) => {
984
+ const handleCopy = async (parsed, messageId) => {
907
985
  try {
908
- const formattedText = blocksToFormattedText(blocks);
909
- await navigator.clipboard.writeText(formattedText);
986
+ const tempDiv = document.createElement("div");
987
+ let textContent = "";
988
+ if (parsed.title) textContent += parsed.title + "\n\n";
989
+ if (parsed.subtitle) textContent += parsed.subtitle + "\n\n";
990
+ if (parsed.articleHtml) {
991
+ tempDiv.innerHTML = parsed.articleHtml;
992
+ textContent += tempDiv.textContent || tempDiv.innerText || "";
993
+ }
994
+ if (parsed.metaDescription) textContent += "\n\nMeta Description:\n" + parsed.metaDescription;
995
+ await navigator.clipboard.writeText(textContent);
910
996
  setCopiedId(messageId);
911
997
  setTimeout(() => setCopiedId(null), 2e3);
912
998
  } catch (err) {
913
999
  console.error("Failed to copy:", err);
914
1000
  }
915
1001
  };
916
- const blocksToFormattedText = (blocks) => {
917
- return blocks.map((block) => {
918
- if (block.type === "h1") return `# ${block.text}`;
919
- if (block.type === "h2") return `## ${block.text}`;
920
- return block.text;
921
- }).join("\n\n");
922
- };
923
- const handleEdit = (blocks, metaKeywords, messageId) => {
924
- const formattedContent = blocksToFormattedText(blocks);
925
- setEditModal({ isOpen: true, content: formattedContent, metaKeywords, messageId });
1002
+ const handleEdit = (parsed, messageId) => {
1003
+ setEditModal({
1004
+ isOpen: true,
1005
+ title: parsed.title || "",
1006
+ subtitle: parsed.subtitle || "",
1007
+ content: parsed.articleHtml || "",
1008
+ metaDescription: parsed.metaDescription || "",
1009
+ metaKeywords: parsed.metaKeywords,
1010
+ messageId
1011
+ });
926
1012
  };
927
1013
  const handleCloseModal = () => {
928
- setEditModal({ isOpen: false, content: "", metaKeywords: [], messageId: "" });
1014
+ setEditModal({ isOpen: false, title: "", subtitle: "", content: "", metaDescription: "", metaKeywords: [], messageId: "" });
929
1015
  };
930
- const handleSaveDraft = (content, keywords) => {
931
- console.log("Saving draft:", { content, keywords });
1016
+ const handleSaveDraft = (data) => {
1017
+ console.log("Saving draft:", data);
932
1018
  handleCloseModal();
933
1019
  };
934
- const handlePost = (content, keywords) => {
935
- onPostCallback == null ? void 0 : onPostCallback(content, keywords);
1020
+ const handlePost = (data) => {
1021
+ onPostCallback == null ? void 0 : onPostCallback(data.article, data.keywords);
936
1022
  handleCloseModal();
937
1023
  };
938
1024
  (0, import_react6.useLayoutEffect)(() => {
@@ -1021,77 +1107,62 @@ function ChatWindow({
1021
1107
  document.removeEventListener("mousedown", handleClickOutside);
1022
1108
  };
1023
1109
  }, [showNewsDropdown]);
1024
- function formatAIContent(raw) {
1025
- const extracted = extractArticleContent(raw);
1026
- if (!extracted || !extracted.article) {
1027
- return { blocks: [], metaKeywords: [] };
1028
- }
1029
- const { article, metaKeywords } = extracted;
1030
- 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");
1031
- const lines = normalized.split("\n").map((line) => line.replace(/<[^>]+>/g, "").trim()).filter(Boolean);
1032
- const blocks = [];
1033
- lines.forEach((line, index) => {
1034
- if (index === 0 && line.length < 120) {
1035
- blocks.push({ type: "h1", text: line });
1036
- return;
1037
- }
1038
- if (line.length < 90 && !line.endsWith("\u0964") && !line.endsWith(".")) {
1039
- blocks.push({ type: "h2", text: line });
1040
- return;
1041
- }
1042
- blocks.push({ type: "p", text: line });
1043
- });
1044
- return { blocks, metaKeywords };
1045
- }
1046
1110
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "cnfy-chat", children: [
1047
1111
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-chat-area", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "cnfy-chat-scroll", children: [
1048
1112
  messages.map((msg) => {
1049
- var _a2;
1113
+ var _a2, _b2;
1050
1114
  const parsed = formatAIContent(msg.content);
1051
1115
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-msg", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: msg.role === "assistant" ? `cnfy-msg-body` : `cnfy-msg-body you`, children: [
1052
- msg.role === "assistant" && parsed.blocks.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-msg-copy-row", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1116
+ msg.role === "assistant" && parsed.isArticle && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-msg-copy-row", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1053
1117
  "button",
1054
1118
  {
1055
- onClick: () => handleCopy(parsed.blocks, msg.id),
1119
+ onClick: () => handleCopy(parsed, msg.id),
1056
1120
  className: "cnfy-copy-btn",
1057
1121
  title: "Copy to clipboard",
1058
1122
  children: copiedId === msg.id ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Check, { size: 16, className: "cnfy-copy-icon--copied" }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Copy, { size: 16 })
1059
1123
  }
1060
1124
  ) }),
1061
- parsed.blocks.map((block, idx) => {
1062
- if (block.type === "h1") {
1063
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1064
- "h1",
1065
- {
1066
- className: "cnfy-block-h1",
1067
- children: block.text
1068
- },
1069
- idx
1070
- );
1071
- }
1072
- if (block.type === "h2") {
1073
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1074
- "h2",
1075
- {
1076
- className: "cnfy-block-h2",
1077
- children: block.text
1078
- },
1079
- idx
1080
- );
1081
- }
1082
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "cnfy-block-p", children: block.text }, idx);
1083
- }),
1084
- parsed.metaKeywords.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-msg-keywords", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-msg-keywords-list", children: parsed.metaKeywords.map((tag, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1085
- "span",
1086
- {
1087
- className: "cnfy-msg-keyword-tag",
1088
- children: [
1089
- "#",
1090
- tag
1091
- ]
1092
- },
1093
- i
1094
- )) }) }),
1125
+ msg.role === "assistant" && isStreaming && msg.id === ((_a2 = messages[messages.length - 1]) == null ? void 0 : _a2.id) && !parsed.isArticle && !parsed.plainText && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "cnfy-creating-indicator", children: [
1126
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Loader2, { size: 16, className: "cnfy-animate-spin" }),
1127
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Creating article..." })
1128
+ ] }),
1129
+ parsed.isArticle ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1130
+ parsed.title && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("h1", { className: "cnfy-block-h1", children: [
1131
+ parsed.title,
1132
+ activeField === "title" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "cnfy-cursor-blink", children: "|" })
1133
+ ] }),
1134
+ parsed.subtitle && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("h1", { className: "cnfy-block-h2", children: [
1135
+ parsed.subtitle,
1136
+ activeField === "subtitle" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "cnfy-cursor-blink", children: "|" })
1137
+ ] }),
1138
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("hr", { className: "cnfy-divider" }),
1139
+ parsed.articleHtml && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1140
+ "div",
1141
+ {
1142
+ className: "cnfy-article-content",
1143
+ dangerouslySetInnerHTML: { __html: parsed.articleHtml }
1144
+ }
1145
+ ),
1146
+ activeField === "article" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "cnfy-cursor-blink", children: "|" })
1147
+ ] }) : (
1148
+ /* Plain text messages (status, error, options, etc.) */
1149
+ parsed.plainText && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "cnfy-block-p", children: parsed.plainText })
1150
+ ),
1151
+ parsed.metaDescription && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "cnfy-msg-keywords", children: [
1152
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-msg-keywords-label", children: "Meta Description" }),
1153
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-block-p", children: parsed.metaDescription })
1154
+ ] }),
1155
+ parsed.metaKeywords.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "cnfy-msg-keywords", children: [
1156
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-msg-keywords-label", children: "Keywords" }),
1157
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-msg-keywords-list", children: parsed.metaKeywords.map((tag, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1158
+ "span",
1159
+ {
1160
+ className: "cnfy-msg-keyword-tag",
1161
+ children: tag
1162
+ },
1163
+ i
1164
+ )) })
1165
+ ] }),
1095
1166
  msg.role === "assistant" && (analyzedData == null ? void 0 : analyzedData.messageId) === msg.id && analyzedData.options.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-action-options", children: analyzedData.options.map((option) => {
1096
1167
  const IconComponent = ACTION_ICONS[option.id] || import_lucide_react4.FileText;
1097
1168
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
@@ -1107,11 +1178,11 @@ function ChatWindow({
1107
1178
  option.id
1108
1179
  );
1109
1180
  }) }),
1110
- 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__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "cnfy-msg-actions", children: [
1181
+ 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__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "cnfy-msg-actions", children: [
1111
1182
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1112
1183
  "button",
1113
1184
  {
1114
- onClick: () => handleEdit(parsed.blocks, parsed.metaKeywords, msg.id),
1185
+ onClick: () => handleEdit(parsed, msg.id),
1115
1186
  className: "cnfy-btn-edit",
1116
1187
  children: [
1117
1188
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Edit3, { size: 16 }),
@@ -1122,7 +1193,7 @@ function ChatWindow({
1122
1193
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1123
1194
  "button",
1124
1195
  {
1125
- onClick: () => handlePost(msg.content, parsed.metaKeywords),
1196
+ onClick: () => handlePost({ article: parsed.articleHtml, keywords: parsed.metaKeywords }),
1126
1197
  className: "cnfy-btn-post",
1127
1198
  style: { backgroundColor: primaryColor, color: "#fff" },
1128
1199
  children: [
@@ -1264,7 +1335,8 @@ function ChatWindow({
1264
1335
  rows: 1,
1265
1336
  placeholder: "Ask AI something\u2026",
1266
1337
  className: `cnfy-chat-textarea ${!showNewsPanel ? "cnfy-chat-textarea--with-pulse" : ""}`,
1267
- style: { maxHeight: "200px", overflowY: input.split("\n").length > 6 ? "auto" : "hidden" }
1338
+ style: { maxHeight: "200px", overflowY: input.split("\n").length > 6 ? "auto" : "hidden" },
1339
+ disabled: true
1268
1340
  }
1269
1341
  ),
1270
1342
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
@@ -1281,11 +1353,14 @@ function ChatWindow({
1281
1353
  EditModal,
1282
1354
  {
1283
1355
  isOpen: editModal.isOpen,
1356
+ initialTitle: editModal.title,
1357
+ initialSubtitle: editModal.subtitle,
1284
1358
  initialContent: editModal.content,
1359
+ initialMetaDescription: editModal.metaDescription,
1285
1360
  metaKeywords: editModal.metaKeywords,
1286
1361
  onClose: handleCloseModal,
1287
- onSaveDraft: handleSaveDraft,
1288
- onPost: handlePost
1362
+ onSaveDraft: (data) => handleSaveDraft({ article: data.article, keywords: data.keywords }),
1363
+ onPost: (data) => handlePost({ article: data.article, keywords: data.keywords })
1289
1364
  }
1290
1365
  )
1291
1366
  ] });
@@ -1400,6 +1475,15 @@ var ClientSelect = (props) => {
1400
1475
  };
1401
1476
  var ClientSelect_default = ClientSelect;
1402
1477
 
1478
+ // src/utils/util.ts
1479
+ var hexToRgba = (hex, opacity) => {
1480
+ const sanitizedHex = hex.replace("#", "");
1481
+ const r = parseInt(sanitizedHex.substring(0, 2), 16);
1482
+ const g = parseInt(sanitizedHex.substring(2, 4), 16);
1483
+ const b = parseInt(sanitizedHex.substring(4, 6), 16);
1484
+ return `rgba(${r}, ${g}, ${b}, ${opacity})`;
1485
+ };
1486
+
1403
1487
  // components/preferences/Preferences.tsx
1404
1488
  var import_jsx_runtime10 = require("react/jsx-runtime");
1405
1489
  var STATE_OPTIONS = [
@@ -1718,12 +1802,72 @@ function PreferencesPage() {
1718
1802
  }
1719
1803
 
1720
1804
  // services/chat.service.ts
1805
+ function getHeaders() {
1806
+ const apiKey = getApiKey();
1807
+ const headers = {
1808
+ "Content-Type": "application/json"
1809
+ };
1810
+ if (apiKey) {
1811
+ headers["x-api-key"] = apiKey;
1812
+ }
1813
+ return headers;
1814
+ }
1815
+ async function processSSEStream(reader, callbacks) {
1816
+ const decoder = new TextDecoder();
1817
+ let buffer = "";
1818
+ const processLines = (lines) => {
1819
+ var _a, _b, _c, _d, _e;
1820
+ for (const line of lines) {
1821
+ const trimmed = line.trim();
1822
+ if (!trimmed.startsWith("data: ")) continue;
1823
+ const data = trimmed.slice(6).trim();
1824
+ try {
1825
+ const event = JSON.parse(data);
1826
+ switch (event.type) {
1827
+ case "start":
1828
+ break;
1829
+ case "field_start":
1830
+ (_a = callbacks.onFieldStart) == null ? void 0 : _a.call(callbacks, event.field);
1831
+ break;
1832
+ case "content":
1833
+ callbacks.onContent(event.field, event.content);
1834
+ break;
1835
+ case "keywords":
1836
+ (_b = callbacks.onKeywords) == null ? void 0 : _b.call(callbacks, event.content);
1837
+ break;
1838
+ case "field_end":
1839
+ (_c = callbacks.onFieldEnd) == null ? void 0 : _c.call(callbacks, event.field);
1840
+ break;
1841
+ case "done":
1842
+ (_d = callbacks.onComplete) == null ? void 0 : _d.call(callbacks, event.data);
1843
+ return true;
1844
+ case "error":
1845
+ (_e = callbacks.onError) == null ? void 0 : _e.call(callbacks, new Error(event.message));
1846
+ return true;
1847
+ }
1848
+ } catch (e) {
1849
+ }
1850
+ }
1851
+ return false;
1852
+ };
1853
+ while (true) {
1854
+ const { done, value } = await reader.read();
1855
+ if (done) break;
1856
+ buffer += decoder.decode(value, { stream: true });
1857
+ const lines = buffer.split("\n");
1858
+ buffer = lines.pop() || "";
1859
+ if (processLines(lines)) return;
1860
+ }
1861
+ if (buffer.trim()) {
1862
+ processLines([buffer]);
1863
+ }
1864
+ }
1721
1865
  var analyzeInputApi = async (input) => {
1722
- const { data } = await api_default.post("/chat/analyze-input", { input });
1866
+ const { data } = await api_default.post("/chatboat/analyze-input", { input });
1723
1867
  return data.data;
1724
1868
  };
1725
1869
  var createContentApi = async (payload) => {
1726
- const { data } = await api_default.post("/chat/create-content", payload);
1870
+ const { data } = await api_default.post("/chatboat/create-content", payload);
1727
1871
  return data.data;
1728
1872
  };
1729
1873
  var createContentStreamApi = async ({
@@ -1731,22 +1875,18 @@ var createContentStreamApi = async ({
1731
1875
  content,
1732
1876
  actionId,
1733
1877
  settings,
1734
- onChunk,
1878
+ onFieldStart,
1879
+ onContent,
1880
+ onKeywords,
1881
+ onFieldEnd,
1735
1882
  onComplete,
1736
1883
  onError
1737
1884
  }) => {
1738
1885
  var _a;
1739
1886
  const API_BASE_URL = getApiBaseUrl();
1740
- const apiKey = getApiKey();
1741
- const headers = {
1742
- "Content-Type": "application/json"
1743
- };
1744
- if (apiKey) {
1745
- headers["x-api-key"] = apiKey;
1746
- }
1747
1887
  const response = await fetch(`${API_BASE_URL}/chat/create-content/stream`, {
1748
1888
  method: "POST",
1749
- headers,
1889
+ headers: getHeaders(),
1750
1890
  credentials: "include",
1751
1891
  body: JSON.stringify({ url, content, actionId, settings })
1752
1892
  });
@@ -1755,50 +1895,17 @@ var createContentStreamApi = async ({
1755
1895
  throw new Error(`HTTP error! status: ${response.status} - ${errorText}`);
1756
1896
  }
1757
1897
  const reader = (_a = response.body) == null ? void 0 : _a.getReader();
1758
- const decoder = new TextDecoder();
1759
1898
  if (!reader) {
1760
1899
  throw new Error("No reader available");
1761
1900
  }
1762
- let buffer = "";
1763
- let hasReceivedData = false;
1764
- while (true) {
1765
- const { done, value } = await reader.read();
1766
- if (done) {
1767
- if (hasReceivedData) onComplete == null ? void 0 : onComplete();
1768
- break;
1769
- }
1770
- const chunk = decoder.decode(value, { stream: true });
1771
- buffer += chunk;
1772
- const lines = buffer.split("\n");
1773
- buffer = lines.pop() || "";
1774
- for (const line of lines) {
1775
- const trimmedLine = line.trim();
1776
- if (trimmedLine === "") continue;
1777
- hasReceivedData = true;
1778
- if (trimmedLine.startsWith("data: ")) {
1779
- const data = trimmedLine.slice(6).trim();
1780
- if (data === "[DONE]") {
1781
- onComplete == null ? void 0 : onComplete();
1782
- return;
1783
- }
1784
- try {
1785
- const parsed = JSON.parse(data);
1786
- if (parsed.content) onChunk(parsed.content);
1787
- else if (parsed.delta) onChunk(parsed.delta);
1788
- else if (parsed.text) onChunk(parsed.text);
1789
- else if (parsed.chunk) onChunk(parsed.chunk);
1790
- else if (typeof parsed === "string") onChunk(parsed);
1791
- } catch (e) {
1792
- if (data) onChunk(data);
1793
- }
1794
- } else if (trimmedLine) {
1795
- onChunk(trimmedLine + "\n");
1796
- }
1797
- }
1798
- }
1799
- if (!hasReceivedData) {
1800
- throw new Error("No data received from stream");
1801
- }
1901
+ await processSSEStream(reader, {
1902
+ onFieldStart,
1903
+ onContent,
1904
+ onKeywords,
1905
+ onFieldEnd,
1906
+ onComplete,
1907
+ onError
1908
+ });
1802
1909
  };
1803
1910
  var rewriteNewsStreamApi = async ({
1804
1911
  article,
@@ -1810,7 +1917,10 @@ var rewriteNewsStreamApi = async ({
1810
1917
  includeQuotes,
1811
1918
  includeFAQ,
1812
1919
  targetAudience,
1813
- onChunk,
1920
+ onFieldStart,
1921
+ onContent,
1922
+ onKeywords,
1923
+ onFieldEnd,
1814
1924
  onComplete,
1815
1925
  onError
1816
1926
  }) => {
@@ -1819,9 +1929,7 @@ var rewriteNewsStreamApi = async ({
1819
1929
  article,
1820
1930
  language
1821
1931
  };
1822
- if (articleId) {
1823
- payload.articleId = articleId;
1824
- }
1932
+ if (articleId) payload.articleId = articleId;
1825
1933
  if (tone) payload.tone = tone;
1826
1934
  if (style) payload.style = style;
1827
1935
  if (wordCount) payload.wordCount = wordCount;
@@ -1830,99 +1938,42 @@ var rewriteNewsStreamApi = async ({
1830
1938
  if (targetAudience) payload.targetAudience = targetAudience;
1831
1939
  try {
1832
1940
  const API_BASE_URL = getApiBaseUrl();
1833
- const apiKey = getApiKey();
1834
- const headers = {
1835
- "Content-Type": "application/json"
1836
- };
1837
- if (apiKey) {
1838
- headers["x-api-key"] = apiKey;
1839
- }
1840
1941
  const response = await fetch(`${API_BASE_URL}/chatboat/rewrite/stream`, {
1841
1942
  method: "POST",
1842
- headers,
1943
+ headers: getHeaders(),
1843
1944
  credentials: "include",
1844
1945
  body: JSON.stringify(payload)
1845
1946
  });
1846
- console.log("\u{1F4E1} Response status:", response.status);
1847
- console.log("\u{1F4E1} Response headers:", Object.fromEntries(response.headers.entries()));
1848
1947
  if (!response.ok) {
1849
1948
  const errorText = await response.text();
1850
- console.error("\u274C API Error:", response.status, errorText);
1851
1949
  throw new Error(`HTTP error! status: ${response.status} - ${errorText}`);
1852
1950
  }
1951
+ const contentType = response.headers.get("content-type") || "";
1952
+ if (contentType.includes("application/json")) {
1953
+ const json = await response.json();
1954
+ const data = json.data || json;
1955
+ onComplete == null ? void 0 : onComplete({
1956
+ title: data.title || "",
1957
+ subtitle: data.subtitle || "",
1958
+ article: data.article || "",
1959
+ metaDescription: data.metaDescription || "",
1960
+ metaKeywords: Array.isArray(data.metaKeywords) ? data.metaKeywords : []
1961
+ });
1962
+ return;
1963
+ }
1853
1964
  const reader = (_a = response.body) == null ? void 0 : _a.getReader();
1854
- const decoder = new TextDecoder();
1855
1965
  if (!reader) {
1856
1966
  throw new Error("No reader available");
1857
1967
  }
1858
- let buffer = "";
1859
- let hasReceivedData = false;
1860
- while (true) {
1861
- const { done, value } = await reader.read();
1862
- if (done) {
1863
- if (hasReceivedData) {
1864
- onComplete == null ? void 0 : onComplete();
1865
- }
1866
- break;
1867
- }
1868
- const chunk = decoder.decode(value, { stream: true });
1869
- buffer += chunk;
1870
- const lines = buffer.split("\n");
1871
- buffer = lines.pop() || "";
1872
- for (const line of lines) {
1873
- const trimmedLine = line.trim();
1874
- if (trimmedLine === "") continue;
1875
- hasReceivedData = true;
1876
- console.log("\u{1F4E8} Received line:", trimmedLine.substring(0, 100) + (trimmedLine.length > 100 ? "..." : ""));
1877
- if (trimmedLine.startsWith("data: ")) {
1878
- const data = trimmedLine.slice(6).trim();
1879
- if (data === "[DONE]") {
1880
- console.log("\u2705 Stream completed with [DONE] signal");
1881
- onComplete == null ? void 0 : onComplete();
1882
- return;
1883
- }
1884
- try {
1885
- const parsed = JSON.parse(data);
1886
- console.log("\u{1F4CB} Parsed JSON:", parsed);
1887
- if (parsed.content) {
1888
- onChunk(parsed.content);
1889
- } else if (parsed.delta) {
1890
- onChunk(parsed.delta);
1891
- } else if (parsed.text) {
1892
- onChunk(parsed.text);
1893
- } else if (parsed.chunk) {
1894
- onChunk(parsed.chunk);
1895
- } else if (typeof parsed === "string") {
1896
- onChunk(parsed);
1897
- } else {
1898
- console.warn("\u26A0\uFE0F Unknown JSON format:", parsed);
1899
- }
1900
- } catch (e) {
1901
- console.log("\u{1F4DD} Non-JSON data, treating as plain text");
1902
- if (data) {
1903
- onChunk(data);
1904
- }
1905
- }
1906
- } else {
1907
- console.log("\u{1F4C4} Plain text chunk");
1908
- if (trimmedLine) {
1909
- onChunk(trimmedLine + "\n");
1910
- }
1911
- }
1912
- }
1913
- }
1914
- if (!hasReceivedData) {
1915
- console.error("\u274C No data received from stream");
1916
- throw new Error("No data received from stream");
1917
- }
1918
- console.log("\u2705 Stream completed successfully");
1919
- } catch (error) {
1920
- console.error("\u274C Streaming error:", error);
1921
- console.error("Error details:", {
1922
- message: error.message,
1923
- name: error.name,
1924
- stack: error.stack
1968
+ await processSSEStream(reader, {
1969
+ onFieldStart,
1970
+ onContent,
1971
+ onKeywords,
1972
+ onFieldEnd,
1973
+ onComplete,
1974
+ onError
1925
1975
  });
1976
+ } catch (error) {
1926
1977
  onError == null ? void 0 : onError(error);
1927
1978
  throw error;
1928
1979
  }
@@ -1942,16 +1993,14 @@ var rewriteNewsApi = async ({
1942
1993
  article,
1943
1994
  language
1944
1995
  };
1945
- if (articleId) {
1946
- payload.articleId = articleId;
1947
- }
1996
+ if (articleId) payload.articleId = articleId;
1948
1997
  if (tone) payload.tone = tone;
1949
1998
  if (style) payload.style = style;
1950
1999
  if (wordCount) payload.wordCount = wordCount;
1951
2000
  if (includeQuotes !== void 0) payload.includeQuotes = includeQuotes;
1952
2001
  if (includeFAQ !== void 0) payload.includeFAQ = includeFAQ;
1953
2002
  if (targetAudience) payload.targetAudience = targetAudience;
1954
- const { data } = await api_default.post("/chat/rewrite", payload);
2003
+ const { data } = await api_default.post("/chatboat/rewrite", payload);
1955
2004
  return data.data;
1956
2005
  };
1957
2006
 
@@ -1959,12 +2008,16 @@ var rewriteNewsApi = async ({
1959
2008
  var import_react_hot_toast2 = __toESM(require("react-hot-toast"));
1960
2009
  var import_lucide_react7 = require("lucide-react");
1961
2010
  var import_jsx_runtime11 = require("react/jsx-runtime");
2011
+ function buildStructuredContent(title, article, metaKeywords, subtitle = "", metaDescription = "") {
2012
+ return JSON.stringify({ title, subtitle, article, metaDescription, metaKeywords });
2013
+ }
1962
2014
  function ChatBot({ onPost }) {
1963
2015
  const { loading } = useTheme();
1964
2016
  const { preferences } = usePreferences();
1965
2017
  const [preferencesOpen, setPreferencesOpen] = (0, import_react10.useState)(false);
1966
2018
  const [messages, setMessages] = (0, import_react10.useState)([]);
1967
2019
  const [isStreaming, setIsStreaming] = (0, import_react10.useState)(false);
2020
+ const [activeField, setActiveField] = (0, import_react10.useState)(null);
1968
2021
  const [analyzedData, setAnalyzedData] = (0, import_react10.useState)(null);
1969
2022
  const handleRecreate = async ({ title, content, id }) => {
1970
2023
  var _a;
@@ -1975,17 +2028,28 @@ function ChatBot({ onPost }) {
1975
2028
  id: crypto.randomUUID(),
1976
2029
  role: "user",
1977
2030
  content: `Recreate this news:
1978
- ${title}`
2031
+ ${title}`
1979
2032
  },
1980
2033
  {
1981
2034
  id: assistantId,
1982
2035
  role: "assistant",
1983
- content: "\u23F3 Creating article..."
2036
+ content: ""
1984
2037
  }
1985
2038
  ]);
2039
+ setIsStreaming(true);
1986
2040
  try {
1987
- let accumulatedContent = "";
1988
2041
  let hasStartedStreaming = false;
2042
+ let titleBuffer = "";
2043
+ let articleBuffer = "";
2044
+ let keywordsBuffer = [];
2045
+ const updateMessage = () => {
2046
+ const structured = buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
2047
+ setMessages(
2048
+ (prev) => prev.map(
2049
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: structured }) : m
2050
+ )
2051
+ );
2052
+ };
1989
2053
  const contentPrefs = preferences == null ? void 0 : preferences.content;
1990
2054
  const lang = ((_a = preferences == null ? void 0 : preferences.localization) == null ? void 0 : _a.language) === "hi" ? "Hindi" : "English";
1991
2055
  try {
@@ -1999,39 +2063,66 @@ ${title}`
1999
2063
  includeQuotes: contentPrefs == null ? void 0 : contentPrefs.includeQuotes,
2000
2064
  includeFAQ: contentPrefs == null ? void 0 : contentPrefs.includeFAQ,
2001
2065
  targetAudience: contentPrefs == null ? void 0 : contentPrefs.targetAudience,
2002
- onChunk: (chunk) => {
2066
+ onFieldStart: (field) => {
2067
+ setActiveField(field);
2068
+ },
2069
+ onContent: (field, chunk) => {
2003
2070
  hasStartedStreaming = true;
2004
- accumulatedContent += chunk;
2071
+ if (field === "title") titleBuffer += chunk;
2072
+ else articleBuffer += chunk;
2073
+ updateMessage();
2074
+ },
2075
+ onKeywords: (keywords) => {
2076
+ keywordsBuffer = keywords;
2077
+ updateMessage();
2078
+ },
2079
+ onFieldEnd: () => {
2080
+ setActiveField(null);
2081
+ },
2082
+ onComplete: (data) => {
2083
+ const finalContent = data && (data.title || data.article) ? JSON.stringify(data) : buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
2005
2084
  setMessages(
2006
2085
  (prev) => prev.map(
2007
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: accumulatedContent }) : m
2086
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: finalContent }) : m
2008
2087
  )
2009
2088
  );
2010
- },
2011
- onComplete: () => {
2012
- console.log("Streaming completed successfully");
2089
+ setIsStreaming(false);
2090
+ setActiveField(null);
2013
2091
  import_react_hot_toast2.default.success("Article created successfully!");
2014
2092
  },
2015
2093
  onError: async (error) => {
2016
2094
  console.error("Streaming error:", error);
2017
2095
  if (!hasStartedStreaming) {
2018
- console.log("Falling back to regular API...");
2019
2096
  throw error;
2020
2097
  } else {
2098
+ setIsStreaming(false);
2099
+ setActiveField(null);
2021
2100
  import_react_hot_toast2.default.error("Failed to complete article generation");
2022
2101
  setMessages(
2023
2102
  (prev) => prev.map(
2024
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: accumulatedContent || "\u274C Failed to create article. Please try again." }) : m
2103
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: titleBuffer || articleBuffer ? buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer) : "Failed to create article. Please try again." }) : m
2025
2104
  )
2026
2105
  );
2027
2106
  }
2028
2107
  }
2029
2108
  });
2109
+ if (isStreaming) {
2110
+ if (titleBuffer || articleBuffer) {
2111
+ const structured = buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
2112
+ setMessages(
2113
+ (prev) => prev.map(
2114
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: structured }) : m
2115
+ )
2116
+ );
2117
+ }
2118
+ setIsStreaming(false);
2119
+ setActiveField(null);
2120
+ }
2030
2121
  } catch (streamError) {
2031
2122
  console.log("Streaming failed, using regular API...", streamError);
2032
2123
  setMessages(
2033
2124
  (prev) => prev.map(
2034
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "\u23F3 Generating with regular API..." }) : m
2125
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "" }) : m
2035
2126
  )
2036
2127
  );
2037
2128
  const result = await rewriteNewsApi({
@@ -2047,17 +2138,20 @@ ${title}`
2047
2138
  });
2048
2139
  setMessages(
2049
2140
  (prev) => prev.map(
2050
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: result.article || result }) : m
2141
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: typeof result === "string" ? result : JSON.stringify(result) }) : m
2051
2142
  )
2052
2143
  );
2144
+ setIsStreaming(false);
2053
2145
  import_react_hot_toast2.default.success("Article created successfully!");
2054
2146
  }
2055
2147
  } catch (err) {
2056
2148
  console.error("Complete Error:", err);
2149
+ setIsStreaming(false);
2150
+ setActiveField(null);
2057
2151
  import_react_hot_toast2.default.error((err == null ? void 0 : err.message) || "An error occurred while creating the article");
2058
2152
  setMessages(
2059
2153
  (prev) => prev.map(
2060
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "\u274C Failed to create article. Please try again." }) : m
2154
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "Failed to create article. Please try again." }) : m
2061
2155
  )
2062
2156
  );
2063
2157
  }
@@ -2075,9 +2169,10 @@ ${title}`
2075
2169
  if (hasUrl) {
2076
2170
  setMessages((prev) => [
2077
2171
  ...prev,
2078
- { id: assistantId, role: "assistant", content: "\u{1F50D} Analyzing your link..." }
2172
+ { id: assistantId, role: "assistant", content: "Analyzing your link..." }
2079
2173
  ]);
2080
2174
  try {
2175
+ return;
2081
2176
  const result = await analyzeInputApi(text);
2082
2177
  if (result.hasUrl && ((_a = result.options) == null ? void 0 : _a.length) > 0) {
2083
2178
  setAnalyzedData({
@@ -2108,20 +2203,30 @@ ${optionsList}`
2108
2203
  import_react_hot_toast2.default.error("Failed to analyze the link");
2109
2204
  setMessages(
2110
2205
  (prev) => prev.map(
2111
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "\u274C Failed to analyze the link. Please try again." }) : m
2206
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "Failed to analyze the link. Please try again." }) : m
2112
2207
  )
2113
2208
  );
2114
2209
  }
2115
2210
  } else {
2116
2211
  setMessages((prev) => [
2117
2212
  ...prev,
2118
- { id: assistantId, role: "assistant", content: "\u23F3 Generating content..." }
2213
+ { id: assistantId, role: "assistant", content: "" }
2119
2214
  ]);
2120
2215
  setIsStreaming(true);
2121
2216
  try {
2122
2217
  const contentPrefs = preferences == null ? void 0 : preferences.content;
2123
2218
  const lang = ((_b = preferences == null ? void 0 : preferences.localization) == null ? void 0 : _b.language) === "hi" ? "Hindi" : "English";
2124
- let accumulatedContent = "";
2219
+ let titleBuffer = "";
2220
+ let articleBuffer = "";
2221
+ let keywordsBuffer = [];
2222
+ const updateMessage = () => {
2223
+ const structured = buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
2224
+ setMessages(
2225
+ (prev) => prev.map(
2226
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: structured }) : m
2227
+ )
2228
+ );
2229
+ };
2125
2230
  await rewriteNewsStreamApi({
2126
2231
  article: text,
2127
2232
  language: lang,
@@ -2131,30 +2236,58 @@ ${optionsList}`
2131
2236
  includeQuotes: contentPrefs == null ? void 0 : contentPrefs.includeQuotes,
2132
2237
  includeFAQ: contentPrefs == null ? void 0 : contentPrefs.includeFAQ,
2133
2238
  targetAudience: contentPrefs == null ? void 0 : contentPrefs.targetAudience,
2134
- onChunk: (chunk) => {
2135
- accumulatedContent += chunk;
2239
+ onFieldStart: (field) => {
2240
+ setActiveField(field);
2241
+ },
2242
+ onContent: (field, chunk) => {
2243
+ if (field === "title") titleBuffer += chunk;
2244
+ else articleBuffer += chunk;
2245
+ updateMessage();
2246
+ },
2247
+ onKeywords: (keywords) => {
2248
+ keywordsBuffer = keywords;
2249
+ updateMessage();
2250
+ },
2251
+ onFieldEnd: () => {
2252
+ setActiveField(null);
2253
+ },
2254
+ onComplete: (data) => {
2255
+ const finalContent = data && (data.title || data.article) ? JSON.stringify(data) : buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
2136
2256
  setMessages(
2137
2257
  (prev) => prev.map(
2138
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: accumulatedContent }) : m
2258
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: finalContent }) : m
2139
2259
  )
2140
2260
  );
2141
- },
2142
- onComplete: () => {
2143
2261
  setIsStreaming(false);
2262
+ setActiveField(null);
2144
2263
  import_react_hot_toast2.default.success("Content generated!");
2145
2264
  },
2146
2265
  onError: (error) => {
2147
2266
  console.error("Stream error:", error);
2148
2267
  setIsStreaming(false);
2268
+ setActiveField(null);
2149
2269
  import_react_hot_toast2.default.error("Failed to generate content");
2150
2270
  }
2151
2271
  });
2272
+ if (isStreaming) {
2273
+ if (titleBuffer || articleBuffer) {
2274
+ const structured = buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
2275
+ setMessages(
2276
+ (prev) => prev.map(
2277
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: structured }) : m
2278
+ )
2279
+ );
2280
+ }
2281
+ setIsStreaming(false);
2282
+ setActiveField(null);
2283
+ }
2152
2284
  } catch (err) {
2153
2285
  console.error("Send message error:", err);
2154
2286
  setIsStreaming(false);
2287
+ setActiveField(null);
2155
2288
  setMessages(
2156
2289
  (prev) => prev.map(
2157
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "\u274C Failed to generate content. Please try again." }) : m
2290
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "Failed to generate content. Please try again." }) : m
2158
2291
  )
2159
2292
  );
2160
2293
  }
@@ -2168,12 +2301,22 @@ ${optionsList}`
2168
2301
  setMessages((prev) => [
2169
2302
  ...prev,
2170
2303
  { id: crypto.randomUUID(), role: "user", content: `Action: ${actionName}` },
2171
- { id: assistantId, role: "assistant", content: "\u23F3 Creating content..." }
2304
+ { id: assistantId, role: "assistant", content: "" }
2172
2305
  ]);
2173
2306
  setIsStreaming(true);
2174
2307
  try {
2175
2308
  const contentPrefs = preferences == null ? void 0 : preferences.content;
2176
- let accumulatedContent = "";
2309
+ let titleBuffer = "";
2310
+ let articleBuffer = "";
2311
+ let keywordsBuffer = [];
2312
+ const updateMessage = () => {
2313
+ const structured = buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
2314
+ setMessages(
2315
+ (prev) => prev.map(
2316
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: structured }) : m
2317
+ )
2318
+ );
2319
+ };
2177
2320
  try {
2178
2321
  await createContentStreamApi({
2179
2322
  url,
@@ -2184,24 +2327,51 @@ ${optionsList}`
2184
2327
  style: contentPrefs == null ? void 0 : contentPrefs.style,
2185
2328
  wordCount: contentPrefs == null ? void 0 : contentPrefs.wordCount
2186
2329
  },
2187
- onChunk: (chunk) => {
2188
- accumulatedContent += chunk;
2330
+ onFieldStart: (field) => {
2331
+ setActiveField(field);
2332
+ },
2333
+ onContent: (field, chunk) => {
2334
+ if (field === "title") titleBuffer += chunk;
2335
+ else articleBuffer += chunk;
2336
+ updateMessage();
2337
+ },
2338
+ onKeywords: (keywords) => {
2339
+ keywordsBuffer = keywords;
2340
+ updateMessage();
2341
+ },
2342
+ onFieldEnd: () => {
2343
+ setActiveField(null);
2344
+ },
2345
+ onComplete: (data) => {
2346
+ const finalContent = data && (data.title || data.article) ? JSON.stringify(data) : buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
2189
2347
  setMessages(
2190
2348
  (prev) => prev.map(
2191
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: accumulatedContent }) : m
2349
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: finalContent }) : m
2192
2350
  )
2193
2351
  );
2194
- },
2195
- onComplete: () => {
2196
2352
  setIsStreaming(false);
2353
+ setActiveField(null);
2197
2354
  import_react_hot_toast2.default.success("Content created!");
2198
2355
  },
2199
2356
  onError: (error) => {
2200
2357
  console.error("Stream error:", error);
2201
2358
  setIsStreaming(false);
2359
+ setActiveField(null);
2202
2360
  import_react_hot_toast2.default.error("Failed to create content");
2203
2361
  }
2204
2362
  });
2363
+ if (isStreaming) {
2364
+ if (titleBuffer || articleBuffer) {
2365
+ const structured = buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
2366
+ setMessages(
2367
+ (prev) => prev.map(
2368
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: structured }) : m
2369
+ )
2370
+ );
2371
+ }
2372
+ setIsStreaming(false);
2373
+ setActiveField(null);
2374
+ }
2205
2375
  } catch (e) {
2206
2376
  const result = await createContentApi({ url, content, actionId, settings: {
2207
2377
  tone: contentPrefs == null ? void 0 : contentPrefs.tone,
@@ -2210,18 +2380,20 @@ ${optionsList}`
2210
2380
  } });
2211
2381
  setMessages(
2212
2382
  (prev) => prev.map(
2213
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: result.article || result.content || JSON.stringify(result) }) : m
2383
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: typeof result === "string" ? result : JSON.stringify(result) }) : m
2214
2384
  )
2215
2385
  );
2216
2386
  setIsStreaming(false);
2387
+ setActiveField(null);
2217
2388
  import_react_hot_toast2.default.success("Content created!");
2218
2389
  }
2219
2390
  } catch (err) {
2220
2391
  console.error("Create content error:", err);
2221
2392
  setIsStreaming(false);
2393
+ setActiveField(null);
2222
2394
  setMessages(
2223
2395
  (prev) => prev.map(
2224
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "\u274C Failed to create content. Please try again." }) : m
2396
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "Failed to create content. Please try again." }) : m
2225
2397
  )
2226
2398
  );
2227
2399
  }
@@ -2242,6 +2414,7 @@ ${optionsList}`
2242
2414
  onSend: handleSendMessage,
2243
2415
  onSelectNews: handleRecreate,
2244
2416
  isStreaming,
2417
+ activeField,
2245
2418
  analyzedData,
2246
2419
  onSelectAction: handleSelectAction,
2247
2420
  onPost