@contenify/chatbot 3.0.0 → 4.0.1
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 +70 -203
- package/dist/index.d.mts +1 -42
- package/dist/index.d.ts +1 -42
- package/dist/index.js +1693 -1225
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1677 -1209
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +1311 -78
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -53,251 +53,10 @@ __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"));
|
|
63
|
-
|
|
64
|
-
// lib/config.ts
|
|
65
|
-
var _apiUrl = process.env.NEXT_PUBLIC_CONTENIFY_API_URL || "http://localhost:8080/api";
|
|
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
|
-
}
|
|
82
|
-
|
|
83
|
-
// lib/api.ts
|
|
84
|
-
var api = import_axios.default.create({
|
|
85
|
-
baseURL: getApiBaseUrl(),
|
|
86
|
-
withCredentials: true,
|
|
87
|
-
headers: {
|
|
88
|
-
"Content-Type": "application/json"
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
api.interceptors.request.use(
|
|
92
|
-
(config) => {
|
|
93
|
-
const apiKey = getApiKey();
|
|
94
|
-
if (apiKey) {
|
|
95
|
-
config.headers["x-api-key"] = apiKey;
|
|
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);
|
|
107
|
-
}
|
|
108
|
-
);
|
|
109
|
-
var api_default = api;
|
|
110
|
-
|
|
111
|
-
// services/preferences.service.ts
|
|
112
|
-
var getMyPreferencesApi = async () => {
|
|
113
|
-
const { data } = await api_default.get("/chatboat/preferences/me");
|
|
114
|
-
return data.data;
|
|
115
|
-
};
|
|
116
|
-
var savePreferencesApi = async ({
|
|
117
|
-
botName,
|
|
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);
|
|
190
|
-
}
|
|
191
|
-
const { data } = await api_default.put("/chatboat/preferences", formData, {
|
|
192
|
-
headers: {
|
|
193
|
-
"Content-Type": "multipart/form-data"
|
|
194
|
-
}
|
|
195
|
-
});
|
|
196
|
-
return data.data;
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
// contexts/PreferencesContext.tsx
|
|
200
|
-
var import_jsx_runtime = require("react/jsx-runtime");
|
|
201
|
-
var defaultPreferences = {
|
|
202
|
-
chatbot: {
|
|
203
|
-
name: "ContenifyAI Assistant",
|
|
204
|
-
primaryColor: "#10b981",
|
|
205
|
-
secondaryColor: "#064e3b",
|
|
206
|
-
theme: "system"
|
|
207
|
-
},
|
|
208
|
-
localization: {
|
|
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
|
|
288
|
-
}
|
|
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");
|
|
295
|
-
}
|
|
296
|
-
return context;
|
|
297
|
-
}
|
|
56
|
+
var import_react10 = require("react");
|
|
298
57
|
|
|
299
58
|
// components/chatbot/ChatBot.tsx
|
|
300
|
-
var
|
|
59
|
+
var import_react9 = require("react");
|
|
301
60
|
|
|
302
61
|
// src/utils/ aiJsonParser.ts
|
|
303
62
|
function extractJSON(raw) {
|
|
@@ -351,7 +110,8 @@ function formatAIContent(raw) {
|
|
|
351
110
|
article: "",
|
|
352
111
|
metaDescription: "",
|
|
353
112
|
metaKeywords: [],
|
|
354
|
-
plainText: ""
|
|
113
|
+
plainText: "",
|
|
114
|
+
images: []
|
|
355
115
|
};
|
|
356
116
|
if (!raw) return empty;
|
|
357
117
|
const parsed = extractJSON(raw);
|
|
@@ -368,7 +128,9 @@ function formatAIContent(raw) {
|
|
|
368
128
|
article: rawArticle,
|
|
369
129
|
metaDescription: parsed.metaDescription || "",
|
|
370
130
|
metaKeywords: Array.isArray(parsed.metaKeywords) ? parsed.metaKeywords : [],
|
|
371
|
-
plainText: raw
|
|
131
|
+
plainText: raw,
|
|
132
|
+
seoAnalysis: parsed.seoAnalysis || null,
|
|
133
|
+
images: Array.isArray(parsed.images) ? parsed.images : []
|
|
372
134
|
};
|
|
373
135
|
}
|
|
374
136
|
return __spreadProps(__spreadValues({}, empty), { plainText: raw });
|
|
@@ -376,25 +138,25 @@ function formatAIContent(raw) {
|
|
|
376
138
|
|
|
377
139
|
// components/chatbot/ChatWindow.tsx
|
|
378
140
|
var import_lucide_react4 = require("lucide-react");
|
|
379
|
-
var
|
|
141
|
+
var import_react5 = require("react");
|
|
380
142
|
|
|
381
143
|
// components/chatbot/EditModal.tsx
|
|
382
144
|
var import_lucide_react2 = require("lucide-react");
|
|
383
|
-
var
|
|
145
|
+
var import_react3 = require("react");
|
|
384
146
|
|
|
385
147
|
// components/chatbot/RichTextEditor.tsx
|
|
386
|
-
var
|
|
148
|
+
var import_react = require("@tiptap/react");
|
|
387
149
|
var import_starter_kit = __toESM(require("@tiptap/starter-kit"));
|
|
388
150
|
var import_extension_placeholder = __toESM(require("@tiptap/extension-placeholder"));
|
|
389
151
|
var import_lucide_react = require("lucide-react");
|
|
390
|
-
var
|
|
391
|
-
var
|
|
152
|
+
var import_react2 = require("react");
|
|
153
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
392
154
|
function RichTextEditor({
|
|
393
155
|
content,
|
|
394
156
|
onChange,
|
|
395
157
|
placeholder = "Start writing your article..."
|
|
396
158
|
}) {
|
|
397
|
-
const editor = (0,
|
|
159
|
+
const editor = (0, import_react.useEditor)({
|
|
398
160
|
immediatelyRender: false,
|
|
399
161
|
// Prevents SSR hydration mismatch
|
|
400
162
|
extensions: [
|
|
@@ -417,102 +179,102 @@ function RichTextEditor({
|
|
|
417
179
|
onChange(editor2.getHTML());
|
|
418
180
|
}
|
|
419
181
|
});
|
|
420
|
-
(0,
|
|
182
|
+
(0, import_react2.useEffect)(() => {
|
|
421
183
|
if (editor && content !== editor.getHTML()) {
|
|
422
184
|
editor.commands.setContent(content);
|
|
423
185
|
}
|
|
424
186
|
}, [content, editor]);
|
|
425
187
|
if (!editor) {
|
|
426
|
-
return /* @__PURE__ */ (0,
|
|
188
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "cnfy-editor-loading", children: "Loading editor..." });
|
|
427
189
|
}
|
|
428
|
-
return /* @__PURE__ */ (0,
|
|
429
|
-
/* @__PURE__ */ (0,
|
|
430
|
-
/* @__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)(
|
|
431
193
|
ToolbarButton,
|
|
432
194
|
{
|
|
433
195
|
onClick: () => editor.chain().focus().toggleHeading({ level: 1 }).run(),
|
|
434
196
|
isActive: editor.isActive("heading", { level: 1 }),
|
|
435
197
|
title: "Heading 1",
|
|
436
|
-
children: /* @__PURE__ */ (0,
|
|
198
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Heading1, { size: 18 })
|
|
437
199
|
}
|
|
438
200
|
),
|
|
439
|
-
/* @__PURE__ */ (0,
|
|
201
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
440
202
|
ToolbarButton,
|
|
441
203
|
{
|
|
442
204
|
onClick: () => editor.chain().focus().toggleHeading({ level: 2 }).run(),
|
|
443
205
|
isActive: editor.isActive("heading", { level: 2 }),
|
|
444
206
|
title: "Heading 2",
|
|
445
|
-
children: /* @__PURE__ */ (0,
|
|
207
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Heading2, { size: 18 })
|
|
446
208
|
}
|
|
447
209
|
),
|
|
448
|
-
/* @__PURE__ */ (0,
|
|
210
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
449
211
|
ToolbarButton,
|
|
450
212
|
{
|
|
451
213
|
onClick: () => editor.chain().focus().setParagraph().run(),
|
|
452
214
|
isActive: editor.isActive("paragraph"),
|
|
453
215
|
title: "Paragraph",
|
|
454
|
-
children: /* @__PURE__ */ (0,
|
|
216
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Pilcrow, { size: 18 })
|
|
455
217
|
}
|
|
456
218
|
),
|
|
457
|
-
/* @__PURE__ */ (0,
|
|
458
|
-
/* @__PURE__ */ (0,
|
|
219
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "cnfy-toolbar-divider" }),
|
|
220
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
459
221
|
ToolbarButton,
|
|
460
222
|
{
|
|
461
223
|
onClick: () => editor.chain().focus().toggleBold().run(),
|
|
462
224
|
isActive: editor.isActive("bold"),
|
|
463
225
|
title: "Bold",
|
|
464
|
-
children: /* @__PURE__ */ (0,
|
|
226
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Bold, { size: 18 })
|
|
465
227
|
}
|
|
466
228
|
),
|
|
467
|
-
/* @__PURE__ */ (0,
|
|
229
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
468
230
|
ToolbarButton,
|
|
469
231
|
{
|
|
470
232
|
onClick: () => editor.chain().focus().toggleItalic().run(),
|
|
471
233
|
isActive: editor.isActive("italic"),
|
|
472
234
|
title: "Italic",
|
|
473
|
-
children: /* @__PURE__ */ (0,
|
|
235
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Italic, { size: 18 })
|
|
474
236
|
}
|
|
475
237
|
),
|
|
476
|
-
/* @__PURE__ */ (0,
|
|
477
|
-
/* @__PURE__ */ (0,
|
|
238
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "cnfy-toolbar-divider" }),
|
|
239
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
478
240
|
ToolbarButton,
|
|
479
241
|
{
|
|
480
242
|
onClick: () => editor.chain().focus().toggleBulletList().run(),
|
|
481
243
|
isActive: editor.isActive("bulletList"),
|
|
482
244
|
title: "Bullet List",
|
|
483
|
-
children: /* @__PURE__ */ (0,
|
|
245
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.List, { size: 18 })
|
|
484
246
|
}
|
|
485
247
|
),
|
|
486
|
-
/* @__PURE__ */ (0,
|
|
248
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
487
249
|
ToolbarButton,
|
|
488
250
|
{
|
|
489
251
|
onClick: () => editor.chain().focus().toggleOrderedList().run(),
|
|
490
252
|
isActive: editor.isActive("orderedList"),
|
|
491
253
|
title: "Numbered List",
|
|
492
|
-
children: /* @__PURE__ */ (0,
|
|
254
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ListOrdered, { size: 18 })
|
|
493
255
|
}
|
|
494
256
|
),
|
|
495
|
-
/* @__PURE__ */ (0,
|
|
496
|
-
/* @__PURE__ */ (0,
|
|
257
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "cnfy-toolbar-divider" }),
|
|
258
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
497
259
|
ToolbarButton,
|
|
498
260
|
{
|
|
499
261
|
onClick: () => editor.chain().focus().undo().run(),
|
|
500
262
|
disabled: !editor.can().undo(),
|
|
501
263
|
title: "Undo",
|
|
502
|
-
children: /* @__PURE__ */ (0,
|
|
264
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Undo, { size: 18 })
|
|
503
265
|
}
|
|
504
266
|
),
|
|
505
|
-
/* @__PURE__ */ (0,
|
|
267
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
506
268
|
ToolbarButton,
|
|
507
269
|
{
|
|
508
270
|
onClick: () => editor.chain().focus().redo().run(),
|
|
509
271
|
disabled: !editor.can().redo(),
|
|
510
272
|
title: "Redo",
|
|
511
|
-
children: /* @__PURE__ */ (0,
|
|
273
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Redo, { size: 18 })
|
|
512
274
|
}
|
|
513
275
|
)
|
|
514
276
|
] }),
|
|
515
|
-
/* @__PURE__ */ (0,
|
|
277
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.EditorContent, { editor, className: "cnfy-editor-content" })
|
|
516
278
|
] });
|
|
517
279
|
}
|
|
518
280
|
function ToolbarButton({
|
|
@@ -522,7 +284,7 @@ function ToolbarButton({
|
|
|
522
284
|
title,
|
|
523
285
|
children
|
|
524
286
|
}) {
|
|
525
|
-
return /* @__PURE__ */ (0,
|
|
287
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
526
288
|
"button",
|
|
527
289
|
{
|
|
528
290
|
onClick,
|
|
@@ -536,48 +298,66 @@ function ToolbarButton({
|
|
|
536
298
|
|
|
537
299
|
// hooks/useTheme.ts
|
|
538
300
|
function useTheme() {
|
|
539
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
540
|
-
const { preferences, loading } = usePreferences();
|
|
541
301
|
return {
|
|
542
|
-
loading,
|
|
543
|
-
primaryColor:
|
|
544
|
-
secondaryColor:
|
|
545
|
-
botName:
|
|
546
|
-
logoUrl:
|
|
547
|
-
theme:
|
|
548
|
-
showSidebar:
|
|
549
|
-
showHeader:
|
|
550
|
-
activeThemePreset:
|
|
551
|
-
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
|
|
552
312
|
};
|
|
553
313
|
}
|
|
554
314
|
|
|
555
315
|
// components/chatbot/EditModal.tsx
|
|
556
|
-
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
|
+
}
|
|
557
330
|
function toSlug(text) {
|
|
558
331
|
return text.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-");
|
|
559
332
|
}
|
|
560
333
|
function EditModal({
|
|
561
334
|
isOpen,
|
|
335
|
+
inline = false,
|
|
562
336
|
initialTitle = "",
|
|
563
337
|
initialSubtitle = "",
|
|
564
338
|
initialContent,
|
|
565
339
|
initialMetaDescription = "",
|
|
566
340
|
metaKeywords,
|
|
341
|
+
initialFeaturedImage = "",
|
|
342
|
+
seoAnalysis = null,
|
|
567
343
|
onClose,
|
|
568
|
-
|
|
569
|
-
|
|
344
|
+
onDownload,
|
|
345
|
+
downloadingFormat,
|
|
346
|
+
onPost,
|
|
347
|
+
isPosting = false
|
|
570
348
|
}) {
|
|
571
349
|
const { primaryColor } = useTheme();
|
|
572
|
-
const [title, setTitle] = (0,
|
|
573
|
-
const [
|
|
574
|
-
const [
|
|
575
|
-
const [
|
|
576
|
-
const [
|
|
577
|
-
const [
|
|
578
|
-
const [
|
|
579
|
-
const [
|
|
580
|
-
(0,
|
|
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)(() => {
|
|
581
361
|
setTitle(initialTitle);
|
|
582
362
|
setSubtitle(initialSubtitle);
|
|
583
363
|
setMetaDescription(initialMetaDescription);
|
|
@@ -585,8 +365,9 @@ function EditModal({
|
|
|
585
365
|
setSlugEdited(false);
|
|
586
366
|
setContent(initialContent);
|
|
587
367
|
setKeywords(metaKeywords);
|
|
588
|
-
|
|
589
|
-
|
|
368
|
+
setFeaturedImage(initialFeaturedImage);
|
|
369
|
+
}, [initialTitle, initialSubtitle, initialContent, initialMetaDescription, metaKeywords, initialFeaturedImage]);
|
|
370
|
+
(0, import_react3.useEffect)(() => {
|
|
590
371
|
if (isOpen) {
|
|
591
372
|
document.body.style.overflow = "hidden";
|
|
592
373
|
} else {
|
|
@@ -627,138 +408,313 @@ function EditModal({
|
|
|
627
408
|
article: content,
|
|
628
409
|
metaDescription,
|
|
629
410
|
slug,
|
|
630
|
-
keywords
|
|
411
|
+
keywords,
|
|
412
|
+
featuredImage: featuredImage || void 0
|
|
631
413
|
});
|
|
632
414
|
if (!isOpen) return null;
|
|
633
|
-
|
|
634
|
-
/* @__PURE__ */ (0,
|
|
635
|
-
|
|
636
|
-
/* @__PURE__ */ (0,
|
|
637
|
-
|
|
638
|
-
|
|
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)(
|
|
431
|
+
"button",
|
|
432
|
+
{
|
|
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 })
|
|
438
|
+
}
|
|
439
|
+
)
|
|
639
440
|
] }),
|
|
640
|
-
/* @__PURE__ */ (0,
|
|
641
|
-
/* @__PURE__ */ (0,
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
/* @__PURE__ */ (0,
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
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
|
+
)
|
|
453
|
+
] }),
|
|
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",
|
|
458
|
+
{
|
|
459
|
+
type: "text",
|
|
460
|
+
value: subtitle,
|
|
461
|
+
onChange: (e) => setSubtitle(e.target.value),
|
|
462
|
+
placeholder: "Article subtitle...",
|
|
463
|
+
className: "cnfy-edit-input"
|
|
464
|
+
}
|
|
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",
|
|
471
|
+
{
|
|
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"
|
|
477
|
+
}
|
|
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,
|
|
484
|
+
{
|
|
485
|
+
content,
|
|
486
|
+
onChange: setContent,
|
|
487
|
+
placeholder: "Start writing your article..."
|
|
488
|
+
}
|
|
489
|
+
)
|
|
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",
|
|
658
511
|
{
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
placeholder: "Article subtitle...",
|
|
663
|
-
className: "cnfy-edit-input"
|
|
512
|
+
onClick: () => handleRemoveKeyword(index),
|
|
513
|
+
className: "cnfy-keyword-remove-btn",
|
|
514
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.X, { size: 14 })
|
|
664
515
|
}
|
|
665
516
|
)
|
|
666
|
-
] }),
|
|
667
|
-
/* @__PURE__ */ (0,
|
|
668
|
-
/* @__PURE__ */ (0,
|
|
669
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
517
|
+
] }, index)) }),
|
|
518
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "cnfy-keyword-input-row", children: [
|
|
519
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
670
520
|
"input",
|
|
671
521
|
{
|
|
672
522
|
type: "text",
|
|
673
|
-
value:
|
|
674
|
-
onChange: (e) =>
|
|
675
|
-
|
|
676
|
-
|
|
523
|
+
value: newKeyword,
|
|
524
|
+
onChange: (e) => setNewKeyword(e.target.value),
|
|
525
|
+
onKeyDown: handleKeyDown,
|
|
526
|
+
placeholder: "Add a keyword...",
|
|
527
|
+
className: "cnfy-keyword-input"
|
|
677
528
|
}
|
|
678
|
-
)
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
]
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
"input",
|
|
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",
|
|
721
571
|
{
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
onChange: (e) => setNewKeyword(e.target.value),
|
|
725
|
-
onKeyDown: handleKeyDown,
|
|
726
|
-
placeholder: "Add a keyword...",
|
|
727
|
-
className: "cnfy-keyword-input"
|
|
572
|
+
className: "cnfy-seo-overall-fill",
|
|
573
|
+
style: { width: `${seo.overall}%`, background: seoScoreColor(seo.overall) }
|
|
728
574
|
}
|
|
729
|
-
),
|
|
730
|
-
/* @__PURE__ */ (0,
|
|
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
|
+
] })
|
|
731
616
|
] })
|
|
732
|
-
] })
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
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
|
+
)
|
|
754
665
|
] })
|
|
755
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;
|
|
756
690
|
}
|
|
757
691
|
|
|
758
692
|
// components/news/NewsList.tsx
|
|
759
|
-
var
|
|
693
|
+
var import_react4 = require("react");
|
|
760
694
|
var import_lucide_react3 = require("lucide-react");
|
|
761
|
-
|
|
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");
|
|
762
718
|
var dateFormatter = new Intl.DateTimeFormat("en-GB", {
|
|
763
719
|
day: "2-digit",
|
|
764
720
|
month: "short",
|
|
@@ -789,62 +745,52 @@ function NewsList({
|
|
|
789
745
|
onRecreate
|
|
790
746
|
}) {
|
|
791
747
|
const { primaryColor } = useTheme();
|
|
792
|
-
const [primaryColorState] = (0,
|
|
793
|
-
|
|
794
|
-
hex = hex.replace("#", "");
|
|
795
|
-
let r = parseInt(hex.substring(0, 2), 16);
|
|
796
|
-
let g = parseInt(hex.substring(2, 4), 16);
|
|
797
|
-
let b = parseInt(hex.substring(4, 6), 16);
|
|
798
|
-
r = Math.round(r + (255 - r) * (percent / 100));
|
|
799
|
-
g = Math.round(g + (255 - g) * (percent / 100));
|
|
800
|
-
b = Math.round(b + (255 - b) * (percent / 100));
|
|
801
|
-
return `rgb(${r}, ${g}, ${b})`;
|
|
802
|
-
};
|
|
803
|
-
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) => {
|
|
804
750
|
const publishedDate = new Date(item.publishedAt);
|
|
805
751
|
const links = extractLinksFromContent(item.content);
|
|
806
752
|
if (links.length > 0) {
|
|
807
|
-
return links.map((link, idx) => /* @__PURE__ */ (0,
|
|
753
|
+
return links.map((link, idx) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
808
754
|
"div",
|
|
809
755
|
{
|
|
810
756
|
className: "cnfy-news-card",
|
|
811
757
|
children: [
|
|
812
|
-
/* @__PURE__ */ (0,
|
|
813
|
-
/* @__PURE__ */ (0,
|
|
814
|
-
/* @__PURE__ */ (0,
|
|
815
|
-
/* @__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)(
|
|
816
762
|
"span",
|
|
817
763
|
{
|
|
818
764
|
className: "cnfy-news-badge",
|
|
819
765
|
style: {
|
|
820
|
-
|
|
821
|
-
color:
|
|
822
|
-
|
|
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)"
|
|
823
769
|
},
|
|
824
770
|
children: link.source || item.sourceName
|
|
825
771
|
}
|
|
826
772
|
),
|
|
827
|
-
/* @__PURE__ */ (0,
|
|
828
|
-
/* @__PURE__ */ (0,
|
|
829
|
-
/* @__PURE__ */ (0,
|
|
830
|
-
/* @__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: [
|
|
831
777
|
dateFormatter.format(publishedDate),
|
|
832
778
|
" ",
|
|
833
779
|
timeFormatter.format(publishedDate)
|
|
834
780
|
] })
|
|
835
781
|
] })
|
|
836
782
|
] }),
|
|
837
|
-
/* @__PURE__ */ (0,
|
|
838
|
-
/* @__PURE__ */ (0,
|
|
783
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cnfy-news-card-actions", children: [
|
|
784
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
839
785
|
"button",
|
|
840
786
|
{
|
|
841
787
|
onClick: () => window.open(link.url, "_blank"),
|
|
842
788
|
className: "cnfy-news-action-btn",
|
|
843
789
|
title: "View",
|
|
844
|
-
children: /* @__PURE__ */ (0,
|
|
790
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.Eye, { size: 16 })
|
|
845
791
|
}
|
|
846
792
|
),
|
|
847
|
-
/* @__PURE__ */ (0,
|
|
793
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
848
794
|
"button",
|
|
849
795
|
{
|
|
850
796
|
onClick: () => onRecreate({
|
|
@@ -854,7 +800,7 @@ function NewsList({
|
|
|
854
800
|
}),
|
|
855
801
|
className: "cnfy-news-action-btn",
|
|
856
802
|
title: "Recreate",
|
|
857
|
-
children: /* @__PURE__ */ (0,
|
|
803
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.RefreshCcw, { size: 16 })
|
|
858
804
|
}
|
|
859
805
|
)
|
|
860
806
|
] })
|
|
@@ -863,15 +809,15 @@ function NewsList({
|
|
|
863
809
|
`${item._id}-link-${idx}`
|
|
864
810
|
));
|
|
865
811
|
}
|
|
866
|
-
return /* @__PURE__ */ (0,
|
|
812
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
867
813
|
"div",
|
|
868
814
|
{
|
|
869
815
|
className: "cnfy-news-card",
|
|
870
816
|
children: [
|
|
871
|
-
/* @__PURE__ */ (0,
|
|
872
|
-
/* @__PURE__ */ (0,
|
|
873
|
-
/* @__PURE__ */ (0,
|
|
874
|
-
/* @__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)(
|
|
875
821
|
"span",
|
|
876
822
|
{
|
|
877
823
|
className: "cnfy-news-badge",
|
|
@@ -883,27 +829,27 @@ function NewsList({
|
|
|
883
829
|
children: item.sourceName
|
|
884
830
|
}
|
|
885
831
|
),
|
|
886
|
-
/* @__PURE__ */ (0,
|
|
887
|
-
/* @__PURE__ */ (0,
|
|
888
|
-
/* @__PURE__ */ (0,
|
|
889
|
-
/* @__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: [
|
|
890
836
|
dateFormatter.format(publishedDate),
|
|
891
837
|
" ",
|
|
892
838
|
timeFormatter.format(publishedDate)
|
|
893
839
|
] })
|
|
894
840
|
] })
|
|
895
841
|
] }),
|
|
896
|
-
/* @__PURE__ */ (0,
|
|
897
|
-
/* @__PURE__ */ (0,
|
|
842
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cnfy-news-card-actions", children: [
|
|
843
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
898
844
|
"button",
|
|
899
845
|
{
|
|
900
846
|
onClick: () => onView(item),
|
|
901
847
|
className: "cnfy-news-action-btn",
|
|
902
848
|
title: "View",
|
|
903
|
-
children: /* @__PURE__ */ (0,
|
|
849
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.Eye, { size: 16 })
|
|
904
850
|
}
|
|
905
851
|
),
|
|
906
|
-
/* @__PURE__ */ (0,
|
|
852
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
907
853
|
"button",
|
|
908
854
|
{
|
|
909
855
|
onClick: () => onRecreate({
|
|
@@ -913,7 +859,7 @@ function NewsList({
|
|
|
913
859
|
}),
|
|
914
860
|
className: "cnfy-news-action-btn",
|
|
915
861
|
title: "Recreate",
|
|
916
|
-
children: /* @__PURE__ */ (0,
|
|
862
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.RefreshCcw, { size: 16 })
|
|
917
863
|
}
|
|
918
864
|
)
|
|
919
865
|
] })
|
|
@@ -924,27 +870,103 @@ function NewsList({
|
|
|
924
870
|
}) });
|
|
925
871
|
}
|
|
926
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
|
+
|
|
927
937
|
// services/news.service.ts
|
|
928
938
|
var getTrendingNews = async () => {
|
|
929
|
-
const { data } = await api_default.get("/
|
|
939
|
+
const { data } = await api_default.get("/news/trending");
|
|
930
940
|
return data.data;
|
|
931
941
|
};
|
|
932
942
|
var getNewsSources = async () => {
|
|
933
|
-
const { data } = await api_default.get("/
|
|
943
|
+
const { data } = await api_default.get("/news/sources");
|
|
934
944
|
return data.data;
|
|
935
945
|
};
|
|
936
946
|
var scrapeNewsSource = async (sourceId) => {
|
|
937
|
-
const { data } = await api_default.post("/
|
|
947
|
+
const { data } = await api_default.post("/news/source", { sourceId });
|
|
938
948
|
return data.data;
|
|
939
949
|
};
|
|
940
950
|
var getNewsBySource = async (sourceId) => {
|
|
941
|
-
const { data } = await api_default.get(`/
|
|
951
|
+
const { data } = await api_default.get(`/news/by-source/${sourceId}`);
|
|
942
952
|
return data.data;
|
|
943
953
|
};
|
|
944
954
|
|
|
945
955
|
// components/chatbot/ChatWindow.tsx
|
|
946
|
-
var
|
|
947
|
-
|
|
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
|
+
}
|
|
948
970
|
var ACTION_ICONS = {
|
|
949
971
|
recreate_article: import_lucide_react4.RefreshCcw,
|
|
950
972
|
create_summary: import_lucide_react4.ListChecks,
|
|
@@ -961,26 +983,48 @@ function ChatWindow({
|
|
|
961
983
|
activeField,
|
|
962
984
|
analyzedData,
|
|
963
985
|
onSelectAction,
|
|
964
|
-
|
|
986
|
+
onResolveSeo
|
|
965
987
|
}) {
|
|
966
|
-
var _a, _b;
|
|
967
988
|
const { showNewsPanel } = useTheme();
|
|
968
|
-
const
|
|
969
|
-
const
|
|
970
|
-
const
|
|
971
|
-
const
|
|
972
|
-
const
|
|
973
|
-
const [
|
|
974
|
-
const [
|
|
975
|
-
const [
|
|
976
|
-
const [
|
|
977
|
-
const [
|
|
978
|
-
const [
|
|
979
|
-
const [
|
|
980
|
-
const [
|
|
981
|
-
const
|
|
982
|
-
const
|
|
983
|
-
const
|
|
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
|
+
}, []);
|
|
984
1028
|
const handleCopy = async (parsed, messageId) => {
|
|
985
1029
|
try {
|
|
986
1030
|
const tempDiv = document.createElement("div");
|
|
@@ -1000,6 +1044,7 @@ function ChatWindow({
|
|
|
1000
1044
|
}
|
|
1001
1045
|
};
|
|
1002
1046
|
const handleEdit = (parsed, messageId) => {
|
|
1047
|
+
var _a;
|
|
1003
1048
|
setEditModal({
|
|
1004
1049
|
isOpen: true,
|
|
1005
1050
|
title: parsed.title || "",
|
|
@@ -1007,23 +1052,99 @@ function ChatWindow({
|
|
|
1007
1052
|
content: parsed.articleHtml || "",
|
|
1008
1053
|
metaDescription: parsed.metaDescription || "",
|
|
1009
1054
|
metaKeywords: parsed.metaKeywords,
|
|
1010
|
-
messageId
|
|
1055
|
+
messageId,
|
|
1056
|
+
featuredImage: selectedImages[messageId] || "",
|
|
1057
|
+
seoAnalysis: (_a = parsed.seoAnalysis) != null ? _a : null
|
|
1011
1058
|
});
|
|
1012
1059
|
};
|
|
1060
|
+
const [downloadingKey, setDownloadingKey] = (0, import_react5.useState)(null);
|
|
1013
1061
|
const handleCloseModal = () => {
|
|
1014
|
-
setEditModal({ isOpen: false, title: "", subtitle: "", content: "", metaDescription: "", metaKeywords: [], messageId: "" });
|
|
1062
|
+
setEditModal({ isOpen: false, title: "", subtitle: "", content: "", metaDescription: "", metaKeywords: [], messageId: "", featuredImage: "", seoAnalysis: null });
|
|
1015
1063
|
};
|
|
1016
|
-
const
|
|
1017
|
-
|
|
1018
|
-
|
|
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
|
+
}
|
|
1019
1135
|
};
|
|
1020
|
-
const
|
|
1021
|
-
|
|
1022
|
-
|
|
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
|
+
}
|
|
1023
1144
|
};
|
|
1024
|
-
(0,
|
|
1025
|
-
var
|
|
1026
|
-
(
|
|
1145
|
+
(0, import_react5.useLayoutEffect)(() => {
|
|
1146
|
+
var _a;
|
|
1147
|
+
(_a = bottomRef.current) == null ? void 0 : _a.scrollIntoView({ block: "end" });
|
|
1027
1148
|
}, [messages]);
|
|
1028
1149
|
const handleSend = () => {
|
|
1029
1150
|
if (!input.trim()) return;
|
|
@@ -1031,13 +1152,16 @@ function ChatWindow({
|
|
|
1031
1152
|
setInput("");
|
|
1032
1153
|
if (textareaRef.current) {
|
|
1033
1154
|
textareaRef.current.style.height = "auto";
|
|
1155
|
+
textareaRef.current.style.borderRadius = "50px";
|
|
1034
1156
|
}
|
|
1035
1157
|
};
|
|
1036
|
-
(0,
|
|
1158
|
+
(0, import_react5.useEffect)(() => {
|
|
1037
1159
|
const textarea = textareaRef.current;
|
|
1038
1160
|
if (textarea) {
|
|
1039
1161
|
textarea.style.height = "auto";
|
|
1040
1162
|
textarea.style.height = `${textarea.scrollHeight}px`;
|
|
1163
|
+
const isMultiline = textarea.scrollHeight > 48;
|
|
1164
|
+
textarea.style.borderRadius = isMultiline ? "1.25rem" : "50px";
|
|
1041
1165
|
}
|
|
1042
1166
|
}, [input]);
|
|
1043
1167
|
const fetchSources = async () => {
|
|
@@ -1063,6 +1187,7 @@ function ChatWindow({
|
|
|
1063
1187
|
setTrendingNews(data || []);
|
|
1064
1188
|
} catch (err) {
|
|
1065
1189
|
console.error("Failed to fetch news:", err);
|
|
1190
|
+
setTrendingNews([]);
|
|
1066
1191
|
} finally {
|
|
1067
1192
|
setLoadingNews(false);
|
|
1068
1193
|
}
|
|
@@ -1079,9 +1204,7 @@ function ChatWindow({
|
|
|
1079
1204
|
setScraping(false);
|
|
1080
1205
|
}
|
|
1081
1206
|
};
|
|
1082
|
-
const handleSourceSelect = (
|
|
1083
|
-
var _a2;
|
|
1084
|
-
const sourceId = (_a2 = option == null ? void 0 : option.value) != null ? _a2 : null;
|
|
1207
|
+
const handleSourceSelect = (sourceId) => {
|
|
1085
1208
|
setSelectedSource(sourceId);
|
|
1086
1209
|
fetchNews(sourceId);
|
|
1087
1210
|
};
|
|
@@ -1094,7 +1217,7 @@ function ChatWindow({
|
|
|
1094
1217
|
fetchNews(selectedSource);
|
|
1095
1218
|
}
|
|
1096
1219
|
};
|
|
1097
|
-
(0,
|
|
1220
|
+
(0, import_react5.useEffect)(() => {
|
|
1098
1221
|
const handleClickOutside = (event) => {
|
|
1099
1222
|
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
|
1100
1223
|
setShowNewsDropdown(false);
|
|
@@ -1107,315 +1230,479 @@ function ChatWindow({
|
|
|
1107
1230
|
document.removeEventListener("mousedown", handleClickOutside);
|
|
1108
1231
|
};
|
|
1109
1232
|
}, [showNewsDropdown]);
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
parsed.title && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("h1", { className: "cnfy-block-h1", children: [
|
|
1131
|
-
parsed.title,
|
|
1132
|
-
activeField === "title" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "cnfy-cursor-blink", children: "|" })
|
|
1133
|
-
] }),
|
|
1134
|
-
parsed.subtitle && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("h1", { className: "cnfy-block-h2", children: [
|
|
1135
|
-
parsed.subtitle,
|
|
1136
|
-
activeField === "subtitle" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "cnfy-cursor-blink", children: "|" })
|
|
1137
|
-
] }),
|
|
1138
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("hr", { className: "cnfy-divider" }),
|
|
1139
|
-
parsed.articleHtml && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1140
|
-
"div",
|
|
1141
|
-
{
|
|
1142
|
-
className: "cnfy-article-content",
|
|
1143
|
-
dangerouslySetInnerHTML: { __html: parsed.articleHtml }
|
|
1144
|
-
}
|
|
1145
|
-
),
|
|
1146
|
-
activeField === "article" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "cnfy-cursor-blink", children: "|" })
|
|
1147
|
-
] }) : (
|
|
1148
|
-
/* Plain text messages (status, error, options, etc.) */
|
|
1149
|
-
parsed.plainText && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "cnfy-block-p", children: parsed.plainText })
|
|
1150
|
-
),
|
|
1151
|
-
parsed.metaDescription && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "cnfy-msg-keywords", children: [
|
|
1152
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-msg-keywords-label", children: "Meta Description" }),
|
|
1153
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-block-p", children: parsed.metaDescription })
|
|
1154
|
-
] }),
|
|
1155
|
-
parsed.metaKeywords.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "cnfy-msg-keywords", children: [
|
|
1156
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-msg-keywords-label", children: "Keywords" }),
|
|
1157
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-msg-keywords-list", children: parsed.metaKeywords.map((tag, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1158
|
-
"span",
|
|
1159
|
-
{
|
|
1160
|
-
className: "cnfy-msg-keyword-tag",
|
|
1161
|
-
children: tag
|
|
1162
|
-
},
|
|
1163
|
-
i
|
|
1164
|
-
)) })
|
|
1165
|
-
] }),
|
|
1166
|
-
msg.role === "assistant" && (analyzedData == null ? void 0 : analyzedData.messageId) === msg.id && analyzedData.options.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-action-options", children: analyzedData.options.map((option) => {
|
|
1167
|
-
const IconComponent = ACTION_ICONS[option.id] || import_lucide_react4.FileText;
|
|
1168
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1169
|
-
"button",
|
|
1170
|
-
{
|
|
1171
|
-
onClick: () => onSelectAction == null ? void 0 : onSelectAction(option.id, analyzedData.url, analyzedData.content),
|
|
1172
|
-
className: "cnfy-action-btn",
|
|
1173
|
-
children: [
|
|
1174
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(IconComponent, { size: 16 }),
|
|
1175
|
-
option.name
|
|
1176
|
-
]
|
|
1177
|
-
},
|
|
1178
|
-
option.id
|
|
1179
|
-
);
|
|
1180
|
-
}) }),
|
|
1181
|
-
msg.role === "assistant" && parsed.isArticle && !(analyzedData == null ? void 0 : analyzedData.messageId) && (!isStreaming || msg.id !== ((_b2 = messages[messages.length - 1]) == null ? void 0 : _b2.id)) && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "cnfy-msg-actions", children: [
|
|
1182
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1183
|
-
"button",
|
|
1184
|
-
{
|
|
1185
|
-
onClick: () => handleEdit(parsed, msg.id),
|
|
1186
|
-
className: "cnfy-btn-edit",
|
|
1187
|
-
children: [
|
|
1188
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Edit3, { size: 16 }),
|
|
1189
|
-
"Edit"
|
|
1190
|
-
]
|
|
1191
|
-
}
|
|
1192
|
-
),
|
|
1193
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1194
|
-
"button",
|
|
1195
|
-
{
|
|
1196
|
-
onClick: () => handlePost({ article: parsed.articleHtml, keywords: parsed.metaKeywords }),
|
|
1197
|
-
className: "cnfy-btn-post",
|
|
1198
|
-
style: { backgroundColor: primaryColor, color: "#fff" },
|
|
1199
|
-
children: [
|
|
1200
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Send, { size: 16 }),
|
|
1201
|
-
"Post"
|
|
1202
|
-
]
|
|
1203
|
-
}
|
|
1204
|
-
)
|
|
1205
|
-
] })
|
|
1206
|
-
] }) }, msg.id);
|
|
1207
|
-
}),
|
|
1208
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { ref: bottomRef })
|
|
1209
|
-
] }) }),
|
|
1210
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-input-area", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "cnfy-input-inner", children: [
|
|
1211
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { ref: dropdownRef, className: "cnfy-news-pulse-wrap", children: [
|
|
1212
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1213
|
-
"button",
|
|
1214
|
-
{
|
|
1215
|
-
onClick: handleOpenNewsDropdown,
|
|
1216
|
-
className: "cnfy-news-pulse-btn cnfy-animate-pulse",
|
|
1217
|
-
title: "Select from trending news",
|
|
1218
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Zap, { size: 16 })
|
|
1219
|
-
}
|
|
1220
|
-
),
|
|
1221
|
-
showNewsDropdown && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "cnfy-news-dropdown", children: [
|
|
1222
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "cnfy-news-dropdown-header", style: { backgroundColor: primaryColor, color: "#fff" }, children: [
|
|
1223
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "cnfy-news-dropdown-title", children: "Select News" }),
|
|
1224
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1225
|
-
"button",
|
|
1226
|
-
{
|
|
1227
|
-
onClick: () => setShowNewsDropdown(false),
|
|
1228
|
-
className: "cnfy-news-dropdown-close",
|
|
1229
|
-
style: { backgroundColor: primaryColor, color: "#fff" },
|
|
1230
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.X, { size: 14 })
|
|
1231
|
-
}
|
|
1232
|
-
)
|
|
1233
|
-
] }),
|
|
1234
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "cnfy-news-dropdown-source", children: [
|
|
1235
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-news-dropdown-select-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1236
|
-
import_react_select.default,
|
|
1237
|
-
{
|
|
1238
|
-
options: [
|
|
1239
|
-
{ value: null, label: "All Sources (Trending)" },
|
|
1240
|
-
...sources.filter((source) => !preferredLanguage || source.language === preferredLanguage).map((source) => ({
|
|
1241
|
-
value: source.id,
|
|
1242
|
-
label: source.name
|
|
1243
|
-
}))
|
|
1244
|
-
],
|
|
1245
|
-
value: selectedSource ? { value: selectedSource, label: ((_b = sources.find((s) => s.id === selectedSource)) == null ? void 0 : _b.name) || selectedSource } : { value: null, label: "All Sources (Trending)" },
|
|
1246
|
-
onChange: handleSourceSelect,
|
|
1247
|
-
isLoading: loadingSources,
|
|
1248
|
-
isDisabled: loadingSources,
|
|
1249
|
-
placeholder: "Select source...",
|
|
1250
|
-
classNamePrefix: "react-select",
|
|
1251
|
-
menuPlacement: "bottom",
|
|
1252
|
-
menuPosition: "fixed",
|
|
1253
|
-
menuShouldScrollIntoView: false,
|
|
1254
|
-
maxMenuHeight: 220,
|
|
1255
|
-
menuPortalTarget: typeof window !== "undefined" ? document.body : null,
|
|
1256
|
-
styles: {
|
|
1257
|
-
container: (base) => __spreadProps(__spreadValues({}, base), {
|
|
1258
|
-
width: "100%"
|
|
1259
|
-
}),
|
|
1260
|
-
control: (base) => __spreadProps(__spreadValues({}, base), {
|
|
1261
|
-
minHeight: "38px",
|
|
1262
|
-
borderColor: "#e5e7eb",
|
|
1263
|
-
boxShadow: "none",
|
|
1264
|
-
width: "100%"
|
|
1265
|
-
}),
|
|
1266
|
-
menu: (base) => __spreadProps(__spreadValues({}, base), {
|
|
1267
|
-
width: "100%",
|
|
1268
|
-
zIndex: 999999
|
|
1269
|
-
}),
|
|
1270
|
-
menuPortal: (base) => __spreadProps(__spreadValues({}, base), {
|
|
1271
|
-
zIndex: 999999
|
|
1272
|
-
}),
|
|
1273
|
-
option: (base, state) => __spreadProps(__spreadValues({}, base), {
|
|
1274
|
-
backgroundColor: state.isSelected ? primaryColor : state.isFocused ? "#f3f4f6" : "white",
|
|
1275
|
-
color: state.isSelected ? "white" : "#374151",
|
|
1276
|
-
cursor: "pointer"
|
|
1277
|
-
})
|
|
1278
|
-
}
|
|
1279
|
-
}
|
|
1280
|
-
) }),
|
|
1281
|
-
selectedSource && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1282
|
-
"button",
|
|
1283
|
-
{
|
|
1284
|
-
onClick: handleScrape,
|
|
1285
|
-
disabled: scraping,
|
|
1286
|
-
className: "cnfy-scrape-btn",
|
|
1287
|
-
title: "Fetch latest news",
|
|
1288
|
-
children: [
|
|
1289
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.RefreshCcw, { size: 14, className: scraping ? "cnfy-animate-spin" : "" }),
|
|
1290
|
-
scraping ? "Scraping..." : "Scrape"
|
|
1291
|
-
]
|
|
1292
|
-
}
|
|
1293
|
-
),
|
|
1294
|
-
!selectedSource && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1295
|
-
"button",
|
|
1296
|
-
{
|
|
1297
|
-
onClick: () => fetchNews(null),
|
|
1298
|
-
disabled: loadingNews,
|
|
1299
|
-
className: "cnfy-refresh-btn",
|
|
1300
|
-
children: [
|
|
1301
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.RefreshCcw, { size: 14, className: loadingNews ? "cnfy-animate-spin" : "" }),
|
|
1302
|
-
"Refresh"
|
|
1303
|
-
]
|
|
1304
|
-
}
|
|
1305
|
-
)
|
|
1306
|
-
] }),
|
|
1307
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-news-dropdown-list", children: loadingNews ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-news-dropdown-msg", children: "Loading news..." }) : trendingNews.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "cnfy-news-dropdown-msg", children: [
|
|
1308
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { children: "No news found." }),
|
|
1309
|
-
selectedSource && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "cnfy-news-dropdown-hint", children: 'Click "Scrape" to fetch latest news.' })
|
|
1310
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1311
|
-
NewsList,
|
|
1312
|
-
{
|
|
1313
|
-
news: trendingNews.slice(0, 10),
|
|
1314
|
-
onView: (item) => window.open(item.sourceUrl || item.link, "_blank"),
|
|
1315
|
-
onRecreate: (payload) => {
|
|
1316
|
-
setShowNewsDropdown(false);
|
|
1317
|
-
onSelectNews == null ? void 0 : onSelectNews(payload);
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1320
|
-
) })
|
|
1321
|
-
] })
|
|
1322
|
-
] }),
|
|
1323
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1324
|
-
"textarea",
|
|
1325
|
-
{
|
|
1326
|
-
ref: textareaRef,
|
|
1327
|
-
value: input,
|
|
1328
|
-
onChange: (e) => setInput(e.target.value),
|
|
1329
|
-
onKeyDown: (e) => {
|
|
1330
|
-
if (e.key === "Enter" && !e.shiftKey) {
|
|
1331
|
-
e.preventDefault();
|
|
1332
|
-
handleSend();
|
|
1333
|
-
}
|
|
1334
|
-
},
|
|
1335
|
-
rows: 1,
|
|
1336
|
-
placeholder: "Ask AI something\u2026",
|
|
1337
|
-
className: `cnfy-chat-textarea ${!showNewsPanel ? "cnfy-chat-textarea--with-pulse" : ""}`,
|
|
1338
|
-
style: { maxHeight: "200px", overflowY: input.split("\n").length > 6 ? "auto" : "hidden" },
|
|
1339
|
-
disabled: true
|
|
1340
|
-
}
|
|
1341
|
-
),
|
|
1342
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1343
|
-
"button",
|
|
1344
|
-
{
|
|
1345
|
-
onClick: handleSend,
|
|
1346
|
-
className: "cnfy-send-btn",
|
|
1347
|
-
style: { backgroundColor: primaryColor, color: "#fff" },
|
|
1348
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.SendHorizontal, { size: 18 })
|
|
1349
|
-
}
|
|
1350
|
-
)
|
|
1351
|
-
] }) }),
|
|
1352
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1353
|
-
EditModal,
|
|
1354
|
-
{
|
|
1355
|
-
isOpen: editModal.isOpen,
|
|
1356
|
-
initialTitle: editModal.title,
|
|
1357
|
-
initialSubtitle: editModal.subtitle,
|
|
1358
|
-
initialContent: editModal.content,
|
|
1359
|
-
initialMetaDescription: editModal.metaDescription,
|
|
1360
|
-
metaKeywords: editModal.metaKeywords,
|
|
1361
|
-
onClose: handleCloseModal,
|
|
1362
|
-
onSaveDraft: (data) => handleSaveDraft({ article: data.article, keywords: data.keywords }),
|
|
1363
|
-
onPost: (data) => handlePost({ article: data.article, keywords: data.keywords })
|
|
1364
|
-
}
|
|
1365
|
-
)
|
|
1366
|
-
] });
|
|
1367
|
-
}
|
|
1368
|
-
|
|
1369
|
-
// components/chatbot/UserMenu.tsx
|
|
1370
|
-
var import_lucide_react5 = require("lucide-react");
|
|
1371
|
-
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1372
|
-
function UserMenu({
|
|
1373
|
-
onOpenPreferences
|
|
1374
|
-
}) {
|
|
1375
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "cnfy-user-menu", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1376
|
-
"button",
|
|
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",
|
|
1377
1253
|
{
|
|
1378
|
-
|
|
1379
|
-
className: "cnfy-
|
|
1380
|
-
|
|
1381
|
-
|
|
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",
|
|
1542
|
+
{
|
|
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
|
+
]
|
|
1553
|
+
},
|
|
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
|
+
]
|
|
1661
|
+
}
|
|
1662
|
+
),
|
|
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,
|
|
1671
|
+
{
|
|
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
|
|
1686
|
+
}
|
|
1687
|
+
)
|
|
1688
|
+
}
|
|
1689
|
+
)
|
|
1690
|
+
]
|
|
1382
1691
|
}
|
|
1383
|
-
)
|
|
1384
|
-
}
|
|
1385
|
-
|
|
1386
|
-
// components/chatbot/headerBar.tsx
|
|
1387
|
-
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1388
|
-
function HeaderBar({
|
|
1389
|
-
onOpenPreferences
|
|
1390
|
-
}) {
|
|
1391
|
-
const { primaryColor, botName, logoUrl } = useTheme();
|
|
1392
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("header", { className: "cnfy-header", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "cnfy-header-inner", children: [
|
|
1393
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "cnfy-header-left", children: [
|
|
1394
|
-
logoUrl ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("img", { src: logoUrl, alt: "Logo", className: "cnfy-header-logo-img" }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1395
|
-
"div",
|
|
1396
|
-
{
|
|
1397
|
-
className: "cnfy-header-logo-fallback",
|
|
1398
|
-
style: { backgroundColor: primaryColor },
|
|
1399
|
-
children: botName.charAt(0).toUpperCase()
|
|
1400
|
-
}
|
|
1401
|
-
),
|
|
1402
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "cnfy-header-brand", children: botName.toUpperCase() })
|
|
1403
|
-
] }),
|
|
1404
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "cnfy-header-right", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(UserMenu, { onOpenPreferences }) })
|
|
1405
|
-
] }) });
|
|
1692
|
+
);
|
|
1406
1693
|
}
|
|
1407
1694
|
|
|
1408
1695
|
// components/ui/Drawer.tsx
|
|
1409
|
-
var
|
|
1410
|
-
var
|
|
1411
|
-
var
|
|
1696
|
+
var import_react6 = require("react");
|
|
1697
|
+
var import_lucide_react5 = require("lucide-react");
|
|
1698
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1412
1699
|
function Drawer({
|
|
1413
1700
|
open,
|
|
1414
1701
|
onClose,
|
|
1415
1702
|
title,
|
|
1416
1703
|
children
|
|
1417
1704
|
}) {
|
|
1418
|
-
(0,
|
|
1705
|
+
(0, import_react6.useEffect)(() => {
|
|
1419
1706
|
if (open) {
|
|
1420
1707
|
document.body.style.overflow = "hidden";
|
|
1421
1708
|
} else {
|
|
@@ -1426,20 +1713,20 @@ function Drawer({
|
|
|
1426
1713
|
};
|
|
1427
1714
|
}, [open]);
|
|
1428
1715
|
if (!open) return null;
|
|
1429
|
-
return /* @__PURE__ */ (0,
|
|
1430
|
-
/* @__PURE__ */ (0,
|
|
1431
|
-
/* @__PURE__ */ (0,
|
|
1432
|
-
/* @__PURE__ */ (0,
|
|
1433
|
-
title && /* @__PURE__ */ (0,
|
|
1434
|
-
/* @__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 }) })
|
|
1435
1722
|
] }),
|
|
1436
|
-
/* @__PURE__ */ (0,
|
|
1723
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-drawer-body", children })
|
|
1437
1724
|
] })
|
|
1438
1725
|
] });
|
|
1439
1726
|
}
|
|
1440
1727
|
|
|
1441
1728
|
// components/preferences/Preferences.tsx
|
|
1442
|
-
var
|
|
1729
|
+
var import_react7 = require("react");
|
|
1443
1730
|
var import_react_hot_toast = __toESM(require("react-hot-toast"));
|
|
1444
1731
|
|
|
1445
1732
|
// services/settings.service.ts
|
|
@@ -1447,45 +1734,19 @@ var triggerScrape = async ({
|
|
|
1447
1734
|
state,
|
|
1448
1735
|
cities
|
|
1449
1736
|
}) => {
|
|
1450
|
-
const { data } = await api_default.post("/
|
|
1737
|
+
const { data } = await api_default.post("/news/trigger", {
|
|
1451
1738
|
state,
|
|
1452
1739
|
cities
|
|
1453
1740
|
});
|
|
1454
1741
|
return data;
|
|
1455
1742
|
};
|
|
1456
1743
|
var getScrapeStatus = async () => {
|
|
1457
|
-
const { data } = await api_default.get("/
|
|
1744
|
+
const { data } = await api_default.get("/news/status");
|
|
1458
1745
|
return data.data;
|
|
1459
1746
|
};
|
|
1460
1747
|
|
|
1461
|
-
// components/ClientSelect.tsx
|
|
1462
|
-
var import_react8 = require("react");
|
|
1463
|
-
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1464
|
-
var ReactSelect = null;
|
|
1465
|
-
var ClientSelect = (props) => {
|
|
1466
|
-
const [mounted, setMounted] = (0, import_react8.useState)(false);
|
|
1467
|
-
(0, import_react8.useEffect)(() => {
|
|
1468
|
-
setMounted(true);
|
|
1469
|
-
import("react-select").then((mod) => {
|
|
1470
|
-
ReactSelect = mod.default;
|
|
1471
|
-
});
|
|
1472
|
-
}, []);
|
|
1473
|
-
if (!mounted || !ReactSelect) return null;
|
|
1474
|
-
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ReactSelect, __spreadValues({}, props));
|
|
1475
|
-
};
|
|
1476
|
-
var ClientSelect_default = ClientSelect;
|
|
1477
|
-
|
|
1478
|
-
// src/utils/util.ts
|
|
1479
|
-
var hexToRgba = (hex, opacity) => {
|
|
1480
|
-
const sanitizedHex = hex.replace("#", "");
|
|
1481
|
-
const r = parseInt(sanitizedHex.substring(0, 2), 16);
|
|
1482
|
-
const g = parseInt(sanitizedHex.substring(2, 4), 16);
|
|
1483
|
-
const b = parseInt(sanitizedHex.substring(4, 6), 16);
|
|
1484
|
-
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
|
1485
|
-
};
|
|
1486
|
-
|
|
1487
1748
|
// components/preferences/Preferences.tsx
|
|
1488
|
-
var
|
|
1749
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1489
1750
|
var STATE_OPTIONS = [
|
|
1490
1751
|
{ value: "Uttar Pradesh", label: "Uttar Pradesh" }
|
|
1491
1752
|
];
|
|
@@ -1520,27 +1781,24 @@ var LANGUAGE_OPTIONS = [
|
|
|
1520
1781
|
{ value: "hi", label: "Hindi" }
|
|
1521
1782
|
];
|
|
1522
1783
|
function PreferencesPage() {
|
|
1523
|
-
|
|
1524
|
-
const
|
|
1525
|
-
const
|
|
1526
|
-
const [
|
|
1527
|
-
const [
|
|
1528
|
-
const [
|
|
1529
|
-
const [
|
|
1530
|
-
const [
|
|
1531
|
-
const [
|
|
1532
|
-
const [
|
|
1533
|
-
const [
|
|
1534
|
-
const [
|
|
1535
|
-
const [scrapeStatus, setScrapeStatus] = (0, import_react9.useState)(null);
|
|
1536
|
-
const [loadingStatus, setLoadingStatus] = (0, import_react9.useState)(true);
|
|
1537
|
-
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)({
|
|
1538
1796
|
botName: "",
|
|
1539
1797
|
state: void 0,
|
|
1540
1798
|
cities: [],
|
|
1541
1799
|
language: void 0
|
|
1542
1800
|
});
|
|
1543
|
-
(0,
|
|
1801
|
+
(0, import_react7.useEffect)(() => {
|
|
1544
1802
|
const fetchScrapeStatus = async () => {
|
|
1545
1803
|
try {
|
|
1546
1804
|
const data = await getScrapeStatus();
|
|
@@ -1554,12 +1812,11 @@ function PreferencesPage() {
|
|
|
1554
1812
|
fetchScrapeStatus();
|
|
1555
1813
|
}, []);
|
|
1556
1814
|
const handleScrape = async () => {
|
|
1557
|
-
var _a2, _b2;
|
|
1558
1815
|
setScraping(true);
|
|
1559
1816
|
try {
|
|
1560
1817
|
await triggerScrape({
|
|
1561
|
-
state:
|
|
1562
|
-
cities: (
|
|
1818
|
+
state: state == null ? void 0 : state.value,
|
|
1819
|
+
cities: cities.map((c) => c.value)
|
|
1563
1820
|
});
|
|
1564
1821
|
import_react_hot_toast.default.success("News scraping started successfully!");
|
|
1565
1822
|
setTimeout(async () => {
|
|
@@ -1577,40 +1834,12 @@ function PreferencesPage() {
|
|
|
1577
1834
|
setScraping(false);
|
|
1578
1835
|
}
|
|
1579
1836
|
};
|
|
1580
|
-
(0, import_react9.useEffect)(() => {
|
|
1581
|
-
var _a2, _b2, _c2, _d, _e;
|
|
1582
|
-
if (preferences) {
|
|
1583
|
-
const name = ((_a2 = preferences.chatbot) == null ? void 0 : _a2.name) || "";
|
|
1584
|
-
const pState = (_b2 = preferences.localization) == null ? void 0 : _b2.state;
|
|
1585
|
-
const pCities = ((_c2 = preferences.localization) == null ? void 0 : _c2.cities) || [];
|
|
1586
|
-
const pLanguage = (_d = preferences.localization) == null ? void 0 : _d.language;
|
|
1587
|
-
setBotName(name);
|
|
1588
|
-
if ((_e = preferences.chatbot) == null ? void 0 : _e.logoUrl) setLogoUrl(preferences.chatbot.logoUrl);
|
|
1589
|
-
if (pState) {
|
|
1590
|
-
setState({ value: pState, label: pState });
|
|
1591
|
-
}
|
|
1592
|
-
if (Array.isArray(pCities)) {
|
|
1593
|
-
setCities(pCities.map((city) => ({ value: city, label: city })));
|
|
1594
|
-
}
|
|
1595
|
-
if (pLanguage) {
|
|
1596
|
-
const langOption = LANGUAGE_OPTIONS.find((opt) => opt.value === pLanguage);
|
|
1597
|
-
setLanguage(langOption || null);
|
|
1598
|
-
}
|
|
1599
|
-
setOriginalValues({
|
|
1600
|
-
botName: name,
|
|
1601
|
-
state: pState,
|
|
1602
|
-
cities: pCities,
|
|
1603
|
-
language: pLanguage
|
|
1604
|
-
});
|
|
1605
|
-
}
|
|
1606
|
-
}, [preferences]);
|
|
1607
1837
|
const handleSave = async () => {
|
|
1608
1838
|
if (!botName) {
|
|
1609
1839
|
import_react_hot_toast.default.error("Chatbot name is required");
|
|
1610
1840
|
return;
|
|
1611
1841
|
}
|
|
1612
1842
|
setSaving(true);
|
|
1613
|
-
setSuccess(false);
|
|
1614
1843
|
try {
|
|
1615
1844
|
const payload = {};
|
|
1616
1845
|
if (botName !== originalValues.botName) {
|
|
@@ -1637,13 +1866,6 @@ function PreferencesPage() {
|
|
|
1637
1866
|
setSaving(false);
|
|
1638
1867
|
return;
|
|
1639
1868
|
}
|
|
1640
|
-
const updatedPreferences = await savePreferencesApi(payload);
|
|
1641
|
-
if (updatedPreferences) {
|
|
1642
|
-
updatePreferences(updatedPreferences);
|
|
1643
|
-
} else {
|
|
1644
|
-
await refreshPreferences();
|
|
1645
|
-
}
|
|
1646
|
-
setSuccess(true);
|
|
1647
1869
|
setLogo(null);
|
|
1648
1870
|
import_react_hot_toast.default.success("Preferences saved successfully!");
|
|
1649
1871
|
} catch (error) {
|
|
@@ -1653,15 +1875,12 @@ function PreferencesPage() {
|
|
|
1653
1875
|
setSaving(false);
|
|
1654
1876
|
}
|
|
1655
1877
|
};
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "cnfy-dash-field", children: [
|
|
1663
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { className: "cnfy-dash-label", children: "Chatbot Name" }),
|
|
1664
|
-
/* @__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)(
|
|
1665
1884
|
"input",
|
|
1666
1885
|
{
|
|
1667
1886
|
value: botName,
|
|
@@ -1671,32 +1890,38 @@ function PreferencesPage() {
|
|
|
1671
1890
|
}
|
|
1672
1891
|
)
|
|
1673
1892
|
] }),
|
|
1674
|
-
/* @__PURE__ */ (0,
|
|
1675
|
-
/* @__PURE__ */ (0,
|
|
1676
|
-
/* @__PURE__ */ (0,
|
|
1677
|
-
/* @__PURE__ */ (0,
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
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)(
|
|
1693
1918
|
"input",
|
|
1694
1919
|
{
|
|
1695
1920
|
type: "file",
|
|
1696
1921
|
accept: "image/*",
|
|
1697
1922
|
onChange: (e) => {
|
|
1698
|
-
var
|
|
1699
|
-
return setLogo(((
|
|
1923
|
+
var _a;
|
|
1924
|
+
return setLogo(((_a = e.target.files) == null ? void 0 : _a[0]) || null);
|
|
1700
1925
|
},
|
|
1701
1926
|
className: "cnfy-file-input"
|
|
1702
1927
|
}
|
|
@@ -1704,38 +1929,38 @@ function PreferencesPage() {
|
|
|
1704
1929
|
] })
|
|
1705
1930
|
] })
|
|
1706
1931
|
] }),
|
|
1707
|
-
/* @__PURE__ */ (0,
|
|
1708
|
-
/* @__PURE__ */ (0,
|
|
1709
|
-
/* @__PURE__ */ (0,
|
|
1710
|
-
/* @__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" })
|
|
1711
1936
|
] }),
|
|
1712
|
-
!loadingStatus && scrapeStatus && /* @__PURE__ */ (0,
|
|
1713
|
-
/* @__PURE__ */ (0,
|
|
1714
|
-
/* @__PURE__ */ (0,
|
|
1715
|
-
/* @__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" }) })
|
|
1716
1941
|
] }),
|
|
1717
|
-
scrapeStatus.lastRun && /* @__PURE__ */ (0,
|
|
1718
|
-
/* @__PURE__ */ (0,
|
|
1719
|
-
/* @__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() })
|
|
1720
1945
|
] }),
|
|
1721
|
-
scrapeStatus.totalScraped !== void 0 && /* @__PURE__ */ (0,
|
|
1722
|
-
/* @__PURE__ */ (0,
|
|
1723
|
-
/* @__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: [
|
|
1724
1949
|
scrapeStatus.totalScraped.toLocaleString(),
|
|
1725
1950
|
" articles"
|
|
1726
1951
|
] })
|
|
1727
1952
|
] })
|
|
1728
1953
|
] }),
|
|
1729
|
-
/* @__PURE__ */ (0,
|
|
1730
|
-
/* @__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: [
|
|
1731
1956
|
"Current configuration: ",
|
|
1732
|
-
/* @__PURE__ */ (0,
|
|
1733
|
-
|
|
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: [
|
|
1734
1959
|
" - ",
|
|
1735
|
-
|
|
1960
|
+
cities.map((c) => c.label).join(", ")
|
|
1736
1961
|
] })
|
|
1737
1962
|
] }),
|
|
1738
|
-
/* @__PURE__ */ (0,
|
|
1963
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1739
1964
|
"button",
|
|
1740
1965
|
{
|
|
1741
1966
|
onClick: handleScrape,
|
|
@@ -1747,48 +1972,54 @@ function PreferencesPage() {
|
|
|
1747
1972
|
)
|
|
1748
1973
|
] })
|
|
1749
1974
|
] }),
|
|
1750
|
-
/* @__PURE__ */ (0,
|
|
1751
|
-
/* @__PURE__ */ (0,
|
|
1752
|
-
/* @__PURE__ */ (0,
|
|
1753
|
-
/* @__PURE__ */ (0,
|
|
1754
|
-
/* @__PURE__ */ (0,
|
|
1755
|
-
|
|
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",
|
|
1756
1981
|
{
|
|
1757
|
-
|
|
1758
|
-
value: state,
|
|
1759
|
-
onChange: (
|
|
1760
|
-
|
|
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
|
+
]
|
|
1761
1989
|
}
|
|
1762
1990
|
)
|
|
1763
1991
|
] }),
|
|
1764
|
-
/* @__PURE__ */ (0,
|
|
1765
|
-
/* @__PURE__ */ (0,
|
|
1766
|
-
/* @__PURE__ */ (0,
|
|
1767
|
-
|
|
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",
|
|
1768
1996
|
{
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
value: cities,
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
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))
|
|
1775
2003
|
}
|
|
1776
2004
|
)
|
|
1777
2005
|
] }),
|
|
1778
|
-
/* @__PURE__ */ (0,
|
|
1779
|
-
/* @__PURE__ */ (0,
|
|
1780
|
-
/* @__PURE__ */ (0,
|
|
1781
|
-
|
|
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",
|
|
1782
2010
|
{
|
|
1783
|
-
|
|
1784
|
-
value: language,
|
|
1785
|
-
onChange: (
|
|
1786
|
-
|
|
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
|
+
]
|
|
1787
2018
|
}
|
|
1788
2019
|
)
|
|
1789
2020
|
] })
|
|
1790
2021
|
] }),
|
|
1791
|
-
/* @__PURE__ */ (0,
|
|
2022
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "cnfy-dash-actions", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1792
2023
|
"button",
|
|
1793
2024
|
{
|
|
1794
2025
|
onClick: handleSave,
|
|
@@ -1801,14 +2032,245 @@ function PreferencesPage() {
|
|
|
1801
2032
|
] });
|
|
1802
2033
|
}
|
|
1803
2034
|
|
|
1804
|
-
//
|
|
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
|
|
1805
2040
|
function getHeaders() {
|
|
2041
|
+
const token = typeof window !== "undefined" ? localStorage.getItem("token") : null;
|
|
1806
2042
|
const apiKey = getApiKey();
|
|
1807
|
-
|
|
2043
|
+
return __spreadValues(__spreadValues({
|
|
1808
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
|
+
}
|
|
1809
2148
|
};
|
|
1810
|
-
|
|
1811
|
-
|
|
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
|
+
|
|
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}`;
|
|
1812
2274
|
}
|
|
1813
2275
|
return headers;
|
|
1814
2276
|
}
|
|
@@ -1816,7 +2278,7 @@ async function processSSEStream(reader, callbacks) {
|
|
|
1816
2278
|
const decoder = new TextDecoder();
|
|
1817
2279
|
let buffer = "";
|
|
1818
2280
|
const processLines = (lines) => {
|
|
1819
|
-
var _a, _b, _c, _d, _e;
|
|
2281
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1820
2282
|
for (const line of lines) {
|
|
1821
2283
|
const trimmed = line.trim();
|
|
1822
2284
|
if (!trimmed.startsWith("data: ")) continue;
|
|
@@ -1826,6 +2288,7 @@ async function processSSEStream(reader, callbacks) {
|
|
|
1826
2288
|
switch (event.type) {
|
|
1827
2289
|
case "start":
|
|
1828
2290
|
break;
|
|
2291
|
+
// Structured streaming (articleId present): field-scoped chunks
|
|
1829
2292
|
case "field_start":
|
|
1830
2293
|
(_a = callbacks.onFieldStart) == null ? void 0 : _a.call(callbacks, event.field);
|
|
1831
2294
|
break;
|
|
@@ -1841,8 +2304,12 @@ async function processSSEStream(reader, callbacks) {
|
|
|
1841
2304
|
case "done":
|
|
1842
2305
|
(_d = callbacks.onComplete) == null ? void 0 : _d.call(callbacks, event.data);
|
|
1843
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;
|
|
1844
2311
|
case "error":
|
|
1845
|
-
(
|
|
2312
|
+
(_f = callbacks.onError) == null ? void 0 : _f.call(callbacks, new Error(event.message));
|
|
1846
2313
|
return true;
|
|
1847
2314
|
}
|
|
1848
2315
|
} catch (e) {
|
|
@@ -1858,16 +2325,14 @@ async function processSSEStream(reader, callbacks) {
|
|
|
1858
2325
|
buffer = lines.pop() || "";
|
|
1859
2326
|
if (processLines(lines)) return;
|
|
1860
2327
|
}
|
|
1861
|
-
if (buffer.trim())
|
|
1862
|
-
processLines([buffer]);
|
|
1863
|
-
}
|
|
2328
|
+
if (buffer.trim()) processLines([buffer]);
|
|
1864
2329
|
}
|
|
1865
2330
|
var analyzeInputApi = async (input) => {
|
|
1866
|
-
const { data } = await api_default.post("/
|
|
2331
|
+
const { data } = await api_default.post("/contenify/analyze-input", { input });
|
|
1867
2332
|
return data.data;
|
|
1868
2333
|
};
|
|
1869
2334
|
var createContentApi = async (payload) => {
|
|
1870
|
-
const { data } = await api_default.post("/
|
|
2335
|
+
const { data } = await api_default.post("/contenify/create-content", payload);
|
|
1871
2336
|
return data.data;
|
|
1872
2337
|
};
|
|
1873
2338
|
var createContentStreamApi = async ({
|
|
@@ -1884,9 +2349,9 @@ var createContentStreamApi = async ({
|
|
|
1884
2349
|
}) => {
|
|
1885
2350
|
var _a;
|
|
1886
2351
|
const API_BASE_URL = getApiBaseUrl();
|
|
1887
|
-
const response = await fetch(`${API_BASE_URL}/
|
|
2352
|
+
const response = await fetch(`${API_BASE_URL}/contenify/create-content/stream`, {
|
|
1888
2353
|
method: "POST",
|
|
1889
|
-
headers:
|
|
2354
|
+
headers: getHeaders2(),
|
|
1890
2355
|
credentials: "include",
|
|
1891
2356
|
body: JSON.stringify({ url, content, actionId, settings })
|
|
1892
2357
|
});
|
|
@@ -1895,17 +2360,8 @@ var createContentStreamApi = async ({
|
|
|
1895
2360
|
throw new Error(`HTTP error! status: ${response.status} - ${errorText}`);
|
|
1896
2361
|
}
|
|
1897
2362
|
const reader = (_a = response.body) == null ? void 0 : _a.getReader();
|
|
1898
|
-
if (!reader)
|
|
1899
|
-
|
|
1900
|
-
}
|
|
1901
|
-
await processSSEStream(reader, {
|
|
1902
|
-
onFieldStart,
|
|
1903
|
-
onContent,
|
|
1904
|
-
onKeywords,
|
|
1905
|
-
onFieldEnd,
|
|
1906
|
-
onComplete,
|
|
1907
|
-
onError
|
|
1908
|
-
});
|
|
2363
|
+
if (!reader) throw new Error("No reader available");
|
|
2364
|
+
await processSSEStream(reader, { onFieldStart, onContent, onKeywords, onFieldEnd, onComplete, onError });
|
|
1909
2365
|
};
|
|
1910
2366
|
var rewriteNewsStreamApi = async ({
|
|
1911
2367
|
article,
|
|
@@ -1925,10 +2381,7 @@ var rewriteNewsStreamApi = async ({
|
|
|
1925
2381
|
onError
|
|
1926
2382
|
}) => {
|
|
1927
2383
|
var _a;
|
|
1928
|
-
const payload = {
|
|
1929
|
-
article,
|
|
1930
|
-
language
|
|
1931
|
-
};
|
|
2384
|
+
const payload = { article, language, temperature: 1 };
|
|
1932
2385
|
if (articleId) payload.articleId = articleId;
|
|
1933
2386
|
if (tone) payload.tone = tone;
|
|
1934
2387
|
if (style) payload.style = style;
|
|
@@ -1938,9 +2391,9 @@ var rewriteNewsStreamApi = async ({
|
|
|
1938
2391
|
if (targetAudience) payload.targetAudience = targetAudience;
|
|
1939
2392
|
try {
|
|
1940
2393
|
const API_BASE_URL = getApiBaseUrl();
|
|
1941
|
-
const response = await fetch(`${API_BASE_URL}/
|
|
2394
|
+
const response = await fetch(`${API_BASE_URL}/contenify/rewrite/stream`, {
|
|
1942
2395
|
method: "POST",
|
|
1943
|
-
headers:
|
|
2396
|
+
headers: getHeaders2(),
|
|
1944
2397
|
credentials: "include",
|
|
1945
2398
|
body: JSON.stringify(payload)
|
|
1946
2399
|
});
|
|
@@ -1951,33 +2404,56 @@ var rewriteNewsStreamApi = async ({
|
|
|
1951
2404
|
const contentType = response.headers.get("content-type") || "";
|
|
1952
2405
|
if (contentType.includes("application/json")) {
|
|
1953
2406
|
const json = await response.json();
|
|
1954
|
-
const
|
|
2407
|
+
const d = json.data || json;
|
|
1955
2408
|
onComplete == null ? void 0 : onComplete({
|
|
1956
|
-
title:
|
|
1957
|
-
subtitle:
|
|
1958
|
-
article:
|
|
1959
|
-
metaDescription:
|
|
1960
|
-
metaKeywords: Array.isArray(
|
|
2409
|
+
title: d.title || "",
|
|
2410
|
+
subtitle: d.subtitle || "",
|
|
2411
|
+
article: d.article || "",
|
|
2412
|
+
metaDescription: d.metaDescription || "",
|
|
2413
|
+
metaKeywords: Array.isArray(d.metaKeywords) ? d.metaKeywords : []
|
|
1961
2414
|
});
|
|
1962
2415
|
return;
|
|
1963
2416
|
}
|
|
1964
2417
|
const reader = (_a = response.body) == null ? void 0 : _a.getReader();
|
|
1965
|
-
if (!reader)
|
|
1966
|
-
|
|
1967
|
-
}
|
|
1968
|
-
await processSSEStream(reader, {
|
|
1969
|
-
onFieldStart,
|
|
1970
|
-
onContent,
|
|
1971
|
-
onKeywords,
|
|
1972
|
-
onFieldEnd,
|
|
1973
|
-
onComplete,
|
|
1974
|
-
onError
|
|
1975
|
-
});
|
|
2418
|
+
if (!reader) throw new Error("No reader available");
|
|
2419
|
+
await processSSEStream(reader, { onFieldStart, onContent, onKeywords, onFieldEnd, onComplete, onError });
|
|
1976
2420
|
} catch (error) {
|
|
1977
|
-
onError == null ? void 0 : onError(error);
|
|
2421
|
+
onError == null ? void 0 : onError(error instanceof Error ? error : new Error(String(error)));
|
|
1978
2422
|
throw error;
|
|
1979
2423
|
}
|
|
1980
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
|
+
};
|
|
1981
2457
|
var rewriteNewsApi = async ({
|
|
1982
2458
|
article,
|
|
1983
2459
|
language = "English",
|
|
@@ -1989,10 +2465,7 @@ var rewriteNewsApi = async ({
|
|
|
1989
2465
|
includeFAQ,
|
|
1990
2466
|
targetAudience
|
|
1991
2467
|
}) => {
|
|
1992
|
-
const payload = {
|
|
1993
|
-
article,
|
|
1994
|
-
language
|
|
1995
|
-
};
|
|
2468
|
+
const payload = { article, language, temperature: 1 };
|
|
1996
2469
|
if (articleId) payload.articleId = articleId;
|
|
1997
2470
|
if (tone) payload.tone = tone;
|
|
1998
2471
|
if (style) payload.style = style;
|
|
@@ -2000,69 +2473,49 @@ var rewriteNewsApi = async ({
|
|
|
2000
2473
|
if (includeQuotes !== void 0) payload.includeQuotes = includeQuotes;
|
|
2001
2474
|
if (includeFAQ !== void 0) payload.includeFAQ = includeFAQ;
|
|
2002
2475
|
if (targetAudience) payload.targetAudience = targetAudience;
|
|
2003
|
-
const { data } = await api_default.post("/
|
|
2476
|
+
const { data } = await api_default.post("/contenify/rewrite", payload);
|
|
2004
2477
|
return data.data;
|
|
2005
2478
|
};
|
|
2006
2479
|
|
|
2007
2480
|
// components/chatbot/ChatBot.tsx
|
|
2008
2481
|
var import_react_hot_toast2 = __toESM(require("react-hot-toast"));
|
|
2009
2482
|
var import_lucide_react7 = require("lucide-react");
|
|
2010
|
-
var
|
|
2483
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
2011
2484
|
function buildStructuredContent(title, article, metaKeywords, subtitle = "", metaDescription = "") {
|
|
2012
2485
|
return JSON.stringify({ title, subtitle, article, metaDescription, metaKeywords });
|
|
2013
2486
|
}
|
|
2014
|
-
function ChatBot(
|
|
2487
|
+
function ChatBot() {
|
|
2015
2488
|
const { loading } = useTheme();
|
|
2016
|
-
const
|
|
2017
|
-
const [
|
|
2018
|
-
const [
|
|
2019
|
-
const [
|
|
2020
|
-
const [
|
|
2021
|
-
const [analyzedData, setAnalyzedData] = (0, import_react10.useState)(null);
|
|
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);
|
|
2022
2494
|
const handleRecreate = async ({ title, content, id }) => {
|
|
2023
|
-
var _a;
|
|
2024
2495
|
const assistantId = crypto.randomUUID();
|
|
2025
2496
|
setMessages((prev) => [
|
|
2026
2497
|
...prev,
|
|
2027
|
-
{
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
content: `Recreate this news:
|
|
2031
|
-
${title}`
|
|
2032
|
-
},
|
|
2033
|
-
{
|
|
2034
|
-
id: assistantId,
|
|
2035
|
-
role: "assistant",
|
|
2036
|
-
content: ""
|
|
2037
|
-
}
|
|
2498
|
+
{ id: crypto.randomUUID(), role: "user", content: `Recreate this news:
|
|
2499
|
+
${title}` },
|
|
2500
|
+
{ id: assistantId, role: "assistant", content: "" }
|
|
2038
2501
|
]);
|
|
2039
2502
|
setIsStreaming(true);
|
|
2040
2503
|
try {
|
|
2041
2504
|
let hasStartedStreaming = false;
|
|
2505
|
+
let streamDone = false;
|
|
2506
|
+
let pendingError = null;
|
|
2042
2507
|
let titleBuffer = "";
|
|
2043
2508
|
let articleBuffer = "";
|
|
2044
2509
|
let keywordsBuffer = [];
|
|
2045
2510
|
const updateMessage = () => {
|
|
2046
2511
|
const structured = buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
|
|
2047
|
-
setMessages(
|
|
2048
|
-
(prev) => prev.map(
|
|
2049
|
-
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: structured }) : m
|
|
2050
|
-
)
|
|
2051
|
-
);
|
|
2512
|
+
setMessages((prev) => prev.map((m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: structured }) : m));
|
|
2052
2513
|
};
|
|
2053
|
-
const contentPrefs = preferences == null ? void 0 : preferences.content;
|
|
2054
|
-
const lang = ((_a = preferences == null ? void 0 : preferences.localization) == null ? void 0 : _a.language) === "hi" ? "Hindi" : "English";
|
|
2055
2514
|
try {
|
|
2056
2515
|
await rewriteNewsStreamApi({
|
|
2057
2516
|
article: content || "",
|
|
2058
|
-
language:
|
|
2517
|
+
language: "English",
|
|
2059
2518
|
articleId: id,
|
|
2060
|
-
tone: contentPrefs == null ? void 0 : contentPrefs.tone,
|
|
2061
|
-
style: contentPrefs == null ? void 0 : contentPrefs.style,
|
|
2062
|
-
wordCount: contentPrefs == null ? void 0 : contentPrefs.wordCount,
|
|
2063
|
-
includeQuotes: contentPrefs == null ? void 0 : contentPrefs.includeQuotes,
|
|
2064
|
-
includeFAQ: contentPrefs == null ? void 0 : contentPrefs.includeFAQ,
|
|
2065
|
-
targetAudience: contentPrefs == null ? void 0 : contentPrefs.targetAudience,
|
|
2066
2519
|
onFieldStart: (field) => {
|
|
2067
2520
|
setActiveField(field);
|
|
2068
2521
|
},
|
|
@@ -2080,99 +2533,69 @@ function ChatBot({ onPost }) {
|
|
|
2080
2533
|
setActiveField(null);
|
|
2081
2534
|
},
|
|
2082
2535
|
onComplete: (data) => {
|
|
2536
|
+
streamDone = true;
|
|
2083
2537
|
const finalContent = data && (data.title || data.article) ? JSON.stringify(data) : buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
|
|
2084
|
-
setMessages(
|
|
2085
|
-
(prev) => prev.map(
|
|
2086
|
-
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: finalContent }) : m
|
|
2087
|
-
)
|
|
2088
|
-
);
|
|
2538
|
+
setMessages((prev) => prev.map((m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: finalContent }) : m));
|
|
2089
2539
|
setIsStreaming(false);
|
|
2090
2540
|
setActiveField(null);
|
|
2091
2541
|
import_react_hot_toast2.default.success("Article created successfully!");
|
|
2092
2542
|
},
|
|
2093
|
-
onError:
|
|
2094
|
-
console.error("Streaming error:", error);
|
|
2543
|
+
onError: (error) => {
|
|
2095
2544
|
if (!hasStartedStreaming) {
|
|
2096
|
-
|
|
2545
|
+
pendingError = error instanceof Error ? error : new Error(String(error));
|
|
2097
2546
|
} else {
|
|
2547
|
+
streamDone = true;
|
|
2098
2548
|
setIsStreaming(false);
|
|
2099
2549
|
setActiveField(null);
|
|
2100
2550
|
import_react_hot_toast2.default.error("Failed to complete article generation");
|
|
2101
|
-
setMessages(
|
|
2102
|
-
(
|
|
2103
|
-
|
|
2104
|
-
)
|
|
2105
|
-
);
|
|
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
|
+
));
|
|
2106
2554
|
}
|
|
2107
2555
|
}
|
|
2108
2556
|
});
|
|
2109
|
-
if (
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: structured }) : m
|
|
2115
|
-
)
|
|
2116
|
-
);
|
|
2117
|
-
}
|
|
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
|
+
));
|
|
2118
2562
|
setIsStreaming(false);
|
|
2119
2563
|
setActiveField(null);
|
|
2120
2564
|
}
|
|
2121
|
-
} catch (
|
|
2122
|
-
|
|
2123
|
-
setMessages(
|
|
2124
|
-
(prev) => prev.map(
|
|
2125
|
-
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "" }) : m
|
|
2126
|
-
)
|
|
2127
|
-
);
|
|
2565
|
+
} catch (e) {
|
|
2566
|
+
setMessages((prev) => prev.map((m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "" }) : m));
|
|
2128
2567
|
const result = await rewriteNewsApi({
|
|
2129
2568
|
article: content || "",
|
|
2130
|
-
language:
|
|
2131
|
-
articleId: id
|
|
2132
|
-
tone: contentPrefs == null ? void 0 : contentPrefs.tone,
|
|
2133
|
-
style: contentPrefs == null ? void 0 : contentPrefs.style,
|
|
2134
|
-
wordCount: contentPrefs == null ? void 0 : contentPrefs.wordCount,
|
|
2135
|
-
includeQuotes: contentPrefs == null ? void 0 : contentPrefs.includeQuotes,
|
|
2136
|
-
includeFAQ: contentPrefs == null ? void 0 : contentPrefs.includeFAQ,
|
|
2137
|
-
targetAudience: contentPrefs == null ? void 0 : contentPrefs.targetAudience
|
|
2569
|
+
language: "English",
|
|
2570
|
+
articleId: id
|
|
2138
2571
|
});
|
|
2139
|
-
setMessages(
|
|
2140
|
-
(
|
|
2141
|
-
|
|
2142
|
-
)
|
|
2143
|
-
);
|
|
2572
|
+
setMessages((prev) => prev.map(
|
|
2573
|
+
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: typeof result === "string" ? result : JSON.stringify(result) }) : m
|
|
2574
|
+
));
|
|
2144
2575
|
setIsStreaming(false);
|
|
2145
2576
|
import_react_hot_toast2.default.success("Article created successfully!");
|
|
2146
2577
|
}
|
|
2147
2578
|
} catch (err) {
|
|
2579
|
+
if (err instanceof ApiKeyInvalidError) return;
|
|
2148
2580
|
console.error("Complete Error:", err);
|
|
2149
2581
|
setIsStreaming(false);
|
|
2150
2582
|
setActiveField(null);
|
|
2151
2583
|
import_react_hot_toast2.default.error((err == null ? void 0 : err.message) || "An error occurred while creating the article");
|
|
2152
|
-
setMessages(
|
|
2153
|
-
(
|
|
2154
|
-
|
|
2155
|
-
)
|
|
2156
|
-
);
|
|
2584
|
+
setMessages((prev) => prev.map(
|
|
2585
|
+
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "Failed to create article. Please try again." }) : m
|
|
2586
|
+
));
|
|
2157
2587
|
}
|
|
2158
2588
|
};
|
|
2159
2589
|
const handleSendMessage = async (text) => {
|
|
2160
|
-
var _a
|
|
2590
|
+
var _a;
|
|
2161
2591
|
const userMsgId = crypto.randomUUID();
|
|
2162
2592
|
const assistantId = crypto.randomUUID();
|
|
2163
|
-
setMessages((prev) => [
|
|
2164
|
-
...prev,
|
|
2165
|
-
{ id: userMsgId, role: "user", content: text }
|
|
2166
|
-
]);
|
|
2593
|
+
setMessages((prev) => [...prev, { id: userMsgId, role: "user", content: text }]);
|
|
2167
2594
|
const urlRegex = /https?:\/\/[^\s]+/;
|
|
2168
2595
|
const hasUrl = urlRegex.test(text);
|
|
2169
2596
|
if (hasUrl) {
|
|
2170
|
-
setMessages((prev) => [
|
|
2171
|
-
...prev,
|
|
2172
|
-
{ id: assistantId, role: "assistant", content: "Analyzing your link..." }
|
|
2173
|
-
]);
|
|
2597
|
+
setMessages((prev) => [...prev, { id: assistantId, role: "assistant", content: "Analyzing your link..." }]);
|
|
2174
2598
|
try {
|
|
2175
|
-
return;
|
|
2176
2599
|
const result = await analyzeInputApi(text);
|
|
2177
2600
|
if (result.hasUrl && ((_a = result.options) == null ? void 0 : _a.length) > 0) {
|
|
2178
2601
|
setAnalyzedData({
|
|
@@ -2182,60 +2605,39 @@ function ChatBot({ onPost }) {
|
|
|
2182
2605
|
messageId: assistantId
|
|
2183
2606
|
});
|
|
2184
2607
|
const optionsList = result.options.map((opt) => `\u2022 **${opt.name}**${opt.description ? ` \u2013 ${opt.description}` : ""}`).join("\n");
|
|
2185
|
-
setMessages(
|
|
2186
|
-
(
|
|
2187
|
-
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), {
|
|
2188
|
-
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?
|
|
2189
2610
|
|
|
2190
|
-
${optionsList}`
|
|
2191
|
-
|
|
2192
|
-
)
|
|
2193
|
-
);
|
|
2611
|
+
${optionsList}` }) : m
|
|
2612
|
+
));
|
|
2194
2613
|
} else {
|
|
2195
|
-
setMessages(
|
|
2196
|
-
(
|
|
2197
|
-
|
|
2198
|
-
)
|
|
2199
|
-
);
|
|
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
|
+
));
|
|
2200
2617
|
}
|
|
2201
2618
|
} catch (err) {
|
|
2619
|
+
if (err instanceof ApiKeyInvalidError) return;
|
|
2202
2620
|
console.error("Analyze input error:", err);
|
|
2203
2621
|
import_react_hot_toast2.default.error("Failed to analyze the link");
|
|
2204
|
-
setMessages(
|
|
2205
|
-
(
|
|
2206
|
-
|
|
2207
|
-
)
|
|
2208
|
-
);
|
|
2622
|
+
setMessages((prev) => prev.map(
|
|
2623
|
+
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "Failed to analyze the link. Please try again." }) : m
|
|
2624
|
+
));
|
|
2209
2625
|
}
|
|
2210
2626
|
} else {
|
|
2211
|
-
setMessages((prev) => [
|
|
2212
|
-
...prev,
|
|
2213
|
-
{ id: assistantId, role: "assistant", content: "" }
|
|
2214
|
-
]);
|
|
2627
|
+
setMessages((prev) => [...prev, { id: assistantId, role: "assistant", content: "" }]);
|
|
2215
2628
|
setIsStreaming(true);
|
|
2216
2629
|
try {
|
|
2217
|
-
|
|
2218
|
-
const lang = ((_b = preferences == null ? void 0 : preferences.localization) == null ? void 0 : _b.language) === "hi" ? "Hindi" : "English";
|
|
2630
|
+
let streamDone = false;
|
|
2219
2631
|
let titleBuffer = "";
|
|
2220
2632
|
let articleBuffer = "";
|
|
2221
2633
|
let keywordsBuffer = [];
|
|
2222
2634
|
const updateMessage = () => {
|
|
2223
2635
|
const structured = buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
|
|
2224
|
-
setMessages(
|
|
2225
|
-
(prev) => prev.map(
|
|
2226
|
-
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: structured }) : m
|
|
2227
|
-
)
|
|
2228
|
-
);
|
|
2636
|
+
setMessages((prev) => prev.map((m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: structured }) : m));
|
|
2229
2637
|
};
|
|
2230
2638
|
await rewriteNewsStreamApi({
|
|
2231
2639
|
article: text,
|
|
2232
|
-
language:
|
|
2233
|
-
tone: contentPrefs == null ? void 0 : contentPrefs.tone,
|
|
2234
|
-
style: contentPrefs == null ? void 0 : contentPrefs.style,
|
|
2235
|
-
wordCount: contentPrefs == null ? void 0 : contentPrefs.wordCount,
|
|
2236
|
-
includeQuotes: contentPrefs == null ? void 0 : contentPrefs.includeQuotes,
|
|
2237
|
-
includeFAQ: contentPrefs == null ? void 0 : contentPrefs.includeFAQ,
|
|
2238
|
-
targetAudience: contentPrefs == null ? void 0 : contentPrefs.targetAudience,
|
|
2640
|
+
language: "English",
|
|
2239
2641
|
onFieldStart: (field) => {
|
|
2240
2642
|
setActiveField(field);
|
|
2241
2643
|
},
|
|
@@ -2252,44 +2654,37 @@ ${optionsList}`
|
|
|
2252
2654
|
setActiveField(null);
|
|
2253
2655
|
},
|
|
2254
2656
|
onComplete: (data) => {
|
|
2657
|
+
streamDone = true;
|
|
2255
2658
|
const finalContent = data && (data.title || data.article) ? JSON.stringify(data) : buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
|
|
2256
|
-
setMessages(
|
|
2257
|
-
(prev) => prev.map(
|
|
2258
|
-
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: finalContent }) : m
|
|
2259
|
-
)
|
|
2260
|
-
);
|
|
2659
|
+
setMessages((prev) => prev.map((m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: finalContent }) : m));
|
|
2261
2660
|
setIsStreaming(false);
|
|
2262
2661
|
setActiveField(null);
|
|
2263
2662
|
import_react_hot_toast2.default.success("Content generated!");
|
|
2264
2663
|
},
|
|
2265
2664
|
onError: (error) => {
|
|
2665
|
+
if (error instanceof ApiKeyInvalidError) return;
|
|
2266
2666
|
console.error("Stream error:", error);
|
|
2667
|
+
streamDone = true;
|
|
2267
2668
|
setIsStreaming(false);
|
|
2268
2669
|
setActiveField(null);
|
|
2269
2670
|
import_react_hot_toast2.default.error("Failed to generate content");
|
|
2270
2671
|
}
|
|
2271
2672
|
});
|
|
2272
|
-
if (
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
(prev) => prev.map(
|
|
2277
|
-
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: structured }) : m
|
|
2278
|
-
)
|
|
2279
|
-
);
|
|
2280
|
-
}
|
|
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
|
+
));
|
|
2281
2677
|
setIsStreaming(false);
|
|
2282
2678
|
setActiveField(null);
|
|
2283
2679
|
}
|
|
2284
2680
|
} catch (err) {
|
|
2681
|
+
if (err instanceof ApiKeyInvalidError) return;
|
|
2285
2682
|
console.error("Send message error:", err);
|
|
2286
2683
|
setIsStreaming(false);
|
|
2287
2684
|
setActiveField(null);
|
|
2288
|
-
setMessages(
|
|
2289
|
-
(
|
|
2290
|
-
|
|
2291
|
-
)
|
|
2292
|
-
);
|
|
2685
|
+
setMessages((prev) => prev.map(
|
|
2686
|
+
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "Failed to generate content. Please try again." }) : m
|
|
2687
|
+
));
|
|
2293
2688
|
}
|
|
2294
2689
|
}
|
|
2295
2690
|
};
|
|
@@ -2305,28 +2700,20 @@ ${optionsList}`
|
|
|
2305
2700
|
]);
|
|
2306
2701
|
setIsStreaming(true);
|
|
2307
2702
|
try {
|
|
2308
|
-
|
|
2703
|
+
let streamDone = false;
|
|
2309
2704
|
let titleBuffer = "";
|
|
2310
2705
|
let articleBuffer = "";
|
|
2311
2706
|
let keywordsBuffer = [];
|
|
2312
2707
|
const updateMessage = () => {
|
|
2313
2708
|
const structured = buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
|
|
2314
|
-
setMessages(
|
|
2315
|
-
(prev) => prev.map(
|
|
2316
|
-
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: structured }) : m
|
|
2317
|
-
)
|
|
2318
|
-
);
|
|
2709
|
+
setMessages((prev) => prev.map((m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: structured }) : m));
|
|
2319
2710
|
};
|
|
2320
2711
|
try {
|
|
2321
2712
|
await createContentStreamApi({
|
|
2322
2713
|
url,
|
|
2323
2714
|
content,
|
|
2324
2715
|
actionId,
|
|
2325
|
-
settings: {
|
|
2326
|
-
tone: contentPrefs == null ? void 0 : contentPrefs.tone,
|
|
2327
|
-
style: contentPrefs == null ? void 0 : contentPrefs.style,
|
|
2328
|
-
wordCount: contentPrefs == null ? void 0 : contentPrefs.wordCount
|
|
2329
|
-
},
|
|
2716
|
+
settings: {},
|
|
2330
2717
|
onFieldStart: (field) => {
|
|
2331
2718
|
setActiveField(field);
|
|
2332
2719
|
},
|
|
@@ -2343,71 +2730,146 @@ ${optionsList}`
|
|
|
2343
2730
|
setActiveField(null);
|
|
2344
2731
|
},
|
|
2345
2732
|
onComplete: (data) => {
|
|
2733
|
+
streamDone = true;
|
|
2346
2734
|
const finalContent = data && (data.title || data.article) ? JSON.stringify(data) : buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
|
|
2347
|
-
setMessages(
|
|
2348
|
-
(prev) => prev.map(
|
|
2349
|
-
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: finalContent }) : m
|
|
2350
|
-
)
|
|
2351
|
-
);
|
|
2735
|
+
setMessages((prev) => prev.map((m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: finalContent }) : m));
|
|
2352
2736
|
setIsStreaming(false);
|
|
2353
2737
|
setActiveField(null);
|
|
2354
2738
|
import_react_hot_toast2.default.success("Content created!");
|
|
2355
2739
|
},
|
|
2356
2740
|
onError: (error) => {
|
|
2741
|
+
if (error instanceof ApiKeyInvalidError) return;
|
|
2357
2742
|
console.error("Stream error:", error);
|
|
2743
|
+
streamDone = true;
|
|
2358
2744
|
setIsStreaming(false);
|
|
2359
2745
|
setActiveField(null);
|
|
2360
2746
|
import_react_hot_toast2.default.error("Failed to create content");
|
|
2361
2747
|
}
|
|
2362
2748
|
});
|
|
2363
|
-
if (
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
(prev) => prev.map(
|
|
2368
|
-
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: structured }) : m
|
|
2369
|
-
)
|
|
2370
|
-
);
|
|
2371
|
-
}
|
|
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
|
+
));
|
|
2372
2753
|
setIsStreaming(false);
|
|
2373
2754
|
setActiveField(null);
|
|
2374
2755
|
}
|
|
2375
2756
|
} catch (e) {
|
|
2376
|
-
const result = await createContentApi({ url, content, actionId, settings: {
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
} });
|
|
2381
|
-
setMessages(
|
|
2382
|
-
(prev) => prev.map(
|
|
2383
|
-
(m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: typeof result === "string" ? result : JSON.stringify(result) }) : m
|
|
2384
|
-
)
|
|
2385
|
-
);
|
|
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
|
+
));
|
|
2386
2761
|
setIsStreaming(false);
|
|
2387
2762
|
setActiveField(null);
|
|
2388
2763
|
import_react_hot_toast2.default.success("Content created!");
|
|
2389
2764
|
}
|
|
2390
2765
|
} catch (err) {
|
|
2766
|
+
if (err instanceof ApiKeyInvalidError) return;
|
|
2391
2767
|
console.error("Create content error:", err);
|
|
2392
2768
|
setIsStreaming(false);
|
|
2393
2769
|
setActiveField(null);
|
|
2394
|
-
setMessages(
|
|
2395
|
-
(
|
|
2396
|
-
|
|
2397
|
-
|
|
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
|
|
2398
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");
|
|
2399
2855
|
}
|
|
2400
2856
|
};
|
|
2401
2857
|
if (loading) {
|
|
2402
|
-
return /* @__PURE__ */ (0,
|
|
2403
|
-
/* @__PURE__ */ (0,
|
|
2404
|
-
/* @__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..." })
|
|
2405
2861
|
] }) });
|
|
2406
2862
|
}
|
|
2407
|
-
return /* @__PURE__ */ (0,
|
|
2408
|
-
/* @__PURE__ */ (0,
|
|
2409
|
-
|
|
2410
|
-
|
|
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)(
|
|
2411
2873
|
ChatWindow,
|
|
2412
2874
|
{
|
|
2413
2875
|
messages,
|
|
@@ -2417,33 +2879,39 @@ ${optionsList}`
|
|
|
2417
2879
|
activeField,
|
|
2418
2880
|
analyzedData,
|
|
2419
2881
|
onSelectAction: handleSelectAction,
|
|
2420
|
-
|
|
2882
|
+
onResolveSeo: handleResolveSeo
|
|
2421
2883
|
}
|
|
2422
|
-
)
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "cnfy-empty-state-subtitle", children: "Type a message or paste a link to begin" })
|
|
2426
|
-
] }) })
|
|
2427
|
-
] }) }),
|
|
2428
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2884
|
+
)
|
|
2885
|
+
] }) }) }),
|
|
2886
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2429
2887
|
Drawer,
|
|
2430
2888
|
{
|
|
2431
2889
|
open: preferencesOpen,
|
|
2432
2890
|
onClose: () => setPreferencesOpen(false),
|
|
2433
2891
|
title: "Settings",
|
|
2434
|
-
children: /* @__PURE__ */ (0,
|
|
2892
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PreferencesPage, {})
|
|
2435
2893
|
}
|
|
2436
2894
|
)
|
|
2437
2895
|
] });
|
|
2438
2896
|
}
|
|
2439
2897
|
|
|
2440
2898
|
// src/index.tsx
|
|
2441
|
-
var
|
|
2899
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
2442
2900
|
function ContenifyChatBot({ apiUrl, apiKey, domain, onPost }) {
|
|
2443
|
-
(0,
|
|
2901
|
+
(0, import_react10.useEffect)(() => {
|
|
2444
2902
|
setConfig({ apiUrl, apiKey, domain });
|
|
2445
2903
|
}, [apiUrl, apiKey, domain]);
|
|
2446
|
-
|
|
2904
|
+
(0, import_react10.useEffect)(() => {
|
|
2905
|
+
if (!onPost) return;
|
|
2906
|
+
const handler = (e) => {
|
|
2907
|
+
var _a, _b;
|
|
2908
|
+
const detail = e.detail;
|
|
2909
|
+
onPost((_a = detail.article) != null ? _a : "", (_b = detail.keywords) != null ? _b : []);
|
|
2910
|
+
};
|
|
2911
|
+
window.addEventListener("contenify-post-article", handler);
|
|
2912
|
+
return () => window.removeEventListener("contenify-post-article", handler);
|
|
2913
|
+
}, [onPost]);
|
|
2914
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChatBot, {});
|
|
2447
2915
|
}
|
|
2448
2916
|
var index_default = ContenifyChatBot;
|
|
2449
2917
|
// Annotate the CommonJS export names for ESM import in node:
|