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