@contenify/chatbot 1.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
@@ -110,7 +110,7 @@ var api_default = api;
110
110
 
111
111
  // services/preferences.service.ts
112
112
  var getMyPreferencesApi = async () => {
113
- const { data } = await api_default.get("/preferences/me");
113
+ const { data } = await api_default.get("/chatboat/preferences/me");
114
114
  return data.data;
115
115
  };
116
116
  var savePreferencesApi = async ({
@@ -188,7 +188,7 @@ var savePreferencesApi = async ({
188
188
  if (targetAudience !== void 0) {
189
189
  formData.append("targetAudience", targetAudience);
190
190
  }
191
- const { data } = await api_default.put("/preferences", formData, {
191
+ const { data } = await api_default.put("/chatboat/preferences", formData, {
192
192
  headers: {
193
193
  "Content-Type": "multipart/form-data"
194
194
  }
@@ -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,31 +299,79 @@ 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
376
 
329
377
  // components/chatbot/ChatWindow.tsx
@@ -506,38 +554,38 @@ function useTheme() {
506
554
 
507
555
  // components/chatbot/EditModal.tsx
508
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
+ }
509
560
  function EditModal({
510
561
  isOpen,
562
+ initialTitle = "",
563
+ initialSubtitle = "",
511
564
  initialContent,
565
+ initialMetaDescription = "",
512
566
  metaKeywords,
513
567
  onClose,
514
568
  onSaveDraft,
515
569
  onPost
516
570
  }) {
517
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)("");
518
577
  const [content, setContent] = (0, import_react4.useState)("");
519
578
  const [keywords, setKeywords] = (0, import_react4.useState)(metaKeywords);
520
579
  const [newKeyword, setNewKeyword] = (0, import_react4.useState)("");
521
- const markdownToHtml = (text) => {
522
- const lines = text.split("\n").filter((line) => line.trim());
523
- let html = "";
524
- lines.forEach((line) => {
525
- const trimmed = line.trim();
526
- if (trimmed.startsWith("# ")) {
527
- html += `<h1>${trimmed.replace(/^#\s+/, "")}</h1>`;
528
- } else if (trimmed.startsWith("## ")) {
529
- html += `<h2>${trimmed.replace(/^##\s+/, "")}</h2>`;
530
- } else {
531
- html += `<p>${trimmed}</p>`;
532
- }
533
- });
534
- return html;
535
- };
536
580
  (0, import_react4.useEffect)(() => {
537
- const htmlContent = markdownToHtml(initialContent);
538
- setContent(htmlContent);
581
+ setTitle(initialTitle);
582
+ setSubtitle(initialSubtitle);
583
+ setMetaDescription(initialMetaDescription);
584
+ setSlug(toSlug(initialTitle));
585
+ setSlugEdited(false);
586
+ setContent(initialContent);
539
587
  setKeywords(metaKeywords);
540
- }, [initialContent, metaKeywords]);
588
+ }, [initialTitle, initialSubtitle, initialContent, initialMetaDescription, metaKeywords]);
541
589
  (0, import_react4.useEffect)(() => {
542
590
  if (isOpen) {
543
591
  document.body.style.overflow = "hidden";
@@ -548,6 +596,16 @@ function EditModal({
548
596
  document.body.style.overflow = "";
549
597
  };
550
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
+ };
551
609
  const handleAddKeyword = () => {
552
610
  if (newKeyword.trim() && !keywords.includes(newKeyword.trim())) {
553
611
  setKeywords([...keywords, newKeyword.trim()]);
@@ -563,28 +621,62 @@ function EditModal({
563
621
  handleAddKeyword();
564
622
  }
565
623
  };
624
+ const getEditData = () => ({
625
+ title,
626
+ subtitle,
627
+ article: content,
628
+ metaDescription,
629
+ slug,
630
+ keywords
631
+ });
566
632
  if (!isOpen) return null;
567
633
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cnfy-edit-modal-overlay", children: [
568
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
569
- "div",
570
- {
571
- className: "cnfy-edit-modal-backdrop",
572
- onClick: onClose
573
- }
574
- ),
634
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cnfy-edit-modal-backdrop", onClick: onClose }),
575
635
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cnfy-edit-modal", children: [
576
636
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cnfy-edit-modal-header", children: [
577
637
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { className: "cnfy-edit-modal-title", children: "Edit Article" }),
578
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
579
- "button",
580
- {
581
- onClick: onClose,
582
- className: "cnfy-edit-modal-close-btn",
583
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react2.X, { size: 20, className: "cnfy-edit-modal-close-icon" })
584
- }
585
- )
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" }) })
586
639
  ] }),
587
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
+ ] }),
588
680
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
589
681
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { className: "cnfy-edit-label", children: "Article Content" }),
590
682
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
@@ -597,26 +689,32 @@ function EditModal({
597
689
  )
598
690
  ] }),
599
691
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
600
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { className: "cnfy-edit-label", children: "Meta Keywords" }),
601
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cnfy-keyword-list", children: keywords.map((keyword, index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
602
- "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",
603
695
  {
604
- className: "cnfy-keyword-tag",
605
- children: [
606
- "#",
607
- keyword,
608
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
609
- "button",
610
- {
611
- onClick: () => handleRemoveKeyword(index),
612
- className: "cnfy-keyword-remove-btn",
613
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react2.X, { size: 14 })
614
- }
615
- )
616
- ]
617
- },
618
- index
619
- )) }),
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)) }),
620
718
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cnfy-keyword-input-row", children: [
621
719
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
622
720
  "input",
@@ -629,30 +727,16 @@ function EditModal({
629
727
  className: "cnfy-keyword-input"
630
728
  }
631
729
  ),
632
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
633
- "button",
634
- {
635
- onClick: handleAddKeyword,
636
- className: "cnfy-btn-add-keyword",
637
- children: "Add"
638
- }
639
- )
730
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { onClick: handleAddKeyword, className: "cnfy-btn-add-keyword", children: "Add" })
640
731
  ] })
641
732
  ] })
642
733
  ] }),
643
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" }),
644
736
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
645
737
  "button",
646
738
  {
647
- onClick: onClose,
648
- className: "cnfy-btn-footer-cancel",
649
- children: "Cancel"
650
- }
651
- ),
652
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
653
- "button",
654
- {
655
- onClick: () => onSaveDraft(content, keywords),
739
+ onClick: () => onSaveDraft(getEditData()),
656
740
  className: "cnfy-btn-save-draft",
657
741
  children: "Save Draft"
658
742
  }
