@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/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 import_react11 = require("react");
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 import_react10 = require("react");
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 import_react6 = require("react");
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 import_react4 = require("react");
145
+ var import_react3 = require("react");
384
146
 
385
147
  // components/chatbot/RichTextEditor.tsx
386
- var import_react2 = require("@tiptap/react");
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 import_react3 = require("react");
391
- var import_jsx_runtime2 = require("react/jsx-runtime");
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, import_react2.useEditor)({
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, import_react3.useEffect)(() => {
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, import_jsx_runtime2.jsx)("div", { className: "cnfy-editor-loading", children: "Loading editor..." });
188
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "cnfy-editor-loading", children: "Loading editor..." });
427
189
  }
428
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "cnfy-editor", children: [
429
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "cnfy-toolbar", children: [
430
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
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, import_jsx_runtime2.jsx)(import_lucide_react.Heading1, { size: 18 })
198
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Heading1, { size: 18 })
437
199
  }
438
200
  ),
439
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
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, import_jsx_runtime2.jsx)(import_lucide_react.Heading2, { size: 18 })
207
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Heading2, { size: 18 })
446
208
  }
447
209
  ),
448
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
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, import_jsx_runtime2.jsx)(import_lucide_react.Pilcrow, { size: 18 })
216
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Pilcrow, { size: 18 })
455
217
  }
456
218
  ),
457
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "cnfy-toolbar-divider" }),
458
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
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, import_jsx_runtime2.jsx)(import_lucide_react.Bold, { size: 18 })
226
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Bold, { size: 18 })
465
227
  }
466
228
  ),
467
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
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, import_jsx_runtime2.jsx)(import_lucide_react.Italic, { size: 18 })
235
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Italic, { size: 18 })
474
236
  }
475
237
  ),
476
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "cnfy-toolbar-divider" }),
477
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
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, import_jsx_runtime2.jsx)(import_lucide_react.List, { size: 18 })
245
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.List, { size: 18 })
484
246
  }
485
247
  ),
486
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
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, import_jsx_runtime2.jsx)(import_lucide_react.ListOrdered, { size: 18 })
254
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ListOrdered, { size: 18 })
493
255
  }
494
256
  ),
495
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "cnfy-toolbar-divider" }),
496
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
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, import_jsx_runtime2.jsx)(import_lucide_react.Undo, { size: 18 })
264
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Undo, { size: 18 })
503
265
  }
504
266
  ),
505
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
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, import_jsx_runtime2.jsx)(import_lucide_react.Redo, { size: 18 })
273
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Redo, { size: 18 })
512
274
  }
513
275
  )
514
276
  ] }),
