@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/README.md +202 -48
- package/dist/index.js +527 -354
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +533 -360
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +110 -12
- package/package.json +1 -1
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: "
|
|
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
|
-
//
|
|
303
|
-
function
|
|
304
|
-
if (!raw)
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
324
|
-
|
|
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
|
-
|
|
545
|
-
|
|
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
|
|
608
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
609
|
-
"
|
|
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
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
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:
|
|
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(
|
|
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 {
|
|
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 (
|
|
984
|
+
const handleCopy = async (parsed, messageId) => {
|
|
907
985
|
try {
|
|
908
|
-
const
|
|
909
|
-
|
|
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
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
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 = (
|
|
931
|
-
console.log("Saving draft:",
|
|
1016
|
+
const handleSaveDraft = (data) => {
|
|
1017
|
+
console.log("Saving draft:", data);
|
|
932
1018
|
handleCloseModal();
|
|
933
1019
|
};
|
|
934
|
-
const handlePost = (
|
|
935
|
-
onPostCallback == null ? void 0 : onPostCallback(
|
|
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.
|
|
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
|
|
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.
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
})
|
|
1084
|
-
|
|
1085
|
-
"
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
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.
|
|
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
|
|
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(
|
|
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("/
|
|
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("/
|
|
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
|
-
|
|
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
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
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("/
|
|
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: "
|
|
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
|
-
|
|
2066
|
+
onFieldStart: (field) => {
|
|
2067
|
+
setActiveField(field);
|
|
2068
|
+
},
|
|
2069
|
+
onContent: (field, chunk) => {
|
|
2003
2070
|
hasStartedStreaming = true;
|
|
2004
|
-
|
|
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:
|
|
2086
|
+
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: finalContent }) : m
|
|
2008
2087
|
)
|
|
2009
2088
|
);
|
|
2010
|
-
|
|
2011
|
-
|
|
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:
|
|
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: "
|
|
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
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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
|
|
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
|
-
|
|
2135
|
-
|
|
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:
|
|
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: "
|
|
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: "
|
|
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
|
|
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
|
-
|
|
2188
|
-
|
|
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:
|
|
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
|
|
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: "
|
|
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
|