@@ -660,7 +744,7 @@ function EditModal({
660
744
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
661
745
  "button",
662
746
  {
663
- onClick: () => onPost(content, keywords),
747
+ onClick: () => onPost(getEditData()),
664
748
  className: "cnfy-btn-post-article",
665
749
  style: { backgroundColor: primaryColor, color: "#fff" },
666
750
  children: "Post"
@@ -842,19 +926,19 @@ function NewsList({
842
926
 
843
927
  // services/news.service.ts
844
928
  var getTrendingNews = async () => {
845
- const { data } = await api_default.get("/news/trending");
929
+ const { data } = await api_default.get("/chatboat/trending");
846
930
  return data.data;
847
931
  };
848
932
  var getNewsSources = async () => {
849
- const { data } = await api_default.get("/news/sources");
933
+ const { data } = await api_default.get("/chatboat/scrape/sources");
850
934
  return data.data;
851
935
  };
852
936
  var scrapeNewsSource = async (sourceId) => {
853
- const { data } = await api_default.post("/news/scrape-source", { sourceId });
937
+ const { data } = await api_default.post("/chatboat/scrape/source", { sourceId });
854
938
  return data.data;
855
939
  };
856
940
  var getNewsBySource = async (sourceId) => {
857
- const { data } = await api_default.get(`/news/by-source/${sourceId}`);
941
+ const { data } = await api_default.get(`/chatboat/scrape/by-source/${sourceId}`);
858
942
  return data.data;
859
943
  };
860
944
 
@@ -874,12 +958,13 @@ function ChatWindow({
874
958
  onSend,
875
959
  onSelectNews,
876
960
  isStreaming = false,
961
+ activeField,
877
962
  analyzedData,
878
963
  onSelectAction,
879
964
  onPost: onPostCallback
880
965
  }) {
881
966
  var _a, _b;
882
- const { loading, showNewsPanel } = useTheme();
967
+ const { showNewsPanel } = useTheme();
883
968
  const bottomRef = (0, import_react6.useRef)(null);
884
969
  const textareaRef = (0, import_react6.useRef)(null);
885
970
  const dropdownRef = (0, import_react6.useRef)(null);
@@ -892,40 +977,48 @@ function ChatWindow({
892
977
  const [selectedSource, setSelectedSource] = (0, import_react6.useState)(null);
893
978
  const [loadingSources, setLoadingSources] = (0, import_react6.useState)(false);
894
979
  const [scraping, setScraping] = (0, import_react6.useState)(false);
895
- 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: "" });
896
981
  const { primaryColor } = useTheme();
897
982
  const { preferences } = usePreferences();
898
983
  const preferredLanguage = (_a = preferences == null ? void 0 : preferences.localization) == null ? void 0 : _a.language;
899
- const handleCopy = async (blocks, messageId) => {
984
+ const handleCopy = async (parsed, messageId) => {
900
985
  try {
901
- const formattedText = blocksToFormattedText(blocks);
902
- 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);
903
996
  setCopiedId(messageId);
904
997
  setTimeout(() => setCopiedId(null), 2e3);
905
998
  } catch (err) {
906
999
  console.error("Failed to copy:", err);
907
1000
  }
908
1001
  };
909
- const blocksToFormattedText = (blocks) => {
910
- return blocks.map((block) => {
911
- if (block.type === "h1") return `# ${block.text}`;
912
- if (block.type === "h2") return `## ${block.text}`;
913
- return block.text;
914
- }).join("\n\n");
915
- };
916
- const handleEdit = (blocks, metaKeywords, messageId) => {
917
- const formattedContent = blocksToFormattedText(blocks);
918
- 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
+ });
919
1012
  };
920
1013
  const handleCloseModal = () => {
921
- setEditModal({ isOpen: false, content: "", metaKeywords: [], messageId: "" });
1014
+ setEditModal({ isOpen: false, title: "", subtitle: "", content: "", metaDescription: "", metaKeywords: [], messageId: "" });
922
1015
  };
923
- const handleSaveDraft = (content, keywords) => {
924
- console.log("Saving draft:", { content, keywords });
1016
+ const handleSaveDraft = (data) => {
1017
+ console.log("Saving draft:", data);
925
1018
  handleCloseModal();
926
1019
  };
927
- const handlePost = (content, keywords) => {
928
- onPostCallback == null ? void 0 : onPostCallback(content, keywords);
1020
+ const handlePost = (data) => {
1021
+ onPostCallback == null ? void 0 : onPostCallback(data.article, data.keywords);
929
1022
  handleCloseModal();
930
1023
  };
931
1024
  (0, import_react6.useLayoutEffect)(() => {
@@ -989,7 +1082,6 @@ function ChatWindow({
989
1082
  const handleSourceSelect = (option) => {
990
1083
  var _a2;
991
1084
  const sourceId = (_a2 = option == null ? void 0 : option.value) != null ? _a2 : null;
992
- console.log("Selected source:", option);
993
1085
  setSelectedSource(sourceId);
994
1086
  fetchNews(sourceId);
995
1087
  };
@@ -1015,130 +1107,103 @@ function ChatWindow({
1015
1107
  document.removeEventListener("mousedown", handleClickOutside);
1016
1108
  };
1017
1109
  }, [showNewsDropdown]);
1018
- function formatAIContent(raw) {
1019
- const extracted = extractArticleContent(raw);
1020
- if (!extracted || !extracted.article) {
1021
- return { blocks: [], metaKeywords: [] };
1022
- }
1023
- const { article, metaKeywords } = extracted;
1024
- 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");
1025
- const lines = normalized.split("\n").map((line) => line.replace(/<[^>]+>/g, "").trim()).filter(Boolean);
1026
- const blocks = [];
1027
- lines.forEach((line, index) => {
1028
- if (index === 0 && line.length < 120) {
1029
- blocks.push({ type: "h1", text: line });
1030
- return;
1031
- }
1032
- if (line.length < 90 && !line.endsWith("\u0964") && !line.endsWith(".")) {
1033
- blocks.push({ type: "h2", text: line });
1034
- return;
1035
- }
1036
- blocks.push({ type: "p", text: line });
1037
- });
1038
- return { blocks, metaKeywords };
1039
- }
1040
1110
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "cnfy-chat", children: [
1041
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: [
1042
1112
  messages.map((msg) => {
1043
- var _a2;
1113
+ var _a2, _b2;
1044
1114
  const parsed = formatAIContent(msg.content);
1045
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "cnfy-msg", children: [
1046
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-msg-avatar-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1047
- "div",
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: [
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)(
1117
+ "button",
1048
1118
  {
1049
- className: "cnfy-msg-avatar",
1050
- style: {
1051
- backgroundColor: msg.role === "assistant" ? primaryColor : "#1f2937"
1052
- },
1053
- children: msg.role === "assistant" ? "AI" : "You"
1119
+ onClick: () => handleCopy(parsed, msg.id),
1120
+ className: "cnfy-copy-btn",
1121
+ title: "Copy to clipboard",
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 })
1054
1123
  }
1055
1124
  ) }),
1056
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "cnfy-msg-body", children: [
1057
- 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)(
1058
- "button",
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",
1059
1141
  {
1060
- onClick: () => handleCopy(parsed.blocks, msg.id),
1061
- className: "cnfy-copy-btn",
1062
- title: "Copy to clipboard",
1063
- 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 })
1142
+ className: "cnfy-article-content",
1143
+ dangerouslySetInnerHTML: { __html: parsed.articleHtml }
1064
1144
  }
1065
- ) }),
1066
- parsed.blocks.map((block, idx) => {
1067
- if (block.type === "h1") {
1068
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1069
- "h1",
1070
- {
1071
- className: "cnfy-block-h1",
1072
- children: block.text
1073
- },
1074
- idx
1075
- );
1076
- }
1077
- if (block.type === "h2") {
1078
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1079
- "h2",
1080
- {
1081
- className: "cnfy-block-h2",
1082
- children: block.text
1083
- },
1084
- idx
1085
- );
1086
- }
1087
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "cnfy-block-p", children: block.text }, idx);
1088
- }),
1089
- 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)(
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)(
1090
1158
  "span",
1091
1159
  {
1092
1160
  className: "cnfy-msg-keyword-tag",
1161
+ children: tag
1162
+ },
1163
+ i
1164
+ )) })
1165
+ ] }),
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) => {
1167
+ const IconComponent = ACTION_ICONS[option.id] || import_lucide_react4.FileText;
1168
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1169
+ "button",
1170
+ {
1171
+ onClick: () => onSelectAction == null ? void 0 : onSelectAction(option.id, analyzedData.url, analyzedData.content),
1172
+ className: "cnfy-action-btn",
1093
1173
  children: [
1094
- "#",
1095
- tag
1174
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(IconComponent, { size: 16 }),
1175
+ option.name
1096
1176
  ]
1097
1177
  },
1098
- i
1099
- )) }) }),
1100
- 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) => {
1101
- const IconComponent = ACTION_ICONS[option.id] || import_lucide_react4.FileText;
1102
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1103
- "button",
1104
- {
1105
- onClick: () => onSelectAction == null ? void 0 : onSelectAction(option.id, analyzedData.url, analyzedData.content),
1106
- className: "cnfy-action-btn",
1107
- children: [
1108
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(IconComponent, { size: 16 }),
1109
- option.name
1110
- ]
1111
- },
1112
- option.id
1113
- );
1114
- }) }),
1115
- 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: [
1116
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1117
- "button",
1118
- {
1119
- onClick: () => handleEdit(parsed.blocks, parsed.metaKeywords, msg.id),
1120
- className: "cnfy-btn-edit",
1121
- children: [
1122
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Edit3, { size: 16 }),
1123
- "Edit"
1124
- ]
1125
- }
1126
- ),
1127
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1128
- "button",
1129
- {
1130
- onClick: () => handlePost(msg.content, parsed.metaKeywords),
1131
- className: "cnfy-btn-post",
1132
- style: { backgroundColor: primaryColor, color: "#fff" },
1133
- children: [
1134
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Send, { size: 16 }),
1135
- "Post"
1136
- ]
1137
- }
1138
- )
1139
- ] })
1178
+ option.id
1179
+ );
1180
+ }) }),
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: [
1182
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1183
+ "button",
1184
+ {
1185
+ onClick: () => handleEdit(parsed, msg.id),
1186
+ className: "cnfy-btn-edit",
1187
+ children: [
1188
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Edit3, { size: 16 }),
1189
+ "Edit"
1190
+ ]
1191
+ }
1192
+ ),
1193
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1194
+ "button",
1195
+ {
1196
+ onClick: () => handlePost({ article: parsed.articleHtml, keywords: parsed.metaKeywords }),
1197
+ className: "cnfy-btn-post",
1198
+ style: { backgroundColor: primaryColor, color: "#fff" },
1199
+ children: [
1200
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Send, { size: 16 }),
1201
+ "Post"
1202
+ ]
1203
+ }
1204
+ )
1140
1205
  ] })
