@contenify/chatbot 3.0.0 → 4.0.0

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