515
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react2.EditorContent, { editor, className: "cnfy-editor-content" })
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, import_jsx_runtime2.jsx)(
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: ((_a = preferences == null ? void 0 : preferences.chatbot) == null ? void 0 : _a.primaryColor) || "#10b981",
544
- secondaryColor: ((_b = preferences == null ? void 0 : preferences.chatbot) == null ? void 0 : _b.secondaryColor) || "#064e3b",
545
- botName: ((_c = preferences == null ? void 0 : preferences.chatbot) == null ? void 0 : _c.name) || "AI News Assistant",
546
- logoUrl: (_d = preferences == null ? void 0 : preferences.chatbot) == null ? void 0 : _d.logoUrl,
547
- theme: ((_e = preferences == null ? void 0 : preferences.chatbot) == null ? void 0 : _e.theme) || "system",
548
- showSidebar: (_g = (_f = preferences == null ? void 0 : preferences.appearance) == null ? void 0 : _f.showSidebar) != null ? _g : true,
549
- showHeader: (_i = (_h = preferences == null ? void 0 : preferences.appearance) == null ? void 0 : _h.showHeader) != null ? _i : true,
550
- activeThemePreset: (_k = (_j = preferences == null ? void 0 : preferences.appearance) == null ? void 0 : _j.activeThemePreset) != null ? _k : null,
551
- showNewsPanel: (_m = (_l = preferences == null ? void 0 : preferences.appearance) == null ? void 0 : _l.showNewsPanel) != null ? _m : true
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 import_jsx_runtime3 = require("react/jsx-runtime");
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
- onSaveDraft,
569
- onPost
344
+ onDownload,
345
+ downloadingFormat,
346
+ onPost,
347
+ isPosting = false
570
348
  }) {
571
349
  const { primaryColor } = useTheme();
572
- const [title, setTitle] = (0, import_react4.useState)("");
573
- const [subtitle, setSubtitle] = (0, import_react4.useState)("");
574
- const [slug, setSlug] = (0, import_react4.useState)("");
575
- const [slugEdited, setSlugEdited] = (0, import_react4.useState)(false);
576
- const [metaDescription, setMetaDescription] = (0, import_react4.useState)("");
577
- const [content, setContent] = (0, import_react4.useState)("");
578
- const [keywords, setKeywords] = (0, import_react4.useState)(metaKeywords);
579
- const [newKeyword, setNewKeyword] = (0, import_react4.useState)("");
580
- (0, import_react4.useEffect)(() => {
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
- }, [initialTitle, initialSubtitle, initialContent, initialMetaDescription, metaKeywords]);
589
- (0, import_react4.useEffect)(() => {
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
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cnfy-edit-modal-overlay", children: [
634
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cnfy-edit-modal-backdrop", onClick: onClose }),
635
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cnfy-edit-modal", children: [
636
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cnfy-edit-modal-header", children: [
637
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { className: "cnfy-edit-modal-title", children: "Edit Article" }),
638
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { onClick: onClose, className: "cnfy-edit-modal-close-btn", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react2.X, { size: 20, className: "cnfy-edit-modal-close-icon" }) })
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, import_jsx_runtime3.jsxs)("div", { className: "cnfy-edit-modal-body", children: [
641
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
642
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { className: "cnfy-edit-label", children: "Title" }),
643
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
644
- "input",
645
- {
646
- type: "text",
647
- value: title,
648
- onChange: (e) => handleTitleChange(e.target.value),
649
- placeholder: "Article title...",
650
- className: "cnfy-edit-input"
651
- }
652
- )
653
- ] }),
654
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
655
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { className: "cnfy-edit-label", children: "Subtitle" }),
656
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
657
- "input",
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
- type: "text",
660
- value: subtitle,
661
- onChange: (e) => setSubtitle(e.target.value),
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, import_jsx_runtime3.jsxs)("div", { children: [
668
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { className: "cnfy-edit-label", children: "Slug" }),
669
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
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: slug,
674
- onChange: (e) => handleSlugChange(e.target.value),
675
- placeholder: "article-url-slug",
676
- className: "cnfy-edit-input cnfy-edit-input--mono"
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
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
681
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { className: "cnfy-edit-label", children: "Article Content" }),
682
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
683
- RichTextEditor,
684
- {
685
- content,
686
- onChange: setContent,
687
- placeholder: "Start writing your article..."
688
- }
689
- )
690
- ] }),
691
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
692
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { className: "cnfy-edit-label", children: "Meta Description" }),
693
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
694
- "textarea",
695
- {
696
- value: metaDescription,
697
- onChange: (e) => setMetaDescription(e.target.value),
698
- placeholder: "Brief description for search engines...",
699
- className: "cnfy-edit-textarea",
700
- rows: 3
701
- }
702
- )
703
- ] }),
704
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
705
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { className: "cnfy-edit-label", children: "Meta Keywords" }),
706
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cnfy-keyword-list", children: keywords.map((keyword, index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "cnfy-keyword-tag", children: [
707
- "#",
708
- keyword,
709
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
710
- "button",
711
- {
712
- onClick: () => handleRemoveKeyword(index),
713
- className: "cnfy-keyword-remove-btn",
714
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react2.X, { size: 14 })
715
- }
716
- )
717
- ] }, index)) }),
718
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cnfy-keyword-input-row", children: [
719
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
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
- type: "text",
723
- value: newKeyword,
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, import_jsx_runtime3.jsx)("button", { onClick: handleAddKeyword, className: "cnfy-btn-add-keyword", children: "Add" })
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
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cnfy-edit-modal-footer", children: [
735
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { onClick: onClose, className: "cnfy-btn-footer-cancel", children: "Cancel" }),
736
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
737
- "button",
738
- {
739
- onClick: () => onSaveDraft(getEditData()),
740
- className: "cnfy-btn-save-draft",
741
- children: "Save Draft"
742
- }
743
- ),
744
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
745
- "button",
746
- {
747
- onClick: () => onPost(getEditData()),
748
- className: "cnfy-btn-post-article",
749
- style: { backgroundColor: primaryColor, color: "#fff" },
750
- children: "Post"
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 import_react5 = require("react");
693
+ var import_react4 = require("react");
760
694
  var import_lucide_react3 = require("lucide-react");
761
- var import_jsx_runtime4 = require("react/jsx-runtime");
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, import_react5.useState)(primaryColor);
793
- const lightenColor = (hex, percent = 85) => {
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, import_jsx_runtime4.jsxs)(
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, import_jsx_runtime4.jsxs)("div", { className: "cnfy-news-card-content", children: [
813
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "cnfy-news-card-title", children: link.title }),
814
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "cnfy-news-card-meta", children: [
815
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
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
- backgroundColor: lightenColor(primaryColorState, 95),
821
- color: primaryColor,
822
- borderColor: lightenColor(primaryColorState, 95)
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, import_jsx_runtime4.jsx)("span", { children: "\u2022" }),
828
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "cnfy-news-card-category", children: item.category }),
829
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "\u2022" }),
830
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { children: [
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, import_jsx_runtime4.jsxs)("div", { className: "cnfy-news-card-actions", children: [
838
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
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, import_jsx_runtime4.jsx)(import_lucide_react3.Eye, { size: 16 })
790
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.Eye, { size: 16 })
845
791
  }
846
792
  ),