1141
- ] }, msg.id);
1206
+ ] }) }, msg.id);
1142
1207
  }),
1143
1208
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { ref: bottomRef })
1144
1209
  ] }) }),
@@ -1270,7 +1335,8 @@ function ChatWindow({
1270
1335
  rows: 1,
1271
1336
  placeholder: "Ask AI something\u2026",
1272
1337
  className: `cnfy-chat-textarea ${!showNewsPanel ? "cnfy-chat-textarea--with-pulse" : ""}`,
1273
- 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
1274
1340
  }
1275
1341
  ),
1276
1342
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
@@ -1287,11 +1353,14 @@ function ChatWindow({
1287
1353
  EditModal,
1288
1354
  {
1289
1355
  isOpen: editModal.isOpen,
1356
+ initialTitle: editModal.title,
1357
+ initialSubtitle: editModal.subtitle,
1290
1358
  initialContent: editModal.content,
1359
+ initialMetaDescription: editModal.metaDescription,
1291
1360
  metaKeywords: editModal.metaKeywords,
1292
1361
  onClose: handleCloseModal,
1293
- onSaveDraft: handleSaveDraft,
1294
- onPost: handlePost
1362
+ onSaveDraft: (data) => handleSaveDraft({ article: data.article, keywords: data.keywords }),
1363
+ onPost: (data) => handlePost({ article: data.article, keywords: data.keywords })
1295
1364
  }
1296
1365
  )
1297
1366
  ] });
