@contenify/chatbot 2.0.0 → 4.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.d.mts +1 -42
- package/dist/index.d.ts +1 -42
- package/dist/index.js +1950 -1319
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1935 -1304
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +224 -33
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -20,293 +20,96 @@ var __spreadValues = (a, b) => {
|
|
|
20
20
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
21
21
|
|
|
22
22
|
// src/index.tsx
|
|
23
|
-
import { useEffect as
|
|
23
|
+
import { useEffect as useEffect7 } from "react";
|
|
24
24
|
|
|
25
|
-
//
|
|
26
|
-
import {
|
|
27
|
-
|
|
28
|
-
// lib/api.ts
|
|
29
|
-
import axios from "axios";
|
|
30
|
-
|
|
31
|
-
// lib/config.ts
|
|
32
|
-
var _apiUrl = process.env.NEXT_PUBLIC_CONTENIFY_API_URL || "http://localhost:8080/api";
|
|
33
|
-
var _apiKey = process.env.NEXT_PUBLIC_API_KEY || "";
|
|
34
|
-
var _domain = process.env.NEXT_PUBLIC_DOMAIN || "";
|
|
35
|
-
function setConfig(config) {
|
|
36
|
-
if (config.apiUrl) _apiUrl = config.apiUrl;
|
|
37
|
-
if (config.apiKey) _apiKey = config.apiKey;
|
|
38
|
-
if (config.domain) _domain = config.domain;
|
|
39
|
-
}
|
|
40
|
-
function getApiBaseUrl() {
|
|
41
|
-
return _apiUrl;
|
|
42
|
-
}
|
|
43
|
-
function getServerBaseUrl() {
|
|
44
|
-
return _apiUrl.replace(/\/api\/?$/, "");
|
|
45
|
-
}
|
|
46
|
-
function getApiKey() {
|
|
47
|
-
return _apiKey;
|
|
48
|
-
}
|
|
25
|
+
// components/chatbot/ChatBot.tsx
|
|
26
|
+
import { useState as useState6 } from "react";
|
|
49
27
|
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
return config;
|
|
65
|
-
},
|
|
66
|
-
(error) => Promise.reject(error)
|
|
67
|
-
);
|
|
68
|
-
api.interceptors.response.use(
|
|
69
|
-
(response) => response,
|
|
70
|
-
(error) => {
|
|
71
|
-
var _a, _b;
|
|
72
|
-
const message = ((_b = (_a = error.response) == null ? void 0 : _a.data) == null ? void 0 : _b.message) || error.message || "Something went wrong";
|
|
73
|
-
return Promise.reject(message);
|
|
28
|
+
// src/utils/ aiJsonParser.ts
|
|
29
|
+
function extractJSON(raw) {
|
|
30
|
+
if (!raw) return null;
|
|
31
|
+
let cleaned = raw.replace(/```json/gi, "").replace(/```/g, "").trim();
|
|
32
|
+
const first = cleaned.indexOf("{");
|
|
33
|
+
const last = cleaned.lastIndexOf("}");
|
|
34
|
+
if (first === -1 || last === -1 || last <= first) return null;
|
|
35
|
+
const possibleJson = cleaned.substring(first, last + 1);
|
|
36
|
+
try {
|
|
37
|
+
return JSON.parse(possibleJson);
|
|
38
|
+
} catch (err) {
|
|
39
|
+
console.warn("JSON parse failed:", err);
|
|
40
|
+
return null;
|
|
74
41
|
}
|
|
75
|
-
|
|
76
|
-
var api_default = api;
|
|
42
|
+
}
|
|
77
43
|
|
|
78
|
-
//
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
logo,
|
|
86
|
-
state,
|
|
87
|
-
cities,
|
|
88
|
-
primaryColor,
|
|
89
|
-
secondaryColor,
|
|
90
|
-
theme,
|
|
91
|
-
language,
|
|
92
|
-
showSidebar,
|
|
93
|
-
showHeader,
|
|
94
|
-
activeThemePreset,
|
|
95
|
-
showNewsPanel,
|
|
96
|
-
tone,
|
|
97
|
-
style,
|
|
98
|
-
wordCount,
|
|
99
|
-
includeQuotes,
|
|
100
|
-
includeFAQ,
|
|
101
|
-
targetAudience
|
|
102
|
-
}) => {
|
|
103
|
-
const formData = new FormData();
|
|
104
|
-
if (botName) {
|
|
105
|
-
formData.append("botName", botName);
|
|
106
|
-
}
|
|
107
|
-
if (logo) {
|
|
108
|
-
formData.append("logo", logo);
|
|
109
|
-
}
|
|
110
|
-
if (state) {
|
|
111
|
-
formData.append("state", state);
|
|
112
|
-
}
|
|
113
|
-
if (cities) {
|
|
114
|
-
formData.append("cities", JSON.stringify(cities));
|
|
115
|
-
}
|
|
116
|
-
if (primaryColor) {
|
|
117
|
-
formData.append("primaryColor", primaryColor);
|
|
118
|
-
}
|
|
119
|
-
if (secondaryColor) {
|
|
120
|
-
formData.append("secondaryColor", secondaryColor);
|
|
121
|
-
}
|
|
122
|
-
if (theme) {
|
|
123
|
-
formData.append("theme", theme);
|
|
124
|
-
}
|
|
125
|
-
if (language) {
|
|
126
|
-
formData.append("language", language);
|
|
127
|
-
}
|
|
128
|
-
if (showSidebar !== void 0) {
|
|
129
|
-
formData.append("showSidebar", String(showSidebar));
|
|
130
|
-
}
|
|
131
|
-
if (showHeader !== void 0) {
|
|
132
|
-
formData.append("showHeader", String(showHeader));
|
|
133
|
-
}
|
|
134
|
-
if (activeThemePreset !== void 0) {
|
|
135
|
-
formData.append("activeThemePreset", activeThemePreset != null ? activeThemePreset : "");
|
|
136
|
-
}
|
|
137
|
-
if (showNewsPanel !== void 0) {
|
|
138
|
-
formData.append("showNewsPanel", String(showNewsPanel));
|
|
139
|
-
}
|
|
140
|
-
if (tone) {
|
|
141
|
-
formData.append("tone", tone);
|
|
142
|
-
}
|
|
143
|
-
if (style) {
|
|
144
|
-
formData.append("style", style);
|
|
145
|
-
}
|
|
146
|
-
if (wordCount) {
|
|
147
|
-
formData.append("wordCount", wordCount);
|
|
148
|
-
}
|
|
149
|
-
if (includeQuotes !== void 0) {
|
|
150
|
-
formData.append("includeQuotes", String(includeQuotes));
|
|
151
|
-
}
|
|
152
|
-
if (includeFAQ !== void 0) {
|
|
153
|
-
formData.append("includeFAQ", String(includeFAQ));
|
|
154
|
-
}
|
|
155
|
-
if (targetAudience !== void 0) {
|
|
156
|
-
formData.append("targetAudience", targetAudience);
|
|
44
|
+
// src/utils/decodeUnicode.ts
|
|
45
|
+
function decodeUnicode(text) {
|
|
46
|
+
if (!text) return "";
|
|
47
|
+
try {
|
|
48
|
+
return JSON.parse(`"${text.replace(/"/g, '\\"')}"`);
|
|
49
|
+
} catch (e) {
|
|
50
|
+
return text;
|
|
157
51
|
}
|
|
158
|
-
|
|
159
|
-
headers: {
|
|
160
|
-
"Content-Type": "multipart/form-data"
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
return data.data;
|
|
164
|
-
};
|
|
52
|
+
}
|
|
165
53
|
|
|
166
|
-
//
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
country: "India",
|
|
177
|
-
state: "Uttar Pradesh",
|
|
178
|
-
cities: [],
|
|
179
|
-
language: "en",
|
|
180
|
-
locale: "en-IN",
|
|
181
|
-
timezone: "Asia/Kolkata"
|
|
182
|
-
},
|
|
183
|
-
appearance: {
|
|
184
|
-
showSidebar: true,
|
|
185
|
-
showHeader: true,
|
|
186
|
-
activeThemePreset: null,
|
|
187
|
-
showNewsPanel: true
|
|
188
|
-
},
|
|
189
|
-
content: {
|
|
190
|
-
tone: "engaging",
|
|
191
|
-
style: "blog",
|
|
192
|
-
wordCount: "medium",
|
|
193
|
-
includeQuotes: true,
|
|
194
|
-
includeFAQ: false,
|
|
195
|
-
targetAudience: ""
|
|
196
|
-
}
|
|
197
|
-
};
|
|
198
|
-
var PreferencesContext = createContext(void 0);
|
|
199
|
-
function PreferencesProvider({ children }) {
|
|
200
|
-
const [preferences, setPreferences] = useState(defaultPreferences);
|
|
201
|
-
const [loading, setLoading] = useState(true);
|
|
202
|
-
const [error, setError] = useState(null);
|
|
203
|
-
const refreshPreferences = async () => {
|
|
204
|
-
var _a, _b, _c;
|
|
205
|
-
try {
|
|
206
|
-
setLoading(true);
|
|
207
|
-
setError(null);
|
|
208
|
-
const data = await getMyPreferencesApi();
|
|
209
|
-
if (data) {
|
|
210
|
-
if (((_a = data.chatbot) == null ? void 0 : _a.logoUrl) && !data.chatbot.logoUrl.startsWith("http")) {
|
|
211
|
-
data.chatbot.logoUrl = `${getServerBaseUrl()}/${data.chatbot.logoUrl}`;
|
|
212
|
-
}
|
|
213
|
-
const mergedPreferences = __spreadProps(__spreadValues(__spreadValues({}, defaultPreferences), data), {
|
|
214
|
-
chatbot: __spreadValues(__spreadValues({}, defaultPreferences.chatbot), data.chatbot),
|
|
215
|
-
localization: __spreadValues(__spreadValues({}, defaultPreferences.localization), data.localization),
|
|
216
|
-
appearance: __spreadValues(__spreadValues({}, defaultPreferences.appearance), data.appearance),
|
|
217
|
-
content: __spreadValues(__spreadValues({}, defaultPreferences.content), data.content)
|
|
218
|
-
});
|
|
219
|
-
setPreferences(mergedPreferences);
|
|
220
|
-
if (typeof document !== "undefined") {
|
|
221
|
-
document.documentElement.style.setProperty("--color-primary", ((_b = mergedPreferences.chatbot) == null ? void 0 : _b.primaryColor) || "#10b981");
|
|
222
|
-
document.documentElement.style.setProperty("--color-secondary", ((_c = mergedPreferences.chatbot) == null ? void 0 : _c.secondaryColor) || "#064e3b");
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
} catch (err) {
|
|
226
|
-
console.error("Failed to fetch preferences:", err);
|
|
227
|
-
setError(err instanceof Error ? err.message : "Failed to load preferences");
|
|
228
|
-
setPreferences(defaultPreferences);
|
|
229
|
-
} finally {
|
|
230
|
-
setLoading(false);
|
|
231
|
-
}
|
|
232
|
-
};
|
|
233
|
-
const updatePreferences = (newPreferences) => {
|
|
234
|
-
var _a, _b;
|
|
235
|
-
setPreferences(newPreferences);
|
|
236
|
-
if (typeof document !== "undefined") {
|
|
237
|
-
document.documentElement.style.setProperty("--color-primary", ((_a = newPreferences.chatbot) == null ? void 0 : _a.primaryColor) || "#10b981");
|
|
238
|
-
document.documentElement.style.setProperty("--color-secondary", ((_b = newPreferences.chatbot) == null ? void 0 : _b.secondaryColor) || "#064e3b");
|
|
239
|
-
}
|
|
240
|
-
};
|
|
241
|
-
useEffect(() => {
|
|
242
|
-
refreshPreferences();
|
|
243
|
-
}, []);
|
|
244
|
-
return /* @__PURE__ */ jsx(
|
|
245
|
-
PreferencesContext.Provider,
|
|
246
|
-
{
|
|
247
|
-
value: {
|
|
248
|
-
preferences,
|
|
249
|
-
loading,
|
|
250
|
-
error,
|
|
251
|
-
refreshPreferences,
|
|
252
|
-
updatePreferences
|
|
253
|
-
},
|
|
254
|
-
children
|
|
54
|
+
// src/utils/articleTextToHtml.ts
|
|
55
|
+
function articleTextToHtml(text) {
|
|
56
|
+
if (!text) return "";
|
|
57
|
+
text = text.replace(/\\n/g, "\n");
|
|
58
|
+
const lines = text.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
59
|
+
let html = "";
|
|
60
|
+
for (const line of lines) {
|
|
61
|
+
if (line.length < 80 && !line.endsWith("\u0964") && !line.endsWith(".")) {
|
|
62
|
+
html += `<h2>${line}</h2>`;
|
|
63
|
+
continue;
|
|
255
64
|
}
|
|
256
|
-
|
|
257
|
-
}
|
|
258
|
-
function usePreferences() {
|
|
259
|
-
const context = useContext(PreferencesContext);
|
|
260
|
-
if (context === void 0) {
|
|
261
|
-
throw new Error("usePreferences must be used within a PreferencesProvider");
|
|
65
|
+
html += `<p>${line}</p>`;
|
|
262
66
|
}
|
|
263
|
-
return
|
|
67
|
+
return html;
|
|
264
68
|
}
|
|
265
69
|
|
|
266
|
-
//
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
70
|
+
// src/utils/formatAIContent.ts
|
|
71
|
+
function formatAIContent(raw) {
|
|
72
|
+
const empty = {
|
|
73
|
+
isArticle: false,
|
|
74
|
+
title: "",
|
|
75
|
+
subtitle: "",
|
|
76
|
+
articleHtml: "",
|
|
77
|
+
article: "",
|
|
78
|
+
metaDescription: "",
|
|
79
|
+
metaKeywords: [],
|
|
80
|
+
plainText: "",
|
|
81
|
+
images: []
|
|
82
|
+
};
|
|
83
|
+
if (!raw) return empty;
|
|
84
|
+
const parsed = extractJSON(raw);
|
|
85
|
+
if (parsed && (parsed.title || parsed.article)) {
|
|
86
|
+
const rawArticle = (parsed.article || "").trim();
|
|
87
|
+
const safeArticle = decodeUnicode(rawArticle);
|
|
88
|
+
const isHtml = /<[a-z][\s\S]*>/i.test(safeArticle);
|
|
89
|
+
const articleHtml = isHtml ? safeArticle : articleTextToHtml(safeArticle);
|
|
282
90
|
return {
|
|
91
|
+
isArticle: true,
|
|
283
92
|
title: parsed.title || "",
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
93
|
+
subtitle: parsed.subtitle || "",
|
|
94
|
+
articleHtml,
|
|
95
|
+
article: rawArticle,
|
|
96
|
+
metaDescription: parsed.metaDescription || "",
|
|
97
|
+
metaKeywords: Array.isArray(parsed.metaKeywords) ? parsed.metaKeywords : [],
|
|
98
|
+
plainText: raw,
|
|
99
|
+
seoAnalysis: parsed.seoAnalysis || null,
|
|
100
|
+
images: Array.isArray(parsed.images) ? parsed.images : []
|
|
292
101
|
};
|
|
293
102
|
}
|
|
103
|
+
return __spreadProps(__spreadValues({}, empty), { plainText: raw });
|
|
294
104
|
}
|
|
295
|
-
var hexToRgba = (hex, opacity) => {
|
|
296
|
-
const sanitizedHex = hex.replace("#", "");
|
|
297
|
-
const r = parseInt(sanitizedHex.substring(0, 2), 16);
|
|
298
|
-
const g = parseInt(sanitizedHex.substring(2, 4), 16);
|
|
299
|
-
const b = parseInt(sanitizedHex.substring(4, 6), 16);
|
|
300
|
-
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
|
301
|
-
};
|
|
302
105
|
|
|
303
106
|
// components/chatbot/ChatWindow.tsx
|
|
304
|
-
import { Check, Copy, Edit3,
|
|
305
|
-
import { useLayoutEffect, useRef, useState as
|
|
107
|
+
import { Check, Copy, Edit3, FileDown as FileDown2, SendHorizontal, Zap, X as X2, RefreshCcw as RefreshCcw2, FileText, ListChecks, Share2, BookOpen, Mail, Key, Loader2 as Loader22, Sparkles, Link2, PenLine, LayoutList, BarChart2 as BarChart22, ChevronDown as ChevronDown2, ChevronUp as ChevronUp2, AlertTriangle as AlertTriangle2, Lightbulb as Lightbulb2, Wand2 } from "lucide-react";
|
|
108
|
+
import { useLayoutEffect as useLayoutEffect2, useRef, useState as useState3, useEffect as useEffect3, useCallback } from "react";
|
|
306
109
|
|
|
307
110
|
// components/chatbot/EditModal.tsx
|
|
308
|
-
import { X } from "lucide-react";
|
|
309
|
-
import { useEffect as
|
|
111
|
+
import { X, FileDown, Loader2, BarChart2, AlertTriangle, Lightbulb, ChevronDown, ChevronUp, Send } from "lucide-react";
|
|
112
|
+
import { useEffect as useEffect2, useLayoutEffect, useState } from "react";
|
|
310
113
|
|
|
311
114
|
// components/chatbot/RichTextEditor.tsx
|
|
312
115
|
import { useEditor, EditorContent } from "@tiptap/react";
|
|
@@ -323,8 +126,8 @@ import {
|
|
|
323
126
|
Redo,
|
|
324
127
|
Pilcrow
|
|
325
128
|
} from "lucide-react";
|
|
326
|
-
import { useEffect
|
|
327
|
-
import { jsx
|
|
129
|
+
import { useEffect } from "react";
|
|
130
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
328
131
|
function RichTextEditor({
|
|
329
132
|
content,
|
|
330
133
|
onChange,
|
|
@@ -353,102 +156,102 @@ function RichTextEditor({
|
|
|
353
156
|
onChange(editor2.getHTML());
|
|
354
157
|
}
|
|
355
158
|
});
|
|
356
|
-
|
|
159
|
+
useEffect(() => {
|
|
357
160
|
if (editor && content !== editor.getHTML()) {
|
|
358
161
|
editor.commands.setContent(content);
|
|
359
162
|
}
|
|
360
163
|
}, [content, editor]);
|
|
361
164
|
if (!editor) {
|
|
362
|
-
return /* @__PURE__ */
|
|
165
|
+
return /* @__PURE__ */ jsx("div", { className: "cnfy-editor-loading", children: "Loading editor..." });
|
|
363
166
|
}
|
|
364
167
|
return /* @__PURE__ */ jsxs("div", { className: "cnfy-editor", children: [
|
|
365
168
|
/* @__PURE__ */ jsxs("div", { className: "cnfy-toolbar", children: [
|
|
366
|
-
/* @__PURE__ */
|
|
169
|
+
/* @__PURE__ */ jsx(
|
|
367
170
|
ToolbarButton,
|
|
368
171
|
{
|
|
369
172
|
onClick: () => editor.chain().focus().toggleHeading({ level: 1 }).run(),
|
|
370
173
|
isActive: editor.isActive("heading", { level: 1 }),
|
|
371
174
|
title: "Heading 1",
|
|
372
|
-
children: /* @__PURE__ */
|
|
175
|
+
children: /* @__PURE__ */ jsx(Heading1, { size: 18 })
|
|
373
176
|
}
|
|
374
177
|
),
|
|
375
|
-
/* @__PURE__ */
|
|
178
|
+
/* @__PURE__ */ jsx(
|
|
376
179
|
ToolbarButton,
|
|
377
180
|
{
|
|
378
181
|
onClick: () => editor.chain().focus().toggleHeading({ level: 2 }).run(),
|
|
379
182
|
isActive: editor.isActive("heading", { level: 2 }),
|
|
380
183
|
title: "Heading 2",
|
|
381
|
-
children: /* @__PURE__ */
|
|
184
|
+
children: /* @__PURE__ */ jsx(Heading2, { size: 18 })
|
|
382
185
|
}
|
|
383
186
|
),
|
|
384
|
-
/* @__PURE__ */
|
|
187
|
+
/* @__PURE__ */ jsx(
|
|
385
188
|
ToolbarButton,
|
|
386
189
|
{
|
|
387
190
|
onClick: () => editor.chain().focus().setParagraph().run(),
|
|
388
191
|
isActive: editor.isActive("paragraph"),
|
|
389
192
|
title: "Paragraph",
|
|
390
|
-
children: /* @__PURE__ */
|
|
193
|
+
children: /* @__PURE__ */ jsx(Pilcrow, { size: 18 })
|
|
391
194
|
}
|
|
392
195
|
),
|
|
393
|
-
/* @__PURE__ */
|
|
394
|
-
/* @__PURE__ */
|
|
196
|
+
/* @__PURE__ */ jsx("div", { className: "cnfy-toolbar-divider" }),
|
|
197
|
+
/* @__PURE__ */ jsx(
|
|
395
198
|
ToolbarButton,
|
|
396
199
|
{
|
|
397
200
|
onClick: () => editor.chain().focus().toggleBold().run(),
|
|
398
201
|
isActive: editor.isActive("bold"),
|
|
399
202
|
title: "Bold",
|
|
400
|
-
children: /* @__PURE__ */
|
|
203
|
+
children: /* @__PURE__ */ jsx(Bold, { size: 18 })
|
|
401
204
|
}
|
|
402
205
|
),
|
|
403
|
-
/* @__PURE__ */
|
|
206
|
+
/* @__PURE__ */ jsx(
|
|
404
207
|
ToolbarButton,
|
|
405
208
|
{
|
|
406
209
|
onClick: () => editor.chain().focus().toggleItalic().run(),
|
|
407
210
|
isActive: editor.isActive("italic"),
|
|
408
211
|
title: "Italic",
|
|
409
|
-
children: /* @__PURE__ */
|
|
212
|
+
children: /* @__PURE__ */ jsx(Italic, { size: 18 })
|
|
410
213
|
}
|
|
411
214
|
),
|
|
412
|
-
/* @__PURE__ */
|
|
413
|
-
/* @__PURE__ */
|
|
215
|
+
/* @__PURE__ */ jsx("div", { className: "cnfy-toolbar-divider" }),
|
|
216
|
+
/* @__PURE__ */ jsx(
|
|
414
217
|
ToolbarButton,
|
|
415
218
|
{
|
|
416
219
|
onClick: () => editor.chain().focus().toggleBulletList().run(),
|
|
417
220
|
isActive: editor.isActive("bulletList"),
|
|
418
221
|
title: "Bullet List",
|
|
419
|
-
children: /* @__PURE__ */
|
|
222
|
+
children: /* @__PURE__ */ jsx(List, { size: 18 })
|
|
420
223
|
}
|
|
421
224
|
),
|
|
422
|
-
/* @__PURE__ */
|
|
225
|
+
/* @__PURE__ */ jsx(
|
|
423
226
|
ToolbarButton,
|
|
424
227
|
{
|
|
425
228
|
onClick: () => editor.chain().focus().toggleOrderedList().run(),
|
|
426
229
|
isActive: editor.isActive("orderedList"),
|
|
427
230
|
title: "Numbered List",
|
|
428
|
-
children: /* @__PURE__ */
|
|
231
|
+
children: /* @__PURE__ */ jsx(ListOrdered, { size: 18 })
|
|
429
232
|
}
|
|
430
233
|
),
|
|
431
|
-
/* @__PURE__ */
|
|
432
|
-
/* @__PURE__ */
|
|
234
|
+
/* @__PURE__ */ jsx("div", { className: "cnfy-toolbar-divider" }),
|
|
235
|
+
/* @__PURE__ */ jsx(
|
|
433
236
|
ToolbarButton,
|
|
434
237
|
{
|
|
435
238
|
onClick: () => editor.chain().focus().undo().run(),
|
|
436
239
|
disabled: !editor.can().undo(),
|
|
437
240
|
title: "Undo",
|
|
438
|
-
children: /* @__PURE__ */
|
|
241
|
+
children: /* @__PURE__ */ jsx(Undo, { size: 18 })
|
|
439
242
|
}
|
|
440
243
|
),
|
|
441
|
-
/* @__PURE__ */
|
|
244
|
+
/* @__PURE__ */ jsx(
|
|
442
245
|
ToolbarButton,
|
|
443
246
|
{
|
|
444
247
|
onClick: () => editor.chain().focus().redo().run(),
|
|
445
248
|
disabled: !editor.can().redo(),
|
|
446
249
|
title: "Redo",
|
|
447
|
-
children: /* @__PURE__ */
|
|
250
|
+
children: /* @__PURE__ */ jsx(Redo, { size: 18 })
|
|
448
251
|
}
|
|
449
252
|
)
|
|
450
253
|
] }),
|
|
451
|
-
/* @__PURE__ */
|
|
254
|
+
/* @__PURE__ */ jsx(EditorContent, { editor, className: "cnfy-editor-content" })
|
|
452
255
|
] });
|
|
453
256
|
}
|
|
454
257
|
function ToolbarButton({
|
|
@@ -458,7 +261,7 @@ function ToolbarButton({
|
|
|
458
261
|
title,
|
|
459
262
|
children
|
|
460
263
|
}) {
|
|
461
|
-
return /* @__PURE__ */
|
|
264
|
+
return /* @__PURE__ */ jsx(
|
|
462
265
|
"button",
|
|
463
266
|
{
|
|
464
267
|
onClick,
|
|
@@ -472,57 +275,76 @@ function ToolbarButton({
|
|
|
472
275
|
|
|
473
276
|
// hooks/useTheme.ts
|
|
474
277
|
function useTheme() {
|
|
475
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
476
|
-
const { preferences, loading } = usePreferences();
|
|
477
278
|
return {
|
|
478
|
-
loading,
|
|
479
|
-
primaryColor:
|
|
480
|
-
secondaryColor:
|
|
481
|
-
botName:
|
|
482
|
-
logoUrl:
|
|
483
|
-
theme:
|
|
484
|
-
showSidebar:
|
|
485
|
-
showHeader:
|
|
486
|
-
activeThemePreset:
|
|
487
|
-
showNewsPanel:
|
|
279
|
+
loading: false,
|
|
280
|
+
primaryColor: "#10b981",
|
|
281
|
+
secondaryColor: "#064e3b",
|
|
282
|
+
botName: "ContenifyAI Assistant",
|
|
283
|
+
logoUrl: "/logo.png",
|
|
284
|
+
theme: "system",
|
|
285
|
+
showSidebar: true,
|
|
286
|
+
showHeader: true,
|
|
287
|
+
activeThemePreset: null,
|
|
288
|
+
showNewsPanel: true
|
|
488
289
|
};
|
|
489
290
|
}
|
|
490
291
|
|
|
491
292
|
// components/chatbot/EditModal.tsx
|
|
492
|
-
import { jsx as
|
|
293
|
+
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
294
|
+
function seoScoreColor(score) {
|
|
295
|
+
if (score >= 80) return "#10b981";
|
|
296
|
+
if (score >= 60) return "#f59e0b";
|
|
297
|
+
if (score >= 40) return "#f97316";
|
|
298
|
+
return "#ef4444";
|
|
299
|
+
}
|
|
300
|
+
function seoGradeColor(grade) {
|
|
301
|
+
if (grade === "A") return "#10b981";
|
|
302
|
+
if (grade === "B") return "#3b82f6";
|
|
303
|
+
if (grade === "C") return "#f59e0b";
|
|
304
|
+
if (grade === "D") return "#f97316";
|
|
305
|
+
return "#ef4444";
|
|
306
|
+
}
|
|
307
|
+
function toSlug(text) {
|
|
308
|
+
return text.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-");
|
|
309
|
+
}
|
|
493
310
|
function EditModal({
|
|
494
311
|
isOpen,
|
|
312
|
+
inline = false,
|
|
313
|
+
initialTitle = "",
|
|
314
|
+
initialSubtitle = "",
|
|
495
315
|
initialContent,
|
|
316
|
+
initialMetaDescription = "",
|
|
496
317
|
metaKeywords,
|
|
318
|
+
initialFeaturedImage = "",
|
|
319
|
+
seoAnalysis = null,
|
|
497
320
|
onClose,
|
|
498
|
-
|
|
499
|
-
|
|
321
|
+
onDownload,
|
|
322
|
+
downloadingFormat,
|
|
323
|
+
onPost,
|
|
324
|
+
isPosting = false
|
|
500
325
|
}) {
|
|
501
326
|
const { primaryColor } = useTheme();
|
|
502
|
-
const [
|
|
503
|
-
const [
|
|
504
|
-
const [
|
|
505
|
-
const
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
};
|
|
520
|
-
useEffect3(() => {
|
|
521
|
-
const htmlContent = markdownToHtml(initialContent);
|
|
522
|
-
setContent(htmlContent);
|
|
327
|
+
const [title, setTitle] = useState("");
|
|
328
|
+
const [seoSugOpen, setSeoSugOpen] = useState(false);
|
|
329
|
+
const [subtitle, setSubtitle] = useState("");
|
|
330
|
+
const [slug, setSlug] = useState("");
|
|
331
|
+
const [slugEdited, setSlugEdited] = useState(false);
|
|
332
|
+
const [metaDescription, setMetaDescription] = useState("");
|
|
333
|
+
const [content, setContent] = useState("");
|
|
334
|
+
const [keywords, setKeywords] = useState(metaKeywords);
|
|
335
|
+
const [newKeyword, setNewKeyword] = useState("");
|
|
336
|
+
const [featuredImage, setFeaturedImage] = useState(initialFeaturedImage);
|
|
337
|
+
useLayoutEffect(() => {
|
|
338
|
+
setTitle(initialTitle);
|
|
339
|
+
setSubtitle(initialSubtitle);
|
|
340
|
+
setMetaDescription(initialMetaDescription);
|
|
341
|
+
setSlug(toSlug(initialTitle));
|
|
342
|
+
setSlugEdited(false);
|
|
343
|
+
setContent(initialContent);
|
|
523
344
|
setKeywords(metaKeywords);
|
|
524
|
-
|
|
525
|
-
|
|
345
|
+
setFeaturedImage(initialFeaturedImage);
|
|
346
|
+
}, [initialTitle, initialSubtitle, initialContent, initialMetaDescription, metaKeywords, initialFeaturedImage]);
|
|
347
|
+
useEffect2(() => {
|
|
526
348
|
if (isOpen) {
|
|
527
349
|
document.body.style.overflow = "hidden";
|
|
528
350
|
} else {
|
|
@@ -532,6 +354,16 @@ function EditModal({
|
|
|
532
354
|
document.body.style.overflow = "";
|
|
533
355
|
};
|
|
534
356
|
}, [isOpen]);
|
|
357
|
+
const handleTitleChange = (value) => {
|
|
358
|
+
setTitle(value);
|
|
359
|
+
if (!slugEdited) {
|
|
360
|
+
setSlug(toSlug(value));
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
const handleSlugChange = (value) => {
|
|
364
|
+
setSlug(toSlug(value));
|
|
365
|
+
setSlugEdited(true);
|
|
366
|
+
};
|
|
535
367
|
const handleAddKeyword = () => {
|
|
536
368
|
if (newKeyword.trim() && !keywords.includes(newKeyword.trim())) {
|
|
537
369
|
setKeywords([...keywords, newKeyword.trim()]);
|
|
@@ -547,118 +379,319 @@ function EditModal({
|
|
|
547
379
|
handleAddKeyword();
|
|
548
380
|
}
|
|
549
381
|
};
|
|
382
|
+
const getEditData = () => ({
|
|
383
|
+
title,
|
|
384
|
+
subtitle,
|
|
385
|
+
article: content,
|
|
386
|
+
metaDescription,
|
|
387
|
+
slug,
|
|
388
|
+
keywords,
|
|
389
|
+
featuredImage: featuredImage || void 0
|
|
390
|
+
});
|
|
550
391
|
if (!isOpen) return null;
|
|
551
|
-
|
|
552
|
-
/* @__PURE__ */
|
|
553
|
-
"
|
|
554
|
-
{
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
392
|
+
const editContent = /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
393
|
+
/* @__PURE__ */ jsxs2("div", { className: "cnfy-edit-modal-header", children: [
|
|
394
|
+
/* @__PURE__ */ jsx2("h2", { className: "cnfy-edit-modal-title", children: "Edit Article" }),
|
|
395
|
+
/* @__PURE__ */ jsx2("button", { onClick: onClose, className: "cnfy-edit-modal-close-btn", children: /* @__PURE__ */ jsx2(X, { size: 20, className: "cnfy-edit-modal-close-icon" }) })
|
|
396
|
+
] }),
|
|
397
|
+
/* @__PURE__ */ jsxs2("div", { className: "cnfy-edit-modal-body", children: [
|
|
398
|
+
featuredImage && /* @__PURE__ */ jsxs2("div", { className: "cnfy-edit-featured-image-wrap", children: [
|
|
399
|
+
/* @__PURE__ */ jsx2(
|
|
400
|
+
"img",
|
|
401
|
+
{
|
|
402
|
+
src: featuredImage,
|
|
403
|
+
alt: "Featured",
|
|
404
|
+
className: "cnfy-edit-featured-image"
|
|
405
|
+
}
|
|
406
|
+
),
|
|
407
|
+
/* @__PURE__ */ jsx2(
|
|
563
408
|
"button",
|
|
564
409
|
{
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
410
|
+
type: "button",
|
|
411
|
+
onClick: () => setFeaturedImage(""),
|
|
412
|
+
className: "cnfy-edit-featured-image-remove",
|
|
413
|
+
title: "Remove image",
|
|
414
|
+
children: /* @__PURE__ */ jsx2(X, { size: 14 })
|
|
568
415
|
}
|
|
569
416
|
)
|
|
570
417
|
] }),
|
|
571
|
-
/* @__PURE__ */ jsxs2("div", {
|
|
572
|
-
/* @__PURE__ */
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
/* @__PURE__ */ jsxs2("div", { children: [
|
|
584
|
-
/* @__PURE__ */ jsx3("label", { className: "cnfy-edit-label", children: "Meta Keywords" }),
|
|
585
|
-
/* @__PURE__ */ jsx3("div", { className: "cnfy-keyword-list", children: keywords.map((keyword, index) => /* @__PURE__ */ jsxs2(
|
|
586
|
-
"span",
|
|
587
|
-
{
|
|
588
|
-
className: "cnfy-keyword-tag",
|
|
589
|
-
children: [
|
|
590
|
-
"#",
|
|
591
|
-
keyword,
|
|
592
|
-
/* @__PURE__ */ jsx3(
|
|
593
|
-
"button",
|
|
594
|
-
{
|
|
595
|
-
onClick: () => handleRemoveKeyword(index),
|
|
596
|
-
className: "cnfy-keyword-remove-btn",
|
|
597
|
-
children: /* @__PURE__ */ jsx3(X, { size: 14 })
|
|
598
|
-
}
|
|
599
|
-
)
|
|
600
|
-
]
|
|
601
|
-
},
|
|
602
|
-
index
|
|
603
|
-
)) }),
|
|
604
|
-
/* @__PURE__ */ jsxs2("div", { className: "cnfy-keyword-input-row", children: [
|
|
605
|
-
/* @__PURE__ */ jsx3(
|
|
606
|
-
"input",
|
|
607
|
-
{
|
|
608
|
-
type: "text",
|
|
609
|
-
value: newKeyword,
|
|
610
|
-
onChange: (e) => setNewKeyword(e.target.value),
|
|
611
|
-
onKeyDown: handleKeyDown,
|
|
612
|
-
placeholder: "Add a keyword...",
|
|
613
|
-
className: "cnfy-keyword-input"
|
|
614
|
-
}
|
|
615
|
-
),
|
|
616
|
-
/* @__PURE__ */ jsx3(
|
|
617
|
-
"button",
|
|
618
|
-
{
|
|
619
|
-
onClick: handleAddKeyword,
|
|
620
|
-
className: "cnfy-btn-add-keyword",
|
|
621
|
-
children: "Add"
|
|
622
|
-
}
|
|
623
|
-
)
|
|
624
|
-
] })
|
|
625
|
-
] })
|
|
418
|
+
/* @__PURE__ */ jsxs2("div", { children: [
|
|
419
|
+
/* @__PURE__ */ jsx2("label", { className: "cnfy-edit-label", children: "Title" }),
|
|
420
|
+
/* @__PURE__ */ jsx2(
|
|
421
|
+
"input",
|
|
422
|
+
{
|
|
423
|
+
type: "text",
|
|
424
|
+
value: title,
|
|
425
|
+
onChange: (e) => handleTitleChange(e.target.value),
|
|
426
|
+
placeholder: "Article title...",
|
|
427
|
+
className: "cnfy-edit-input"
|
|
428
|
+
}
|
|
429
|
+
)
|
|
626
430
|
] }),
|
|
627
|
-
/* @__PURE__ */ jsxs2("div", {
|
|
628
|
-
/* @__PURE__ */
|
|
629
|
-
|
|
431
|
+
/* @__PURE__ */ jsxs2("div", { children: [
|
|
432
|
+
/* @__PURE__ */ jsx2("label", { className: "cnfy-edit-label", children: "Subtitle" }),
|
|
433
|
+
/* @__PURE__ */ jsx2(
|
|
434
|
+
"input",
|
|
630
435
|
{
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
436
|
+
type: "text",
|
|
437
|
+
value: subtitle,
|
|
438
|
+
onChange: (e) => setSubtitle(e.target.value),
|
|
439
|
+
placeholder: "Article subtitle...",
|
|
440
|
+
className: "cnfy-edit-input"
|
|
634
441
|
}
|
|
635
|
-
)
|
|
636
|
-
|
|
637
|
-
|
|
442
|
+
)
|
|
443
|
+
] }),
|
|
444
|
+
/* @__PURE__ */ jsxs2("div", { children: [
|
|
445
|
+
/* @__PURE__ */ jsx2("label", { className: "cnfy-edit-label", children: "Slug" }),
|
|
446
|
+
/* @__PURE__ */ jsx2(
|
|
447
|
+
"input",
|
|
638
448
|
{
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
449
|
+
type: "text",
|
|
450
|
+
value: slug,
|
|
451
|
+
onChange: (e) => handleSlugChange(e.target.value),
|
|
452
|
+
placeholder: "article-url-slug",
|
|
453
|
+
className: "cnfy-edit-input cnfy-edit-input--mono"
|
|
642
454
|
}
|
|
643
|
-
)
|
|
644
|
-
|
|
645
|
-
|
|
455
|
+
)
|
|
456
|
+
] }),
|
|
457
|
+
/* @__PURE__ */ jsxs2("div", { children: [
|
|
458
|
+
/* @__PURE__ */ jsx2("label", { className: "cnfy-edit-label", children: "Article Content" }),
|
|
459
|
+
/* @__PURE__ */ jsx2(
|
|
460
|
+
RichTextEditor,
|
|
646
461
|
{
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
children: "Post"
|
|
462
|
+
content,
|
|
463
|
+
onChange: setContent,
|
|
464
|
+
placeholder: "Start writing your article..."
|
|
651
465
|
}
|
|
652
466
|
)
|
|
653
|
-
] })
|
|
467
|
+
] }),
|
|
468
|
+
/* @__PURE__ */ jsxs2("div", { children: [
|
|
469
|
+
/* @__PURE__ */ jsx2("label", { className: "cnfy-edit-label", children: "Meta Description" }),
|
|
470
|
+
/* @__PURE__ */ jsx2(
|
|
471
|
+
"textarea",
|
|
472
|
+
{
|
|
473
|
+
value: metaDescription,
|
|
474
|
+
onChange: (e) => setMetaDescription(e.target.value),
|
|
475
|
+
placeholder: "Brief description for search engines...",
|
|
476
|
+
className: "cnfy-edit-textarea",
|
|
477
|
+
rows: 3
|
|
478
|
+
}
|
|
479
|
+
)
|
|
480
|
+
] }),
|
|
481
|
+
/* @__PURE__ */ jsxs2("div", { children: [
|
|
482
|
+
/* @__PURE__ */ jsx2("label", { className: "cnfy-edit-label", children: "Meta Keywords" }),
|
|
483
|
+
/* @__PURE__ */ jsx2("div", { className: "cnfy-keyword-list", children: keywords.map((keyword, index) => /* @__PURE__ */ jsxs2("span", { className: "cnfy-keyword-tag", children: [
|
|
484
|
+
"#",
|
|
485
|
+
keyword,
|
|
486
|
+
/* @__PURE__ */ jsx2(
|
|
487
|
+
"button",
|
|
488
|
+
{
|
|
489
|
+
onClick: () => handleRemoveKeyword(index),
|
|
490
|
+
className: "cnfy-keyword-remove-btn",
|
|
491
|
+
children: /* @__PURE__ */ jsx2(X, { size: 14 })
|
|
492
|
+
}
|
|
493
|
+
)
|
|
494
|
+
] }, index)) }),
|
|
495
|
+
/* @__PURE__ */ jsxs2("div", { className: "cnfy-keyword-input-row", children: [
|
|
496
|
+
/* @__PURE__ */ jsx2(
|
|
497
|
+
"input",
|
|
498
|
+
{
|
|
499
|
+
type: "text",
|
|
500
|
+
value: newKeyword,
|
|
501
|
+
onChange: (e) => setNewKeyword(e.target.value),
|
|
502
|
+
onKeyDown: handleKeyDown,
|
|
503
|
+
placeholder: "Add a keyword...",
|
|
504
|
+
className: "cnfy-keyword-input"
|
|
505
|
+
}
|
|
506
|
+
),
|
|
507
|
+
/* @__PURE__ */ jsx2("button", { onClick: handleAddKeyword, className: "cnfy-btn-add-keyword", children: "Add" })
|
|
508
|
+
] })
|
|
509
|
+
] }),
|
|
510
|
+
seoAnalysis && (() => {
|
|
511
|
+
const seo = seoAnalysis;
|
|
512
|
+
const breakdown = [
|
|
513
|
+
{ key: "title", label: "Title", section: seo.breakdown.title },
|
|
514
|
+
{ key: "meta", label: "Meta", section: seo.breakdown.meta },
|
|
515
|
+
{ key: "content", label: "Content", section: seo.breakdown.content },
|
|
516
|
+
{ key: "keywords", label: "Keywords", section: seo.breakdown.keywords },
|
|
517
|
+
{ key: "readability", label: "Readability", section: seo.breakdown.readability }
|
|
518
|
+
];
|
|
519
|
+
const allIssues = breakdown.flatMap((b) => {
|
|
520
|
+
var _a, _b;
|
|
521
|
+
return (_b = (_a = b.section) == null ? void 0 : _a.issues) != null ? _b : [];
|
|
522
|
+
}).filter(Boolean);
|
|
523
|
+
return /* @__PURE__ */ jsxs2("div", { className: "cnfy-seo-panel", children: [
|
|
524
|
+
/* @__PURE__ */ jsxs2("div", { className: "cnfy-seo-header", children: [
|
|
525
|
+
/* @__PURE__ */ jsxs2("div", { className: "cnfy-seo-title", children: [
|
|
526
|
+
/* @__PURE__ */ jsx2(BarChart2, { size: 13 }),
|
|
527
|
+
"SEO Analysis",
|
|
528
|
+
seo.improvement != null && seo.improvement > 0 && /* @__PURE__ */ jsxs2("span", { className: "cnfy-seo-improvement-badge", children: [
|
|
529
|
+
"\u2191 +",
|
|
530
|
+
seo.improvement,
|
|
531
|
+
" pts"
|
|
532
|
+
] })
|
|
533
|
+
] }),
|
|
534
|
+
/* @__PURE__ */ jsxs2("div", { className: "cnfy-seo-score-info", children: [
|
|
535
|
+
seo.before && /* @__PURE__ */ jsxs2("span", { className: "cnfy-seo-before", children: [
|
|
536
|
+
"was ",
|
|
537
|
+
seo.before.overall,
|
|
538
|
+
/* @__PURE__ */ jsx2("span", { className: "cnfy-seo-before-grade", style: { background: seoGradeColor(seo.before.grade) }, children: seo.before.grade })
|
|
539
|
+
] }),
|
|
540
|
+
/* @__PURE__ */ jsx2("span", { className: "cnfy-seo-score-num", children: seo.overall }),
|
|
541
|
+
/* @__PURE__ */ jsx2("span", { className: "cnfy-seo-score-denom", children: "/100" }),
|
|
542
|
+
/* @__PURE__ */ jsx2("div", { className: "cnfy-seo-grade", style: { background: seoGradeColor(seo.grade) }, children: seo.grade })
|
|
543
|
+
] })
|
|
544
|
+
] }),
|
|
545
|
+
/* @__PURE__ */ jsxs2("div", { className: "cnfy-seo-body", children: [
|
|
546
|
+
/* @__PURE__ */ jsx2("div", { className: "cnfy-seo-overall-bar", children: /* @__PURE__ */ jsx2(
|
|
547
|
+
"div",
|
|
548
|
+
{
|
|
549
|
+
className: "cnfy-seo-overall-fill",
|
|
550
|
+
style: { width: `${seo.overall}%`, background: seoScoreColor(seo.overall) }
|
|
551
|
+
}
|
|
552
|
+
) }),
|
|
553
|
+
/* @__PURE__ */ jsx2("div", { className: "cnfy-seo-breakdown", children: breakdown.map(({ key, label, section }) => section && /* @__PURE__ */ jsxs2("div", { className: "cnfy-seo-breakdown-row", children: [
|
|
554
|
+
/* @__PURE__ */ jsx2("span", { className: "cnfy-seo-bd-label", children: label }),
|
|
555
|
+
/* @__PURE__ */ jsx2("div", { className: "cnfy-seo-bd-bar", children: /* @__PURE__ */ jsx2(
|
|
556
|
+
"div",
|
|
557
|
+
{
|
|
558
|
+
className: "cnfy-seo-bd-fill",
|
|
559
|
+
style: { width: `${section.score}%`, background: seoScoreColor(section.score) }
|
|
560
|
+
}
|
|
561
|
+
) }),
|
|
562
|
+
/* @__PURE__ */ jsx2("span", { className: "cnfy-seo-bd-score", style: { color: seoScoreColor(section.score) }, children: section.score }),
|
|
563
|
+
section.readingLabel && /* @__PURE__ */ jsx2("span", { className: "cnfy-seo-bd-extra", children: section.readingLabel })
|
|
564
|
+
] }, key)) }),
|
|
565
|
+
allIssues.length > 0 && /* @__PURE__ */ jsxs2("div", { className: "cnfy-seo-issues", children: [
|
|
566
|
+
/* @__PURE__ */ jsxs2("div", { className: "cnfy-seo-issues-title", children: [
|
|
567
|
+
/* @__PURE__ */ jsx2(AlertTriangle, { size: 11 }),
|
|
568
|
+
allIssues.length,
|
|
569
|
+
" ",
|
|
570
|
+
allIssues.length === 1 ? "Issue" : "Issues"
|
|
571
|
+
] }),
|
|
572
|
+
allIssues.map((issue, i) => /* @__PURE__ */ jsx2("div", { className: "cnfy-seo-issue-item", children: issue }, i))
|
|
573
|
+
] }),
|
|
574
|
+
seo.suggestions.length > 0 && /* @__PURE__ */ jsxs2("div", { className: "cnfy-seo-suggestions", children: [
|
|
575
|
+
/* @__PURE__ */ jsxs2(
|
|
576
|
+
"button",
|
|
577
|
+
{
|
|
578
|
+
type: "button",
|
|
579
|
+
className: "cnfy-seo-suggestions-toggle",
|
|
580
|
+
onClick: () => setSeoSugOpen((v) => !v),
|
|
581
|
+
children: [
|
|
582
|
+
/* @__PURE__ */ jsxs2("span", { style: { display: "flex", alignItems: "center", gap: 4 }, children: [
|
|
583
|
+
/* @__PURE__ */ jsx2(Lightbulb, { size: 11 }),
|
|
584
|
+
seo.suggestions.length,
|
|
585
|
+
" Suggestions"
|
|
586
|
+
] }),
|
|
587
|
+
seoSugOpen ? /* @__PURE__ */ jsx2(ChevronUp, { size: 12 }) : /* @__PURE__ */ jsx2(ChevronDown, { size: 12 })
|
|
588
|
+
]
|
|
589
|
+
}
|
|
590
|
+
),
|
|
591
|
+
seoSugOpen && /* @__PURE__ */ jsx2("div", { className: "cnfy-seo-suggestions-list", children: seo.suggestions.map((s, i) => /* @__PURE__ */ jsx2("div", { className: "cnfy-seo-suggestion-item", children: s }, i)) })
|
|
592
|
+
] })
|
|
593
|
+
] })
|
|
594
|
+
] });
|
|
595
|
+
})()
|
|
596
|
+
] }),
|
|
597
|
+
/* @__PURE__ */ jsxs2("div", { className: "cnfy-edit-modal-footer", children: [
|
|
598
|
+
/* @__PURE__ */ jsx2("button", { onClick: onClose, className: "cnfy-btn-footer-cancel", children: "Cancel" }),
|
|
599
|
+
/* @__PURE__ */ jsxs2(
|
|
600
|
+
"button",
|
|
601
|
+
{
|
|
602
|
+
onClick: () => onDownload == null ? void 0 : onDownload(getEditData(), "docx"),
|
|
603
|
+
disabled: downloadingFormat === "docx",
|
|
604
|
+
className: "cnfy-btn-download",
|
|
605
|
+
children: [
|
|
606
|
+
downloadingFormat === "docx" ? /* @__PURE__ */ jsx2(Loader2, { size: 15, className: "cnfy-animate-spin" }) : /* @__PURE__ */ jsx2(FileDown, { size: 15 }),
|
|
607
|
+
"DOCX"
|
|
608
|
+
]
|
|
609
|
+
}
|
|
610
|
+
),
|
|
611
|
+
/* @__PURE__ */ jsxs2(
|
|
612
|
+
"button",
|
|
613
|
+
{
|
|
614
|
+
onClick: () => onDownload == null ? void 0 : onDownload(getEditData(), "pdf"),
|
|
615
|
+
disabled: downloadingFormat === "pdf",
|
|
616
|
+
className: "cnfy-btn-download cnfy-btn-download--pdf",
|
|
617
|
+
style: { backgroundColor: primaryColor, color: "#fff", border: "none" },
|
|
618
|
+
children: [
|
|
619
|
+
downloadingFormat === "pdf" ? /* @__PURE__ */ jsx2(Loader2, { size: 15, className: "cnfy-animate-spin" }) : /* @__PURE__ */ jsx2(FileDown, { size: 15 }),
|
|
620
|
+
"PDF"
|
|
621
|
+
]
|
|
622
|
+
}
|
|
623
|
+
),
|
|
624
|
+
onPost && /* @__PURE__ */ jsxs2(
|
|
625
|
+
"button",
|
|
626
|
+
{
|
|
627
|
+
onClick: () => onPost(getEditData()),
|
|
628
|
+
disabled: isPosting,
|
|
629
|
+
className: "cnfy-btn-download",
|
|
630
|
+
style: {
|
|
631
|
+
background: "linear-gradient(135deg, #8b5cf6 0%, #ec4899 100%)",
|
|
632
|
+
color: "#fff",
|
|
633
|
+
border: "none",
|
|
634
|
+
opacity: isPosting ? 0.7 : 1
|
|
635
|
+
},
|
|
636
|
+
children: [
|
|
637
|
+
isPosting ? /* @__PURE__ */ jsx2(Loader2, { size: 15, className: "cnfy-animate-spin" }) : /* @__PURE__ */ jsx2(Send, { size: 15 }),
|
|
638
|
+
"Post"
|
|
639
|
+
]
|
|
640
|
+
}
|
|
641
|
+
)
|
|
654
642
|
] })
|
|
655
643
|
] });
|
|
644
|
+
if (inline) {
|
|
645
|
+
return /* @__PURE__ */ jsx2("div", { className: "cnfy-edit-inline", children: editContent });
|
|
646
|
+
}
|
|
647
|
+
return /* @__PURE__ */ jsxs2("div", { className: "cnfy-edit-modal-overlay", children: [
|
|
648
|
+
/* @__PURE__ */ jsx2("div", { className: "cnfy-edit-modal-backdrop", onClick: onClose }),
|
|
649
|
+
/* @__PURE__ */ jsx2("div", { className: "cnfy-edit-modal", children: editContent })
|
|
650
|
+
] });
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// lib/config.ts
|
|
654
|
+
var _apiUrl = process.env.NEXT_PUBLIC_CONTENIFY_API_URL || "http://localhost:8080/api";
|
|
655
|
+
var _apiKey = process.env.NEXT_PUBLIC_API_KEY || "";
|
|
656
|
+
var _domain = process.env.NEXT_PUBLIC_CONTENIFY_DOMAIN || "";
|
|
657
|
+
function setConfig(config) {
|
|
658
|
+
if (config.apiUrl) _apiUrl = config.apiUrl;
|
|
659
|
+
if (config.apiKey) _apiKey = config.apiKey;
|
|
660
|
+
if (config.domain) _domain = config.domain;
|
|
661
|
+
}
|
|
662
|
+
function getApiBaseUrl() {
|
|
663
|
+
return _apiUrl;
|
|
664
|
+
}
|
|
665
|
+
function getApiKey() {
|
|
666
|
+
return _apiKey;
|
|
656
667
|
}
|
|
657
668
|
|
|
658
669
|
// components/news/NewsList.tsx
|
|
659
|
-
import { useState as
|
|
670
|
+
import { useState as useState2 } from "react";
|
|
660
671
|
import { Eye, RefreshCcw } from "lucide-react";
|
|
661
|
-
|
|
672
|
+
|
|
673
|
+
// src/utils/util.ts
|
|
674
|
+
var lightenColor = (hex, amount) => {
|
|
675
|
+
const sanitized = hex.replace("#", "");
|
|
676
|
+
const r = parseInt(sanitized.substring(0, 2), 16);
|
|
677
|
+
const g = parseInt(sanitized.substring(2, 4), 16);
|
|
678
|
+
const b = parseInt(sanitized.substring(4, 6), 16);
|
|
679
|
+
const mix = amount / 100;
|
|
680
|
+
const lr = Math.round(r + (255 - r) * mix);
|
|
681
|
+
const lg = Math.round(g + (255 - g) * mix);
|
|
682
|
+
const lb = Math.round(b + (255 - b) * mix);
|
|
683
|
+
return `#${lr.toString(16).padStart(2, "0")}${lg.toString(16).padStart(2, "0")}${lb.toString(16).padStart(2, "0")}`;
|
|
684
|
+
};
|
|
685
|
+
var hexToRgba = (hex, opacity) => {
|
|
686
|
+
const sanitizedHex = hex.replace("#", "");
|
|
687
|
+
const r = parseInt(sanitizedHex.substring(0, 2), 16);
|
|
688
|
+
const g = parseInt(sanitizedHex.substring(2, 4), 16);
|
|
689
|
+
const b = parseInt(sanitizedHex.substring(4, 6), 16);
|
|
690
|
+
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
|
691
|
+
};
|
|
692
|
+
|
|
693
|
+
// components/news/NewsList.tsx
|
|
694
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
662
695
|
var dateFormatter = new Intl.DateTimeFormat("en-GB", {
|
|
663
696
|
day: "2-digit",
|
|
664
697
|
month: "short",
|
|
@@ -689,18 +722,8 @@ function NewsList({
|
|
|
689
722
|
onRecreate
|
|
690
723
|
}) {
|
|
691
724
|
const { primaryColor } = useTheme();
|
|
692
|
-
const [primaryColorState] =
|
|
693
|
-
|
|
694
|
-
hex = hex.replace("#", "");
|
|
695
|
-
let r = parseInt(hex.substring(0, 2), 16);
|
|
696
|
-
let g = parseInt(hex.substring(2, 4), 16);
|
|
697
|
-
let b = parseInt(hex.substring(4, 6), 16);
|
|
698
|
-
r = Math.round(r + (255 - r) * (percent / 100));
|
|
699
|
-
g = Math.round(g + (255 - g) * (percent / 100));
|
|
700
|
-
b = Math.round(b + (255 - b) * (percent / 100));
|
|
701
|
-
return `rgb(${r}, ${g}, ${b})`;
|
|
702
|
-
};
|
|
703
|
-
return /* @__PURE__ */ jsx4("div", { className: "cnfy-news-list", children: news.flatMap((item) => {
|
|
725
|
+
const [primaryColorState] = useState2(primaryColor);
|
|
726
|
+
return /* @__PURE__ */ jsx3("div", { className: "cnfy-news-list", children: news.flatMap((item) => {
|
|
704
727
|
const publishedDate = new Date(item.publishedAt);
|
|
705
728
|
const links = extractLinksFromContent(item.content);
|
|
706
729
|
if (links.length > 0) {
|
|
@@ -710,23 +733,23 @@ function NewsList({
|
|
|
710
733
|
className: "cnfy-news-card",
|
|
711
734
|
children: [
|
|
712
735
|
/* @__PURE__ */ jsxs3("div", { className: "cnfy-news-card-content", children: [
|
|
713
|
-
/* @__PURE__ */
|
|
736
|
+
/* @__PURE__ */ jsx3("p", { className: "cnfy-news-card-title", children: link.title }),
|
|
714
737
|
/* @__PURE__ */ jsxs3("div", { className: "cnfy-news-card-meta", children: [
|
|
715
|
-
/* @__PURE__ */
|
|
738
|
+
/* @__PURE__ */ jsx3(
|
|
716
739
|
"span",
|
|
717
740
|
{
|
|
718
741
|
className: "cnfy-news-badge",
|
|
719
742
|
style: {
|
|
720
|
-
|
|
721
|
-
color:
|
|
722
|
-
|
|
743
|
+
background: "linear-gradient(135deg, rgba(139,92,246,0.12), rgba(236,72,153,0.10))",
|
|
744
|
+
color: "#8b5cf6",
|
|
745
|
+
border: "1px solid rgba(139,92,246,0.2)"
|
|
723
746
|
},
|
|
724
747
|
children: link.source || item.sourceName
|
|
725
748
|
}
|
|
726
749
|
),
|
|
727
|
-
/* @__PURE__ */
|
|
728
|
-
/* @__PURE__ */
|
|
729
|
-
/* @__PURE__ */
|
|
750
|
+
/* @__PURE__ */ jsx3("span", { children: "\u2022" }),
|
|
751
|
+
/* @__PURE__ */ jsx3("span", { className: "cnfy-news-card-category", children: item.category }),
|
|
752
|
+
/* @__PURE__ */ jsx3("span", { children: "\u2022" }),
|
|
730
753
|
/* @__PURE__ */ jsxs3("span", { children: [
|
|
731
754
|
dateFormatter.format(publishedDate),
|
|
732
755
|
" ",
|
|
@@ -735,16 +758,16 @@ function NewsList({
|
|
|
735
758
|
] })
|
|
736
759
|
] }),
|
|
737
760
|
/* @__PURE__ */ jsxs3("div", { className: "cnfy-news-card-actions", children: [
|
|
738
|
-
/* @__PURE__ */
|
|
761
|
+
/* @__PURE__ */ jsx3(
|
|
739
762
|
"button",
|
|
740
763
|
{
|
|
741
764
|
onClick: () => window.open(link.url, "_blank"),
|
|
742
765
|
className: "cnfy-news-action-btn",
|
|
743
766
|
title: "View",
|
|
744
|
-
children: /* @__PURE__ */
|
|
767
|
+
children: /* @__PURE__ */ jsx3(Eye, { size: 16 })
|
|
745
768
|
}
|
|
746
769
|
),
|
|
747
|
-
/* @__PURE__ */
|
|
770
|
+
/* @__PURE__ */ jsx3(
|
|
748
771
|
"button",
|
|
749
772
|
{
|
|
750
773
|
onClick: () => onRecreate({
|
|
@@ -754,7 +777,7 @@ function NewsList({
|
|
|
754
777
|
}),
|
|
755
778
|
className: "cnfy-news-action-btn",
|
|
756
779
|
title: "Recreate",
|
|
757
|
-
children: /* @__PURE__ */
|
|
780
|
+
children: /* @__PURE__ */ jsx3(RefreshCcw, { size: 16 })
|
|
758
781
|
}
|
|
759
782
|
)
|
|
760
783
|
] })
|
|
@@ -769,9 +792,9 @@ function NewsList({
|
|
|
769
792
|
className: "cnfy-news-card",
|
|
770
793
|
children: [
|
|
771
794
|
/* @__PURE__ */ jsxs3("div", { className: "cnfy-news-card-content", children: [
|
|
772
|
-
/* @__PURE__ */
|
|
795
|
+
/* @__PURE__ */ jsx3("p", { className: "cnfy-news-card-title", children: item.title }),
|
|
773
796
|
/* @__PURE__ */ jsxs3("div", { className: "cnfy-news-card-meta", children: [
|
|
774
|
-
/* @__PURE__ */
|
|
797
|
+
/* @__PURE__ */ jsx3(
|
|
775
798
|
"span",
|
|
776
799
|
{
|
|
777
800
|
className: "cnfy-news-badge",
|
|
@@ -783,9 +806,9 @@ function NewsList({
|
|
|
783
806
|
children: item.sourceName
|
|
784
807
|
}
|
|
785
808
|
),
|
|
786
|
-
/* @__PURE__ */
|
|
787
|
-
/* @__PURE__ */
|
|
788
|
-
/* @__PURE__ */
|
|
809
|
+
/* @__PURE__ */ jsx3("span", { children: "\u2022" }),
|
|
810
|
+
/* @__PURE__ */ jsx3("span", { className: "cnfy-news-card-category", children: item.category }),
|
|
811
|
+
/* @__PURE__ */ jsx3("span", { children: "\u2022" }),
|
|
789
812
|
/* @__PURE__ */ jsxs3("span", { children: [
|
|
790
813
|
dateFormatter.format(publishedDate),
|
|
791
814
|
" ",
|
|
@@ -794,16 +817,16 @@ function NewsList({
|
|
|
794
817
|
] })
|
|
795
818
|
] }),
|
|
796
819
|
/* @__PURE__ */ jsxs3("div", { className: "cnfy-news-card-actions", children: [
|
|
797
|
-
/* @__PURE__ */
|
|
820
|
+
/* @__PURE__ */ jsx3(
|
|
798
821
|
"button",
|
|
799
822
|
{
|
|
800
823
|
onClick: () => onView(item),
|
|
801
824
|
className: "cnfy-news-action-btn",
|
|
802
825
|
title: "View",
|
|
803
|
-
children: /* @__PURE__ */
|
|
826
|
+
children: /* @__PURE__ */ jsx3(Eye, { size: 16 })
|
|
804
827
|
}
|
|
805
828
|
),
|
|
806
|
-
/* @__PURE__ */
|
|
829
|
+
/* @__PURE__ */ jsx3(
|
|
807
830
|
"button",
|
|
808
831
|
{
|
|
809
832
|
onClick: () => onRecreate({
|
|
@@ -813,7 +836,7 @@ function NewsList({
|
|
|
813
836
|
}),
|
|
814
837
|
className: "cnfy-news-action-btn",
|
|
815
838
|
title: "Recreate",
|
|
816
|
-
children: /* @__PURE__ */
|
|
839
|
+
children: /* @__PURE__ */ jsx3(RefreshCcw, { size: 16 })
|
|
817
840
|
}
|
|
818
841
|
)
|
|
819
842
|
] })
|
|
@@ -824,27 +847,103 @@ function NewsList({
|
|
|
824
847
|
}) });
|
|
825
848
|
}
|
|
826
849
|
|
|
850
|
+
// lib/api.ts
|
|
851
|
+
import axios from "axios";
|
|
852
|
+
var ApiKeyInvalidError = class extends Error {
|
|
853
|
+
constructor() {
|
|
854
|
+
super("Invalid or expired subscription key");
|
|
855
|
+
this.isApiKeyInvalid = true;
|
|
856
|
+
this.name = "ApiKeyInvalidError";
|
|
857
|
+
}
|
|
858
|
+
};
|
|
859
|
+
function emitApiKeyInvalid() {
|
|
860
|
+
if (typeof window !== "undefined") {
|
|
861
|
+
window.dispatchEvent(new CustomEvent("api-key-invalid"));
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
var api = axios.create({
|
|
865
|
+
withCredentials: true,
|
|
866
|
+
timeout: 15e3,
|
|
867
|
+
// 15 s — prevents requests hanging indefinitely
|
|
868
|
+
headers: {
|
|
869
|
+
"Content-Type": "application/json"
|
|
870
|
+
}
|
|
871
|
+
});
|
|
872
|
+
api.interceptors.request.use(
|
|
873
|
+
(config) => {
|
|
874
|
+
config.baseURL = getApiBaseUrl();
|
|
875
|
+
const apiKey = getApiKey();
|
|
876
|
+
if (!apiKey) {
|
|
877
|
+
emitApiKeyInvalid();
|
|
878
|
+
return Promise.reject(new ApiKeyInvalidError());
|
|
879
|
+
}
|
|
880
|
+
config.headers["x-api-key"] = apiKey;
|
|
881
|
+
if (typeof window !== "undefined") {
|
|
882
|
+
const token = localStorage.getItem("token");
|
|
883
|
+
if (token) {
|
|
884
|
+
config.headers["Authorization"] = `Bearer ${token}`;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
return config;
|
|
888
|
+
},
|
|
889
|
+
(error) => Promise.reject(error)
|
|
890
|
+
);
|
|
891
|
+
api.interceptors.response.use(
|
|
892
|
+
(response) => {
|
|
893
|
+
const data = response.data;
|
|
894
|
+
if (data && data.success === false && typeof data.message === "string" && data.message.toLowerCase().includes("subscription key")) {
|
|
895
|
+
emitApiKeyInvalid();
|
|
896
|
+
}
|
|
897
|
+
return response;
|
|
898
|
+
},
|
|
899
|
+
(error) => {
|
|
900
|
+
var _a, _b, _c;
|
|
901
|
+
const status = (_a = error.response) == null ? void 0 : _a.status;
|
|
902
|
+
if (status === 401 || status === 403) {
|
|
903
|
+
emitApiKeyInvalid();
|
|
904
|
+
return Promise.reject(new ApiKeyInvalidError());
|
|
905
|
+
}
|
|
906
|
+
const message = ((_c = (_b = error.response) == null ? void 0 : _b.data) == null ? void 0 : _c.message) || error.message || "Something went wrong";
|
|
907
|
+
const err = new Error(message);
|
|
908
|
+
err.status = status;
|
|
909
|
+
return Promise.reject(err);
|
|
910
|
+
}
|
|
911
|
+
);
|
|
912
|
+
var api_default = api;
|
|
913
|
+
|
|
827
914
|
// services/news.service.ts
|
|
828
915
|
var getTrendingNews = async () => {
|
|
829
|
-
const { data } = await api_default.get("/
|
|
916
|
+
const { data } = await api_default.get("/news/trending");
|
|
830
917
|
return data.data;
|
|
831
918
|
};
|
|
832
919
|
var getNewsSources = async () => {
|
|
833
|
-
const { data } = await api_default.get("/
|
|
920
|
+
const { data } = await api_default.get("/news/sources");
|
|
834
921
|
return data.data;
|
|
835
922
|
};
|
|
836
923
|
var scrapeNewsSource = async (sourceId) => {
|
|
837
|
-
const { data } = await api_default.post("/
|
|
924
|
+
const { data } = await api_default.post("/news/source", { sourceId });
|
|
838
925
|
return data.data;
|
|
839
926
|
};
|
|
840
927
|
var getNewsBySource = async (sourceId) => {
|
|
841
|
-
const { data } = await api_default.get(`/
|
|
928
|
+
const { data } = await api_default.get(`/news/by-source/${sourceId}`);
|
|
842
929
|
return data.data;
|
|
843
930
|
};
|
|
844
931
|
|
|
845
932
|
// components/chatbot/ChatWindow.tsx
|
|
846
|
-
import
|
|
847
|
-
|
|
933
|
+
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
934
|
+
function seoScoreColor2(score) {
|
|
935
|
+
if (score >= 80) return "#10b981";
|
|
936
|
+
if (score >= 60) return "#f59e0b";
|
|
937
|
+
if (score >= 40) return "#f97316";
|
|
938
|
+
return "#ef4444";
|
|
939
|
+
}
|
|
940
|
+
function seoGradeColor2(grade) {
|
|
941
|
+
if (grade === "A") return "#10b981";
|
|
942
|
+
if (grade === "B") return "#3b82f6";
|
|
943
|
+
if (grade === "C") return "#f59e0b";
|
|
944
|
+
if (grade === "D") return "#f97316";
|
|
945
|
+
return "#ef4444";
|
|
946
|
+
}
|
|
848
947
|
var ACTION_ICONS = {
|
|
849
948
|
recreate_article: RefreshCcw2,
|
|
850
949
|
create_summary: ListChecks,
|
|
@@ -858,63 +957,171 @@ function ChatWindow({
|
|
|
858
957
|
onSend,
|
|
859
958
|
onSelectNews,
|
|
860
959
|
isStreaming = false,
|
|
960
|
+
activeField,
|
|
861
961
|
analyzedData,
|
|
862
962
|
onSelectAction,
|
|
863
|
-
|
|
963
|
+
onResolveSeo
|
|
864
964
|
}) {
|
|
865
|
-
|
|
866
|
-
const
|
|
965
|
+
const { showNewsPanel } = useTheme();
|
|
966
|
+
const THEME_GRADIENT = "linear-gradient(135deg, #8b5cf6 0%, #ec4899 100%)";
|
|
867
967
|
const bottomRef = useRef(null);
|
|
868
968
|
const textareaRef = useRef(null);
|
|
869
969
|
const dropdownRef = useRef(null);
|
|
870
|
-
const
|
|
871
|
-
const [
|
|
872
|
-
const [
|
|
873
|
-
const [
|
|
874
|
-
const [
|
|
875
|
-
const [
|
|
876
|
-
const [
|
|
877
|
-
const [
|
|
878
|
-
const [
|
|
879
|
-
const [
|
|
880
|
-
const {
|
|
881
|
-
const
|
|
882
|
-
const
|
|
883
|
-
const
|
|
970
|
+
const pillsRef = useRef(null);
|
|
971
|
+
const [input, setInput] = useState3("");
|
|
972
|
+
const [copiedId, setCopiedId] = useState3(null);
|
|
973
|
+
const [showNewsDropdown, setShowNewsDropdown] = useState3(false);
|
|
974
|
+
const [trendingNews, setTrendingNews] = useState3([]);
|
|
975
|
+
const [loadingNews, setLoadingNews] = useState3(false);
|
|
976
|
+
const [sources, setSources] = useState3([]);
|
|
977
|
+
const [selectedSource, setSelectedSource] = useState3(null);
|
|
978
|
+
const [loadingSources, setLoadingSources] = useState3(false);
|
|
979
|
+
const [scraping, setScraping] = useState3(false);
|
|
980
|
+
const [editModal, setEditModal] = useState3({ isOpen: false, title: "", subtitle: "", content: "", metaDescription: "", metaKeywords: [], messageId: "", featuredImage: "", seoAnalysis: null });
|
|
981
|
+
const [selectedImages, setSelectedImages] = useState3({});
|
|
982
|
+
const [seoOpen, setSeoOpen] = useState3({});
|
|
983
|
+
const [seoSugOpen, setSeoSugOpen] = useState3({});
|
|
984
|
+
const [isPosting, setIsPosting] = useState3(false);
|
|
985
|
+
const [splitPct, setSplitPct] = useState3(50);
|
|
986
|
+
const wrapperRef = useRef(null);
|
|
987
|
+
const isDragging = useRef(false);
|
|
988
|
+
const handleDragStart = useCallback((e) => {
|
|
989
|
+
e.preventDefault();
|
|
990
|
+
isDragging.current = true;
|
|
991
|
+
const onMove = (ev) => {
|
|
992
|
+
if (!isDragging.current || !wrapperRef.current) return;
|
|
993
|
+
const rect = wrapperRef.current.getBoundingClientRect();
|
|
994
|
+
const pct = (ev.clientX - rect.left) / rect.width * 100;
|
|
995
|
+
setSplitPct(Math.min(80, Math.max(20, pct)));
|
|
996
|
+
};
|
|
997
|
+
const onUp = () => {
|
|
998
|
+
isDragging.current = false;
|
|
999
|
+
window.removeEventListener("mousemove", onMove);
|
|
1000
|
+
window.removeEventListener("mouseup", onUp);
|
|
1001
|
+
};
|
|
1002
|
+
window.addEventListener("mousemove", onMove);
|
|
1003
|
+
window.addEventListener("mouseup", onUp);
|
|
1004
|
+
}, []);
|
|
1005
|
+
const handleCopy = async (parsed, messageId) => {
|
|
884
1006
|
try {
|
|
885
|
-
const
|
|
886
|
-
|
|
1007
|
+
const tempDiv = document.createElement("div");
|
|
1008
|
+
let textContent = "";
|
|
1009
|
+
if (parsed.title) textContent += parsed.title + "\n\n";
|
|
1010
|
+
if (parsed.subtitle) textContent += parsed.subtitle + "\n\n";
|
|
1011
|
+
if (parsed.articleHtml) {
|
|
1012
|
+
tempDiv.innerHTML = parsed.articleHtml;
|
|
1013
|
+
textContent += tempDiv.textContent || tempDiv.innerText || "";
|
|
1014
|
+
}
|
|
1015
|
+
if (parsed.metaDescription) textContent += "\n\nMeta Description:\n" + parsed.metaDescription;
|
|
1016
|
+
await navigator.clipboard.writeText(textContent);
|
|
887
1017
|
setCopiedId(messageId);
|
|
888
1018
|
setTimeout(() => setCopiedId(null), 2e3);
|
|
889
1019
|
} catch (err) {
|
|
890
1020
|
console.error("Failed to copy:", err);
|
|
891
1021
|
}
|
|
892
1022
|
};
|
|
893
|
-
const
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
1023
|
+
const handleEdit = (parsed, messageId) => {
|
|
1024
|
+
var _a;
|
|
1025
|
+
setEditModal({
|
|
1026
|
+
isOpen: true,
|
|
1027
|
+
title: parsed.title || "",
|
|
1028
|
+
subtitle: parsed.subtitle || "",
|
|
1029
|
+
content: parsed.articleHtml || "",
|
|
1030
|
+
metaDescription: parsed.metaDescription || "",
|
|
1031
|
+
metaKeywords: parsed.metaKeywords,
|
|
1032
|
+
messageId,
|
|
1033
|
+
featuredImage: selectedImages[messageId] || "",
|
|
1034
|
+
seoAnalysis: (_a = parsed.seoAnalysis) != null ? _a : null
|
|
1035
|
+
});
|
|
903
1036
|
};
|
|
1037
|
+
const [downloadingKey, setDownloadingKey] = useState3(null);
|
|
904
1038
|
const handleCloseModal = () => {
|
|
905
|
-
setEditModal({ isOpen: false, content: "", metaKeywords: [], messageId: "" });
|
|
1039
|
+
setEditModal({ isOpen: false, title: "", subtitle: "", content: "", metaDescription: "", metaKeywords: [], messageId: "", featuredImage: "", seoAnalysis: null });
|
|
1040
|
+
};
|
|
1041
|
+
const handleDownload = async (parsed, messageId, format) => {
|
|
1042
|
+
const key = `${messageId}-${format}`;
|
|
1043
|
+
setDownloadingKey(key);
|
|
1044
|
+
try {
|
|
1045
|
+
const apiUrl = getApiBaseUrl();
|
|
1046
|
+
const token = typeof window !== "undefined" ? localStorage.getItem("token") : null;
|
|
1047
|
+
const apiKey = getApiKey();
|
|
1048
|
+
const res = await fetch(`${apiUrl}/contenify/rewrite/download`, {
|
|
1049
|
+
method: "POST",
|
|
1050
|
+
credentials: "include",
|
|
1051
|
+
headers: __spreadValues(__spreadValues({
|
|
1052
|
+
"Content-Type": "application/json"
|
|
1053
|
+
}, token ? { Authorization: `Bearer ${token}` } : {}), apiKey ? { "x-api-key": apiKey } : {}),
|
|
1054
|
+
body: JSON.stringify({
|
|
1055
|
+
title: parsed.title,
|
|
1056
|
+
subtitle: parsed.subtitle,
|
|
1057
|
+
article: parsed.articleHtml,
|
|
1058
|
+
metaDescription: parsed.metaDescription,
|
|
1059
|
+
metaKeywords: parsed.metaKeywords,
|
|
1060
|
+
format
|
|
1061
|
+
})
|
|
1062
|
+
});
|
|
1063
|
+
if (!res.ok) throw new Error("Download failed");
|
|
1064
|
+
const blob = await res.blob();
|
|
1065
|
+
const url = URL.createObjectURL(blob);
|
|
1066
|
+
const a = document.createElement("a");
|
|
1067
|
+
a.href = url;
|
|
1068
|
+
a.download = `article.${format}`;
|
|
1069
|
+
a.click();
|
|
1070
|
+
URL.revokeObjectURL(url);
|
|
1071
|
+
} catch (err) {
|
|
1072
|
+
console.error("Download failed:", err);
|
|
1073
|
+
} finally {
|
|
1074
|
+
setDownloadingKey(null);
|
|
1075
|
+
}
|
|
906
1076
|
};
|
|
907
|
-
const
|
|
908
|
-
|
|
909
|
-
|
|
1077
|
+
const handleDownloadFromModal = async (data, format) => {
|
|
1078
|
+
const key = `modal-${format}`;
|
|
1079
|
+
setDownloadingKey(key);
|
|
1080
|
+
try {
|
|
1081
|
+
const apiUrl = getApiBaseUrl();
|
|
1082
|
+
const token = typeof window !== "undefined" ? localStorage.getItem("token") : null;
|
|
1083
|
+
const apiKey = getApiKey();
|
|
1084
|
+
const res = await fetch(`${apiUrl}/contenify/rewrite/download`, {
|
|
1085
|
+
method: "POST",
|
|
1086
|
+
credentials: "include",
|
|
1087
|
+
headers: __spreadValues(__spreadValues({
|
|
1088
|
+
"Content-Type": "application/json"
|
|
1089
|
+
}, token ? { Authorization: `Bearer ${token}` } : {}), apiKey ? { "x-api-key": apiKey } : {}),
|
|
1090
|
+
body: JSON.stringify({
|
|
1091
|
+
title: data.title,
|
|
1092
|
+
subtitle: data.subtitle,
|
|
1093
|
+
article: data.article,
|
|
1094
|
+
metaDescription: data.metaDescription,
|
|
1095
|
+
metaKeywords: data.keywords,
|
|
1096
|
+
format
|
|
1097
|
+
})
|
|
1098
|
+
});
|
|
1099
|
+
if (!res.ok) throw new Error("Download failed");
|
|
1100
|
+
const blob = await res.blob();
|
|
1101
|
+
const url = URL.createObjectURL(blob);
|
|
1102
|
+
const a = document.createElement("a");
|
|
1103
|
+
a.href = url;
|
|
1104
|
+
a.download = `article.${format}`;
|
|
1105
|
+
a.click();
|
|
1106
|
+
URL.revokeObjectURL(url);
|
|
1107
|
+
} catch (err) {
|
|
1108
|
+
console.error("Download failed:", err);
|
|
1109
|
+
} finally {
|
|
1110
|
+
setDownloadingKey(null);
|
|
1111
|
+
}
|
|
910
1112
|
};
|
|
911
|
-
const
|
|
912
|
-
|
|
913
|
-
|
|
1113
|
+
const handlePostFromModal = (data) => {
|
|
1114
|
+
setIsPosting(true);
|
|
1115
|
+
try {
|
|
1116
|
+
console.log("Posting article with data:", data);
|
|
1117
|
+
window.dispatchEvent(new CustomEvent("contenify-post-article", { detail: data }));
|
|
1118
|
+
} finally {
|
|
1119
|
+
setIsPosting(false);
|
|
1120
|
+
}
|
|
914
1121
|
};
|
|
915
|
-
|
|
916
|
-
var
|
|
917
|
-
(
|
|
1122
|
+
useLayoutEffect2(() => {
|
|
1123
|
+
var _a;
|
|
1124
|
+
(_a = bottomRef.current) == null ? void 0 : _a.scrollIntoView({ block: "end" });
|
|
918
1125
|
}, [messages]);
|
|
919
1126
|
const handleSend = () => {
|
|
920
1127
|
if (!input.trim()) return;
|
|
@@ -922,13 +1129,16 @@ function ChatWindow({
|
|
|
922
1129
|
setInput("");
|
|
923
1130
|
if (textareaRef.current) {
|
|
924
1131
|
textareaRef.current.style.height = "auto";
|
|
1132
|
+
textareaRef.current.style.borderRadius = "50px";
|
|
925
1133
|
}
|
|
926
1134
|
};
|
|
927
|
-
|
|
1135
|
+
useEffect3(() => {
|
|
928
1136
|
const textarea = textareaRef.current;
|
|
929
1137
|
if (textarea) {
|
|
930
1138
|
textarea.style.height = "auto";
|
|
931
1139
|
textarea.style.height = `${textarea.scrollHeight}px`;
|
|
1140
|
+
const isMultiline = textarea.scrollHeight > 48;
|
|
1141
|
+
textarea.style.borderRadius = isMultiline ? "1.25rem" : "50px";
|
|
932
1142
|
}
|
|
933
1143
|
}, [input]);
|
|
934
1144
|
const fetchSources = async () => {
|
|
@@ -954,6 +1164,7 @@ function ChatWindow({
|
|
|
954
1164
|
setTrendingNews(data || []);
|
|
955
1165
|
} catch (err) {
|
|
956
1166
|
console.error("Failed to fetch news:", err);
|
|
1167
|
+
setTrendingNews([]);
|
|
957
1168
|
} finally {
|
|
958
1169
|
setLoadingNews(false);
|
|
959
1170
|
}
|
|
@@ -970,9 +1181,7 @@ function ChatWindow({
|
|
|
970
1181
|
setScraping(false);
|
|
971
1182
|
}
|
|
972
1183
|
};
|
|
973
|
-
const handleSourceSelect = (
|
|
974
|
-
var _a2;
|
|
975
|
-
const sourceId = (_a2 = option == null ? void 0 : option.value) != null ? _a2 : null;
|
|
1184
|
+
const handleSourceSelect = (sourceId) => {
|
|
976
1185
|
setSelectedSource(sourceId);
|
|
977
1186
|
fetchNews(sourceId);
|
|
978
1187
|
};
|
|
@@ -985,7 +1194,7 @@ function ChatWindow({
|
|
|
985
1194
|
fetchNews(selectedSource);
|
|
986
1195
|
}
|
|
987
1196
|
};
|
|
988
|
-
|
|
1197
|
+
useEffect3(() => {
|
|
989
1198
|
const handleClickOutside = (event) => {
|
|
990
1199
|
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
|
991
1200
|
setShowNewsDropdown(false);
|
|
@@ -998,326 +1207,479 @@ function ChatWindow({
|
|
|
998
1207
|
document.removeEventListener("mousedown", handleClickOutside);
|
|
999
1208
|
};
|
|
1000
1209
|
}, [showNewsDropdown]);
|
|
1001
|
-
|
|
1002
|
-
const
|
|
1003
|
-
if (!
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1210
|
+
useEffect3(() => {
|
|
1211
|
+
const el = pillsRef.current;
|
|
1212
|
+
if (!el) return;
|
|
1213
|
+
const onWheel = (e) => {
|
|
1214
|
+
if (e.deltaY === 0) return;
|
|
1215
|
+
e.preventDefault();
|
|
1216
|
+
el.scrollLeft += e.deltaY;
|
|
1217
|
+
};
|
|
1218
|
+
el.addEventListener("wheel", onWheel, { passive: false });
|
|
1219
|
+
return () => el.removeEventListener("wheel", onWheel);
|
|
1220
|
+
}, [showNewsDropdown, loadingSources]);
|
|
1221
|
+
const SUGGESTIONS = [
|
|
1222
|
+
{ icon: /* @__PURE__ */ jsx4(Link2, { size: 13 }), label: "Paste a news URL to rewrite" },
|
|
1223
|
+
{ icon: /* @__PURE__ */ jsx4(PenLine, { size: 13 }), label: "Write a blog post about AI" },
|
|
1224
|
+
{ icon: /* @__PURE__ */ jsx4(LayoutList, { size: 13 }), label: "Summarize today's top stories" },
|
|
1225
|
+
{ icon: /* @__PURE__ */ jsx4(Share2, { size: 13 }), label: "Create social media posts" }
|
|
1226
|
+
];
|
|
1227
|
+
const isEmpty = messages.length === 0;
|
|
1228
|
+
return /* @__PURE__ */ jsxs4(
|
|
1229
|
+
"div",
|
|
1230
|
+
{
|
|
1231
|
+
ref: wrapperRef,
|
|
1232
|
+
className: `cnfy-editor-wrapper${editModal.isOpen ? " cnfy-editor-wrapper--editing" : ""}`,
|
|
1233
|
+
children: [
|
|
1234
|
+
/* @__PURE__ */ jsxs4(
|
|
1235
|
+
"div",
|
|
1236
|
+
{
|
|
1237
|
+
className: `cnfy-chat cnfy-editor-chat-pane${isEmpty ? " cnfy-chat--empty" : ""}`,
|
|
1238
|
+
style: editModal.isOpen ? { flex: `0 0 ${splitPct}%`, width: `${splitPct}%` } : void 0,
|
|
1239
|
+
children: [
|
|
1240
|
+
isEmpty && /* @__PURE__ */ jsxs4("div", { className: "cnfy-empty-hero", children: [
|
|
1241
|
+
/* @__PURE__ */ jsx4("div", { className: "cnfy-empty-hero-icon", children: /* @__PURE__ */ jsx4(Sparkles, { size: 22 }) }),
|
|
1242
|
+
/* @__PURE__ */ jsxs4("div", { children: [
|
|
1243
|
+
/* @__PURE__ */ jsx4("p", { className: "cnfy-empty-state-title", children: "AI News Assistant" }),
|
|
1244
|
+
/* @__PURE__ */ jsx4("p", { className: "cnfy-empty-state-subtitle", children: "Generate, rewrite and publish content in seconds" })
|
|
1245
|
+
] })
|
|
1246
|
+
] }),
|
|
1247
|
+
/* @__PURE__ */ jsx4("div", { className: "cnfy-chat-area", children: /* @__PURE__ */ jsxs4("div", { className: "cnfy-chat-scroll", children: [
|
|
1248
|
+
messages.map((msg) => {
|
|
1249
|
+
var _a, _b, _c;
|
|
1250
|
+
const parsed = formatAIContent(msg.content);
|
|
1251
|
+
return /* @__PURE__ */ jsx4("div", { className: "cnfy-msg", children: /* @__PURE__ */ jsxs4("div", { className: msg.role === "assistant" ? `cnfy-msg-body` : `cnfy-msg-body you`, children: [
|
|
1252
|
+
msg.role === "assistant" && parsed.isArticle && /* @__PURE__ */ jsxs4("div", { className: "cnfy-msg-copy-row", children: [
|
|
1253
|
+
parsed.seoAnalysis && (!isStreaming || msg.id !== ((_a = messages[messages.length - 1]) == null ? void 0 : _a.id)) && /* @__PURE__ */ jsxs4(
|
|
1254
|
+
"button",
|
|
1255
|
+
{
|
|
1256
|
+
type: "button",
|
|
1257
|
+
onClick: () => setSeoOpen((prev) => __spreadProps(__spreadValues({}, prev), { [msg.id]: !prev[msg.id] })),
|
|
1258
|
+
className: `cnfy-seo-toggle-btn${seoOpen[msg.id] ? " cnfy-seo-toggle-btn--active" : ""}`,
|
|
1259
|
+
title: "SEO Analysis",
|
|
1260
|
+
children: [
|
|
1261
|
+
/* @__PURE__ */ jsx4(BarChart22, { size: 13 }),
|
|
1262
|
+
/* @__PURE__ */ jsxs4(
|
|
1263
|
+
"span",
|
|
1264
|
+
{
|
|
1265
|
+
className: "cnfy-seo-toggle-score",
|
|
1266
|
+
style: { color: seoScoreColor2(parsed.seoAnalysis.overall) },
|
|
1267
|
+
children: [
|
|
1268
|
+
parsed.seoAnalysis.overall,
|
|
1269
|
+
"%"
|
|
1270
|
+
]
|
|
1271
|
+
}
|
|
1272
|
+
)
|
|
1273
|
+
]
|
|
1274
|
+
}
|
|
1275
|
+
),
|
|
1276
|
+
/* @__PURE__ */ jsx4(
|
|
1277
|
+
"button",
|
|
1278
|
+
{
|
|
1279
|
+
onClick: () => handleCopy(parsed, msg.id),
|
|
1280
|
+
className: "cnfy-copy-btn",
|
|
1281
|
+
title: "Copy to clipboard",
|
|
1282
|
+
children: copiedId === msg.id ? /* @__PURE__ */ jsx4(Check, { size: 16, className: "cnfy-copy-icon--copied" }) : /* @__PURE__ */ jsx4(Copy, { size: 16 })
|
|
1283
|
+
}
|
|
1284
|
+
)
|
|
1285
|
+
] }),
|
|
1286
|
+
msg.role === "assistant" && isStreaming && msg.id === ((_b = messages[messages.length - 1]) == null ? void 0 : _b.id) && !parsed.isArticle && !parsed.plainText && /* @__PURE__ */ jsxs4("div", { className: "cnfy-creating-indicator", children: [
|
|
1287
|
+
/* @__PURE__ */ jsx4(Loader22, { size: 16, className: "cnfy-animate-spin" }),
|
|
1288
|
+
/* @__PURE__ */ jsx4("span", { children: "Creating article..." })
|
|
1289
|
+
] }),
|
|
1290
|
+
parsed.isArticle && parsed.seoAnalysis && seoOpen[msg.id] && (() => {
|
|
1291
|
+
const seo = parsed.seoAnalysis;
|
|
1292
|
+
const breakdown = [
|
|
1293
|
+
{ key: "title", label: "Title", section: seo.breakdown.title },
|
|
1294
|
+
{ key: "meta", label: "Meta", section: seo.breakdown.meta },
|
|
1295
|
+
{ key: "content", label: "Content", section: seo.breakdown.content },
|
|
1296
|
+
{ key: "keywords", label: "Keywords", section: seo.breakdown.keywords },
|
|
1297
|
+
{ key: "readability", label: "Readability", section: seo.breakdown.readability }
|
|
1298
|
+
];
|
|
1299
|
+
const allIssues = breakdown.flatMap((b) => {
|
|
1300
|
+
var _a2, _b2;
|
|
1301
|
+
return (_b2 = (_a2 = b.section) == null ? void 0 : _a2.issues) != null ? _b2 : [];
|
|
1302
|
+
}).filter(Boolean);
|
|
1303
|
+
const sugOpen = !!seoSugOpen[msg.id];
|
|
1304
|
+
return /* @__PURE__ */ jsxs4("div", { className: "cnfy-seo-panel", children: [
|
|
1305
|
+
/* @__PURE__ */ jsxs4("div", { className: "cnfy-seo-header", children: [
|
|
1306
|
+
/* @__PURE__ */ jsxs4("div", { className: "cnfy-seo-title", children: [
|
|
1307
|
+
/* @__PURE__ */ jsx4(BarChart22, { size: 13 }),
|
|
1308
|
+
"SEO Analysis",
|
|
1309
|
+
seo.improvement != null && seo.improvement > 0 && /* @__PURE__ */ jsxs4("span", { className: "cnfy-seo-improvement-badge", children: [
|
|
1310
|
+
"\u2191 +",
|
|
1311
|
+
seo.improvement,
|
|
1312
|
+
" pts"
|
|
1313
|
+
] })
|
|
1314
|
+
] }),
|
|
1315
|
+
/* @__PURE__ */ jsxs4("div", { className: "cnfy-seo-score-info", children: [
|
|
1316
|
+
seo.before && /* @__PURE__ */ jsxs4("span", { className: "cnfy-seo-before", children: [
|
|
1317
|
+
"was ",
|
|
1318
|
+
seo.before.overall,
|
|
1319
|
+
/* @__PURE__ */ jsx4("span", { className: "cnfy-seo-before-grade", style: { background: seoGradeColor2(seo.before.grade) }, children: seo.before.grade })
|
|
1320
|
+
] }),
|
|
1321
|
+
/* @__PURE__ */ jsx4("span", { className: "cnfy-seo-score-num", children: seo.overall }),
|
|
1322
|
+
/* @__PURE__ */ jsx4("span", { className: "cnfy-seo-score-denom", children: "/100" }),
|
|
1323
|
+
/* @__PURE__ */ jsx4("div", { className: "cnfy-seo-grade", style: { background: seoGradeColor2(seo.grade) }, children: seo.grade })
|
|
1324
|
+
] })
|
|
1325
|
+
] }),
|
|
1326
|
+
/* @__PURE__ */ jsxs4("div", { className: "cnfy-seo-body", children: [
|
|
1327
|
+
/* @__PURE__ */ jsx4("div", { className: "cnfy-seo-overall-bar", children: /* @__PURE__ */ jsx4(
|
|
1328
|
+
"div",
|
|
1329
|
+
{
|
|
1330
|
+
className: "cnfy-seo-overall-fill",
|
|
1331
|
+
style: { width: `${seo.overall}%`, background: seoScoreColor2(seo.overall) }
|
|
1332
|
+
}
|
|
1333
|
+
) }),
|
|
1334
|
+
/* @__PURE__ */ jsx4("div", { className: "cnfy-seo-breakdown", children: breakdown.map(({ key, label, section }) => section && /* @__PURE__ */ jsxs4("div", { className: "cnfy-seo-breakdown-row", children: [
|
|
1335
|
+
/* @__PURE__ */ jsx4("span", { className: "cnfy-seo-bd-label", children: label }),
|
|
1336
|
+
/* @__PURE__ */ jsx4("div", { className: "cnfy-seo-bd-bar", children: /* @__PURE__ */ jsx4(
|
|
1337
|
+
"div",
|
|
1338
|
+
{
|
|
1339
|
+
className: "cnfy-seo-bd-fill",
|
|
1340
|
+
style: { width: `${section.score}%`, background: seoScoreColor2(section.score) }
|
|
1341
|
+
}
|
|
1342
|
+
) }),
|
|
1343
|
+
/* @__PURE__ */ jsx4("span", { className: "cnfy-seo-bd-score", style: { color: seoScoreColor2(section.score) }, children: section.score }),
|
|
1344
|
+
section.readingLabel && /* @__PURE__ */ jsx4("span", { className: "cnfy-seo-bd-extra", children: section.readingLabel })
|
|
1345
|
+
] }, key)) }),
|
|
1346
|
+
allIssues.length > 0 && /* @__PURE__ */ jsxs4("div", { className: "cnfy-seo-issues", children: [
|
|
1347
|
+
/* @__PURE__ */ jsxs4("div", { className: "cnfy-seo-issues-title", children: [
|
|
1348
|
+
/* @__PURE__ */ jsx4(AlertTriangle2, { size: 11 }),
|
|
1349
|
+
allIssues.length,
|
|
1350
|
+
" ",
|
|
1351
|
+
allIssues.length === 1 ? "Issue" : "Issues"
|
|
1352
|
+
] }),
|
|
1353
|
+
allIssues.map((issue, i) => /* @__PURE__ */ jsx4("div", { className: "cnfy-seo-issue-item", children: issue }, i))
|
|
1354
|
+
] }),
|
|
1355
|
+
/* @__PURE__ */ jsx4("div", { style: { display: "flex", justifyContent: "flex-end", padding: "8px 0" }, children: /* @__PURE__ */ jsxs4(
|
|
1356
|
+
"button",
|
|
1357
|
+
{
|
|
1358
|
+
onClick: () => {
|
|
1359
|
+
var _a2, _b2, _c2, _d;
|
|
1360
|
+
return onResolveSeo == null ? void 0 : onResolveSeo({
|
|
1361
|
+
title: parsed.title,
|
|
1362
|
+
subtitle: parsed.subtitle,
|
|
1363
|
+
article: parsed.articleHtml,
|
|
1364
|
+
metaDescription: parsed.metaDescription,
|
|
1365
|
+
metaKeywords: parsed.metaKeywords,
|
|
1366
|
+
seoAnalysis: parsed.seoAnalysis,
|
|
1367
|
+
language: (_a2 = parsed.article) == null ? void 0 : _a2.language,
|
|
1368
|
+
historyId: (_d = (_b2 = parsed.article) == null ? void 0 : _b2._id) != null ? _d : (_c2 = parsed.article) == null ? void 0 : _c2.historyId
|
|
1369
|
+
});
|
|
1370
|
+
},
|
|
1371
|
+
disabled: isStreaming,
|
|
1372
|
+
className: "cnfy-btn-fix-seo",
|
|
1373
|
+
children: [
|
|
1374
|
+
/* @__PURE__ */ jsx4(Wand2, { size: 16 }),
|
|
1375
|
+
"Fix SEO"
|
|
1376
|
+
]
|
|
1377
|
+
}
|
|
1378
|
+
) }),
|
|
1379
|
+
seo.suggestions.length > 0 && /* @__PURE__ */ jsxs4("div", { className: "cnfy-seo-suggestions", children: [
|
|
1380
|
+
/* @__PURE__ */ jsxs4(
|
|
1381
|
+
"button",
|
|
1382
|
+
{
|
|
1383
|
+
type: "button",
|
|
1384
|
+
className: "cnfy-seo-suggestions-toggle",
|
|
1385
|
+
onClick: () => setSeoSugOpen((prev) => __spreadProps(__spreadValues({}, prev), { [msg.id]: !prev[msg.id] })),
|
|
1386
|
+
children: [
|
|
1387
|
+
/* @__PURE__ */ jsxs4("span", { style: { display: "flex", alignItems: "center", gap: 4 }, children: [
|
|
1388
|
+
/* @__PURE__ */ jsx4(Lightbulb2, { size: 11 }),
|
|
1389
|
+
seo.suggestions.length,
|
|
1390
|
+
" Suggestions"
|
|
1391
|
+
] }),
|
|
1392
|
+
sugOpen ? /* @__PURE__ */ jsx4(ChevronUp2, { size: 12 }) : /* @__PURE__ */ jsx4(ChevronDown2, { size: 12 })
|
|
1393
|
+
]
|
|
1394
|
+
}
|
|
1395
|
+
),
|
|
1396
|
+
sugOpen && /* @__PURE__ */ jsx4("div", { className: "cnfy-seo-suggestions-list", children: seo.suggestions.map((s, i) => /* @__PURE__ */ jsx4("div", { className: "cnfy-seo-suggestion-item", children: s }, i)) })
|
|
1397
|
+
] })
|
|
1398
|
+
] })
|
|
1399
|
+
] });
|
|
1400
|
+
})(),
|
|
1401
|
+
msg.role === "assistant" && (analyzedData == null ? void 0 : analyzedData.messageId) === msg.id && analyzedData.options.length > 0 && /* @__PURE__ */ jsx4("div", { className: "cnfy-action-options", children: analyzedData.options.map((option) => {
|
|
1402
|
+
const IconComponent = ACTION_ICONS[option.id] || FileText;
|
|
1403
|
+
return /* @__PURE__ */ jsxs4(
|
|
1404
|
+
"button",
|
|
1405
|
+
{
|
|
1406
|
+
onClick: () => onSelectAction == null ? void 0 : onSelectAction(option.id, analyzedData.url, analyzedData.content),
|
|
1407
|
+
className: "cnfy-action-btn",
|
|
1408
|
+
children: [
|
|
1409
|
+
/* @__PURE__ */ jsx4(IconComponent, { size: 16 }),
|
|
1410
|
+
option.name
|
|
1411
|
+
]
|
|
1412
|
+
},
|
|
1413
|
+
option.id
|
|
1414
|
+
);
|
|
1415
|
+
}) }),
|
|
1416
|
+
parsed.isArticle ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1417
|
+
parsed.title && /* @__PURE__ */ jsxs4("h1", { className: "cnfy-block-h1", children: [
|
|
1418
|
+
parsed.title,
|
|
1419
|
+
activeField === "title" && /* @__PURE__ */ jsx4("span", { className: "cnfy-cursor-blink", children: "|" })
|
|
1420
|
+
] }),
|
|
1421
|
+
parsed.subtitle && /* @__PURE__ */ jsxs4("h2", { className: "cnfy-block-h2", children: [
|
|
1422
|
+
parsed.subtitle,
|
|
1423
|
+
activeField === "subtitle" && /* @__PURE__ */ jsx4("span", { className: "cnfy-cursor-blink", children: "|" })
|
|
1424
|
+
] }),
|
|
1425
|
+
/* @__PURE__ */ jsx4("hr", { className: "cnfy-divider" }),
|
|
1426
|
+
parsed.articleHtml && /* @__PURE__ */ jsx4(
|
|
1427
|
+
"div",
|
|
1428
|
+
{
|
|
1429
|
+
className: "cnfy-article-content",
|
|
1430
|
+
dangerouslySetInnerHTML: { __html: parsed.articleHtml }
|
|
1431
|
+
}
|
|
1432
|
+
),
|
|
1433
|
+
activeField === "article" && /* @__PURE__ */ jsx4("span", { className: "cnfy-cursor-blink", children: "|" })
|
|
1434
|
+
] }) : (
|
|
1435
|
+
/* Plain text messages (status, error, options, etc.) */
|
|
1436
|
+
parsed.plainText && /* @__PURE__ */ jsx4("p", { className: "cnfy-block-p", children: parsed.plainText })
|
|
1437
|
+
),
|
|
1438
|
+
parsed.metaDescription && /* @__PURE__ */ jsxs4("div", { className: "cnfy-msg-keywords", children: [
|
|
1439
|
+
/* @__PURE__ */ jsx4("div", { className: "cnfy-msg-keywords-label", children: "Meta Description" }),
|
|
1440
|
+
/* @__PURE__ */ jsx4("div", { className: "cnfy-block-p", children: parsed.metaDescription })
|
|
1441
|
+
] }),
|
|
1442
|
+
parsed.metaKeywords.length > 0 && /* @__PURE__ */ jsxs4("div", { className: "cnfy-msg-keywords", children: [
|
|
1443
|
+
/* @__PURE__ */ jsx4("div", { className: "cnfy-msg-keywords-label", children: "Keywords" }),
|
|
1444
|
+
/* @__PURE__ */ jsx4("div", { className: "cnfy-msg-keywords-list", children: parsed.metaKeywords.map((tag, i) => /* @__PURE__ */ jsx4(
|
|
1445
|
+
"span",
|
|
1446
|
+
{
|
|
1447
|
+
className: "cnfy-msg-keyword-tag",
|
|
1448
|
+
children: tag
|
|
1449
|
+
},
|
|
1450
|
+
i
|
|
1451
|
+
)) })
|
|
1452
|
+
] }),
|
|
1453
|
+
parsed.isArticle && parsed.images.length > 0 && /* @__PURE__ */ jsxs4("div", { className: "cnfy-image-slider-wrap", children: [
|
|
1454
|
+
/* @__PURE__ */ jsx4("div", { className: "cnfy-image-slider-label", children: "Select a featured image" }),
|
|
1455
|
+
/* @__PURE__ */ jsx4("div", { className: "cnfy-image-slider", children: parsed.images.map((img, i) => {
|
|
1456
|
+
const isSelected = selectedImages[msg.id] === img.url;
|
|
1457
|
+
return /* @__PURE__ */ jsxs4(
|
|
1458
|
+
"button",
|
|
1459
|
+
{
|
|
1460
|
+
type: "button",
|
|
1461
|
+
onClick: () => setSelectedImages((prev) => __spreadProps(__spreadValues({}, prev), {
|
|
1462
|
+
[msg.id]: isSelected ? "" : img.url
|
|
1463
|
+
})),
|
|
1464
|
+
className: `cnfy-image-slider-item${isSelected ? " cnfy-image-slider-item--selected" : ""}`,
|
|
1465
|
+
title: img.photographer ? `Photo by ${img.photographer}` : void 0,
|
|
1466
|
+
children: [
|
|
1467
|
+
/* @__PURE__ */ jsx4("img", { src: img.thumb, alt: `Image ${i + 1}`, className: "cnfy-image-slider-thumb" }),
|
|
1468
|
+
isSelected && /* @__PURE__ */ jsx4("span", { className: "cnfy-image-slider-check", children: /* @__PURE__ */ jsx4(Check, { size: 12 }) })
|
|
1469
|
+
]
|
|
1470
|
+
},
|
|
1471
|
+
i
|
|
1472
|
+
);
|
|
1473
|
+
}) })
|
|
1474
|
+
] }),
|
|
1475
|
+
msg.role === "assistant" && parsed.isArticle && !(analyzedData == null ? void 0 : analyzedData.messageId) && (!isStreaming || msg.id !== ((_c = messages[messages.length - 1]) == null ? void 0 : _c.id)) && /* @__PURE__ */ jsxs4("div", { className: "cnfy-msg-actions", children: [
|
|
1476
|
+
/* @__PURE__ */ jsxs4(
|
|
1477
|
+
"button",
|
|
1478
|
+
{
|
|
1479
|
+
onClick: () => handleEdit(parsed, msg.id),
|
|
1480
|
+
className: "cnfy-btn-edit",
|
|
1481
|
+
children: [
|
|
1482
|
+
/* @__PURE__ */ jsx4(Edit3, { size: 16 }),
|
|
1483
|
+
"Edit"
|
|
1484
|
+
]
|
|
1485
|
+
}
|
|
1486
|
+
),
|
|
1487
|
+
/* @__PURE__ */ jsxs4(
|
|
1488
|
+
"button",
|
|
1489
|
+
{
|
|
1490
|
+
onClick: () => handleDownload(parsed, msg.id, "docx"),
|
|
1491
|
+
disabled: downloadingKey === `${msg.id}-docx`,
|
|
1492
|
+
className: "cnfy-btn-download",
|
|
1493
|
+
children: [
|
|
1494
|
+
downloadingKey === `${msg.id}-docx` ? /* @__PURE__ */ jsx4(Loader22, { size: 16, className: "cnfy-animate-spin" }) : /* @__PURE__ */ jsx4(FileDown2, { size: 16 }),
|
|
1495
|
+
"DOCX"
|
|
1496
|
+
]
|
|
1497
|
+
}
|
|
1498
|
+
),
|
|
1499
|
+
/* @__PURE__ */ jsxs4(
|
|
1500
|
+
"button",
|
|
1501
|
+
{
|
|
1502
|
+
onClick: () => handleDownload(parsed, msg.id, "pdf"),
|
|
1503
|
+
disabled: downloadingKey === `${msg.id}-pdf`,
|
|
1504
|
+
className: "cnfy-btn-download cnfy-btn-download--pdf",
|
|
1505
|
+
style: { background: THEME_GRADIENT, color: "#fff", border: "none" },
|
|
1506
|
+
children: [
|
|
1507
|
+
downloadingKey === `${msg.id}-pdf` ? /* @__PURE__ */ jsx4(Loader22, { size: 16, className: "cnfy-animate-spin" }) : /* @__PURE__ */ jsx4(FileDown2, { size: 16 }),
|
|
1508
|
+
"PDF"
|
|
1509
|
+
]
|
|
1510
|
+
}
|
|
1511
|
+
)
|
|
1512
|
+
] })
|
|
1513
|
+
] }) }, msg.id);
|
|
1514
|
+
}),
|
|
1515
|
+
/* @__PURE__ */ jsx4("div", { ref: bottomRef })
|
|
1516
|
+
] }) }),
|
|
1517
|
+
isEmpty && /* @__PURE__ */ jsx4("div", { className: "cnfy-suggestions", children: SUGGESTIONS.map((s) => /* @__PURE__ */ jsxs4(
|
|
1518
|
+
"button",
|
|
1052
1519
|
{
|
|
1053
|
-
className: "cnfy-
|
|
1054
|
-
|
|
1520
|
+
className: "cnfy-suggestion-chip",
|
|
1521
|
+
onClick: () => {
|
|
1522
|
+
var _a;
|
|
1523
|
+
setInput(s.label);
|
|
1524
|
+
(_a = textareaRef.current) == null ? void 0 : _a.focus();
|
|
1525
|
+
},
|
|
1526
|
+
children: [
|
|
1527
|
+
s.icon,
|
|
1528
|
+
s.label
|
|
1529
|
+
]
|
|
1055
1530
|
},
|
|
1056
|
-
|
|
1057
|
-
)
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
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
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1531
|
+
s.label
|
|
1532
|
+
)) }),
|
|
1533
|
+
/* @__PURE__ */ jsx4("div", { className: "cnfy-input-area", children: /* @__PURE__ */ jsxs4("div", { className: "cnfy-input-inner", children: [
|
|
1534
|
+
/* @__PURE__ */ jsxs4("div", { ref: dropdownRef, className: "cnfy-news-pulse-wrap", children: [
|
|
1535
|
+
/* @__PURE__ */ jsx4(
|
|
1536
|
+
"button",
|
|
1537
|
+
{
|
|
1538
|
+
onClick: handleOpenNewsDropdown,
|
|
1539
|
+
className: "cnfy-news-pulse-btn cnfy-animate-pulse",
|
|
1540
|
+
title: "Select from trending news",
|
|
1541
|
+
children: /* @__PURE__ */ jsx4(Zap, { size: 16 })
|
|
1542
|
+
}
|
|
1543
|
+
),
|
|
1544
|
+
showNewsDropdown && /* @__PURE__ */ jsxs4("div", { className: "cnfy-news-dropdown", children: [
|
|
1545
|
+
/* @__PURE__ */ jsxs4("div", { className: "cnfy-news-dropdown-header", style: { background: THEME_GRADIENT, color: "#fff" }, children: [
|
|
1546
|
+
/* @__PURE__ */ jsx4("span", { className: "cnfy-news-dropdown-title", children: "Select News" }),
|
|
1547
|
+
/* @__PURE__ */ jsx4(
|
|
1548
|
+
"button",
|
|
1549
|
+
{
|
|
1550
|
+
onClick: () => setShowNewsDropdown(false),
|
|
1551
|
+
className: "cnfy-news-dropdown-close",
|
|
1552
|
+
style: { background: "transparent", color: "#fff" },
|
|
1553
|
+
children: /* @__PURE__ */ jsx4(X2, { size: 14 })
|
|
1554
|
+
}
|
|
1555
|
+
)
|
|
1556
|
+
] }),
|
|
1557
|
+
/* @__PURE__ */ jsxs4("div", { className: "cnfy-source-pills-wrap", children: [
|
|
1558
|
+
loadingSources ? /* @__PURE__ */ jsxs4("div", { className: "cnfy-source-pills-loading", children: [
|
|
1559
|
+
/* @__PURE__ */ jsx4(Loader22, { size: 14, className: "cnfy-animate-spin" }),
|
|
1560
|
+
" Loading sources\u2026"
|
|
1561
|
+
] }) : /* @__PURE__ */ jsxs4("div", { className: "cnfy-source-pills", ref: pillsRef, children: [
|
|
1562
|
+
/* @__PURE__ */ jsx4(
|
|
1563
|
+
"button",
|
|
1564
|
+
{
|
|
1565
|
+
className: `cnfy-source-pill ${!selectedSource ? "cnfy-source-pill--active" : ""}`,
|
|
1566
|
+
style: !selectedSource ? { background: THEME_GRADIENT, borderColor: "transparent", color: "#fff" } : {},
|
|
1567
|
+
onClick: () => handleSourceSelect(null),
|
|
1568
|
+
children: "All"
|
|
1569
|
+
}
|
|
1570
|
+
),
|
|
1571
|
+
sources.map((source) => /* @__PURE__ */ jsx4(
|
|
1572
|
+
"button",
|
|
1573
|
+
{
|
|
1574
|
+
className: `cnfy-source-pill ${selectedSource === source.id ? "cnfy-source-pill--active" : ""}`,
|
|
1575
|
+
style: selectedSource === source.id ? { background: THEME_GRADIENT, borderColor: "transparent", color: "#fff" } : {},
|
|
1576
|
+
onClick: () => handleSourceSelect(source.id),
|
|
1577
|
+
children: source.name
|
|
1578
|
+
},
|
|
1579
|
+
source.id
|
|
1580
|
+
))
|
|
1581
|
+
] }),
|
|
1582
|
+
/* @__PURE__ */ jsx4(
|
|
1583
|
+
"button",
|
|
1584
|
+
{
|
|
1585
|
+
onClick: () => selectedSource ? handleScrape() : fetchNews(null),
|
|
1586
|
+
disabled: scraping || loadingNews,
|
|
1587
|
+
className: "cnfy-source-refresh-btn",
|
|
1588
|
+
title: selectedSource ? "Fetch latest news" : "Refresh",
|
|
1589
|
+
children: /* @__PURE__ */ jsx4(RefreshCcw2, { size: 13, className: scraping || loadingNews ? "cnfy-animate-spin" : "" })
|
|
1590
|
+
}
|
|
1591
|
+
)
|
|
1592
|
+
] }),
|
|
1593
|
+
/* @__PURE__ */ jsx4("div", { className: "cnfy-news-dropdown-list", children: loadingNews ? /* @__PURE__ */ jsx4("div", { className: "cnfy-news-dropdown-msg", children: "Loading news..." }) : trendingNews.length === 0 ? /* @__PURE__ */ jsxs4("div", { className: "cnfy-news-dropdown-msg", children: [
|
|
1594
|
+
/* @__PURE__ */ jsx4("p", { children: "No news found." }),
|
|
1595
|
+
selectedSource && /* @__PURE__ */ jsx4("p", { className: "cnfy-news-dropdown-hint", children: 'Click "Scrape" to fetch latest news.' })
|
|
1596
|
+
] }) : /* @__PURE__ */ jsx4(
|
|
1597
|
+
NewsList,
|
|
1598
|
+
{
|
|
1599
|
+
news: trendingNews.slice(0, 10),
|
|
1600
|
+
onView: (item) => window.open(item.sourceUrl || item.link, "_blank"),
|
|
1601
|
+
onRecreate: (payload) => {
|
|
1602
|
+
setShowNewsDropdown(false);
|
|
1603
|
+
onSelectNews == null ? void 0 : onSelectNews(payload);
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
) })
|
|
1607
|
+
] })
|
|
1608
|
+
] }),
|
|
1609
|
+
/* @__PURE__ */ jsx4(
|
|
1610
|
+
"textarea",
|
|
1611
|
+
{
|
|
1612
|
+
ref: textareaRef,
|
|
1613
|
+
value: input,
|
|
1614
|
+
onChange: (e) => setInput(e.target.value),
|
|
1615
|
+
onKeyDown: (e) => {
|
|
1616
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
1617
|
+
e.preventDefault();
|
|
1618
|
+
handleSend();
|
|
1619
|
+
}
|
|
1620
|
+
},
|
|
1621
|
+
rows: 1,
|
|
1622
|
+
placeholder: "Ask AI something\u2026",
|
|
1623
|
+
className: `cnfy-chat-textarea ${!showNewsPanel ? "cnfy-chat-textarea--with-trigger" : ""}`,
|
|
1624
|
+
style: { maxHeight: "200px", overflowY: input.split("\n").length > 6 ? "auto" : "hidden" }
|
|
1625
|
+
}
|
|
1626
|
+
),
|
|
1627
|
+
/* @__PURE__ */ jsx4(
|
|
1628
|
+
"button",
|
|
1629
|
+
{
|
|
1630
|
+
onClick: handleSend,
|
|
1631
|
+
className: "cnfy-send-btn",
|
|
1632
|
+
style: { background: THEME_GRADIENT, color: "#fff" },
|
|
1633
|
+
children: /* @__PURE__ */ jsx4(SendHorizontal, { size: 18 })
|
|
1634
|
+
}
|
|
1635
|
+
)
|
|
1636
|
+
] }) })
|
|
1637
|
+
]
|
|
1125
1638
|
}
|
|
1126
1639
|
),
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
style: { backgroundColor: primaryColor, color: "#fff" },
|
|
1136
|
-
children: /* @__PURE__ */ jsx5(X2, { size: 14 })
|
|
1137
|
-
}
|
|
1138
|
-
)
|
|
1139
|
-
] }),
|
|
1140
|
-
/* @__PURE__ */ jsxs4("div", { className: "cnfy-news-dropdown-source", children: [
|
|
1141
|
-
/* @__PURE__ */ jsx5("div", { className: "cnfy-news-dropdown-select-wrap", children: /* @__PURE__ */ jsx5(
|
|
1142
|
-
Select,
|
|
1143
|
-
{
|
|
1144
|
-
options: [
|
|
1145
|
-
{ value: null, label: "All Sources (Trending)" },
|
|
1146
|
-
...sources.filter((source) => !preferredLanguage || source.language === preferredLanguage).map((source) => ({
|
|
1147
|
-
value: source.id,
|
|
1148
|
-
label: source.name
|
|
1149
|
-
}))
|
|
1150
|
-
],
|
|
1151
|
-
value: selectedSource ? { value: selectedSource, label: ((_b = sources.find((s) => s.id === selectedSource)) == null ? void 0 : _b.name) || selectedSource } : { value: null, label: "All Sources (Trending)" },
|
|
1152
|
-
onChange: handleSourceSelect,
|
|
1153
|
-
isLoading: loadingSources,
|
|
1154
|
-
isDisabled: loadingSources,
|
|
1155
|
-
placeholder: "Select source...",
|
|
1156
|
-
classNamePrefix: "react-select",
|
|
1157
|
-
menuPlacement: "bottom",
|
|
1158
|
-
menuPosition: "fixed",
|
|
1159
|
-
menuShouldScrollIntoView: false,
|
|
1160
|
-
maxMenuHeight: 220,
|
|
1161
|
-
menuPortalTarget: typeof window !== "undefined" ? document.body : null,
|
|
1162
|
-
styles: {
|
|
1163
|
-
container: (base) => __spreadProps(__spreadValues({}, base), {
|
|
1164
|
-
width: "100%"
|
|
1165
|
-
}),
|
|
1166
|
-
control: (base) => __spreadProps(__spreadValues({}, base), {
|
|
1167
|
-
minHeight: "38px",
|
|
1168
|
-
borderColor: "#e5e7eb",
|
|
1169
|
-
boxShadow: "none",
|
|
1170
|
-
width: "100%"
|
|
1171
|
-
}),
|
|
1172
|
-
menu: (base) => __spreadProps(__spreadValues({}, base), {
|
|
1173
|
-
width: "100%",
|
|
1174
|
-
zIndex: 999999
|
|
1175
|
-
}),
|
|
1176
|
-
menuPortal: (base) => __spreadProps(__spreadValues({}, base), {
|
|
1177
|
-
zIndex: 999999
|
|
1178
|
-
}),
|
|
1179
|
-
option: (base, state) => __spreadProps(__spreadValues({}, base), {
|
|
1180
|
-
backgroundColor: state.isSelected ? primaryColor : state.isFocused ? "#f3f4f6" : "white",
|
|
1181
|
-
color: state.isSelected ? "white" : "#374151",
|
|
1182
|
-
cursor: "pointer"
|
|
1183
|
-
})
|
|
1184
|
-
}
|
|
1185
|
-
}
|
|
1186
|
-
) }),
|
|
1187
|
-
selectedSource && /* @__PURE__ */ jsxs4(
|
|
1188
|
-
"button",
|
|
1189
|
-
{
|
|
1190
|
-
onClick: handleScrape,
|
|
1191
|
-
disabled: scraping,
|
|
1192
|
-
className: "cnfy-scrape-btn",
|
|
1193
|
-
title: "Fetch latest news",
|
|
1194
|
-
children: [
|
|
1195
|
-
/* @__PURE__ */ jsx5(RefreshCcw2, { size: 14, className: scraping ? "cnfy-animate-spin" : "" }),
|
|
1196
|
-
scraping ? "Scraping..." : "Scrape"
|
|
1197
|
-
]
|
|
1198
|
-
}
|
|
1199
|
-
),
|
|
1200
|
-
!selectedSource && /* @__PURE__ */ jsxs4(
|
|
1201
|
-
"button",
|
|
1640
|
+
editModal.isOpen && /* @__PURE__ */ jsx4("div", { className: "cnfy-resize-handle", onMouseDown: handleDragStart, children: /* @__PURE__ */ jsx4("div", { className: "cnfy-resize-handle-bar" }) }),
|
|
1641
|
+
/* @__PURE__ */ jsx4(
|
|
1642
|
+
"div",
|
|
1643
|
+
{
|
|
1644
|
+
className: "cnfy-editor-edit-pane",
|
|
1645
|
+
style: editModal.isOpen ? { flex: `0 0 ${100 - splitPct}%`, width: `${100 - splitPct}%` } : void 0,
|
|
1646
|
+
children: /* @__PURE__ */ jsx4(
|
|
1647
|
+
EditModal,
|
|
1202
1648
|
{
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1649
|
+
isOpen: editModal.isOpen,
|
|
1650
|
+
inline: true,
|
|
1651
|
+
initialTitle: editModal.title,
|
|
1652
|
+
initialSubtitle: editModal.subtitle,
|
|
1653
|
+
initialContent: editModal.content,
|
|
1654
|
+
initialMetaDescription: editModal.metaDescription,
|
|
1655
|
+
metaKeywords: editModal.metaKeywords,
|
|
1656
|
+
initialFeaturedImage: editModal.featuredImage,
|
|
1657
|
+
seoAnalysis: editModal.seoAnalysis,
|
|
1658
|
+
onClose: handleCloseModal,
|
|
1659
|
+
onDownload: handleDownloadFromModal,
|
|
1660
|
+
downloadingFormat: (downloadingKey == null ? void 0 : downloadingKey.startsWith("modal-")) ? downloadingKey.replace("modal-", "") : null,
|
|
1661
|
+
onPost: handlePostFromModal,
|
|
1662
|
+
isPosting
|
|
1210
1663
|
}
|
|
1211
1664
|
)
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
selectedSource && /* @__PURE__ */ jsx5("p", { className: "cnfy-news-dropdown-hint", children: 'Click "Scrape" to fetch latest news.' })
|
|
1216
|
-
] }) : /* @__PURE__ */ jsx5(
|
|
1217
|
-
NewsList,
|
|
1218
|
-
{
|
|
1219
|
-
news: trendingNews.slice(0, 10),
|
|
1220
|
-
onView: (item) => window.open(item.sourceUrl || item.link, "_blank"),
|
|
1221
|
-
onRecreate: (payload) => {
|
|
1222
|
-
setShowNewsDropdown(false);
|
|
1223
|
-
onSelectNews == null ? void 0 : onSelectNews(payload);
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
) })
|
|
1227
|
-
] })
|
|
1228
|
-
] }),
|
|
1229
|
-
/* @__PURE__ */ jsx5(
|
|
1230
|
-
"textarea",
|
|
1231
|
-
{
|
|
1232
|
-
ref: textareaRef,
|
|
1233
|
-
value: input,
|
|
1234
|
-
onChange: (e) => setInput(e.target.value),
|
|
1235
|
-
onKeyDown: (e) => {
|
|
1236
|
-
if (e.key === "Enter" && !e.shiftKey) {
|
|
1237
|
-
e.preventDefault();
|
|
1238
|
-
handleSend();
|
|
1239
|
-
}
|
|
1240
|
-
},
|
|
1241
|
-
rows: 1,
|
|
1242
|
-
placeholder: "Ask AI something\u2026",
|
|
1243
|
-
className: `cnfy-chat-textarea ${!showNewsPanel ? "cnfy-chat-textarea--with-pulse" : ""}`,
|
|
1244
|
-
style: { maxHeight: "200px", overflowY: input.split("\n").length > 6 ? "auto" : "hidden" }
|
|
1245
|
-
}
|
|
1246
|
-
),
|
|
1247
|
-
/* @__PURE__ */ jsx5(
|
|
1248
|
-
"button",
|
|
1249
|
-
{
|
|
1250
|
-
onClick: handleSend,
|
|
1251
|
-
className: "cnfy-send-btn",
|
|
1252
|
-
style: { backgroundColor: primaryColor, color: "#fff" },
|
|
1253
|
-
children: /* @__PURE__ */ jsx5(SendHorizontal, { size: 18 })
|
|
1254
|
-
}
|
|
1255
|
-
)
|
|
1256
|
-
] }) }),
|
|
1257
|
-
/* @__PURE__ */ jsx5(
|
|
1258
|
-
EditModal,
|
|
1259
|
-
{
|
|
1260
|
-
isOpen: editModal.isOpen,
|
|
1261
|
-
initialContent: editModal.content,
|
|
1262
|
-
metaKeywords: editModal.metaKeywords,
|
|
1263
|
-
onClose: handleCloseModal,
|
|
1264
|
-
onSaveDraft: handleSaveDraft,
|
|
1265
|
-
onPost: handlePost
|
|
1266
|
-
}
|
|
1267
|
-
)
|
|
1268
|
-
] });
|
|
1269
|
-
}
|
|
1270
|
-
|
|
1271
|
-
// components/chatbot/UserMenu.tsx
|
|
1272
|
-
import { Settings } from "lucide-react";
|
|
1273
|
-
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
1274
|
-
function UserMenu({
|
|
1275
|
-
onOpenPreferences
|
|
1276
|
-
}) {
|
|
1277
|
-
return /* @__PURE__ */ jsx6("div", { className: "cnfy-user-menu", children: /* @__PURE__ */ jsx6(
|
|
1278
|
-
"button",
|
|
1279
|
-
{
|
|
1280
|
-
onClick: () => onOpenPreferences == null ? void 0 : onOpenPreferences(),
|
|
1281
|
-
className: "cnfy-user-menu-trigger",
|
|
1282
|
-
"aria-label": "Settings",
|
|
1283
|
-
children: /* @__PURE__ */ jsx6(Settings, { size: 20, className: "cnfy-user-menu-trigger-icon" })
|
|
1665
|
+
}
|
|
1666
|
+
)
|
|
1667
|
+
]
|
|
1284
1668
|
}
|
|
1285
|
-
)
|
|
1286
|
-
}
|
|
1287
|
-
|
|
1288
|
-
// components/chatbot/headerBar.tsx
|
|
1289
|
-
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1290
|
-
function HeaderBar({
|
|
1291
|
-
onOpenPreferences
|
|
1292
|
-
}) {
|
|
1293
|
-
const { primaryColor, botName, logoUrl } = useTheme();
|
|
1294
|
-
return /* @__PURE__ */ jsx7("header", { className: "cnfy-header", children: /* @__PURE__ */ jsxs5("div", { className: "cnfy-header-inner", children: [
|
|
1295
|
-
/* @__PURE__ */ jsxs5("div", { className: "cnfy-header-left", children: [
|
|
1296
|
-
logoUrl ? /* @__PURE__ */ jsx7("img", { src: logoUrl, alt: "Logo", className: "cnfy-header-logo-img" }) : /* @__PURE__ */ jsx7(
|
|
1297
|
-
"div",
|
|
1298
|
-
{
|
|
1299
|
-
className: "cnfy-header-logo-fallback",
|
|
1300
|
-
style: { backgroundColor: primaryColor },
|
|
1301
|
-
children: botName.charAt(0).toUpperCase()
|
|
1302
|
-
}
|
|
1303
|
-
),
|
|
1304
|
-
/* @__PURE__ */ jsx7("span", { className: "cnfy-header-brand", children: botName.toUpperCase() })
|
|
1305
|
-
] }),
|
|
1306
|
-
/* @__PURE__ */ jsx7("div", { className: "cnfy-header-right", children: /* @__PURE__ */ jsx7(UserMenu, { onOpenPreferences }) })
|
|
1307
|
-
] }) });
|
|
1669
|
+
);
|
|
1308
1670
|
}
|
|
1309
1671
|
|
|
1310
1672
|
// components/ui/Drawer.tsx
|
|
1311
|
-
import { useEffect as
|
|
1673
|
+
import { useEffect as useEffect4 } from "react";
|
|
1312
1674
|
import { X as X3 } from "lucide-react";
|
|
1313
|
-
import { jsx as
|
|
1675
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1314
1676
|
function Drawer({
|
|
1315
1677
|
open,
|
|
1316
1678
|
onClose,
|
|
1317
1679
|
title,
|
|
1318
1680
|
children
|
|
1319
1681
|
}) {
|
|
1320
|
-
|
|
1682
|
+
useEffect4(() => {
|
|
1321
1683
|
if (open) {
|
|
1322
1684
|
document.body.style.overflow = "hidden";
|
|
1323
1685
|
} else {
|
|
@@ -1328,20 +1690,20 @@ function Drawer({
|
|
|
1328
1690
|
};
|
|
1329
1691
|
}, [open]);
|
|
1330
1692
|
if (!open) return null;
|
|
1331
|
-
return /* @__PURE__ */
|
|
1332
|
-
/* @__PURE__ */
|
|
1333
|
-
/* @__PURE__ */
|
|
1334
|
-
/* @__PURE__ */
|
|
1335
|
-
title && /* @__PURE__ */
|
|
1336
|
-
/* @__PURE__ */
|
|
1693
|
+
return /* @__PURE__ */ jsxs5("div", { className: "cnfy-drawer-overlay", children: [
|
|
1694
|
+
/* @__PURE__ */ jsx5("div", { className: "cnfy-drawer-backdrop", onClick: onClose }),
|
|
1695
|
+
/* @__PURE__ */ jsxs5("div", { className: "cnfy-drawer-panel", children: [
|
|
1696
|
+
/* @__PURE__ */ jsxs5("div", { className: "cnfy-drawer-header", children: [
|
|
1697
|
+
title && /* @__PURE__ */ jsx5("h2", { className: "cnfy-drawer-title", children: title }),
|
|
1698
|
+
/* @__PURE__ */ jsx5("button", { onClick: onClose, className: "cnfy-drawer-close-btn", children: /* @__PURE__ */ jsx5(X3, { size: 20 }) })
|
|
1337
1699
|
] }),
|
|
1338
|
-
/* @__PURE__ */
|
|
1700
|
+
/* @__PURE__ */ jsx5("div", { className: "cnfy-drawer-body", children })
|
|
1339
1701
|
] })
|
|
1340
1702
|
] });
|
|
1341
1703
|
}
|
|
1342
1704
|
|
|
1343
1705
|
// components/preferences/Preferences.tsx
|
|
1344
|
-
import { useState as
|
|
1706
|
+
import { useState as useState4, useEffect as useEffect5 } from "react";
|
|
1345
1707
|
import toast from "react-hot-toast";
|
|
1346
1708
|
|
|
1347
1709
|
// services/settings.service.ts
|
|
@@ -1349,36 +1711,19 @@ var triggerScrape = async ({
|
|
|
1349
1711
|
state,
|
|
1350
1712
|
cities
|
|
1351
1713
|
}) => {
|
|
1352
|
-
const { data } = await api_default.post("/
|
|
1714
|
+
const { data } = await api_default.post("/news/trigger", {
|
|
1353
1715
|
state,
|
|
1354
1716
|
cities
|
|
1355
1717
|
});
|
|
1356
1718
|
return data;
|
|
1357
1719
|
};
|
|
1358
|
-
var getScrapeStatus = async () => {
|
|
1359
|
-
const { data } = await api_default.get("/
|
|
1360
|
-
return data.data;
|
|
1361
|
-
};
|
|
1362
|
-
|
|
1363
|
-
// components/ClientSelect.tsx
|
|
1364
|
-
import { useEffect as useEffect6, useState as useState5 } from "react";
|
|
1365
|
-
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
1366
|
-
var ReactSelect = null;
|
|
1367
|
-
var ClientSelect = (props) => {
|
|
1368
|
-
const [mounted, setMounted] = useState5(false);
|
|
1369
|
-
useEffect6(() => {
|
|
1370
|
-
setMounted(true);
|
|
1371
|
-
import("react-select").then((mod) => {
|
|
1372
|
-
ReactSelect = mod.default;
|
|
1373
|
-
});
|
|
1374
|
-
}, []);
|
|
1375
|
-
if (!mounted || !ReactSelect) return null;
|
|
1376
|
-
return /* @__PURE__ */ jsx9(ReactSelect, __spreadValues({}, props));
|
|
1377
|
-
};
|
|
1378
|
-
var ClientSelect_default = ClientSelect;
|
|
1720
|
+
var getScrapeStatus = async () => {
|
|
1721
|
+
const { data } = await api_default.get("/news/status");
|
|
1722
|
+
return data.data;
|
|
1723
|
+
};
|
|
1379
1724
|
|
|
1380
1725
|
// components/preferences/Preferences.tsx
|
|
1381
|
-
import { Fragment, jsx as
|
|
1726
|
+
import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1382
1727
|
var STATE_OPTIONS = [
|
|
1383
1728
|
{ value: "Uttar Pradesh", label: "Uttar Pradesh" }
|
|
1384
1729
|
];
|
|
@@ -1413,27 +1758,24 @@ var LANGUAGE_OPTIONS = [
|
|
|
1413
1758
|
{ value: "hi", label: "Hindi" }
|
|
1414
1759
|
];
|
|
1415
1760
|
function PreferencesPage() {
|
|
1416
|
-
|
|
1417
|
-
const
|
|
1418
|
-
const
|
|
1419
|
-
const [
|
|
1420
|
-
const [
|
|
1421
|
-
const [
|
|
1422
|
-
const [
|
|
1423
|
-
const [
|
|
1424
|
-
const [
|
|
1425
|
-
const [
|
|
1426
|
-
const [
|
|
1427
|
-
const [
|
|
1428
|
-
const [scrapeStatus, setScrapeStatus] = useState6(null);
|
|
1429
|
-
const [loadingStatus, setLoadingStatus] = useState6(true);
|
|
1430
|
-
const [originalValues, setOriginalValues] = useState6({
|
|
1761
|
+
const primaryColor = "#10b981";
|
|
1762
|
+
const [botName, setBotName] = useState4("");
|
|
1763
|
+
const [logo, setLogo] = useState4(null);
|
|
1764
|
+
const [logoUrl, setLogoUrl] = useState4(null);
|
|
1765
|
+
const [state, setState] = useState4(null);
|
|
1766
|
+
const [cities, setCities] = useState4([]);
|
|
1767
|
+
const [language, setLanguage] = useState4(null);
|
|
1768
|
+
const [saving, setSaving] = useState4(false);
|
|
1769
|
+
const [scraping, setScraping] = useState4(false);
|
|
1770
|
+
const [scrapeStatus, setScrapeStatus] = useState4(null);
|
|
1771
|
+
const [loadingStatus, setLoadingStatus] = useState4(true);
|
|
1772
|
+
const [originalValues, setOriginalValues] = useState4({
|
|
1431
1773
|
botName: "",
|
|
1432
1774
|
state: void 0,
|
|
1433
1775
|
cities: [],
|
|
1434
1776
|
language: void 0
|
|
1435
1777
|
});
|
|
1436
|
-
|
|
1778
|
+
useEffect5(() => {
|
|
1437
1779
|
const fetchScrapeStatus = async () => {
|
|
1438
1780
|
try {
|
|
1439
1781
|
const data = await getScrapeStatus();
|
|
@@ -1447,12 +1789,11 @@ function PreferencesPage() {
|
|
|
1447
1789
|
fetchScrapeStatus();
|
|
1448
1790
|
}, []);
|
|
1449
1791
|
const handleScrape = async () => {
|
|
1450
|
-
var _a2, _b2;
|
|
1451
1792
|
setScraping(true);
|
|
1452
1793
|
try {
|
|
1453
1794
|
await triggerScrape({
|
|
1454
|
-
state:
|
|
1455
|
-
cities: (
|
|
1795
|
+
state: state == null ? void 0 : state.value,
|
|
1796
|
+
cities: cities.map((c) => c.value)
|
|
1456
1797
|
});
|
|
1457
1798
|
toast.success("News scraping started successfully!");
|
|
1458
1799
|
setTimeout(async () => {
|
|
@@ -1470,40 +1811,12 @@ function PreferencesPage() {
|
|
|
1470
1811
|
setScraping(false);
|
|
1471
1812
|
}
|
|
1472
1813
|
};
|
|
1473
|
-
useEffect7(() => {
|
|
1474
|
-
var _a2, _b2, _c2, _d, _e;
|
|
1475
|
-
if (preferences) {
|
|
1476
|
-
const name = ((_a2 = preferences.chatbot) == null ? void 0 : _a2.name) || "";
|
|
1477
|
-
const pState = (_b2 = preferences.localization) == null ? void 0 : _b2.state;
|
|
1478
|
-
const pCities = ((_c2 = preferences.localization) == null ? void 0 : _c2.cities) || [];
|
|
1479
|
-
const pLanguage = (_d = preferences.localization) == null ? void 0 : _d.language;
|
|
1480
|
-
setBotName(name);
|
|
1481
|
-
if ((_e = preferences.chatbot) == null ? void 0 : _e.logoUrl) setLogoUrl(preferences.chatbot.logoUrl);
|
|
1482
|
-
if (pState) {
|
|
1483
|
-
setState({ value: pState, label: pState });
|
|
1484
|
-
}
|
|
1485
|
-
if (Array.isArray(pCities)) {
|
|
1486
|
-
setCities(pCities.map((city) => ({ value: city, label: city })));
|
|
1487
|
-
}
|
|
1488
|
-
if (pLanguage) {
|
|
1489
|
-
const langOption = LANGUAGE_OPTIONS.find((opt) => opt.value === pLanguage);
|
|
1490
|
-
setLanguage(langOption || null);
|
|
1491
|
-
}
|
|
1492
|
-
setOriginalValues({
|
|
1493
|
-
botName: name,
|
|
1494
|
-
state: pState,
|
|
1495
|
-
cities: pCities,
|
|
1496
|
-
language: pLanguage
|
|
1497
|
-
});
|
|
1498
|
-
}
|
|
1499
|
-
}, [preferences]);
|
|
1500
1814
|
const handleSave = async () => {
|
|
1501
1815
|
if (!botName) {
|
|
1502
1816
|
toast.error("Chatbot name is required");
|
|
1503
1817
|
return;
|
|
1504
1818
|
}
|
|
1505
1819
|
setSaving(true);
|
|
1506
|
-
setSuccess(false);
|
|
1507
1820
|
try {
|
|
1508
1821
|
const payload = {};
|
|
1509
1822
|
if (botName !== originalValues.botName) {
|
|
@@ -1530,13 +1843,6 @@ function PreferencesPage() {
|
|
|
1530
1843
|
setSaving(false);
|
|
1531
1844
|
return;
|
|
1532
1845
|
}
|
|
1533
|
-
const updatedPreferences = await savePreferencesApi(payload);
|
|
1534
|
-
if (updatedPreferences) {
|
|
1535
|
-
updatePreferences(updatedPreferences);
|
|
1536
|
-
} else {
|
|
1537
|
-
await refreshPreferences();
|
|
1538
|
-
}
|
|
1539
|
-
setSuccess(true);
|
|
1540
1846
|
setLogo(null);
|
|
1541
1847
|
toast.success("Preferences saved successfully!");
|
|
1542
1848
|
} catch (error) {
|
|
@@ -1546,15 +1852,12 @@ function PreferencesPage() {
|
|
|
1546
1852
|
setSaving(false);
|
|
1547
1853
|
}
|
|
1548
1854
|
};
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
/* @__PURE__ */ jsxs7("div", { className: "cnfy-dash-field", children: [
|
|
1556
|
-
/* @__PURE__ */ jsx10("label", { className: "cnfy-dash-label", children: "Chatbot Name" }),
|
|
1557
|
-
/* @__PURE__ */ jsx10(
|
|
1855
|
+
return /* @__PURE__ */ jsxs6("div", { className: "cnfy-dash-page", children: [
|
|
1856
|
+
/* @__PURE__ */ jsxs6("div", { className: "cnfy-dash-card", children: [
|
|
1857
|
+
/* @__PURE__ */ jsx6("h2", { className: "cnfy-dash-card-title", children: "Chatbot Settings" }),
|
|
1858
|
+
/* @__PURE__ */ jsxs6("div", { className: "cnfy-dash-field", children: [
|
|
1859
|
+
/* @__PURE__ */ jsx6("label", { className: "cnfy-dash-label", children: "Chatbot Name" }),
|
|
1860
|
+
/* @__PURE__ */ jsx6(
|
|
1558
1861
|
"input",
|
|
1559
1862
|
{
|
|
1560
1863
|
value: botName,
|
|
@@ -1564,32 +1867,38 @@ function PreferencesPage() {
|
|
|
1564
1867
|
}
|
|
1565
1868
|
)
|
|
1566
1869
|
] }),
|
|
1567
|
-
/* @__PURE__ */
|
|
1568
|
-
/* @__PURE__ */
|
|
1569
|
-
/* @__PURE__ */
|
|
1570
|
-
/* @__PURE__ */
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1870
|
+
/* @__PURE__ */ jsxs6("div", { className: "cnfy-dash-field", children: [
|
|
1871
|
+
/* @__PURE__ */ jsx6("label", { className: "cnfy-dash-label", children: "Chatbot Logo" }),
|
|
1872
|
+
/* @__PURE__ */ jsxs6("div", { className: "cnfy-logo-upload", children: [
|
|
1873
|
+
/* @__PURE__ */ jsx6("div", { className: "cnfy-logo-preview", children: logo ? (
|
|
1874
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
1875
|
+
/* @__PURE__ */ jsx6(
|
|
1876
|
+
"img",
|
|
1877
|
+
{
|
|
1878
|
+
src: URL.createObjectURL(logo),
|
|
1879
|
+
alt: "Logo preview",
|
|
1880
|
+
className: "cnfy-logo-img"
|
|
1881
|
+
}
|
|
1882
|
+
)
|
|
1883
|
+
) : logoUrl ? (
|
|
1884
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
1885
|
+
/* @__PURE__ */ jsx6(
|
|
1886
|
+
"img",
|
|
1887
|
+
{
|
|
1888
|
+
src: logoUrl,
|
|
1889
|
+
alt: "Current logo",
|
|
1890
|
+
className: "cnfy-logo-img"
|
|
1891
|
+
}
|
|
1892
|
+
)
|
|
1893
|
+
) : /* @__PURE__ */ jsx6("span", { className: "cnfy-logo-placeholder", children: "No logo" }) }),
|
|
1894
|
+
/* @__PURE__ */ jsx6(
|
|
1586
1895
|
"input",
|
|
1587
1896
|
{
|
|
1588
1897
|
type: "file",
|
|
1589
1898
|
accept: "image/*",
|
|
1590
1899
|
onChange: (e) => {
|
|
1591
|
-
var
|
|
1592
|
-
return setLogo(((
|
|
1900
|
+
var _a;
|
|
1901
|
+
return setLogo(((_a = e.target.files) == null ? void 0 : _a[0]) || null);
|
|
1593
1902
|
},
|
|
1594
1903
|
className: "cnfy-file-input"
|
|
1595
1904
|
}
|
|
@@ -1597,38 +1906,38 @@ function PreferencesPage() {
|
|
|
1597
1906
|
] })
|
|
1598
1907
|
] })
|
|
1599
1908
|
] }),
|
|
1600
|
-
/* @__PURE__ */
|
|
1601
|
-
/* @__PURE__ */
|
|
1602
|
-
/* @__PURE__ */
|
|
1603
|
-
/* @__PURE__ */
|
|
1909
|
+
/* @__PURE__ */ jsxs6("div", { className: "cnfy-dash-card", children: [
|
|
1910
|
+
/* @__PURE__ */ jsxs6("div", { className: "cnfy-dash-card-header", children: [
|
|
1911
|
+
/* @__PURE__ */ jsx6("h2", { className: "cnfy-dash-card-title-inline", children: "News Scraping" }),
|
|
1912
|
+
/* @__PURE__ */ jsx6("p", { className: "cnfy-dash-card-desc", children: "Scrape latest news from your configured locations" })
|
|
1604
1913
|
] }),
|
|
1605
|
-
!loadingStatus && scrapeStatus && /* @__PURE__ */
|
|
1606
|
-
/* @__PURE__ */
|
|
1607
|
-
/* @__PURE__ */
|
|
1608
|
-
/* @__PURE__ */
|
|
1914
|
+
!loadingStatus && scrapeStatus && /* @__PURE__ */ jsxs6("div", { className: "cnfy-dash-grid cnfy-dash-grid--sm", children: [
|
|
1915
|
+
/* @__PURE__ */ jsxs6("div", { children: [
|
|
1916
|
+
/* @__PURE__ */ jsx6("span", { className: "cnfy-dash-label-muted", children: "Status:" }),
|
|
1917
|
+
/* @__PURE__ */ jsx6("p", { className: "cnfy-dash-label-bold", children: scrapeStatus.isRunning ? /* @__PURE__ */ jsx6("span", { className: "cnfy-dash-badge cnfy-dash-badge--yellow", children: "Running" }) : /* @__PURE__ */ jsx6("span", { className: "cnfy-dash-badge cnfy-dash-badge--green", children: "Idle" }) })
|
|
1609
1918
|
] }),
|
|
1610
|
-
scrapeStatus.lastRun && /* @__PURE__ */
|
|
1611
|
-
/* @__PURE__ */
|
|
1612
|
-
/* @__PURE__ */
|
|
1919
|
+
scrapeStatus.lastRun && /* @__PURE__ */ jsxs6("div", { children: [
|
|
1920
|
+
/* @__PURE__ */ jsx6("span", { className: "cnfy-dash-label-muted", children: "Last Run:" }),
|
|
1921
|
+
/* @__PURE__ */ jsx6("p", { className: "cnfy-dash-label-bold", children: new Date(scrapeStatus.lastRun).toLocaleString() })
|
|
1613
1922
|
] }),
|
|
1614
|
-
scrapeStatus.totalScraped !== void 0 && /* @__PURE__ */
|
|
1615
|
-
/* @__PURE__ */
|
|
1616
|
-
/* @__PURE__ */
|
|
1923
|
+
scrapeStatus.totalScraped !== void 0 && /* @__PURE__ */ jsxs6("div", { children: [
|
|
1924
|
+
/* @__PURE__ */ jsx6("span", { className: "cnfy-dash-label-muted", children: "Total Scraped:" }),
|
|
1925
|
+
/* @__PURE__ */ jsxs6("p", { className: "cnfy-dash-label-bold", children: [
|
|
1617
1926
|
scrapeStatus.totalScraped.toLocaleString(),
|
|
1618
1927
|
" articles"
|
|
1619
1928
|
] })
|
|
1620
1929
|
] })
|
|
1621
1930
|
] }),
|
|
1622
|
-
/* @__PURE__ */
|
|
1623
|
-
/* @__PURE__ */
|
|
1931
|
+
/* @__PURE__ */ jsxs6("div", { children: [
|
|
1932
|
+
/* @__PURE__ */ jsxs6("p", { className: "cnfy-dash-info-text", children: [
|
|
1624
1933
|
"Current configuration: ",
|
|
1625
|
-
/* @__PURE__ */
|
|
1626
|
-
|
|
1934
|
+
/* @__PURE__ */ jsx6("strong", { children: state == null ? void 0 : state.value }),
|
|
1935
|
+
cities.length > 0 && /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
1627
1936
|
" - ",
|
|
1628
|
-
|
|
1937
|
+
cities.map((c) => c.label).join(", ")
|
|
1629
1938
|
] })
|
|
1630
1939
|
] }),
|
|
1631
|
-
/* @__PURE__ */
|
|
1940
|
+
/* @__PURE__ */ jsx6(
|
|
1632
1941
|
"button",
|
|
1633
1942
|
{
|
|
1634
1943
|
onClick: handleScrape,
|
|
@@ -1640,48 +1949,54 @@ function PreferencesPage() {
|
|
|
1640
1949
|
)
|
|
1641
1950
|
] })
|
|
1642
1951
|
] }),
|
|
1643
|
-
/* @__PURE__ */
|
|
1644
|
-
/* @__PURE__ */
|
|
1645
|
-
/* @__PURE__ */
|
|
1646
|
-
/* @__PURE__ */
|
|
1647
|
-
/* @__PURE__ */
|
|
1648
|
-
|
|
1952
|
+
/* @__PURE__ */ jsxs6("div", { className: "cnfy-dash-card", children: [
|
|
1953
|
+
/* @__PURE__ */ jsx6("h2", { className: "cnfy-dash-card-title", children: "Localization Settings" }),
|
|
1954
|
+
/* @__PURE__ */ jsxs6("div", { className: "cnfy-dash-field", children: [
|
|
1955
|
+
/* @__PURE__ */ jsx6("label", { className: "cnfy-dash-label", children: "State" }),
|
|
1956
|
+
/* @__PURE__ */ jsxs6(
|
|
1957
|
+
"select",
|
|
1649
1958
|
{
|
|
1650
|
-
|
|
1651
|
-
value: state,
|
|
1652
|
-
onChange: (
|
|
1653
|
-
|
|
1959
|
+
className: "cnfy-dash-input",
|
|
1960
|
+
value: (state == null ? void 0 : state.value) || "",
|
|
1961
|
+
onChange: (e) => setState(STATE_OPTIONS.find((o) => o.value === e.target.value) || null),
|
|
1962
|
+
children: [
|
|
1963
|
+
/* @__PURE__ */ jsx6("option", { value: "", children: "Select state" }),
|
|
1964
|
+
STATE_OPTIONS.map((opt) => /* @__PURE__ */ jsx6("option", { value: opt.value, children: opt.label }, opt.value))
|
|
1965
|
+
]
|
|
1654
1966
|
}
|
|
1655
1967
|
)
|
|
1656
1968
|
] }),
|
|
1657
|
-
/* @__PURE__ */
|
|
1658
|
-
/* @__PURE__ */
|
|
1659
|
-
/* @__PURE__ */
|
|
1660
|
-
|
|
1969
|
+
/* @__PURE__ */ jsxs6("div", { className: "cnfy-dash-field", children: [
|
|
1970
|
+
/* @__PURE__ */ jsx6("label", { className: "cnfy-dash-label", children: "Cities" }),
|
|
1971
|
+
/* @__PURE__ */ jsx6(
|
|
1972
|
+
"select",
|
|
1661
1973
|
{
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
value: cities,
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1974
|
+
multiple: true,
|
|
1975
|
+
className: "cnfy-dash-input",
|
|
1976
|
+
value: cities.map((c) => c.value),
|
|
1977
|
+
disabled: !state,
|
|
1978
|
+
onChange: (e) => setCities(Array.from(e.target.selectedOptions).map((o) => ({ value: o.value, label: o.text }))),
|
|
1979
|
+
children: CITY_OPTIONS.map((opt) => /* @__PURE__ */ jsx6("option", { value: opt.value, children: opt.label }, opt.value))
|
|
1668
1980
|
}
|
|
1669
1981
|
)
|
|
1670
1982
|
] }),
|
|
1671
|
-
/* @__PURE__ */
|
|
1672
|
-
/* @__PURE__ */
|
|
1673
|
-
/* @__PURE__ */
|
|
1674
|
-
|
|
1983
|
+
/* @__PURE__ */ jsxs6("div", { className: "cnfy-dash-field", children: [
|
|
1984
|
+
/* @__PURE__ */ jsx6("label", { className: "cnfy-dash-label", children: "Language" }),
|
|
1985
|
+
/* @__PURE__ */ jsxs6(
|
|
1986
|
+
"select",
|
|
1675
1987
|
{
|
|
1676
|
-
|
|
1677
|
-
value: language,
|
|
1678
|
-
onChange: (
|
|
1679
|
-
|
|
1988
|
+
className: "cnfy-dash-input",
|
|
1989
|
+
value: (language == null ? void 0 : language.value) || "",
|
|
1990
|
+
onChange: (e) => setLanguage(LANGUAGE_OPTIONS.find((o) => o.value === e.target.value) || null),
|
|
1991
|
+
children: [
|
|
1992
|
+
/* @__PURE__ */ jsx6("option", { value: "", children: "Select language" }),
|
|
1993
|
+
LANGUAGE_OPTIONS.map((opt) => /* @__PURE__ */ jsx6("option", { value: opt.value, children: opt.label }, opt.value))
|
|
1994
|
+
]
|
|
1680
1995
|
}
|
|
1681
1996
|
)
|
|
1682
1997
|
] })
|
|
1683
1998
|
] }),
|
|
1684
|
-
/* @__PURE__ */
|
|
1999
|
+
/* @__PURE__ */ jsx6("div", { className: "cnfy-dash-actions", children: /* @__PURE__ */ jsx6(
|
|
1685
2000
|
"button",
|
|
1686
2001
|
{
|
|
1687
2002
|
onClick: handleSave,
|
|
@@ -1694,13 +2009,307 @@ function PreferencesPage() {
|
|
|
1694
2009
|
] });
|
|
1695
2010
|
}
|
|
1696
2011
|
|
|
2012
|
+
// components/history/HistoryPanel.tsx
|
|
2013
|
+
import { useCallback as useCallback2, useEffect as useEffect6, useState as useState5 } from "react";
|
|
2014
|
+
import { Trash2, Loader2 as Loader23, ChevronDown as ChevronDown3, AlertCircle, FileText as FileText2, SquarePen, History, PanelLeftOpen, PanelLeftClose } from "lucide-react";
|
|
2015
|
+
|
|
2016
|
+
// services/history.service.ts
|
|
2017
|
+
function getHeaders() {
|
|
2018
|
+
const token = typeof window !== "undefined" ? localStorage.getItem("token") : null;
|
|
2019
|
+
const apiKey = getApiKey();
|
|
2020
|
+
return __spreadValues(__spreadValues({
|
|
2021
|
+
"Content-Type": "application/json"
|
|
2022
|
+
}, token ? { Authorization: `Bearer ${token}` } : {}), apiKey ? { "x-api-key": apiKey } : {});
|
|
2023
|
+
}
|
|
2024
|
+
async function getHistory(page = 1, limit = 20) {
|
|
2025
|
+
const apiUrl = getApiBaseUrl();
|
|
2026
|
+
const res = await fetch(`${apiUrl}/contenify/history?page=${page}&limit=${limit}`, {
|
|
2027
|
+
credentials: "include",
|
|
2028
|
+
headers: getHeaders()
|
|
2029
|
+
});
|
|
2030
|
+
if (!res.ok) throw new Error("Failed to fetch history");
|
|
2031
|
+
const { data } = await res.json();
|
|
2032
|
+
return data;
|
|
2033
|
+
}
|
|
2034
|
+
async function getHistoryItem(id) {
|
|
2035
|
+
const apiUrl = getApiBaseUrl();
|
|
2036
|
+
const res = await fetch(`${apiUrl}/contenify/history/${id}`, {
|
|
2037
|
+
credentials: "include",
|
|
2038
|
+
headers: getHeaders()
|
|
2039
|
+
});
|
|
2040
|
+
if (!res.ok) throw new Error("Failed to fetch history item");
|
|
2041
|
+
const { data } = await res.json();
|
|
2042
|
+
return data;
|
|
2043
|
+
}
|
|
2044
|
+
async function deleteHistoryItem(id) {
|
|
2045
|
+
const apiUrl = getApiBaseUrl();
|
|
2046
|
+
const res = await fetch(`${apiUrl}/contenify/history/${id}`, {
|
|
2047
|
+
method: "DELETE",
|
|
2048
|
+
credentials: "include",
|
|
2049
|
+
headers: getHeaders()
|
|
2050
|
+
});
|
|
2051
|
+
if (!res.ok) throw new Error("Failed to delete history item");
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
// components/history/HistoryPanel.tsx
|
|
2055
|
+
import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2056
|
+
function formatDate(iso) {
|
|
2057
|
+
const d = new Date(iso);
|
|
2058
|
+
const now = /* @__PURE__ */ new Date();
|
|
2059
|
+
const diffMs = now.getTime() - d.getTime();
|
|
2060
|
+
const diffDays = Math.floor(diffMs / 864e5);
|
|
2061
|
+
if (diffDays === 0) return "Today";
|
|
2062
|
+
if (diffDays === 1) return "Yesterday";
|
|
2063
|
+
if (diffDays < 7) return `${diffDays} days ago`;
|
|
2064
|
+
return d.toLocaleDateString(void 0, { month: "short", day: "numeric" });
|
|
2065
|
+
}
|
|
2066
|
+
function groupByDate(items) {
|
|
2067
|
+
const groups = {};
|
|
2068
|
+
for (const item of items) {
|
|
2069
|
+
const label = formatDate(item.createdAt);
|
|
2070
|
+
if (!groups[label]) groups[label] = [];
|
|
2071
|
+
groups[label].push(item);
|
|
2072
|
+
}
|
|
2073
|
+
return Object.entries(groups).map(([label, items2]) => ({ label, items: items2 }));
|
|
2074
|
+
}
|
|
2075
|
+
function HistoryPanel({ onSelect, onNewChat }) {
|
|
2076
|
+
const [collapsed, setCollapsed] = useState5(false);
|
|
2077
|
+
const [items, setItems] = useState5([]);
|
|
2078
|
+
const [pagination, setPagination] = useState5(null);
|
|
2079
|
+
const [page, setPage] = useState5(1);
|
|
2080
|
+
const [loading, setLoading] = useState5(false);
|
|
2081
|
+
const [loadingMore, setLoadingMore] = useState5(false);
|
|
2082
|
+
const [error, setError] = useState5(null);
|
|
2083
|
+
const [deletingId, setDeletingId] = useState5(null);
|
|
2084
|
+
const [selectingId, setSelectingId] = useState5(null);
|
|
2085
|
+
const [hoveredId, setHoveredId] = useState5(null);
|
|
2086
|
+
const fetchPage = useCallback2(async (p, replace) => {
|
|
2087
|
+
if (p === 1) setLoading(true);
|
|
2088
|
+
else setLoadingMore(true);
|
|
2089
|
+
setError(null);
|
|
2090
|
+
try {
|
|
2091
|
+
const result = await getHistory(p, 20);
|
|
2092
|
+
setItems((prev) => replace ? result.items : [...prev, ...result.items]);
|
|
2093
|
+
setPagination(result.pagination);
|
|
2094
|
+
setPage(p);
|
|
2095
|
+
} catch (e) {
|
|
2096
|
+
setError("Failed to load history");
|
|
2097
|
+
} finally {
|
|
2098
|
+
setLoading(false);
|
|
2099
|
+
setLoadingMore(false);
|
|
2100
|
+
}
|
|
2101
|
+
}, []);
|
|
2102
|
+
useEffect6(() => {
|
|
2103
|
+
fetchPage(1, true);
|
|
2104
|
+
}, [fetchPage]);
|
|
2105
|
+
const handleDelete = async (e, id) => {
|
|
2106
|
+
e.stopPropagation();
|
|
2107
|
+
setDeletingId(id);
|
|
2108
|
+
try {
|
|
2109
|
+
await deleteHistoryItem(id);
|
|
2110
|
+
setItems((prev) => prev.filter((i) => i._id !== id));
|
|
2111
|
+
setPagination((prev) => prev ? __spreadProps(__spreadValues({}, prev), { total: prev.total - 1 }) : prev);
|
|
2112
|
+
} catch (e2) {
|
|
2113
|
+
} finally {
|
|
2114
|
+
setDeletingId(null);
|
|
2115
|
+
}
|
|
2116
|
+
};
|
|
2117
|
+
const handleSelect = async (id) => {
|
|
2118
|
+
setSelectingId(id);
|
|
2119
|
+
try {
|
|
2120
|
+
await onSelect(id);
|
|
2121
|
+
setCollapsed(true);
|
|
2122
|
+
} finally {
|
|
2123
|
+
setSelectingId(null);
|
|
2124
|
+
}
|
|
2125
|
+
};
|
|
2126
|
+
const hasMore = pagination ? page < pagination.pages : false;
|
|
2127
|
+
const groups = groupByDate(items);
|
|
2128
|
+
if (collapsed) {
|
|
2129
|
+
return /* @__PURE__ */ jsxs7("div", { className: "cnfy-history-panel cnfy-history-panel--collapsed", children: [
|
|
2130
|
+
/* @__PURE__ */ jsx7(
|
|
2131
|
+
"button",
|
|
2132
|
+
{
|
|
2133
|
+
className: "cnfy-history-icon-btn",
|
|
2134
|
+
onClick: () => setCollapsed(false),
|
|
2135
|
+
title: "Expand history",
|
|
2136
|
+
children: /* @__PURE__ */ jsx7(PanelLeftOpen, { size: 18 })
|
|
2137
|
+
}
|
|
2138
|
+
),
|
|
2139
|
+
/* @__PURE__ */ jsx7(
|
|
2140
|
+
"button",
|
|
2141
|
+
{
|
|
2142
|
+
className: "cnfy-history-icon-btn",
|
|
2143
|
+
onClick: onNewChat,
|
|
2144
|
+
title: "New chat",
|
|
2145
|
+
children: /* @__PURE__ */ jsx7(SquarePen, { size: 18 })
|
|
2146
|
+
}
|
|
2147
|
+
),
|
|
2148
|
+
/* @__PURE__ */ jsx7("div", { className: "cnfy-history-icon-divider" }),
|
|
2149
|
+
/* @__PURE__ */ jsx7(
|
|
2150
|
+
"button",
|
|
2151
|
+
{
|
|
2152
|
+
className: "cnfy-history-icon-btn",
|
|
2153
|
+
onClick: () => setCollapsed(false),
|
|
2154
|
+
title: "History",
|
|
2155
|
+
children: /* @__PURE__ */ jsx7(History, { size: 18 })
|
|
2156
|
+
}
|
|
2157
|
+
)
|
|
2158
|
+
] });
|
|
2159
|
+
}
|
|
2160
|
+
return /* @__PURE__ */ jsxs7("div", { className: "cnfy-history-panel", children: [
|
|
2161
|
+
/* @__PURE__ */ jsxs7("div", { className: "cnfy-history-header", children: [
|
|
2162
|
+
/* @__PURE__ */ jsx7(
|
|
2163
|
+
"button",
|
|
2164
|
+
{
|
|
2165
|
+
className: "cnfy-history-icon-btn",
|
|
2166
|
+
onClick: () => setCollapsed(true),
|
|
2167
|
+
title: "Collapse sidebar",
|
|
2168
|
+
children: /* @__PURE__ */ jsx7(PanelLeftClose, { size: 18 })
|
|
2169
|
+
}
|
|
2170
|
+
),
|
|
2171
|
+
/* @__PURE__ */ jsx7(
|
|
2172
|
+
"button",
|
|
2173
|
+
{
|
|
2174
|
+
className: "cnfy-history-icon-btn",
|
|
2175
|
+
onClick: onNewChat,
|
|
2176
|
+
title: "New chat",
|
|
2177
|
+
children: /* @__PURE__ */ jsx7(SquarePen, { size: 18 })
|
|
2178
|
+
}
|
|
2179
|
+
)
|
|
2180
|
+
] }),
|
|
2181
|
+
/* @__PURE__ */ jsxs7("div", { className: "cnfy-history-body", children: [
|
|
2182
|
+
loading && /* @__PURE__ */ jsx7("div", { className: "cnfy-history-loading", children: /* @__PURE__ */ jsx7(Loader23, { size: 18, className: "cnfy-animate-spin" }) }),
|
|
2183
|
+
!loading && error && /* @__PURE__ */ jsxs7("div", { className: "cnfy-history-error", children: [
|
|
2184
|
+
/* @__PURE__ */ jsx7(AlertCircle, { size: 15 }),
|
|
2185
|
+
/* @__PURE__ */ jsx7("span", { children: error }),
|
|
2186
|
+
/* @__PURE__ */ jsx7("button", { onClick: () => fetchPage(1, true), className: "cnfy-history-retry-btn", children: "Retry" })
|
|
2187
|
+
] }),
|
|
2188
|
+
!loading && !error && items.length === 0 && /* @__PURE__ */ jsxs7("div", { className: "cnfy-history-empty", children: [
|
|
2189
|
+
/* @__PURE__ */ jsx7(FileText2, { size: 24, className: "cnfy-history-empty-icon" }),
|
|
2190
|
+
/* @__PURE__ */ jsx7("p", { children: "No history yet" })
|
|
2191
|
+
] }),
|
|
2192
|
+
!loading && groups.map((group) => /* @__PURE__ */ jsxs7("div", { className: "cnfy-history-group", children: [
|
|
2193
|
+
/* @__PURE__ */ jsx7("div", { className: "cnfy-history-group-label", children: group.label }),
|
|
2194
|
+
group.items.map((item) => /* @__PURE__ */ jsxs7(
|
|
2195
|
+
"div",
|
|
2196
|
+
{
|
|
2197
|
+
className: `cnfy-history-item${selectingId ? " cnfy-history-item--disabled" : ""}`,
|
|
2198
|
+
onClick: () => !selectingId && handleSelect(item._id),
|
|
2199
|
+
onMouseEnter: () => setHoveredId(item._id),
|
|
2200
|
+
onMouseLeave: () => setHoveredId(null),
|
|
2201
|
+
role: "button",
|
|
2202
|
+
tabIndex: 0,
|
|
2203
|
+
onKeyDown: (e) => e.key === "Enter" && !selectingId && handleSelect(item._id),
|
|
2204
|
+
children: [
|
|
2205
|
+
/* @__PURE__ */ jsxs7("span", { className: "cnfy-history-item-title", children: [
|
|
2206
|
+
selectingId === item._id ? /* @__PURE__ */ jsx7(Loader23, { size: 12, className: "cnfy-animate-spin cnfy-history-item-spinner" }) : null,
|
|
2207
|
+
item.title || "Untitled"
|
|
2208
|
+
] }),
|
|
2209
|
+
(hoveredId === item._id || deletingId === item._id) && /* @__PURE__ */ jsx7(
|
|
2210
|
+
"button",
|
|
2211
|
+
{
|
|
2212
|
+
className: "cnfy-history-delete-btn",
|
|
2213
|
+
onClick: (e) => handleDelete(e, item._id),
|
|
2214
|
+
disabled: deletingId === item._id,
|
|
2215
|
+
title: "Delete",
|
|
2216
|
+
children: deletingId === item._id ? /* @__PURE__ */ jsx7(Loader23, { size: 12, className: "cnfy-animate-spin" }) : /* @__PURE__ */ jsx7(Trash2, { size: 12 })
|
|
2217
|
+
}
|
|
2218
|
+
)
|
|
2219
|
+
]
|
|
2220
|
+
},
|
|
2221
|
+
item._id
|
|
2222
|
+
))
|
|
2223
|
+
] }, group.label)),
|
|
2224
|
+
hasMore && !loading && /* @__PURE__ */ jsx7(
|
|
2225
|
+
"button",
|
|
2226
|
+
{
|
|
2227
|
+
onClick: () => fetchPage(page + 1, false),
|
|
2228
|
+
disabled: loadingMore,
|
|
2229
|
+
className: "cnfy-history-load-more",
|
|
2230
|
+
children: loadingMore ? /* @__PURE__ */ jsxs7(Fragment4, { children: [
|
|
2231
|
+
/* @__PURE__ */ jsx7(Loader23, { size: 13, className: "cnfy-animate-spin" }),
|
|
2232
|
+
" Loading\u2026"
|
|
2233
|
+
] }) : /* @__PURE__ */ jsxs7(Fragment4, { children: [
|
|
2234
|
+
/* @__PURE__ */ jsx7(ChevronDown3, { size: 13 }),
|
|
2235
|
+
" Load more"
|
|
2236
|
+
] })
|
|
2237
|
+
}
|
|
2238
|
+
)
|
|
2239
|
+
] })
|
|
2240
|
+
] });
|
|
2241
|
+
}
|
|
2242
|
+
|
|
1697
2243
|
// services/chat.service.ts
|
|
2244
|
+
function getHeaders2() {
|
|
2245
|
+
const apiKey = getApiKey();
|
|
2246
|
+
const headers = { "Content-Type": "application/json" };
|
|
2247
|
+
if (apiKey) headers["x-api-key"] = apiKey;
|
|
2248
|
+
if (typeof window !== "undefined") {
|
|
2249
|
+
const token = localStorage.getItem("token");
|
|
2250
|
+
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
2251
|
+
}
|
|
2252
|
+
return headers;
|
|
2253
|
+
}
|
|
2254
|
+
async function processSSEStream(reader, callbacks) {
|
|
2255
|
+
const decoder = new TextDecoder();
|
|
2256
|
+
let buffer = "";
|
|
2257
|
+
const processLines = (lines) => {
|
|
2258
|
+
var _a, _b, _c, _d, _e, _f;
|
|
2259
|
+
for (const line of lines) {
|
|
2260
|
+
const trimmed = line.trim();
|
|
2261
|
+
if (!trimmed.startsWith("data: ")) continue;
|
|
2262
|
+
const data = trimmed.slice(6).trim();
|
|
2263
|
+
try {
|
|
2264
|
+
const event = JSON.parse(data);
|
|
2265
|
+
switch (event.type) {
|
|
2266
|
+
case "start":
|
|
2267
|
+
break;
|
|
2268
|
+
// Structured streaming (articleId present): field-scoped chunks
|
|
2269
|
+
case "field_start":
|
|
2270
|
+
(_a = callbacks.onFieldStart) == null ? void 0 : _a.call(callbacks, event.field);
|
|
2271
|
+
break;
|
|
2272
|
+
case "content":
|
|
2273
|
+
callbacks.onContent(event.field, event.content);
|
|
2274
|
+
break;
|
|
2275
|
+
case "keywords":
|
|
2276
|
+
(_b = callbacks.onKeywords) == null ? void 0 : _b.call(callbacks, event.content);
|
|
2277
|
+
break;
|
|
2278
|
+
case "field_end":
|
|
2279
|
+
(_c = callbacks.onFieldEnd) == null ? void 0 : _c.call(callbacks, event.field);
|
|
2280
|
+
break;
|
|
2281
|
+
case "done":
|
|
2282
|
+
(_d = callbacks.onComplete) == null ? void 0 : _d.call(callbacks, event.data);
|
|
2283
|
+
return true;
|
|
2284
|
+
// Plain token streaming (no articleId): route directly to article body
|
|
2285
|
+
case "token":
|
|
2286
|
+
callbacks.onContent("article", (_e = event.content) != null ? _e : "");
|
|
2287
|
+
break;
|
|
2288
|
+
case "error":
|
|
2289
|
+
(_f = callbacks.onError) == null ? void 0 : _f.call(callbacks, new Error(event.message));
|
|
2290
|
+
return true;
|
|
2291
|
+
}
|
|
2292
|
+
} catch (e) {
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
return false;
|
|
2296
|
+
};
|
|
2297
|
+
while (true) {
|
|
2298
|
+
const { done, value } = await reader.read();
|
|
2299
|
+
if (done) break;
|
|
2300
|
+
buffer += decoder.decode(value, { stream: true });
|
|
2301
|
+
const lines = buffer.split("\n");
|
|
2302
|
+
buffer = lines.pop() || "";
|
|
2303
|
+
if (processLines(lines)) return;
|
|
2304
|
+
}
|
|
2305
|
+
if (buffer.trim()) processLines([buffer]);
|
|
2306
|
+
}
|
|
1698
2307
|
var analyzeInputApi = async (input) => {
|
|
1699
|
-
const { data } = await api_default.post("/
|
|
2308
|
+
const { data } = await api_default.post("/contenify/analyze-input", { input });
|
|
1700
2309
|
return data.data;
|
|
1701
2310
|
};
|
|
1702
2311
|
var createContentApi = async (payload) => {
|
|
1703
|
-
const { data } = await api_default.post("/
|
|
2312
|
+
const { data } = await api_default.post("/contenify/create-content", payload);
|
|
1704
2313
|
return data.data;
|
|
1705
2314
|
};
|
|
1706
2315
|
var createContentStreamApi = async ({
|
|
@@ -1708,22 +2317,18 @@ var createContentStreamApi = async ({
|
|
|
1708
2317
|
content,
|
|
1709
2318
|
actionId,
|
|
1710
2319
|
settings,
|
|
1711
|
-
|
|
2320
|
+
onFieldStart,
|
|
2321
|
+
onContent,
|
|
2322
|
+
onKeywords,
|
|
2323
|
+
onFieldEnd,
|
|
1712
2324
|
onComplete,
|
|
1713
2325
|
onError
|
|
1714
2326
|
}) => {
|
|
1715
2327
|
var _a;
|
|
1716
2328
|
const API_BASE_URL = getApiBaseUrl();
|
|
1717
|
-
const
|
|
1718
|
-
const headers = {
|
|
1719
|
-
"Content-Type": "application/json"
|
|
1720
|
-
};
|
|
1721
|
-
if (apiKey) {
|
|
1722
|
-
headers["x-api-key"] = apiKey;
|
|
1723
|
-
}
|
|
1724
|
-
const response = await fetch(`${API_BASE_URL}/chat/create-content/stream`, {
|
|
2329
|
+
const response = await fetch(`${API_BASE_URL}/contenify/create-content/stream`, {
|
|
1725
2330
|
method: "POST",
|
|
1726
|
-
headers,
|
|
2331
|
+
headers: getHeaders2(),
|
|
1727
2332
|
credentials: "include",
|
|
1728
2333
|
body: JSON.stringify({ url, content, actionId, settings })
|
|
1729
2334
|
});
|
|
@@ -1732,50 +2337,8 @@ var createContentStreamApi = async ({
|
|
|
1732
2337
|
throw new Error(`HTTP error! status: ${response.status} - ${errorText}`);
|
|
1733
2338
|
}
|
|
1734
2339
|
const reader = (_a = response.body) == null ? void 0 : _a.getReader();
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
throw new Error("No reader available");
|
|
1738
|
-
}
|
|
1739
|
-
let buffer = "";
|
|
1740
|
-
let hasReceivedData = false;
|
|
1741
|
-
while (true) {
|
|
1742
|
-
const { done, value } = await reader.read();
|
|
1743
|
-
if (done) {
|
|
1744
|
-
if (hasReceivedData) onComplete == null ? void 0 : onComplete();
|
|
1745
|
-
break;
|
|
1746
|
-
}
|
|
1747
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
1748
|
-
buffer += chunk;
|
|
1749
|
-
const lines = buffer.split("\n");
|
|
1750
|
-
buffer = lines.pop() || "";
|
|
1751
|
-
for (const line of lines) {
|
|
1752
|
-
const trimmedLine = line.trim();
|
|
1753
|
-
if (trimmedLine === "") continue;
|
|
1754
|
-
hasReceivedData = true;
|
|
1755
|
-
if (trimmedLine.startsWith("data: ")) {
|
|
1756
|
-
const data = trimmedLine.slice(6).trim();
|
|
1757
|
-
if (data === "[DONE]") {
|
|
1758
|
-
onComplete == null ? void 0 : onComplete();
|
|
1759
|
-
return;
|
|
1760
|
-
}
|
|
1761
|
-
try {
|
|
1762
|
-
const parsed = JSON.parse(data);
|
|
1763
|
-
if (parsed.content) onChunk(parsed.content);
|
|
1764
|
-
else if (parsed.delta) onChunk(parsed.delta);
|
|
1765
|
-
else if (parsed.text) onChunk(parsed.text);
|
|
1766
|
-
else if (parsed.chunk) onChunk(parsed.chunk);
|
|
1767
|
-
else if (typeof parsed === "string") onChunk(parsed);
|
|
1768
|
-
} catch (e) {
|
|
1769
|
-
if (data) onChunk(data);
|
|
1770
|
-
}
|
|
1771
|
-
} else if (trimmedLine) {
|
|
1772
|
-
onChunk(trimmedLine + "\n");
|
|
1773
|
-
}
|
|
1774
|
-
}
|
|
1775
|
-
}
|
|
1776
|
-
if (!hasReceivedData) {
|
|
1777
|
-
throw new Error("No data received from stream");
|
|
1778
|
-
}
|
|
2340
|
+
if (!reader) throw new Error("No reader available");
|
|
2341
|
+
await processSSEStream(reader, { onFieldStart, onContent, onKeywords, onFieldEnd, onComplete, onError });
|
|
1779
2342
|
};
|
|
1780
2343
|
var rewriteNewsStreamApi = async ({
|
|
1781
2344
|
article,
|
|
@@ -1787,18 +2350,16 @@ var rewriteNewsStreamApi = async ({
|
|
|
1787
2350
|
includeQuotes,
|
|
1788
2351
|
includeFAQ,
|
|
1789
2352
|
targetAudience,
|
|
1790
|
-
|
|
2353
|
+
onFieldStart,
|
|
2354
|
+
onContent,
|
|
2355
|
+
onKeywords,
|
|
2356
|
+
onFieldEnd,
|
|
1791
2357
|
onComplete,
|
|
1792
2358
|
onError
|
|
1793
2359
|
}) => {
|
|
1794
2360
|
var _a;
|
|
1795
|
-
const payload = {
|
|
1796
|
-
|
|
1797
|
-
language
|
|
1798
|
-
};
|
|
1799
|
-
if (articleId) {
|
|
1800
|
-
payload.articleId = articleId;
|
|
1801
|
-
}
|
|
2361
|
+
const payload = { article, language, temperature: 1 };
|
|
2362
|
+
if (articleId) payload.articleId = articleId;
|
|
1802
2363
|
if (tone) payload.tone = tone;
|
|
1803
2364
|
if (style) payload.style = style;
|
|
1804
2365
|
if (wordCount) payload.wordCount = wordCount;
|
|
@@ -1807,103 +2368,69 @@ var rewriteNewsStreamApi = async ({
|
|
|
1807
2368
|
if (targetAudience) payload.targetAudience = targetAudience;
|
|
1808
2369
|
try {
|
|
1809
2370
|
const API_BASE_URL = getApiBaseUrl();
|
|
1810
|
-
const
|
|
1811
|
-
const headers = {
|
|
1812
|
-
"Content-Type": "application/json"
|
|
1813
|
-
};
|
|
1814
|
-
if (apiKey) {
|
|
1815
|
-
headers["x-api-key"] = apiKey;
|
|
1816
|
-
}
|
|
1817
|
-
const response = await fetch(`${API_BASE_URL}/chatboat/rewrite/stream`, {
|
|
2371
|
+
const response = await fetch(`${API_BASE_URL}/contenify/rewrite/stream`, {
|
|
1818
2372
|
method: "POST",
|
|
1819
|
-
headers,
|
|
2373
|
+
headers: getHeaders2(),
|
|
1820
2374
|
credentials: "include",
|
|
1821
2375
|
body: JSON.stringify(payload)
|
|
1822
2376
|
});
|
|
1823
|
-
console.log("\u{1F4E1} Response status:", response.status);
|
|
1824
|
-
console.log("\u{1F4E1} Response headers:", Object.fromEntries(response.headers.entries()));
|
|
1825
2377
|
if (!response.ok) {
|
|
1826
2378
|
const errorText = await response.text();
|
|
1827
|
-
console.error("\u274C API Error:", response.status, errorText);
|
|
1828
2379
|
throw new Error(`HTTP error! status: ${response.status} - ${errorText}`);
|
|
1829
2380
|
}
|
|
1830
|
-
const
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
}
|
|
1843
|
-
break;
|
|
1844
|
-
}
|
|
1845
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
1846
|
-
buffer += chunk;
|
|
1847
|
-
const lines = buffer.split("\n");
|
|
1848
|
-
buffer = lines.pop() || "";
|
|
1849
|
-
for (const line of lines) {
|
|
1850
|
-
const trimmedLine = line.trim();
|
|
1851
|
-
if (trimmedLine === "") continue;
|
|
1852
|
-
hasReceivedData = true;
|
|
1853
|
-
console.log("\u{1F4E8} Received line:", trimmedLine.substring(0, 100) + (trimmedLine.length > 100 ? "..." : ""));
|
|
1854
|
-
if (trimmedLine.startsWith("data: ")) {
|
|
1855
|
-
const data = trimmedLine.slice(6).trim();
|
|
1856
|
-
if (data === "[DONE]") {
|
|
1857
|
-
console.log("\u2705 Stream completed with [DONE] signal");
|
|
1858
|
-
onComplete == null ? void 0 : onComplete();
|
|
1859
|
-
return;
|
|
1860
|
-
}
|
|
1861
|
-
try {
|
|
1862
|
-
const parsed = JSON.parse(data);
|
|
1863
|
-
console.log("\u{1F4CB} Parsed JSON:", parsed);
|
|
1864
|
-
if (parsed.content) {
|
|
1865
|
-
onChunk(parsed.content);
|
|
1866
|
-
} else if (parsed.delta) {
|
|
1867
|
-
onChunk(parsed.delta);
|
|
1868
|
-
} else if (parsed.text) {
|
|
1869
|
-
onChunk(parsed.text);
|
|
1870
|
-
} else if (parsed.chunk) {
|
|
1871
|
-
onChunk(parsed.chunk);
|
|
1872
|
-
} else if (typeof parsed === "string") {
|
|
1873
|
-
onChunk(parsed);
|
|
1874
|
-
} else {
|
|
1875
|
-
console.warn("\u26A0\uFE0F Unknown JSON format:", parsed);
|
|
1876
|
-
}
|
|
1877
|
-
} catch (e) {
|
|
1878
|
-
console.log("\u{1F4DD} Non-JSON data, treating as plain text");
|
|
1879
|
-
if (data) {
|
|
1880
|
-
onChunk(data);
|
|
1881
|
-
}
|
|
1882
|
-
}
|
|
1883
|
-
} else {
|
|
1884
|
-
console.log("\u{1F4C4} Plain text chunk");
|
|
1885
|
-
if (trimmedLine) {
|
|
1886
|
-
onChunk(trimmedLine + "\n");
|
|
1887
|
-
}
|
|
1888
|
-
}
|
|
1889
|
-
}
|
|
1890
|
-
}
|
|
1891
|
-
if (!hasReceivedData) {
|
|
1892
|
-
console.error("\u274C No data received from stream");
|
|
1893
|
-
throw new Error("No data received from stream");
|
|
2381
|
+
const contentType = response.headers.get("content-type") || "";
|
|
2382
|
+
if (contentType.includes("application/json")) {
|
|
2383
|
+
const json = await response.json();
|
|
2384
|
+
const d = json.data || json;
|
|
2385
|
+
onComplete == null ? void 0 : onComplete({
|
|
2386
|
+
title: d.title || "",
|
|
2387
|
+
subtitle: d.subtitle || "",
|
|
2388
|
+
article: d.article || "",
|
|
2389
|
+
metaDescription: d.metaDescription || "",
|
|
2390
|
+
metaKeywords: Array.isArray(d.metaKeywords) ? d.metaKeywords : []
|
|
2391
|
+
});
|
|
2392
|
+
return;
|
|
1894
2393
|
}
|
|
1895
|
-
|
|
2394
|
+
const reader = (_a = response.body) == null ? void 0 : _a.getReader();
|
|
2395
|
+
if (!reader) throw new Error("No reader available");
|
|
2396
|
+
await processSSEStream(reader, { onFieldStart, onContent, onKeywords, onFieldEnd, onComplete, onError });
|
|
1896
2397
|
} catch (error) {
|
|
1897
|
-
|
|
1898
|
-
console.error("Error details:", {
|
|
1899
|
-
message: error.message,
|
|
1900
|
-
name: error.name,
|
|
1901
|
-
stack: error.stack
|
|
1902
|
-
});
|
|
1903
|
-
onError == null ? void 0 : onError(error);
|
|
2398
|
+
onError == null ? void 0 : onError(error instanceof Error ? error : new Error(String(error)));
|
|
1904
2399
|
throw error;
|
|
1905
2400
|
}
|
|
1906
2401
|
};
|
|
2402
|
+
var resolveSeoStreamApi = async ({
|
|
2403
|
+
historyId,
|
|
2404
|
+
title,
|
|
2405
|
+
subtitle,
|
|
2406
|
+
metaDescription,
|
|
2407
|
+
metaKeywords,
|
|
2408
|
+
article,
|
|
2409
|
+
language,
|
|
2410
|
+
seoAnalysis,
|
|
2411
|
+
onFieldStart,
|
|
2412
|
+
onContent,
|
|
2413
|
+
onKeywords,
|
|
2414
|
+
onFieldEnd,
|
|
2415
|
+
onComplete,
|
|
2416
|
+
onError
|
|
2417
|
+
}) => {
|
|
2418
|
+
var _a;
|
|
2419
|
+
const API_BASE_URL = getApiBaseUrl();
|
|
2420
|
+
const response = await fetch(`${API_BASE_URL}/seo-engine/resolve/stream`, {
|
|
2421
|
+
method: "POST",
|
|
2422
|
+
headers: getHeaders2(),
|
|
2423
|
+
credentials: "include",
|
|
2424
|
+
body: JSON.stringify({ historyId, title, subtitle, metaDescription, metaKeywords, article, language, seoAnalysis })
|
|
2425
|
+
});
|
|
2426
|
+
if (!response.ok) {
|
|
2427
|
+
const errorText = await response.text();
|
|
2428
|
+
throw new Error(`HTTP error! status: ${response.status} - ${errorText}`);
|
|
2429
|
+
}
|
|
2430
|
+
const reader = (_a = response.body) == null ? void 0 : _a.getReader();
|
|
2431
|
+
if (!reader) throw new Error("No reader available");
|
|
2432
|
+
await processSSEStream(reader, { onFieldStart, onContent, onKeywords, onFieldEnd, onComplete, onError });
|
|
2433
|
+
};
|
|
1907
2434
|
var rewriteNewsApi = async ({
|
|
1908
2435
|
article,
|
|
1909
2436
|
language = "English",
|
|
@@ -1915,145 +2442,136 @@ var rewriteNewsApi = async ({
|
|
|
1915
2442
|
includeFAQ,
|
|
1916
2443
|
targetAudience
|
|
1917
2444
|
}) => {
|
|
1918
|
-
const payload = {
|
|
1919
|
-
|
|
1920
|
-
language
|
|
1921
|
-
};
|
|
1922
|
-
if (articleId) {
|
|
1923
|
-
payload.articleId = articleId;
|
|
1924
|
-
}
|
|
2445
|
+
const payload = { article, language, temperature: 1 };
|
|
2446
|
+
if (articleId) payload.articleId = articleId;
|
|
1925
2447
|
if (tone) payload.tone = tone;
|
|
1926
2448
|
if (style) payload.style = style;
|
|
1927
2449
|
if (wordCount) payload.wordCount = wordCount;
|
|
1928
2450
|
if (includeQuotes !== void 0) payload.includeQuotes = includeQuotes;
|
|
1929
2451
|
if (includeFAQ !== void 0) payload.includeFAQ = includeFAQ;
|
|
1930
2452
|
if (targetAudience) payload.targetAudience = targetAudience;
|
|
1931
|
-
const { data } = await api_default.post("/
|
|
2453
|
+
const { data } = await api_default.post("/contenify/rewrite", payload);
|
|
1932
2454
|
return data.data;
|
|
1933
2455
|
};
|
|
1934
2456
|
|
|
1935
2457
|
// components/chatbot/ChatBot.tsx
|
|
1936
2458
|
import toast2 from "react-hot-toast";
|
|
1937
|
-
import { Loader2 } from "lucide-react";
|
|
1938
|
-
import { jsx as
|
|
1939
|
-
function
|
|
2459
|
+
import { Loader2 as Loader24 } from "lucide-react";
|
|
2460
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2461
|
+
function buildStructuredContent(title, article, metaKeywords, subtitle = "", metaDescription = "") {
|
|
2462
|
+
return JSON.stringify({ title, subtitle, article, metaDescription, metaKeywords });
|
|
2463
|
+
}
|
|
2464
|
+
function ChatBot() {
|
|
1940
2465
|
const { loading } = useTheme();
|
|
1941
|
-
const
|
|
1942
|
-
const [
|
|
1943
|
-
const [
|
|
1944
|
-
const [
|
|
1945
|
-
const [analyzedData, setAnalyzedData] =
|
|
2466
|
+
const [preferencesOpen, setPreferencesOpen] = useState6(false);
|
|
2467
|
+
const [messages, setMessages] = useState6([]);
|
|
2468
|
+
const [isStreaming, setIsStreaming] = useState6(false);
|
|
2469
|
+
const [activeField, setActiveField] = useState6(null);
|
|
2470
|
+
const [analyzedData, setAnalyzedData] = useState6(null);
|
|
1946
2471
|
const handleRecreate = async ({ title, content, id }) => {
|
|
1947
|
-
var _a;
|
|
1948
2472
|
const assistantId = crypto.randomUUID();
|
|
1949
2473
|
setMessages((prev) => [
|
|
1950
2474
|
...prev,
|
|
1951
|
-
{
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
content: `Recreate this news:
|
|
1955
|
-
${title}`
|
|
1956
|
-
},
|
|
1957
|
-
{
|
|
1958
|
-
id: assistantId,
|
|
1959
|
-
role: "assistant",
|
|
1960
|
-
content: "\u23F3 Creating article..."
|
|
1961
|
-
}
|
|
2475
|
+
{ id: crypto.randomUUID(), role: "user", content: `Recreate this news:
|
|
2476
|
+
${title}` },
|
|
2477
|
+
{ id: assistantId, role: "assistant", content: "" }
|
|
1962
2478
|
]);
|
|
2479
|
+
setIsStreaming(true);
|
|
1963
2480
|
try {
|
|
1964
|
-
let accumulatedContent = "";
|
|
1965
2481
|
let hasStartedStreaming = false;
|
|
1966
|
-
|
|
1967
|
-
|
|
2482
|
+
let streamDone = false;
|
|
2483
|
+
let pendingError = null;
|
|
2484
|
+
let titleBuffer = "";
|
|
2485
|
+
let articleBuffer = "";
|
|
2486
|
+
let keywordsBuffer = [];
|
|
2487
|
+
const updateMessage = () => {
|
|
2488
|
+
const structured = buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
|
|
2489
|
+
setMessages((prev) => prev.map((m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: structured }) : m));
|
|
2490
|
+
};
|
|
1968
2491
|
try {
|
|
1969
2492
|
await rewriteNewsStreamApi({
|
|
1970
2493
|
article: content || "",
|
|
1971
|
-
language:
|
|
2494
|
+
language: "English",
|
|
1972
2495
|
articleId: id,
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
includeFAQ: contentPrefs == null ? void 0 : contentPrefs.includeFAQ,
|
|
1978
|
-
targetAudience: contentPrefs == null ? void 0 : contentPrefs.targetAudience,
|
|
1979
|
-
onChunk: (chunk) => {
|
|
2496
|
+
onFieldStart: (field) => {
|
|
2497
|
+
setActiveField(field);
|
|
2498
|
+
},
|
|
2499
|
+
onContent: (field, chunk) => {
|
|
1980
2500
|
hasStartedStreaming = true;
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
2501
|
+
if (field === "title") titleBuffer += chunk;
|
|
2502
|
+
else articleBuffer += chunk;
|
|
2503
|
+
updateMessage();
|
|
2504
|
+
},
|
|
2505
|
+
onKeywords: (keywords) => {
|
|
2506
|
+
keywordsBuffer = keywords;
|
|
2507
|
+
updateMessage();
|
|
1987
2508
|
},
|
|
1988
|
-
|
|
1989
|
-
|
|
2509
|
+
onFieldEnd: () => {
|
|
2510
|
+
setActiveField(null);
|
|
2511
|
+
},
|
|
2512
|
+
onComplete: (data) => {
|
|
2513
|
+
streamDone = true;
|
|
2514
|
+
const finalContent = data && (data.title || data.article) ? JSON.stringify(data) : buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
|
|
2515
|
+
setMessages((prev) => prev.map((m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: finalContent }) : m));
|
|
2516
|
+
setIsStreaming(false);
|
|
2517
|
+
setActiveField(null);
|
|
1990
2518
|
toast2.success("Article created successfully!");
|
|
1991
2519
|
},
|
|
1992
|
-
onError:
|
|
1993
|
-
console.error("Streaming error:", error);
|
|
2520
|
+
onError: (error) => {
|
|
1994
2521
|
if (!hasStartedStreaming) {
|
|
1995
|
-
|
|
1996
|
-
throw error;
|
|
2522
|
+
pendingError = error instanceof Error ? error : new Error(String(error));
|
|
1997
2523
|
} else {
|
|
2524
|
+
streamDone = true;
|
|
2525
|
+
setIsStreaming(false);
|
|
2526
|
+
setActiveField(null);
|
|
1998
2527
|
toast2.error("Failed to complete article generation");
|
|
1999
|
-
setMessages(
|
|
2000
|
-
(
|
|
2001
|
-
|
|
2002
|
-
)
|
|
2003
|
-
);
|
|
2528
|
+
setMessages((prev) => prev.map(
|
|
2529
|
+
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: titleBuffer || articleBuffer ? buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer) : "Failed to create article. Please try again." }) : m
|
|
2530
|
+
));
|
|
2004
2531
|
}
|
|
2005
2532
|
}
|
|
2006
2533
|
});
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
)
|
|
2013
|
-
|
|
2534
|
+
if (pendingError) throw pendingError;
|
|
2535
|
+
if (!streamDone && (titleBuffer || articleBuffer)) {
|
|
2536
|
+
setMessages((prev) => prev.map(
|
|
2537
|
+
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer) }) : m
|
|
2538
|
+
));
|
|
2539
|
+
setIsStreaming(false);
|
|
2540
|
+
setActiveField(null);
|
|
2541
|
+
}
|
|
2542
|
+
} catch (e) {
|
|
2543
|
+
setMessages((prev) => prev.map((m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "" }) : m));
|
|
2014
2544
|
const result = await rewriteNewsApi({
|
|
2015
2545
|
article: content || "",
|
|
2016
|
-
language:
|
|
2017
|
-
articleId: id
|
|
2018
|
-
tone: contentPrefs == null ? void 0 : contentPrefs.tone,
|
|
2019
|
-
style: contentPrefs == null ? void 0 : contentPrefs.style,
|
|
2020
|
-
wordCount: contentPrefs == null ? void 0 : contentPrefs.wordCount,
|
|
2021
|
-
includeQuotes: contentPrefs == null ? void 0 : contentPrefs.includeQuotes,
|
|
2022
|
-
includeFAQ: contentPrefs == null ? void 0 : contentPrefs.includeFAQ,
|
|
2023
|
-
targetAudience: contentPrefs == null ? void 0 : contentPrefs.targetAudience
|
|
2546
|
+
language: "English",
|
|
2547
|
+
articleId: id
|
|
2024
2548
|
});
|
|
2025
|
-
setMessages(
|
|
2026
|
-
(
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
);
|
|
2549
|
+
setMessages((prev) => prev.map(
|
|
2550
|
+
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: typeof result === "string" ? result : JSON.stringify(result) }) : m
|
|
2551
|
+
));
|
|
2552
|
+
setIsStreaming(false);
|
|
2030
2553
|
toast2.success("Article created successfully!");
|
|
2031
2554
|
}
|
|
2032
2555
|
} catch (err) {
|
|
2556
|
+
if (err instanceof ApiKeyInvalidError) return;
|
|
2033
2557
|
console.error("Complete Error:", err);
|
|
2558
|
+
setIsStreaming(false);
|
|
2559
|
+
setActiveField(null);
|
|
2034
2560
|
toast2.error((err == null ? void 0 : err.message) || "An error occurred while creating the article");
|
|
2035
|
-
setMessages(
|
|
2036
|
-
(
|
|
2037
|
-
|
|
2038
|
-
)
|
|
2039
|
-
);
|
|
2561
|
+
setMessages((prev) => prev.map(
|
|
2562
|
+
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "Failed to create article. Please try again." }) : m
|
|
2563
|
+
));
|
|
2040
2564
|
}
|
|
2041
2565
|
};
|
|
2042
2566
|
const handleSendMessage = async (text) => {
|
|
2043
|
-
var _a
|
|
2567
|
+
var _a;
|
|
2044
2568
|
const userMsgId = crypto.randomUUID();
|
|
2045
2569
|
const assistantId = crypto.randomUUID();
|
|
2046
|
-
setMessages((prev) => [
|
|
2047
|
-
...prev,
|
|
2048
|
-
{ id: userMsgId, role: "user", content: text }
|
|
2049
|
-
]);
|
|
2570
|
+
setMessages((prev) => [...prev, { id: userMsgId, role: "user", content: text }]);
|
|
2050
2571
|
const urlRegex = /https?:\/\/[^\s]+/;
|
|
2051
2572
|
const hasUrl = urlRegex.test(text);
|
|
2052
2573
|
if (hasUrl) {
|
|
2053
|
-
setMessages((prev) => [
|
|
2054
|
-
...prev,
|
|
2055
|
-
{ id: assistantId, role: "assistant", content: "\u{1F50D} Analyzing your link..." }
|
|
2056
|
-
]);
|
|
2574
|
+
setMessages((prev) => [...prev, { id: assistantId, role: "assistant", content: "Analyzing your link..." }]);
|
|
2057
2575
|
try {
|
|
2058
2576
|
const result = await analyzeInputApi(text);
|
|
2059
2577
|
if (result.hasUrl && ((_a = result.options) == null ? void 0 : _a.length) > 0) {
|
|
@@ -2064,76 +2582,86 @@ ${title}`
|
|
|
2064
2582
|
messageId: assistantId
|
|
2065
2583
|
});
|
|
2066
2584
|
const optionsList = result.options.map((opt) => `\u2022 **${opt.name}**${opt.description ? ` \u2013 ${opt.description}` : ""}`).join("\n");
|
|
2067
|
-
setMessages(
|
|
2068
|
-
(
|
|
2069
|
-
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), {
|
|
2070
|
-
content: `I found an article (${result.wordCount || 0} words). What would you like to do?
|
|
2585
|
+
setMessages((prev) => prev.map(
|
|
2586
|
+
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: `I found an article (${result.wordCount || 0} words). What would you like to do?
|
|
2071
2587
|
|
|
2072
|
-
${optionsList}`
|
|
2073
|
-
|
|
2074
|
-
)
|
|
2075
|
-
);
|
|
2588
|
+
${optionsList}` }) : m
|
|
2589
|
+
));
|
|
2076
2590
|
} else {
|
|
2077
|
-
setMessages(
|
|
2078
|
-
(
|
|
2079
|
-
|
|
2080
|
-
)
|
|
2081
|
-
);
|
|
2591
|
+
setMessages((prev) => prev.map(
|
|
2592
|
+
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "Could not extract content from that URL. Please try a different link." }) : m
|
|
2593
|
+
));
|
|
2082
2594
|
}
|
|
2083
2595
|
} catch (err) {
|
|
2596
|
+
if (err instanceof ApiKeyInvalidError) return;
|
|
2084
2597
|
console.error("Analyze input error:", err);
|
|
2085
2598
|
toast2.error("Failed to analyze the link");
|
|
2086
|
-
setMessages(
|
|
2087
|
-
(
|
|
2088
|
-
|
|
2089
|
-
)
|
|
2090
|
-
);
|
|
2599
|
+
setMessages((prev) => prev.map(
|
|
2600
|
+
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "Failed to analyze the link. Please try again." }) : m
|
|
2601
|
+
));
|
|
2091
2602
|
}
|
|
2092
2603
|
} else {
|
|
2093
|
-
setMessages((prev) => [
|
|
2094
|
-
...prev,
|
|
2095
|
-
{ id: assistantId, role: "assistant", content: "\u23F3 Generating content..." }
|
|
2096
|
-
]);
|
|
2604
|
+
setMessages((prev) => [...prev, { id: assistantId, role: "assistant", content: "" }]);
|
|
2097
2605
|
setIsStreaming(true);
|
|
2098
2606
|
try {
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
let
|
|
2607
|
+
let streamDone = false;
|
|
2608
|
+
let titleBuffer = "";
|
|
2609
|
+
let articleBuffer = "";
|
|
2610
|
+
let keywordsBuffer = [];
|
|
2611
|
+
const updateMessage = () => {
|
|
2612
|
+
const structured = buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
|
|
2613
|
+
setMessages((prev) => prev.map((m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: structured }) : m));
|
|
2614
|
+
};
|
|
2102
2615
|
await rewriteNewsStreamApi({
|
|
2103
2616
|
article: text,
|
|
2104
|
-
language:
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
wordCount: contentPrefs == null ? void 0 : contentPrefs.wordCount,
|
|
2108
|
-
includeQuotes: contentPrefs == null ? void 0 : contentPrefs.includeQuotes,
|
|
2109
|
-
includeFAQ: contentPrefs == null ? void 0 : contentPrefs.includeFAQ,
|
|
2110
|
-
targetAudience: contentPrefs == null ? void 0 : contentPrefs.targetAudience,
|
|
2111
|
-
onChunk: (chunk) => {
|
|
2112
|
-
accumulatedContent += chunk;
|
|
2113
|
-
setMessages(
|
|
2114
|
-
(prev) => prev.map(
|
|
2115
|
-
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: accumulatedContent }) : m
|
|
2116
|
-
)
|
|
2117
|
-
);
|
|
2617
|
+
language: "English",
|
|
2618
|
+
onFieldStart: (field) => {
|
|
2619
|
+
setActiveField(field);
|
|
2118
2620
|
},
|
|
2119
|
-
|
|
2621
|
+
onContent: (field, chunk) => {
|
|
2622
|
+
if (field === "title") titleBuffer += chunk;
|
|
2623
|
+
else articleBuffer += chunk;
|
|
2624
|
+
updateMessage();
|
|
2625
|
+
},
|
|
2626
|
+
onKeywords: (keywords) => {
|
|
2627
|
+
keywordsBuffer = keywords;
|
|
2628
|
+
updateMessage();
|
|
2629
|
+
},
|
|
2630
|
+
onFieldEnd: () => {
|
|
2631
|
+
setActiveField(null);
|
|
2632
|
+
},
|
|
2633
|
+
onComplete: (data) => {
|
|
2634
|
+
streamDone = true;
|
|
2635
|
+
const finalContent = data && (data.title || data.article) ? JSON.stringify(data) : buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
|
|
2636
|
+
setMessages((prev) => prev.map((m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: finalContent }) : m));
|
|
2120
2637
|
setIsStreaming(false);
|
|
2638
|
+
setActiveField(null);
|
|
2121
2639
|
toast2.success("Content generated!");
|
|
2122
2640
|
},
|
|
2123
2641
|
onError: (error) => {
|
|
2642
|
+
if (error instanceof ApiKeyInvalidError) return;
|
|
2124
2643
|
console.error("Stream error:", error);
|
|
2644
|
+
streamDone = true;
|
|
2125
2645
|
setIsStreaming(false);
|
|
2646
|
+
setActiveField(null);
|
|
2126
2647
|
toast2.error("Failed to generate content");
|
|
2127
2648
|
}
|
|
2128
2649
|
});
|
|
2650
|
+
if (!streamDone && (titleBuffer || articleBuffer)) {
|
|
2651
|
+
setMessages((prev) => prev.map(
|
|
2652
|
+
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer) }) : m
|
|
2653
|
+
));
|
|
2654
|
+
setIsStreaming(false);
|
|
2655
|
+
setActiveField(null);
|
|
2656
|
+
}
|
|
2129
2657
|
} catch (err) {
|
|
2658
|
+
if (err instanceof ApiKeyInvalidError) return;
|
|
2130
2659
|
console.error("Send message error:", err);
|
|
2131
2660
|
setIsStreaming(false);
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
);
|
|
2661
|
+
setActiveField(null);
|
|
2662
|
+
setMessages((prev) => prev.map(
|
|
2663
|
+
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "Failed to generate content. Please try again." }) : m
|
|
2664
|
+
));
|
|
2137
2665
|
}
|
|
2138
2666
|
}
|
|
2139
2667
|
};
|
|
@@ -2145,109 +2673,212 @@ ${optionsList}`
|
|
|
2145
2673
|
setMessages((prev) => [
|
|
2146
2674
|
...prev,
|
|
2147
2675
|
{ id: crypto.randomUUID(), role: "user", content: `Action: ${actionName}` },
|
|
2148
|
-
{ id: assistantId, role: "assistant", content: "
|
|
2676
|
+
{ id: assistantId, role: "assistant", content: "" }
|
|
2149
2677
|
]);
|
|
2150
2678
|
setIsStreaming(true);
|
|
2151
2679
|
try {
|
|
2152
|
-
|
|
2153
|
-
let
|
|
2680
|
+
let streamDone = false;
|
|
2681
|
+
let titleBuffer = "";
|
|
2682
|
+
let articleBuffer = "";
|
|
2683
|
+
let keywordsBuffer = [];
|
|
2684
|
+
const updateMessage = () => {
|
|
2685
|
+
const structured = buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
|
|
2686
|
+
setMessages((prev) => prev.map((m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: structured }) : m));
|
|
2687
|
+
};
|
|
2154
2688
|
try {
|
|
2155
2689
|
await createContentStreamApi({
|
|
2156
2690
|
url,
|
|
2157
2691
|
content,
|
|
2158
2692
|
actionId,
|
|
2159
|
-
settings: {
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
wordCount: contentPrefs == null ? void 0 : contentPrefs.wordCount
|
|
2693
|
+
settings: {},
|
|
2694
|
+
onFieldStart: (field) => {
|
|
2695
|
+
setActiveField(field);
|
|
2163
2696
|
},
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2697
|
+
onContent: (field, chunk) => {
|
|
2698
|
+
if (field === "title") titleBuffer += chunk;
|
|
2699
|
+
else articleBuffer += chunk;
|
|
2700
|
+
updateMessage();
|
|
2701
|
+
},
|
|
2702
|
+
onKeywords: (keywords) => {
|
|
2703
|
+
keywordsBuffer = keywords;
|
|
2704
|
+
updateMessage();
|
|
2705
|
+
},
|
|
2706
|
+
onFieldEnd: () => {
|
|
2707
|
+
setActiveField(null);
|
|
2171
2708
|
},
|
|
2172
|
-
onComplete: () => {
|
|
2709
|
+
onComplete: (data) => {
|
|
2710
|
+
streamDone = true;
|
|
2711
|
+
const finalContent = data && (data.title || data.article) ? JSON.stringify(data) : buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
|
|
2712
|
+
setMessages((prev) => prev.map((m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: finalContent }) : m));
|
|
2173
2713
|
setIsStreaming(false);
|
|
2714
|
+
setActiveField(null);
|
|
2174
2715
|
toast2.success("Content created!");
|
|
2175
2716
|
},
|
|
2176
2717
|
onError: (error) => {
|
|
2718
|
+
if (error instanceof ApiKeyInvalidError) return;
|
|
2177
2719
|
console.error("Stream error:", error);
|
|
2720
|
+
streamDone = true;
|
|
2178
2721
|
setIsStreaming(false);
|
|
2722
|
+
setActiveField(null);
|
|
2179
2723
|
toast2.error("Failed to create content");
|
|
2180
2724
|
}
|
|
2181
2725
|
});
|
|
2726
|
+
if (!streamDone && (titleBuffer || articleBuffer)) {
|
|
2727
|
+
setMessages((prev) => prev.map(
|
|
2728
|
+
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer) }) : m
|
|
2729
|
+
));
|
|
2730
|
+
setIsStreaming(false);
|
|
2731
|
+
setActiveField(null);
|
|
2732
|
+
}
|
|
2182
2733
|
} catch (e) {
|
|
2183
|
-
const result = await createContentApi({ url, content, actionId, settings: {
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
} });
|
|
2188
|
-
setMessages(
|
|
2189
|
-
(prev) => prev.map(
|
|
2190
|
-
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: result.article || result.content || JSON.stringify(result) }) : m
|
|
2191
|
-
)
|
|
2192
|
-
);
|
|
2734
|
+
const result = await createContentApi({ url, content, actionId, settings: {} });
|
|
2735
|
+
setMessages((prev) => prev.map(
|
|
2736
|
+
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: typeof result === "string" ? result : JSON.stringify(result) }) : m
|
|
2737
|
+
));
|
|
2193
2738
|
setIsStreaming(false);
|
|
2739
|
+
setActiveField(null);
|
|
2194
2740
|
toast2.success("Content created!");
|
|
2195
2741
|
}
|
|
2196
2742
|
} catch (err) {
|
|
2743
|
+
if (err instanceof ApiKeyInvalidError) return;
|
|
2197
2744
|
console.error("Create content error:", err);
|
|
2198
2745
|
setIsStreaming(false);
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2746
|
+
setActiveField(null);
|
|
2747
|
+
setMessages((prev) => prev.map(
|
|
2748
|
+
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "Failed to create content. Please try again." }) : m
|
|
2749
|
+
));
|
|
2750
|
+
}
|
|
2751
|
+
};
|
|
2752
|
+
const handleResolveSeo = async (articleData) => {
|
|
2753
|
+
const assistantId = crypto.randomUUID();
|
|
2754
|
+
setMessages((prev) => [
|
|
2755
|
+
...prev,
|
|
2756
|
+
{ id: crypto.randomUUID(), role: "user", content: "Resolve SEO issues" },
|
|
2757
|
+
{ id: assistantId, role: "assistant", content: "" }
|
|
2758
|
+
]);
|
|
2759
|
+
setIsStreaming(true);
|
|
2760
|
+
try {
|
|
2761
|
+
let streamDone = false;
|
|
2762
|
+
let titleBuffer = "";
|
|
2763
|
+
let articleBuffer = "";
|
|
2764
|
+
let keywordsBuffer = [];
|
|
2765
|
+
const updateMessage = () => {
|
|
2766
|
+
setMessages((prev) => prev.map(
|
|
2767
|
+
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer) }) : m
|
|
2768
|
+
));
|
|
2769
|
+
};
|
|
2770
|
+
await resolveSeoStreamApi(__spreadProps(__spreadValues({}, articleData), {
|
|
2771
|
+
onFieldStart: (field) => {
|
|
2772
|
+
setActiveField(field);
|
|
2773
|
+
},
|
|
2774
|
+
onContent: (field, chunk) => {
|
|
2775
|
+
if (field === "title") titleBuffer += chunk;
|
|
2776
|
+
else articleBuffer += chunk;
|
|
2777
|
+
updateMessage();
|
|
2778
|
+
},
|
|
2779
|
+
onKeywords: (keywords) => {
|
|
2780
|
+
keywordsBuffer = keywords;
|
|
2781
|
+
updateMessage();
|
|
2782
|
+
},
|
|
2783
|
+
onFieldEnd: () => {
|
|
2784
|
+
setActiveField(null);
|
|
2785
|
+
},
|
|
2786
|
+
onComplete: (data) => {
|
|
2787
|
+
streamDone = true;
|
|
2788
|
+
const finalContent = data && (data.title || data.article) ? JSON.stringify(data) : buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
|
|
2789
|
+
setMessages((prev) => prev.map((m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: finalContent }) : m));
|
|
2790
|
+
setIsStreaming(false);
|
|
2791
|
+
setActiveField(null);
|
|
2792
|
+
toast2.success("SEO issues resolved!");
|
|
2793
|
+
},
|
|
2794
|
+
onError: () => {
|
|
2795
|
+
streamDone = true;
|
|
2796
|
+
setIsStreaming(false);
|
|
2797
|
+
setActiveField(null);
|
|
2798
|
+
toast2.error("Failed to resolve SEO issues");
|
|
2799
|
+
}
|
|
2800
|
+
}));
|
|
2801
|
+
if (!streamDone && (titleBuffer || articleBuffer)) {
|
|
2802
|
+
setMessages((prev) => prev.map(
|
|
2803
|
+
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer) }) : m
|
|
2804
|
+
));
|
|
2805
|
+
setIsStreaming(false);
|
|
2806
|
+
setActiveField(null);
|
|
2807
|
+
}
|
|
2808
|
+
} catch (err) {
|
|
2809
|
+
if (err instanceof ApiKeyInvalidError) return;
|
|
2810
|
+
console.error("Resolve SEO error:", err);
|
|
2811
|
+
setIsStreaming(false);
|
|
2812
|
+
setActiveField(null);
|
|
2813
|
+
toast2.error((err == null ? void 0 : err.message) || "Failed to resolve SEO issues");
|
|
2814
|
+
setMessages((prev) => prev.map(
|
|
2815
|
+
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "Failed to resolve SEO issues. Please try again." }) : m
|
|
2816
|
+
));
|
|
2817
|
+
}
|
|
2818
|
+
};
|
|
2819
|
+
const handleHistorySelect = async (id) => {
|
|
2820
|
+
try {
|
|
2821
|
+
const item = await getHistoryItem(id);
|
|
2822
|
+
const content = buildStructuredContent(
|
|
2823
|
+
item.title,
|
|
2824
|
+
item.article || "",
|
|
2825
|
+
item.metaKeywords || [],
|
|
2826
|
+
item.subtitle,
|
|
2827
|
+
item.metaDescription
|
|
2203
2828
|
);
|
|
2829
|
+
setMessages((prev) => [...prev, { id: crypto.randomUUID(), role: "assistant", content }]);
|
|
2830
|
+
} catch (e) {
|
|
2831
|
+
toast2.error("Failed to load article");
|
|
2204
2832
|
}
|
|
2205
2833
|
};
|
|
2206
2834
|
if (loading) {
|
|
2207
|
-
return /* @__PURE__ */
|
|
2208
|
-
/* @__PURE__ */
|
|
2209
|
-
/* @__PURE__ */
|
|
2835
|
+
return /* @__PURE__ */ jsx8("div", { className: "cnfy-loading", children: /* @__PURE__ */ jsxs8("div", { className: "cnfy-loading-inner", children: [
|
|
2836
|
+
/* @__PURE__ */ jsx8(Loader24, { className: "cnfy-loading-spinner cnfy-animate-spin" }),
|
|
2837
|
+
/* @__PURE__ */ jsx8("p", { className: "cnfy-loading-text", children: "Loading..." })
|
|
2210
2838
|
] }) });
|
|
2211
2839
|
}
|
|
2212
2840
|
return /* @__PURE__ */ jsxs8("div", { className: "cnfy-root", children: [
|
|
2213
|
-
/* @__PURE__ */
|
|
2214
|
-
|
|
2215
|
-
|
|
2841
|
+
/* @__PURE__ */ jsx8("div", { className: "cnfy-main", children: /* @__PURE__ */ jsx8("section", { className: "cnfy-chat-section cnfy-chat-section--full", children: /* @__PURE__ */ jsxs8("div", { className: "cnfy-chat-inner cnfy-chat-inner--with-history", children: [
|
|
2842
|
+
/* @__PURE__ */ jsx8(
|
|
2843
|
+
HistoryPanel,
|
|
2844
|
+
{
|
|
2845
|
+
onSelect: handleHistorySelect,
|
|
2846
|
+
onNewChat: () => setMessages([])
|
|
2847
|
+
}
|
|
2848
|
+
),
|
|
2849
|
+
/* @__PURE__ */ jsx8(
|
|
2216
2850
|
ChatWindow,
|
|
2217
2851
|
{
|
|
2218
2852
|
messages,
|
|
2219
2853
|
onSend: handleSendMessage,
|
|
2220
2854
|
onSelectNews: handleRecreate,
|
|
2221
2855
|
isStreaming,
|
|
2856
|
+
activeField,
|
|
2222
2857
|
analyzedData,
|
|
2223
2858
|
onSelectAction: handleSelectAction,
|
|
2224
|
-
|
|
2859
|
+
onResolveSeo: handleResolveSeo
|
|
2225
2860
|
}
|
|
2226
|
-
)
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
/* @__PURE__ */ jsx11("p", { className: "cnfy-empty-state-subtitle", children: "Type a message or paste a link to begin" })
|
|
2230
|
-
] }) })
|
|
2231
|
-
] }) }),
|
|
2232
|
-
/* @__PURE__ */ jsx11(
|
|
2861
|
+
)
|
|
2862
|
+
] }) }) }),
|
|
2863
|
+
/* @__PURE__ */ jsx8(
|
|
2233
2864
|
Drawer,
|
|
2234
2865
|
{
|
|
2235
2866
|
open: preferencesOpen,
|
|
2236
2867
|
onClose: () => setPreferencesOpen(false),
|
|
2237
2868
|
title: "Settings",
|
|
2238
|
-
children: /* @__PURE__ */
|
|
2869
|
+
children: /* @__PURE__ */ jsx8(PreferencesPage, {})
|
|
2239
2870
|
}
|
|
2240
2871
|
)
|
|
2241
2872
|
] });
|
|
2242
2873
|
}
|
|
2243
2874
|
|
|
2244
2875
|
// src/index.tsx
|
|
2245
|
-
import { jsx as
|
|
2876
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
2246
2877
|
function ContenifyChatBot({ apiUrl, apiKey, domain, onPost }) {
|
|
2247
|
-
|
|
2878
|
+
useEffect7(() => {
|
|
2248
2879
|
setConfig({ apiUrl, apiKey, domain });
|
|
2249
2880
|
}, [apiUrl, apiKey, domain]);
|
|
2250
|
-
return /* @__PURE__ */
|
|
2881
|
+
return /* @__PURE__ */ jsx9(ChatBot, {});
|
|
2251
2882
|
}
|
|
2252
2883
|
var index_default = ContenifyChatBot;
|
|
2253
2884
|
export {
|