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