@@ -1378,14 +1447,14 @@ var triggerScrape = async ({
1378
1447
  state,
1379
1448
  cities
1380
1449
  }) => {
1381
- const { data } = await api_default.post("/scrape/trigger", {
1450
+ const { data } = await api_default.post("/chatboat/scrape/trigger", {
1382
1451
  state,
1383
1452
  cities
1384
1453
  });
1385
1454
  return data;
1386
1455
  };
1387
1456
  var getScrapeStatus = async () => {
1388
- const { data } = await api_default.get("/scrape/status");
1457
+ const { data } = await api_default.get("/chatboat/scrape/status");
1389
1458
  return data.data;
1390
1459
  };
1391
1460
 
@@ -1406,6 +1475,15 @@ var ClientSelect = (props) => {
1406
1475
  };
1407
1476
  var ClientSelect_default = ClientSelect;
1408
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
+
1409
1487
  // components/preferences/Preferences.tsx
1410
1488
  var import_jsx_runtime10 = require("react/jsx-runtime");
1411
1489
  var STATE_OPTIONS = [
@@ -1441,23 +1519,6 @@ var LANGUAGE_OPTIONS = [
1441
1519
  { value: "en", label: "English" },
1442
1520
  { value: "hi", label: "Hindi" }
1443
1521
  ];
1444
- var TONE_OPTIONS = [
1445
- { value: "formal", label: "Formal" },
1446
- { value: "casual", label: "Casual" },
1447
- { value: "engaging", label: "Engaging" },
1448
- { value: "professional", label: "Professional" }
1449
- ];
1450
- var STYLE_OPTIONS = [
1451
- { value: "news", label: "News Article" },
1452
- { value: "blog", label: "Blog Post" },
1453
- { value: "editorial", label: "Editorial" },
1454
- { value: "summary", label: "Summary" }
1455
- ];
1456
- var WORD_COUNT_OPTIONS = [
1457
- { value: "short", label: "Short (~300 words)" },
1458
- { value: "medium", label: "Medium (~600 words)" },
1459
- { value: "long", label: "Long (~1000+ words)" }
1460
- ];
1461
1522
  function PreferencesPage() {
1462
1523
  var _a, _b, _c;
1463
1524
  const { preferences, loading, refreshPreferences, updatePreferences } = usePreferences();
@@ -1468,12 +1529,6 @@ function PreferencesPage() {
1468
1529
  const [state, setState] = (0, import_react9.useState)(null);
1469
1530
  const [cities, setCities] = (0, import_react9.useState)([]);
1470
1531
  const [language, setLanguage] = (0, import_react9.useState)(null);
1471
- const [tone, setTone] = (0, import_react9.useState)(null);
1472
- const [style, setStyle] = (0, import_react9.useState)(null);
1473
- const [wordCount, setWordCount] = (0, import_react9.useState)(null);
1474
- const [includeQuotes, setIncludeQuotes] = (0, import_react9.useState)(true);
1475
- const [includeFAQ, setIncludeFAQ] = (0, import_react9.useState)(false);
1476
- const [targetAudience, setTargetAudience] = (0, import_react9.useState)("");
1477
1532
  const [saving, setSaving] = (0, import_react9.useState)(false);
1478
1533
  const [success, setSuccess] = (0, import_react9.useState)(false);
1479
1534
  const [scraping, setScraping] = (0, import_react9.useState)(false);
@@ -1483,13 +1538,7 @@ function PreferencesPage() {
1483
1538
  botName: "",
1484
1539
  state: void 0,
1485
1540
  cities: [],
1486
- language: void 0,
1487
- tone: void 0,
1488
- style: void 0,
1489
- wordCount: void 0,
1490
- includeQuotes: true,
1491
- includeFAQ: false,
1492
- targetAudience: ""
1541
+ language: void 0
1493
1542
  });
1494
1543
  (0, import_react9.useEffect)(() => {
1495
1544
  const fetchScrapeStatus = async () => {
@@ -1529,7 +1578,7 @@ function PreferencesPage() {
1529
1578
  }
1530
1579
  };
1531
1580
  (0, import_react9.useEffect)(() => {
1532
- var _a2, _b2, _c2, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
1581
+ var _a2, _b2, _c2, _d, _e;
1533
1582
  if (preferences) {
1534
1583
  const name = ((_a2 = preferences.chatbot) == null ? void 0 : _a2.name) || "";
1535
1584
  const pState = (_b2 = preferences.localization) == null ? void 0 : _b2.state;
@@ -1547,38 +1596,11 @@ function PreferencesPage() {
1547
1596
  const langOption = LANGUAGE_OPTIONS.find((opt) => opt.value === pLanguage);
1548
1597
  setLanguage(langOption || null);
1549
1598
  }
1550
- const pTone = (_f = preferences.content) == null ? void 0 : _f.tone;
1551
- const pStyle = (_g = preferences.content) == null ? void 0 : _g.style;
1552
- const pWordCount = (_h = preferences.content) == null ? void 0 : _h.wordCount;
1553
- const pIncludeQuotes = (_j = (_i = preferences.content) == null ? void 0 : _i.includeQuotes) != null ? _j : true;
1554
- const pIncludeFAQ = (_l = (_k = preferences.content) == null ? void 0 : _k.includeFAQ) != null ? _l : false;
1555
- const pTargetAudience = ((_m = preferences.content) == null ? void 0 : _m.targetAudience) || "";
1556
- if (pTone) {
1557
- const toneOption = TONE_OPTIONS.find((opt) => opt.value === pTone);
1558
- setTone(toneOption || null);
1559
- }
1560
- if (pStyle) {
1561
- const styleOption = STYLE_OPTIONS.find((opt) => opt.value === pStyle);
1562
- setStyle(styleOption || null);
1563
- }
1564
- if (pWordCount) {
1565
- const wordCountOption = WORD_COUNT_OPTIONS.find((opt) => opt.value === pWordCount);
1566
- setWordCount(wordCountOption || null);
1567
- }
1568
- setIncludeQuotes(pIncludeQuotes);
1569
- setIncludeFAQ(pIncludeFAQ);
1570
- setTargetAudience(pTargetAudience);
1571
1599
  setOriginalValues({
1572
1600
  botName: name,
1573
1601
  state: pState,
1574
1602
  cities: pCities,
1575
- language: pLanguage,
1576
- tone: pTone,
1577
- style: pStyle,
1578
- wordCount: pWordCount,
1579
- includeQuotes: pIncludeQuotes,
1580
- includeFAQ: pIncludeFAQ,
1581
- targetAudience: pTargetAudience
1603
+ language: pLanguage
1582
1604
  });
1583
1605
  }
1584
1606
  }, [preferences]);
@@ -1610,27 +1632,6 @@ function PreferencesPage() {
1610
1632
  if (currentLanguage !== originalValues.language) {
1611
1633
  payload.language = currentLanguage;
1612
1634
  }
1613
- const currentTone = tone == null ? void 0 : tone.value;
1614
- if (currentTone !== originalValues.tone) {
1615
- payload.tone = currentTone;
1616
- }
1617
- const currentStyle = style == null ? void 0 : style.value;
1618
- if (currentStyle !== originalValues.style) {
1619
- payload.style = currentStyle;
1620
- }
1621
- const currentWordCount = wordCount == null ? void 0 : wordCount.value;
1622
- if (currentWordCount !== originalValues.wordCount) {
1623
- payload.wordCount = currentWordCount;
1624
- }
1625
- if (includeQuotes !== originalValues.includeQuotes) {
1626
- payload.includeQuotes = includeQuotes;
1627
- }
1628
- if (includeFAQ !== originalValues.includeFAQ) {
1629
- payload.includeFAQ = includeFAQ;
1630
- }
1631
- if (targetAudience !== originalValues.targetAudience) {
1632
- payload.targetAudience = targetAudience;
1633
- }
1634
1635
  if (Object.keys(payload).length === 0) {
1635
1636
  import_react_hot_toast.default.error("No changes to save");
1636
1637
  setSaving(false);
@@ -1703,150 +1704,6 @@ function PreferencesPage() {
1703
1704
  ] })
1704
1705
  ] })
1705
1706
  ] }),
1706
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "cnfy-dash-card", children: [
1707
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { className: "cnfy-dash-card-title", children: "Localization Settings" }),
1708
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "cnfy-dash-field", children: [
1709
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { className: "cnfy-dash-label", children: "State" }),
1710
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1711
- ClientSelect_default,
1712
- {
1713
- options: STATE_OPTIONS,
1714
- value: state,
1715
- onChange: (option) => setState(option),
1716
- placeholder: "Select state"
1717
- }
1718
- )
1719
- ] }),
1720
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "cnfy-dash-field", children: [
1721
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { className: "cnfy-dash-label", children: "Cities" }),
1722
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1723
- ClientSelect_default,
1724
- {
1725
- isMulti: true,
1726
- options: CITY_OPTIONS,
1727
- value: cities,
1728
- onChange: (options) => setCities(options),
1729
- placeholder: "Select cities",
1730
- isDisabled: !state
1731
- }
1732
- )
1733
- ] }),
1734
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "cnfy-dash-field", children: [
1735
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { className: "cnfy-dash-label", children: "Language" }),
1736
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1737
- ClientSelect_default,
1738
- {
1739
- options: LANGUAGE_OPTIONS,
1740
- value: language,
1741
- onChange: (option) => setLanguage(option),
1742
- placeholder: "Select language"
1743
- }
1744
- )
1745
- ] })
1746
- ] }),
1747
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "cnfy-dash-card", children: [
1748
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { className: "cnfy-dash-card-title", children: "Content Generation Settings" }),
1749
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "cnfy-dash-field", children: [
1750
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { className: "cnfy-dash-label", children: "Tone" }),
1751
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1752
- ClientSelect_default,
1753
- {
1754
- options: TONE_OPTIONS,
1755
- value: tone,
1756
- onChange: (option) => setTone(option),
1757
- placeholder: "Select tone"
1758
- }
1759
- ),
1760
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "cnfy-dash-hint", children: "The writing tone for generated content" })
1761
- ] }),
1762
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "cnfy-dash-field", children: [
1763
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { className: "cnfy-dash-label", children: "Style" }),
1764
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1765
- ClientSelect_default,
1766
- {
1767
- options: STYLE_OPTIONS,
1768
- value: style,
1769
- onChange: (option) => setStyle(option),
1770
- placeholder: "Select style"
1771
- }
1772
- ),
1773
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "cnfy-dash-hint", children: "The format/style of generated articles" })
1774
- ] }),
1775
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "cnfy-dash-field", children: [
1776
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { className: "cnfy-dash-label", children: "Word Count" }),
1777
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1778
- ClientSelect_default,
1779
- {
1780
- options: WORD_COUNT_OPTIONS,
1781
- value: wordCount,
1782
- onChange: (option) => setWordCount(option),
1783
- placeholder: "Select word count"
1784
- }
1785
- ),
1786
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "cnfy-dash-hint", children: "Target length for generated content" })
1787
- ] }),
1788
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "cnfy-dash-field", children: [
1789
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { className: "cnfy-dash-label", children: "Target Audience" }),
1790
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1791
- "input",
1792
- {
1793
- value: targetAudience,
1794
- onChange: (e) => setTargetAudience(e.target.value),
1795
- className: "cnfy-dash-input",
1796
- placeholder: "e.g. tech-savvy millennials, business professionals"
1797
- }
1798
- ),
1799
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "cnfy-dash-hint", children: "Describe your target audience for personalized content" })
1800
- ] }),
1801
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "cnfy-toggle-group", children: [
1802
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "cnfy-toggle-row", children: [
1803
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
1804
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "cnfy-toggle-label", children: "Include Quotes" }),
1805
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "cnfy-toggle-desc", children: "Add relevant quotes to generated articles" })
1806
- ] }),
1807
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1808
- "button",
1809
- {
1810
- type: "button",
1811
- role: "switch",
1812
- "aria-checked": includeQuotes,
1813
- onClick: () => setIncludeQuotes(!includeQuotes),
1814
- className: "cnfy-toggle",
1815
- style: { backgroundColor: includeQuotes ? primaryColor : "#d1d5db" },
1816
- children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1817
- "span",
1818
- {
1819
- className: `cnfy-toggle-thumb ${includeQuotes ? "cnfy-toggle-thumb--on" : ""}`
1820
- }
1821
- )
1822
- }
1823
- )
1824
- ] }),
1825
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "cnfy-toggle-row", children: [
1826
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
1827
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "cnfy-toggle-label", children: "Include FAQ Section" }),
1828
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "cnfy-toggle-desc", children: "Add FAQ section at the end of articles" })
1829
- ] }),
1830
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1831
- "button",
1832
- {
1833
- type: "button",
1834
- role: "switch",
1835
- "aria-checked": includeFAQ,
1836
- onClick: () => setIncludeFAQ(!includeFAQ),
1837
- className: "cnfy-toggle",
1838
- style: { backgroundColor: includeFAQ ? primaryColor : "#d1d5db" },
1839
- children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1840
- "span",
1841
- {
1842
- className: `cnfy-toggle-thumb ${includeFAQ ? "cnfy-toggle-thumb--on" : ""}`
1843
- }
1844
- )
1845
- }
1846
- )
1847
- ] })
1848
- ] })
1849
- ] }),
1850
1707
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "cnfy-dash-card", children: [
1851
1708
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "cnfy-dash-card-header", children: [
1852
1709
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { className: "cnfy-dash-card-title-inline", children: "News Scraping" }),
@@ -1884,35 +1741,133 @@ function PreferencesPage() {
1884
1741
  onClick: handleScrape,
1885
1742
  disabled: scraping || (scrapeStatus == null ? void 0 : scrapeStatus.isRunning),
1886
1743
  className: "cnfy-dash-btn-save",
1887
- style: { backgroundColor: primaryColor },
1744
+ style: { backgroundColor: hexToRgba(primaryColor, 0.8) },
1888
1745
  children: scraping ? "Starting Scrape..." : (scrapeStatus == null ? void 0 : scrapeStatus.isRunning) ? "Scraping in Progress..." : "Start Scraping"
1889
1746
  }
1890
1747
  )
1891
1748
  ] })
1892
1749
  ] }),
1893
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "cnfy-dash-actions", children: [
1894
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1895
- "button",
1896
- {
1897
- onClick: handleSave,
1898
- disabled: saving,
1899
- className: "cnfy-dash-btn-save",
1900
- style: { backgroundColor: primaryColor },
1901
- children: saving ? "Saving..." : "Save Preferences"
1902
- }
1903
- ),
1904
- success && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "cnfy-dash-success", style: { color: primaryColor }, children: "Preferences saved successfully \u2705" })
1905
- ] })
1750
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "cnfy-dash-card", children: [
1751
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { className: "cnfy-dash-card-title", children: "Localization Settings" }),
1752
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "cnfy-dash-field", children: [
1753
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { className: "cnfy-dash-label", children: "State" }),
1754
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1755
+ ClientSelect_default,
1756
+ {
1757
+ options: STATE_OPTIONS,
1758
+ value: state,
1759
+ onChange: (option) => setState(option),
1760
+ placeholder: "Select state"
1761
+ }
1762
+ )
1763
+ ] }),
1764
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "cnfy-dash-field", children: [
1765
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { className: "cnfy-dash-label", children: "Cities" }),
1766
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1767
+ ClientSelect_default,
1768
+ {
1769
+ isMulti: true,
1770
+ options: CITY_OPTIONS,
1771
+ value: cities,
1772
+ onChange: (options) => setCities(options),
1773
+ placeholder: "Select cities",
1774
+ isDisabled: !state
1775
+ }
1776
+ )
1777
+ ] }),
1778
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "cnfy-dash-field", children: [
1779
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { className: "cnfy-dash-label", children: "Language" }),
1780
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1781
+ ClientSelect_default,
1782
+ {
1783
+ options: LANGUAGE_OPTIONS,
1784
+ value: language,
1785
+ onChange: (option) => setLanguage(option),
1786
+ placeholder: "Select language"
1787
+ }
1788
+ )
1789
+ ] })
1790
+ ] }),
1791
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "cnfy-dash-actions", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1792
+ "button",
1793
+ {
1794
+ onClick: handleSave,
1795
+ disabled: saving,
1796
+ className: "cnfy-dash-btn-save",
1797
+ style: { backgroundColor: primaryColor },
1798
+ children: saving ? "Saving..." : "Save Preferences"
1799
+ }
1800
+ ) })
1906
1801
  ] });
1907
1802
  }
1908
1803
 
1909
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
+ }
1910
1865
  var analyzeInputApi = async (input) => {
1911
- const { data } = await api_default.post("/chat/analyze-input", { input });
1866
+ const { data } = await api_default.post("/chatboat/analyze-input", { input });
1912
1867
  return data.data;
1913
1868
  };
1914
1869
  var createContentApi = async (payload) => {
1915
- const { data } = await api_default.post("/chat/create-content", payload);
1870
+ const { data } = await api_default.post("/chatboat/create-content", payload);
1916
1871
  return data.data;
1917
1872
  };
1918
1873
  var createContentStreamApi = async ({
@@ -1920,22 +1875,18 @@ var createContentStreamApi = async ({
1920
1875
  content,
1921
1876
  actionId,
1922
1877
  settings,
1923
- onChunk,
1878
+ onFieldStart,
1879
+ onContent,
1880
+ onKeywords,
1881
+ onFieldEnd,
1924
1882
  onComplete,
1925
1883
  onError
1926
1884
  }) => {
1927
1885
  var _a;
1928
1886
  const API_BASE_URL = getApiBaseUrl();
1929
- const apiKey = getApiKey();
1930
- const headers = {
1931
- "Content-Type": "application/json"
1932
- };
1933
- if (apiKey) {
1934
- headers["x-api-key"] = apiKey;
1935
- }
1936
1887
  const response = await fetch(`${API_BASE_URL}/chat/create-content/stream`, {
1937
1888
  method: "POST",
1938
- headers,
1889
+ headers: getHeaders(),
1939
1890
  credentials: "include",
1940
1891
  body: JSON.stringify({ url, content, actionId, settings })
1941
1892
  });
@@ -1944,50 +1895,17 @@ var createContentStreamApi = async ({
1944
1895
  throw new Error(`HTTP error! status: ${response.status} - ${errorText}`);
1945
1896
  }
1946
1897
  const reader = (_a = response.body) == null ? void 0 : _a.getReader();
1947
- const decoder = new TextDecoder();
1948
1898
  if (!reader) {
1949
1899
  throw new Error("No reader available");
1950
1900
  }
1951
- let buffer = "";
1952
- let hasReceivedData = false;
1953
- while (true) {
1954
- const { done, value } = await reader.read();
1955
- if (done) {
1956
- if (hasReceivedData) onComplete == null ? void 0 : onComplete();
1957
- break;
1958
- }
1959
- const chunk = decoder.decode(value, { stream: true });
1960
- buffer += chunk;
1961
- const lines = buffer.split("\n");
1962
- buffer = lines.pop() || "";
1963
- for (const line of lines) {
1964
- const trimmedLine = line.trim();
1965
- if (trimmedLine === "") continue;
1966
- hasReceivedData = true;
1967
- if (trimmedLine.startsWith("data: ")) {
1968
- const data = trimmedLine.slice(6).trim();
1969
- if (data === "[DONE]") {
1970
- onComplete == null ? void 0 : onComplete();
1971
- return;
1972
- }
1973
- try {
1974
- const parsed = JSON.parse(data);
1975
- if (parsed.content) onChunk(parsed.content);
1976
- else if (parsed.delta) onChunk(parsed.delta);
1977
- else if (parsed.text) onChunk(parsed.text);
1978
- else if (parsed.chunk) onChunk(parsed.chunk);
1979
- else if (typeof parsed === "string") onChunk(parsed);
1980
- } catch (e) {
1981
- if (data) onChunk(data);
1982
- }
1983
- } else if (trimmedLine) {
1984
- onChunk(trimmedLine + "\n");
1985
- }
1986
- }
1987
- }
1988
- if (!hasReceivedData) {
1989
- throw new Error("No data received from stream");
1990
- }
1901
+ await processSSEStream(reader, {
1902
+ onFieldStart,
1903
+ onContent,
1904
+ onKeywords,
1905
+ onFieldEnd,
1906
+ onComplete,
1907
+ onError
1908
+ });
1991
1909
  };
1992
1910
  var rewriteNewsStreamApi = async ({
1993
1911
  article,
@@ -1999,7 +1917,10 @@ var rewriteNewsStreamApi = async ({
1999
1917
  includeQuotes,
2000
1918
  includeFAQ,
2001
1919
  targetAudience,
2002
- onChunk,
1920
+ onFieldStart,
1921
+ onContent,
1922
+ onKeywords,
1923
+ onFieldEnd,
2003
1924
  onComplete,
2004
1925
  onError
2005
1926
  }) => {
@@ -2008,9 +1929,7 @@ var rewriteNewsStreamApi = async ({
2008
1929
  article,
2009
1930
  language
2010
1931
  };
2011
- if (articleId) {
2012
- payload.articleId = articleId;
2013
- }
1932
+ if (articleId) payload.articleId = articleId;
2014
1933
  if (tone) payload.tone = tone;
2015
1934
  if (style) payload.style = style;
2016
1935
  if (wordCount) payload.wordCount = wordCount;
@@ -2019,102 +1938,42 @@ var rewriteNewsStreamApi = async ({
2019
1938
  if (targetAudience) payload.targetAudience = targetAudience;
2020
1939
  try {
2021
1940
  const API_BASE_URL = getApiBaseUrl();
2022
- const apiKey = getApiKey();
2023
- console.log("\u{1F680} Starting stream request to:", `${API_BASE_URL}/chat/rewrite/stream`);
2024
- console.log("\u{1F4E6} Payload:", payload);
2025
- const headers = {
2026
- "Content-Type": "application/json"
2027
- };
2028
- if (apiKey) {
2029
- headers["x-api-key"] = apiKey;
2030
- }
2031
- const response = await fetch(`${API_BASE_URL}/chat/rewrite/stream`, {
1941
+ const response = await fetch(`${API_BASE_URL}/chatboat/rewrite/stream`, {
2032
1942
  method: "POST",
2033
- headers,
1943
+ headers: getHeaders(),
2034
1944
  credentials: "include",
2035
- // Include cookies for authentication
2036
1945
  body: JSON.stringify(payload)
2037
1946
  });
2038
- console.log("\u{1F4E1} Response status:", response.status);
2039
- console.log("\u{1F4E1} Response headers:", Object.fromEntries(response.headers.entries()));
2040
1947
  if (!response.ok) {
2041
1948
  const errorText = await response.text();
2042
- console.error("\u274C API Error:", response.status, errorText);
2043
1949
  throw new Error(`HTTP error! status: ${response.status} - ${errorText}`);
2044
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
+ }
2045
1964
  const reader = (_a = response.body) == null ? void 0 : _a.getReader();
2046
- const decoder = new TextDecoder();
2047
1965
  if (!reader) {
2048
1966
  throw new Error("No reader available");
2049
1967
  }
2050
- let buffer = "";
2051
- let hasReceivedData = false;
2052
- while (true) {
2053
- const { done, value } = await reader.read();
2054
- if (done) {
2055
- if (hasReceivedData) {
2056
- onComplete == null ? void 0 : onComplete();
2057
- }
2058
- break;
2059
- }
2060
- const chunk = decoder.decode(value, { stream: true });
2061
- buffer += chunk;
2062
- const lines = buffer.split("\n");
2063
- buffer = lines.pop() || "";
2064
- for (const line of lines) {
2065
- const trimmedLine = line.trim();
2066
- if (trimmedLine === "") continue;
2067
- hasReceivedData = true;
2068
- console.log("\u{1F4E8} Received line:", trimmedLine.substring(0, 100) + (trimmedLine.length > 100 ? "..." : ""));
2069
- if (trimmedLine.startsWith("data: ")) {
2070
- const data = trimmedLine.slice(6).trim();
2071
- if (data === "[DONE]") {
2072
- console.log("\u2705 Stream completed with [DONE] signal");
2073
- onComplete == null ? void 0 : onComplete();
2074
- return;
2075
- }
2076
- try {
2077
- const parsed = JSON.parse(data);
2078
- console.log("\u{1F4CB} Parsed JSON:", parsed);
2079
- if (parsed.content) {
2080
- onChunk(parsed.content);
2081
- } else if (parsed.delta) {
2082
- onChunk(parsed.delta);
2083
- } else if (parsed.text) {
2084
- onChunk(parsed.text);
2085
- } else if (parsed.chunk) {
2086
- onChunk(parsed.chunk);
2087
- } else if (typeof parsed === "string") {
2088
- onChunk(parsed);
2089
- } else {
2090
- console.warn("\u26A0\uFE0F Unknown JSON format:", parsed);
2091
- }
2092
- } catch (e) {
2093
- console.log("\u{1F4DD} Non-JSON data, treating as plain text");
2094
- if (data) {
2095
- onChunk(data);
2096
- }
2097
- }
2098
- } else {
2099
- console.log("\u{1F4C4} Plain text chunk");
2100
- if (trimmedLine) {
2101
- onChunk(trimmedLine + "\n");
2102
- }
2103
- }
2104
- }
2105
- }
2106
- if (!hasReceivedData) {
2107
- console.error("\u274C No data received from stream");
2108
- throw new Error("No data received from stream");
2109
- }
2110
- console.log("\u2705 Stream completed successfully");
2111
- } catch (error) {
2112
- console.error("\u274C Streaming error:", error);
2113
- console.error("Error details:", {
2114
- message: error.message,
2115
- name: error.name,
2116
- stack: error.stack
1968
+ await processSSEStream(reader, {
1969
+ onFieldStart,
1970
+ onContent,
1971
+ onKeywords,
1972
+ onFieldEnd,
1973
+ onComplete,
1974
+ onError
2117
1975
  });
1976
+ } catch (error) {
2118
1977
  onError == null ? void 0 : onError(error);
2119
1978
  throw error;
2120
1979
  }
@@ -2134,16 +1993,14 @@ var rewriteNewsApi = async ({
2134
1993
  article,
2135
1994
  language
2136
1995
  };
2137
- if (articleId) {
2138
- payload.articleId = articleId;
2139
- }
1996
+ if (articleId) payload.articleId = articleId;
2140
1997
  if (tone) payload.tone = tone;
2141
1998
  if (style) payload.style = style;
2142
1999
  if (wordCount) payload.wordCount = wordCount;
2143
2000
  if (includeQuotes !== void 0) payload.includeQuotes = includeQuotes;
2144
2001
  if (includeFAQ !== void 0) payload.includeFAQ = includeFAQ;
2145
2002
  if (targetAudience) payload.targetAudience = targetAudience;
2146
- const { data } = await api_default.post("/chat/rewrite", payload);
2003
+ const { data } = await api_default.post("/chatboat/rewrite", payload);
2147
2004
  return data.data;
2148
2005
  };
2149
2006
 
@@ -2151,12 +2008,16 @@ var rewriteNewsApi = async ({
2151
2008
  var import_react_hot_toast2 = __toESM(require("react-hot-toast"));
2152
2009
  var import_lucide_react7 = require("lucide-react");
2153
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
+ }
2154
2014
  function ChatBot({ onPost }) {
2155
2015
  const { loading } = useTheme();
2156
2016
  const { preferences } = usePreferences();
2157
2017
  const [preferencesOpen, setPreferencesOpen] = (0, import_react10.useState)(false);
2158
2018
  const [messages, setMessages] = (0, import_react10.useState)([]);
2159
2019
  const [isStreaming, setIsStreaming] = (0, import_react10.useState)(false);
2020
+ const [activeField, setActiveField] = (0, import_react10.useState)(null);
2160
2021
  const [analyzedData, setAnalyzedData] = (0, import_react10.useState)(null);
2161
2022
  const handleRecreate = async ({ title, content, id }) => {
2162
2023
  var _a;
@@ -2167,17 +2028,28 @@ function ChatBot({ onPost }) {
2167
2028
  id: crypto.randomUUID(),
2168
2029
  role: "user",
2169
2030
  content: `Recreate this news:
2170
- ${title}`
2031
+ ${title}`
2171
2032
  },
2172
2033
  {
2173
2034
  id: assistantId,
2174
2035
  role: "assistant",
2175
- content: "\u23F3 Creating article..."
2036
+ content: ""
2176
2037
  }
2177
2038
  ]);
2039
+ setIsStreaming(true);
2178
2040
  try {
2179
- let accumulatedContent = "";
2180
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
+ };
2181
2053
  const contentPrefs = preferences == null ? void 0 : preferences.content;
2182
2054
  const lang = ((_a = preferences == null ? void 0 : preferences.localization) == null ? void 0 : _a.language) === "hi" ? "Hindi" : "English";
2183
2055
  try {
@@ -2191,39 +2063,66 @@ ${title}`
2191
2063
  includeQuotes: contentPrefs == null ? void 0 : contentPrefs.includeQuotes,
2192
2064
  includeFAQ: contentPrefs == null ? void 0 : contentPrefs.includeFAQ,
2193
2065
  targetAudience: contentPrefs == null ? void 0 : contentPrefs.targetAudience,
2194
- onChunk: (chunk) => {
2066
+ onFieldStart: (field) => {
2067
+ setActiveField(field);
2068
+ },
2069
+ onContent: (field, chunk) => {
2195
2070
  hasStartedStreaming = true;
2196
- 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);
2197
2084
  setMessages(
2198
2085
  (prev) => prev.map(
2199
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: accumulatedContent }) : m
2086
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: finalContent }) : m
2200
2087
  )
2201
2088
  );
2202
- },
2203
- onComplete: () => {
2204
- console.log("Streaming completed successfully");
2089
+ setIsStreaming(false);
2090
+ setActiveField(null);
2205
2091
  import_react_hot_toast2.default.success("Article created successfully!");
2206
2092
  },
2207
2093
  onError: async (error) => {
2208
2094
  console.error("Streaming error:", error);
2209
2095
  if (!hasStartedStreaming) {
2210
- console.log("Falling back to regular API...");
2211
2096
  throw error;
2212
2097
  } else {
2098
+ setIsStreaming(false);
2099
+ setActiveField(null);
2213
2100
  import_react_hot_toast2.default.error("Failed to complete article generation");
2214
2101
  setMessages(
2215
2102
  (prev) => prev.map(
2216
- (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
2217
2104
  )
2218
2105
  );
2219
2106
  }
2220
2107
  }
2221
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
+ }
2222
2121
  } catch (streamError) {
2223
2122
  console.log("Streaming failed, using regular API...", streamError);
2224
2123
  setMessages(
2225
2124
  (prev) => prev.map(
2226
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "\u23F3 Generating with regular API..." }) : m
2125
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "" }) : m
2227
2126
  )
2228
2127
  );
2229
2128
  const result = await rewriteNewsApi({
@@ -2239,17 +2138,20 @@ ${title}`
2239
2138
  });
2240
2139
  setMessages(
2241
2140
  (prev) => prev.map(
2242
- (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
2243
2142
  )
2244
2143
  );
2144
+ setIsStreaming(false);
2245
2145
  import_react_hot_toast2.default.success("Article created successfully!");
2246
2146
  }
2247
2147
  } catch (err) {
2248
2148
  console.error("Complete Error:", err);
2149
+ setIsStreaming(false);
2150
+ setActiveField(null);
2249
2151
  import_react_hot_toast2.default.error((err == null ? void 0 : err.message) || "An error occurred while creating the article");
2250
2152
  setMessages(
2251
2153
  (prev) => prev.map(
2252
- (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
2253
2155
  )
2254
2156
  );
2255
2157
  }
@@ -2267,9 +2169,10 @@ ${title}`
2267
2169
  if (hasUrl) {
2268
2170
  setMessages((prev) => [
2269
2171
  ...prev,
2270
- { id: assistantId, role: "assistant", content: "\u{1F50D} Analyzing your link..." }
2172
+ { id: assistantId, role: "assistant", content: "Analyzing your link..." }
2271
2173
  ]);
2272
2174
  try {
2175
+ return;
2273
2176
  const result = await analyzeInputApi(text);
2274
2177
  if (result.hasUrl && ((_a = result.options) == null ? void 0 : _a.length) > 0) {
2275
2178
  setAnalyzedData({
@@ -2300,20 +2203,30 @@ ${optionsList}`
2300
2203
  import_react_hot_toast2.default.error("Failed to analyze the link");
2301
2204
  setMessages(
2302
2205
  (prev) => prev.map(
2303
- (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
2304
2207
  )
2305
2208
  );
2306
2209
  }
2307
2210
  } else {
2308
2211
  setMessages((prev) => [
2309
2212
  ...prev,
2310
- { id: assistantId, role: "assistant", content: "\u23F3 Generating content..." }
2213
+ { id: assistantId, role: "assistant", content: "" }
2311
2214
  ]);
2312
2215
  setIsStreaming(true);
2313
2216
  try {
2314
2217
  const contentPrefs = preferences == null ? void 0 : preferences.content;
2315
2218
  const lang = ((_b = preferences == null ? void 0 : preferences.localization) == null ? void 0 : _b.language) === "hi" ? "Hindi" : "English";
2316
- 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
+ };
2317
2230
  await rewriteNewsStreamApi({
2318
2231
  article: text,
2319
2232
  language: lang,
@@ -2323,30 +2236,58 @@ ${optionsList}`
2323
2236
  includeQuotes: contentPrefs == null ? void 0 : contentPrefs.includeQuotes,
2324
2237
  includeFAQ: contentPrefs == null ? void 0 : contentPrefs.includeFAQ,
2325
2238
  targetAudience: contentPrefs == null ? void 0 : contentPrefs.targetAudience,
2326
- onChunk: (chunk) => {
2327
- 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);
2328
2256
  setMessages(
2329
2257
  (prev) => prev.map(
2330
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: accumulatedContent }) : m
2258
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: finalContent }) : m
2331
2259
  )
2332
2260
  );
2333
- },
2334
- onComplete: () => {
2335
2261
  setIsStreaming(false);
2262
+ setActiveField(null);
2336
2263
  import_react_hot_toast2.default.success("Content generated!");
2337
2264
  },
2338
2265
  onError: (error) => {
2339
2266
  console.error("Stream error:", error);
2340
2267
  setIsStreaming(false);
2268
+ setActiveField(null);
2341
2269
  import_react_hot_toast2.default.error("Failed to generate content");
2342
2270
  }
2343
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
+ }
2344
2284
  } catch (err) {
2345
2285
  console.error("Send message error:", err);
2346
2286
  setIsStreaming(false);
2287
+ setActiveField(null);
2347
2288
  setMessages(
2348
2289
  (prev) => prev.map(
2349
- (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
2350
2291
  )
2351
2292
  );
2352
2293
  }
@@ -2360,12 +2301,22 @@ ${optionsList}`
2360
2301
  setMessages((prev) => [
2361
2302
  ...prev,
2362
2303
  { id: crypto.randomUUID(), role: "user", content: `Action: ${actionName}` },
2363
- { id: assistantId, role: "assistant", content: "\u23F3 Creating content..." }
2304
+ { id: assistantId, role: "assistant", content: "" }
2364
2305
  ]);
2365
2306
  setIsStreaming(true);
2366
2307
  try {
2367
2308
  const contentPrefs = preferences == null ? void 0 : preferences.content;
2368
- 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
+ };
2369
2320
  try {
2370
2321
  await createContentStreamApi({
2371
2322
  url,
@@ -2376,24 +2327,51 @@ ${optionsList}`
2376
2327
  style: contentPrefs == null ? void 0 : contentPrefs.style,
2377
2328
  wordCount: contentPrefs == null ? void 0 : contentPrefs.wordCount
2378
2329
  },
2379
- onChunk: (chunk) => {
2380
- 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);
2381
2347
  setMessages(
2382
2348
  (prev) => prev.map(
2383
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: accumulatedContent }) : m
2349
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: finalContent }) : m
2384
2350
  )
2385
2351
  );
2386
- },
2387
- onComplete: () => {
2388
2352
  setIsStreaming(false);
2353
+ setActiveField(null);
2389
2354
  import_react_hot_toast2.default.success("Content created!");
2390
2355
  },
2391
2356
  onError: (error) => {
2392
2357
  console.error("Stream error:", error);
2393
2358
  setIsStreaming(false);
2359
+ setActiveField(null);
2394
2360
  import_react_hot_toast2.default.error("Failed to create content");
2395
2361
  }
2396
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
+ }
2397
2375
  } catch (e) {
2398
2376
  const result = await createContentApi({ url, content, actionId, settings: {
2399
2377
  tone: contentPrefs == null ? void 0 : contentPrefs.tone,
@@ -2402,18 +2380,20 @@ ${optionsList}`
2402
2380
  } });
2403
2381
  setMessages(
2404
2382
  (prev) => prev.map(
2405
- (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
2406
2384
  )
2407
2385
  );
2408
2386
  setIsStreaming(false);
2387
+ setActiveField(null);
2409
2388
  import_react_hot_toast2.default.success("Content created!");
2410
2389
  }
2411
2390
  } catch (err) {
2412
2391
  console.error("Create content error:", err);
2413
2392
  setIsStreaming(false);
2393
+ setActiveField(null);
2414
2394
  setMessages(
2415
2395
  (prev) => prev.map(
2416
- (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
2417
2397
  )
2418
2398
  );
2419
2399
  }
@@ -2434,6 +2414,7 @@ ${optionsList}`
2434
2414
  onSend: handleSendMessage,
2435
2415
  onSelectNews: handleRecreate,
2436
2416
  isStreaming,
2417
+ activeField,
2437
2418
  analyzedData,
2438
2419
  onSelectAction: handleSelectAction,
2439
2420
  onPost