@contenify/chatbot 2.0.0 → 4.0.0

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