847
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
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, import_jsx_runtime4.jsx)(import_lucide_react3.RefreshCcw, { size: 16 })
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, import_jsx_runtime4.jsxs)(
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, import_jsx_runtime4.jsxs)("div", { className: "cnfy-news-card-content", children: [
872
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "cnfy-news-card-title", children: item.title }),
873
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "cnfy-news-card-meta", children: [
874
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
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, import_jsx_runtime4.jsx)("span", { children: "\u2022" }),
887
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "cnfy-news-card-category", children: item.category }),
888
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "\u2022" }),
889
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { children: [
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, import_jsx_runtime4.jsxs)("div", { className: "cnfy-news-card-actions", children: [
897
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
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, import_jsx_runtime4.jsx)(import_lucide_react3.Eye, { size: 16 })
849
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.Eye, { size: 16 })
904
850
  }
905
851
  ),
906
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
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, import_jsx_runtime4.jsx)(import_lucide_react3.RefreshCcw, { size: 16 })
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("/chatboat/trending");
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("/chatboat/scrape/sources");
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("/chatboat/scrape/source", { sourceId });
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(`/chatboat/scrape/by-source/${sourceId}`);
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 import_react_select = __toESM(require("react-select"));
947
- var import_jsx_runtime5 = require("react/jsx-runtime");
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
- onPost: onPostCallback
986
+ onResolveSeo
965
987
  }) {
966
- var _a, _b;
967
988
  const { showNewsPanel } = useTheme();
968
- const bottomRef = (0, import_react6.useRef)(null);
969
- const textareaRef = (0, import_react6.useRef)(null);
970
- const dropdownRef = (0, import_react6.useRef)(null);
971
- const [input, setInput] = (0, import_react6.useState)("");
972
- const [copiedId, setCopiedId] = (0, import_react6.useState)(null);
973
- const [showNewsDropdown, setShowNewsDropdown] = (0, import_react6.useState)(false);
974
- const [trendingNews, setTrendingNews] = (0, import_react6.useState)([]);
975
- const [loadingNews, setLoadingNews] = (0, import_react6.useState)(false);
976
- const [sources, setSources] = (0, import_react6.useState)([]);
977
- const [selectedSource, setSelectedSource] = (0, import_react6.useState)(null);
978
- const [loadingSources, setLoadingSources] = (0, import_react6.useState)(false);
979
- const [scraping, setScraping] = (0, import_react6.useState)(false);
980
- const [editModal, setEditModal] = (0, import_react6.useState)({ isOpen: false, title: "", subtitle: "", content: "", metaDescription: "", metaKeywords: [], messageId: "" });
981
- const { primaryColor } = useTheme();
982
- const { preferences } = usePreferences();
983
- const preferredLanguage = (_a = preferences == null ? void 0 : preferences.localization) == null ? void 0 : _a.language;
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 handleSaveDraft = (data) => {
1017
- console.log("Saving draft:", data);
1018
- handleCloseModal();
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 handlePost = (data) => {
1021
- onPostCallback == null ? void 0 : onPostCallback(data.article, data.keywords);
1022
- handleCloseModal();
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, import_react6.useLayoutEffect)(() => {
1025
- var _a2;
1026
- (_a2 = bottomRef.current) == null ? void 0 : _a2.scrollIntoView({ block: "end" });
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, import_react6.useEffect)(() => {
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 = (option) => {
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, import_react6.useEffect)(() => {
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
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "cnfy-chat", children: [
1111
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-chat-area", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "cnfy-chat-scroll", children: [
1112
- messages.map((msg) => {
1113
- var _a2, _b2;
1114
- const parsed = formatAIContent(msg.content);
1115
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-msg", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: msg.role === "assistant" ? `cnfy-msg-body` : `cnfy-msg-body you`, children: [
1116
- msg.role === "assistant" && parsed.isArticle && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-msg-copy-row", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1117
- "button",
1118
- {
1119
- onClick: () => handleCopy(parsed, msg.id),
1120
- className: "cnfy-copy-btn",
1121
- title: "Copy to clipboard",
1122
- children: copiedId === msg.id ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Check, { size: 16, className: "cnfy-copy-icon--copied" }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Copy, { size: 16 })
1123
- }
1124
- ) }),
1125
- msg.role === "assistant" && isStreaming && msg.id === ((_a2 = messages[messages.length - 1]) == null ? void 0 : _a2.id) && !parsed.isArticle && !parsed.plainText && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "cnfy-creating-indicator", children: [
1126
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Loader2, { size: 16, className: "cnfy-animate-spin" }),
1127
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Creating article..." })
1128
- ] }),
1129
- parsed.isArticle ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1130
- parsed.title && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("h1", { className: "cnfy-block-h1", children: [
1131
- parsed.title,
1132
- activeField === "title" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "cnfy-cursor-blink", children: "|" })
1133
- ] }),
1134
- parsed.subtitle && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("h1", { className: "cnfy-block-h2", children: [
1135
- parsed.subtitle,
1136
- activeField === "subtitle" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "cnfy-cursor-blink", children: "|" })
1137
- ] }),
1138
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("hr", { className: "cnfy-divider" }),
1139
- parsed.articleHtml && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1140
- "div",
1141
- {
1142
- className: "cnfy-article-content",
1143
- dangerouslySetInnerHTML: { __html: parsed.articleHtml }
1144
- }
1145
- ),
1146
- activeField === "article" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "cnfy-cursor-blink", children: "|" })
1147
- ] }) : (
1148
- /* Plain text messages (status, error, options, etc.) */
1149
- parsed.plainText && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "cnfy-block-p", children: parsed.plainText })
1150
- ),
1151
- parsed.metaDescription && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "cnfy-msg-keywords", children: [
1152
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-msg-keywords-label", children: "Meta Description" }),
1153
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-block-p", children: parsed.metaDescription })
1154
- ] }),
1155
- parsed.metaKeywords.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "cnfy-msg-keywords", children: [
1156
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-msg-keywords-label", children: "Keywords" }),
1157
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "cnfy-msg-keywords-list", children: parsed.metaKeywords.map((tag, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1158
- "span",
1159
- {
1160
- className: "cnfy-msg-keyword-tag",
1161
- children: tag
1162
- },
1163
- i
1164
- )) })
1165
- ] }),
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
- onClick: () => onOpenPreferences == null ? void 0 : onOpenPreferences(),
1379
- className: "cnfy-user-menu-trigger",
1380
- "aria-label": "Settings",
1381
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.Settings, { size: 20, className: "cnfy-user-menu-trigger-icon" })
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 import_react7 = require("react");
1410
- var import_lucide_react6 = require("lucide-react");
1411
- var import_jsx_runtime8 = require("react/jsx-runtime");
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, import_react7.useEffect)(() => {
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, import_jsx_runtime8.jsxs)("div", { className: "cnfy-drawer-overlay", children: [
1430
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "cnfy-drawer-backdrop", onClick: onClose }),
1431
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "cnfy-drawer-panel", children: [
1432
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "cnfy-drawer-header", children: [
1433
- title && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h2", { className: "cnfy-drawer-title", children: title }),
1434
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { onClick: onClose, className: "cnfy-drawer-close-btn", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react6.X, { size: 20 }) })
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, import_jsx_runtime8.jsx)("div", { className: "cnfy-drawer-body", children })
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 import_react9 = require("react");
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("/chatboat/scrape/trigger", {
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("/chatboat/scrape/status");
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 import_jsx_runtime10 = require("react/jsx-runtime");
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
- var _a, _b, _c;
1524
- const { preferences, loading, refreshPreferences, updatePreferences } = usePreferences();
1525
- const primaryColor = ((_a = preferences == null ? void 0 : preferences.chatbot) == null ? void 0 : _a.primaryColor) || "#10b981";
1526
- const [botName, setBotName] = (0, import_react9.useState)("");
1527
- const [logo, setLogo] = (0, import_react9.useState)(null);
1528
- const [logoUrl, setLogoUrl] = (0, import_react9.useState)(null);
1529
- const [state, setState] = (0, import_react9.useState)(null);
1530
- const [cities, setCities] = (0, import_react9.useState)([]);
1531
- const [language, setLanguage] = (0, import_react9.useState)(null);
1532
- const [saving, setSaving] = (0, import_react9.useState)(false);
1533
- const [success, setSuccess] = (0, import_react9.useState)(false);
1534
- const [scraping, setScraping] = (0, import_react9.useState)(false);
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, import_react9.useEffect)(() => {
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: (_a2 = preferences == null ? void 0 : preferences.localization) == null ? void 0 : _a2.state,
1562
- cities: (_b2 = preferences == null ? void 0 : preferences.localization) == null ? void 0 : _b2.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
- if (loading) {
1657
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "cnfy-dash-page", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "cnfy-dash-page-subtitle", children: "Loading settings..." }) });
1658
- }
1659
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "cnfy-dash-page", children: [
1660
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "cnfy-dash-card", children: [
1661
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { className: "cnfy-dash-card-title", children: "Chatbot Settings" }),
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, import_jsx_runtime10.jsxs)("div", { className: "cnfy-dash-field", children: [
1675
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { className: "cnfy-dash-label", children: "Chatbot Logo" }),
1676
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "cnfy-logo-upload", children: [
1677
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "cnfy-logo-preview", children: logo ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1678
- "img",
1679
- {
1680
- src: URL.createObjectURL(logo),
1681
- alt: "Logo preview",
1682
- className: "cnfy-logo-img"
1683
- }
1684
- ) : logoUrl ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1685
- "img",
1686
- {
1687
- src: logoUrl,
1688
- alt: "Current logo",
1689
- className: "cnfy-logo-img"
1690
- }
1691
- ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "cnfy-logo-placeholder", children: "No logo" }) }),
1692
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
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 _a2;
1699
- return setLogo(((_a2 = e.target.files) == null ? void 0 : _a2[0]) || null);
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, import_jsx_runtime10.jsxs)("div", { className: "cnfy-dash-card", children: [
1708
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "cnfy-dash-card-header", children: [
1709
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { className: "cnfy-dash-card-title-inline", children: "News Scraping" }),
1710
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "cnfy-dash-card-desc", children: "Scrape latest news from your configured locations" })
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, import_jsx_runtime10.jsxs)("div", { className: "cnfy-dash-grid cnfy-dash-grid--sm", children: [
1713
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
1714
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "cnfy-dash-label-muted", children: "Status:" }),
1715
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "cnfy-dash-label-bold", children: scrapeStatus.isRunning ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "cnfy-dash-badge cnfy-dash-badge--yellow", children: "Running" }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "cnfy-dash-badge cnfy-dash-badge--green", children: "Idle" }) })
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, import_jsx_runtime10.jsxs)("div", { children: [
1718
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "cnfy-dash-label-muted", children: "Last Run:" }),
1719
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "cnfy-dash-label-bold", children: new Date(scrapeStatus.lastRun).toLocaleString() })
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, import_jsx_runtime10.jsxs)("div", { children: [
1722
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "cnfy-dash-label-muted", children: "Total Scraped:" }),
1723
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("p", { className: "cnfy-dash-label-bold", children: [
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, import_jsx_runtime10.jsxs)("div", { children: [
1730
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("p", { className: "cnfy-dash-info-text", children: [
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, import_jsx_runtime10.jsx)("strong", { children: (_b = preferences == null ? void 0 : preferences.localization) == null ? void 0 : _b.state }),
1733
- ((_c = preferences == null ? void 0 : preferences.localization) == null ? void 0 : _c.cities) && preferences.localization.cities.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
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
- preferences.localization.cities.join(", ")
1960
+ cities.map((c) => c.label).join(", ")
1736
1961
  ] })
1737
1962
  ] }),
1738
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
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, import_jsx_runtime10.jsxs)("div", { className: "cnfy-dash-card", children: [
1751
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { className: "cnfy-dash-card-title", children: "Localization Settings" }),
1752
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "cnfy-dash-field", children: [
1753
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { className: "cnfy-dash-label", children: "State" }),
1754
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1755
- ClientSelect_default,
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
- options: STATE_OPTIONS,
1758
- value: state,
1759
- onChange: (option) => setState(option),
1760
- placeholder: "Select state"
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, import_jsx_runtime10.jsxs)("div", { className: "cnfy-dash-field", children: [
1765
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { className: "cnfy-dash-label", children: "Cities" }),
1766
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1767
- ClientSelect_default,
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
- isMulti: true,
1770
- options: CITY_OPTIONS,
1771
- value: cities,
1772
- onChange: (options) => setCities(options),
1773
- placeholder: "Select cities",
1774
- isDisabled: !state
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, import_jsx_runtime10.jsxs)("div", { className: "cnfy-dash-field", children: [
1779
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { className: "cnfy-dash-label", children: "Language" }),
1780
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1781
- ClientSelect_default,
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
- options: LANGUAGE_OPTIONS,
1784
- value: language,
1785
- onChange: (option) => setLanguage(option),
1786
- placeholder: "Select language"
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, import_jsx_runtime10.jsx)("div", { className: "cnfy-dash-actions", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
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
- // services/chat.service.ts
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
- const headers = {
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
- if (apiKey) {
1811
- headers["x-api-key"] = apiKey;
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
- (_e = callbacks.onError) == null ? void 0 : _e.call(callbacks, new Error(event.message));
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("/chatboat/analyze-input", { input });
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("/chatboat/create-content", payload);
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}/chat/create-content/stream`, {
2352
+ const response = await fetch(`${API_BASE_URL}/contenify/create-content/stream`, {
1888
2353
  method: "POST",
1889
- headers: getHeaders(),
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
- throw new Error("No reader available");
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}/chatboat/rewrite/stream`, {
2394
+ const response = await fetch(`${API_BASE_URL}/contenify/rewrite/stream`, {
1942
2395
  method: "POST",
1943
- headers: getHeaders(),
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 data = json.data || json;
2407
+ const d = json.data || json;
1955
2408
  onComplete == null ? void 0 : onComplete({
1956
- title: data.title || "",
1957
- subtitle: data.subtitle || "",
1958
- article: data.article || "",
1959
- metaDescription: data.metaDescription || "",
1960
- metaKeywords: Array.isArray(data.metaKeywords) ? data.metaKeywords : []
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
- throw new Error("No reader available");
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("/chatboat/rewrite", payload);
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 import_jsx_runtime11 = require("react/jsx-runtime");
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({ onPost }) {
2487
+ function ChatBot() {
2015
2488
  const { loading } = useTheme();
2016
- const { preferences } = usePreferences();
2017
- const [preferencesOpen, setPreferencesOpen] = (0, import_react10.useState)(false);
2018
- const [messages, setMessages] = (0, import_react10.useState)([]);
2019
- const [isStreaming, setIsStreaming] = (0, import_react10.useState)(false);
2020
- const [activeField, setActiveField] = (0, import_react10.useState)(null);
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
- id: crypto.randomUUID(),
2029
- role: "user",
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: lang,
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: async (error) => {
2094
- console.error("Streaming error:", error);
2543
+ onError: (error) => {
2095
2544
  if (!hasStartedStreaming) {
2096
- throw error;
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
- (prev) => prev.map(
2103
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: titleBuffer || articleBuffer ? buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer) : "Failed to create article. Please try again." }) : m
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 (isStreaming) {
2110
- if (titleBuffer || articleBuffer) {
2111
- const structured = buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
2112
- setMessages(
2113
- (prev) => prev.map(
2114
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: structured }) : m
2115
- )
2116
- );
2117
- }
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 (streamError) {
2122
- console.log("Streaming failed, using regular API...", streamError);
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: lang,
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
- (prev) => prev.map(
2141
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: typeof result === "string" ? result : JSON.stringify(result) }) : m
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
- (prev) => prev.map(
2154
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "Failed to create article. Please try again." }) : m
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, _b;
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
- (prev) => prev.map(
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
- }) : m
2192
- )
2193
- );
2611
+ ${optionsList}` }) : m
2612
+ ));
2194
2613
  } else {
2195
- setMessages(
2196
- (prev) => prev.map(
2197
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "Could not extract content from that URL. Please try a different link." }) : m
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
- (prev) => prev.map(
2206
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "Failed to analyze the link. Please try again." }) : m
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
- const contentPrefs = preferences == null ? void 0 : preferences.content;
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: lang,
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 (isStreaming) {
2273
- if (titleBuffer || articleBuffer) {
2274
- const structured = buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
2275
- setMessages(
2276
- (prev) => prev.map(
2277
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: structured }) : m
2278
- )
2279
- );
2280
- }
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
- (prev) => prev.map(
2290
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "Failed to generate content. Please try again." }) : m
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
- const contentPrefs = preferences == null ? void 0 : preferences.content;
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 (isStreaming) {
2364
- if (titleBuffer || articleBuffer) {
2365
- const structured = buildStructuredContent(titleBuffer, articleBuffer, keywordsBuffer);
2366
- setMessages(
2367
- (prev) => prev.map(
2368
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: structured }) : m
2369
- )
2370
- );
2371
- }
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
- tone: contentPrefs == null ? void 0 : contentPrefs.tone,
2378
- style: contentPrefs == null ? void 0 : contentPrefs.style,
2379
- wordCount: contentPrefs == null ? void 0 : contentPrefs.wordCount
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
- (prev) => prev.map(
2396
- (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "Failed to create content. Please try again." }) : m
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, import_jsx_runtime11.jsx)("div", { className: "cnfy-loading", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "cnfy-loading-inner", children: [
2403
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react7.Loader2, { className: "cnfy-loading-spinner cnfy-animate-spin" }),
2404
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "cnfy-loading-text", children: "Loading..." })
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, import_jsx_runtime11.jsxs)("div", { className: "cnfy-root", children: [
2408
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(HeaderBar, { onOpenPreferences: () => setPreferencesOpen(true) }),
2409
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "cnfy-main", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("section", { className: "cnfy-chat-section cnfy-chat-section--full", children: [
2410
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "cnfy-chat-inner", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
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
- onPost
2882
+ onResolveSeo: handleResolveSeo
2421
2883
  }
2422
- ) }),
2423
- !messages.length && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "cnfy-empty-state", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
2424
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "cnfy-empty-state-title", children: "AI News Assistant" }),
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, import_jsx_runtime11.jsx)(PreferencesPage, {})
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 import_jsx_runtime12 = require("react/jsx-runtime");
2899
+ var import_jsx_runtime9 = require("react/jsx-runtime");
2442
2900
  function ContenifyChatBot({ apiUrl, apiKey, domain, onPost }) {
2443
- (0, import_react11.useEffect)(() => {
2901
+ (0, import_react10.useEffect)(() => {
2444
2902
  setConfig({ apiUrl, apiKey, domain });
2445
2903
  }, [apiUrl, apiKey, domain]);
2446
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PreferencesProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ChatBot, { onPost }) });
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: