@contenify/chatbot 0.1.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 ADDED
@@ -0,0 +1,2209 @@
1
+ "use strict";
2
+ "use client";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __defProps = Object.defineProperties;
6
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
8
+ var __getOwnPropNames = Object.getOwnPropertyNames;
9
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
10
+ var __getProtoOf = Object.getPrototypeOf;
11
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
12
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
13
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
14
+ var __spreadValues = (a, b) => {
15
+ for (var prop in b || (b = {}))
16
+ if (__hasOwnProp.call(b, prop))
17
+ __defNormalProp(a, prop, b[prop]);
18
+ if (__getOwnPropSymbols)
19
+ for (var prop of __getOwnPropSymbols(b)) {
20
+ if (__propIsEnum.call(b, prop))
21
+ __defNormalProp(a, prop, b[prop]);
22
+ }
23
+ return a;
24
+ };
25
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
26
+ var __export = (target, all) => {
27
+ for (var name in all)
28
+ __defProp(target, name, { get: all[name], enumerable: true });
29
+ };
30
+ var __copyProps = (to, from, except, desc) => {
31
+ if (from && typeof from === "object" || typeof from === "function") {
32
+ for (let key of __getOwnPropNames(from))
33
+ if (!__hasOwnProp.call(to, key) && key !== except)
34
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
35
+ }
36
+ return to;
37
+ };
38
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
39
+ // If the importer is in node compatibility mode or this is not an ESM
40
+ // file that has been converted to a CommonJS file using a Babel-
41
+ // compatible transform (i.e. "__esModule" has not been set), then set
42
+ // "default" to the CommonJS "module.exports" for node compatibility.
43
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
44
+ mod
45
+ ));
46
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
47
+
48
+ // src/index.tsx
49
+ var index_exports = {};
50
+ __export(index_exports, {
51
+ ContenifyChatBot: () => ContenifyChatBot,
52
+ default: () => index_default
53
+ });
54
+ module.exports = __toCommonJS(index_exports);
55
+
56
+ // contexts/PreferencesContext.tsx
57
+ var import_react = require("react");
58
+
59
+ // lib/api.ts
60
+ var import_axios = __toESM(require("axios"));
61
+
62
+ // lib/config.ts
63
+ var DEFAULT_API_BASE_URL = "http://localhost:8080/api";
64
+ function getApiBaseUrl() {
65
+ return process.env.NEXT_PUBLIC_CONTENIFY_API_URL || DEFAULT_API_BASE_URL;
66
+ }
67
+ function getServerBaseUrl() {
68
+ const apiUrl = getApiBaseUrl();
69
+ return apiUrl.replace(/\/api\/?$/, "");
70
+ }
71
+
72
+ // lib/api.ts
73
+ var api = import_axios.default.create({
74
+ baseURL: getApiBaseUrl(),
75
+ withCredentials: true,
76
+ headers: {
77
+ "Content-Type": "application/json"
78
+ }
79
+ });
80
+ api.interceptors.request.use(
81
+ (config) => {
82
+ const apiKey = process.env.NEXT_PUBLIC_API_KEY;
83
+ if (apiKey) {
84
+ config.headers["x-api-key"] = apiKey;
85
+ }
86
+ return config;
87
+ },
88
+ (error) => Promise.reject(error)
89
+ );
90
+ api.interceptors.response.use(
91
+ (response) => response,
92
+ (error) => {
93
+ var _a, _b;
94
+ const message = ((_b = (_a = error.response) == null ? void 0 : _a.data) == null ? void 0 : _b.message) || error.message || "Something went wrong";
95
+ return Promise.reject(message);
96
+ }
97
+ );
98
+ var api_default = api;
99
+
100
+ // services/preferences.service.ts
101
+ var getMyPreferencesApi = async () => {
102
+ const { data } = await api_default.get("/preferences/me");
103
+ return data.data;
104
+ };
105
+
106
+ // contexts/PreferencesContext.tsx
107
+ var import_jsx_runtime = require("react/jsx-runtime");
108
+ var defaultPreferences = {
109
+ chatbot: {
110
+ name: "AI News Assistant",
111
+ primaryColor: "#10b981",
112
+ secondaryColor: "#064e3b",
113
+ theme: "system"
114
+ },
115
+ localization: {
116
+ country: "India",
117
+ state: "Uttar Pradesh",
118
+ cities: [],
119
+ language: "en",
120
+ locale: "en-IN",
121
+ timezone: "Asia/Kolkata"
122
+ },
123
+ appearance: {
124
+ showSidebar: true,
125
+ showHeader: true,
126
+ activeThemePreset: null,
127
+ showNewsPanel: true
128
+ },
129
+ content: {
130
+ tone: "engaging",
131
+ style: "blog",
132
+ wordCount: "medium",
133
+ includeQuotes: true,
134
+ includeFAQ: false,
135
+ targetAudience: ""
136
+ }
137
+ };
138
+ var PreferencesContext = (0, import_react.createContext)(void 0);
139
+ function PreferencesProvider({ children }) {
140
+ const [preferences, setPreferences] = (0, import_react.useState)(defaultPreferences);
141
+ const [loading, setLoading] = (0, import_react.useState)(true);
142
+ const [error, setError] = (0, import_react.useState)(null);
143
+ const refreshPreferences = async () => {
144
+ var _a, _b, _c;
145
+ try {
146
+ setLoading(true);
147
+ setError(null);
148
+ const data = await getMyPreferencesApi();
149
+ if (data) {
150
+ if (((_a = data.chatbot) == null ? void 0 : _a.logoUrl) && !data.chatbot.logoUrl.startsWith("http")) {
151
+ data.chatbot.logoUrl = `${getServerBaseUrl()}/${data.chatbot.logoUrl}`;
152
+ }
153
+ const mergedPreferences = __spreadProps(__spreadValues(__spreadValues({}, defaultPreferences), data), {
154
+ chatbot: __spreadValues(__spreadValues({}, defaultPreferences.chatbot), data.chatbot),
155
+ localization: __spreadValues(__spreadValues({}, defaultPreferences.localization), data.localization),
156
+ appearance: __spreadValues(__spreadValues({}, defaultPreferences.appearance), data.appearance),
157
+ content: __spreadValues(__spreadValues({}, defaultPreferences.content), data.content)
158
+ });
159
+ setPreferences(mergedPreferences);
160
+ if (typeof document !== "undefined") {
161
+ document.documentElement.style.setProperty("--color-primary", ((_b = mergedPreferences.chatbot) == null ? void 0 : _b.primaryColor) || "#10b981");
162
+ document.documentElement.style.setProperty("--color-secondary", ((_c = mergedPreferences.chatbot) == null ? void 0 : _c.secondaryColor) || "#064e3b");
163
+ }
164
+ }
165
+ } catch (err) {
166
+ console.error("Failed to fetch preferences:", err);
167
+ setError(err instanceof Error ? err.message : "Failed to load preferences");
168
+ setPreferences(defaultPreferences);
169
+ } finally {
170
+ setLoading(false);
171
+ }
172
+ };
173
+ const updatePreferences = (newPreferences) => {
174
+ var _a, _b;
175
+ setPreferences(newPreferences);
176
+ if (typeof document !== "undefined") {
177
+ document.documentElement.style.setProperty("--color-primary", ((_a = newPreferences.chatbot) == null ? void 0 : _a.primaryColor) || "#10b981");
178
+ document.documentElement.style.setProperty("--color-secondary", ((_b = newPreferences.chatbot) == null ? void 0 : _b.secondaryColor) || "#064e3b");
179
+ }
180
+ };
181
+ (0, import_react.useEffect)(() => {
182
+ refreshPreferences();
183
+ }, []);
184
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
185
+ PreferencesContext.Provider,
186
+ {
187
+ value: {
188
+ preferences,
189
+ loading,
190
+ error,
191
+ refreshPreferences,
192
+ updatePreferences
193
+ },
194
+ children
195
+ }
196
+ );
197
+ }
198
+ function usePreferences() {
199
+ const context = (0, import_react.useContext)(PreferencesContext);
200
+ if (context === void 0) {
201
+ throw new Error("usePreferences must be used within a PreferencesProvider");
202
+ }
203
+ return context;
204
+ }
205
+
206
+ // components/chatbot/ChatBot.tsx
207
+ var import_react10 = require("react");
208
+
209
+ // components/chatbot/ArticleModal.tsx
210
+ var import_jsx_runtime2 = require("react/jsx-runtime");
211
+ function ArticleModal({
212
+ article,
213
+ onClose,
214
+ onRecreate
215
+ }) {
216
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "fixed inset-0 z-50 bg-black/50 backdrop-blur-sm flex items-center justify-center px-4", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "relative w-full max-w-3xl bg-white rounded-xl shadow-card border border-gray-200 animate-in fade-in zoom-in", children: [
217
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-start justify-between px-6 py-4 border-b border-gray-300", children: [
218
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
219
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs text-gray-500 mb-1", children: "Source Article" }),
220
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { className: "text-lg font-semibold text-gray-900 leading-snug", children: article.title })
221
+ ] }),
222
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
223
+ "button",
224
+ {
225
+ onClick: onClose,
226
+ className: "text-gray-400 hover:text-gray-700 transition",
227
+ "aria-label": "Close modal",
228
+ children: "\u2715"
229
+ }
230
+ )
231
+ ] }),
232
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "px-6 py-5 max-h-[65vh] overflow-y-auto", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "prose prose-sm max-w-none text-gray-800", children: article.content }) }),
233
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center justify-between px-6 py-4 border-t bg-gray-50 rounded-b-xl", children: [
234
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-xs text-gray-400", children: "This content will be recreated using AI" }),
235
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex gap-2", children: [
236
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
237
+ "button",
238
+ {
239
+ onClick: onClose,
240
+ className: "px-4 py-2 text-sm rounded-md border border-gray-300 text-gray-700 hover:bg-gray-100 transition",
241
+ children: "Cancel"
242
+ }
243
+ ),
244
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
245
+ "button",
246
+ {
247
+ onClick: () => onRecreate({
248
+ title: article.title,
249
+ content: article.content,
250
+ id: article._id
251
+ // Include article ID when available
252
+ }),
253
+ className: "px-4 py-2 text-sm rounded-md bg-gray-900 text-white hover:bg-black transition",
254
+ children: "Recreate Article"
255
+ }
256
+ )
257
+ ] })
258
+ ] })
259
+ ] }) });
260
+ }
261
+
262
+ // util/util.ts
263
+ function extractArticleContent(raw) {
264
+ if (!raw) {
265
+ return { article: "", metaKeywords: [] };
266
+ }
267
+ if (!raw.trim().startsWith("{")) {
268
+ return {
269
+ article: raw,
270
+ metaKeywords: []
271
+ };
272
+ }
273
+ try {
274
+ const parsed = JSON.parse(raw);
275
+ return {
276
+ title: parsed.title || "",
277
+ article: parsed.article || parsed.content || "",
278
+ metaKeywords: Array.isArray(parsed.metaKeywords) ? parsed.metaKeywords : []
279
+ };
280
+ } catch (e) {
281
+ const article = raw.replace(/^[\s\S]*?"article"\s*:\s*"/, "").replace(/"\s*,\s*"metaKeywords"[\s\S]*$/, "").replace(/"}\s*$/, "") || raw;
282
+ return {
283
+ article,
284
+ metaKeywords: []
285
+ };
286
+ }
287
+ }
288
+
289
+ // components/chatbot/ChatWindow.tsx
290
+ var import_lucide_react4 = require("lucide-react");
291
+ var import_react6 = require("react");
292
+
293
+ // components/chatbot/EditModal.tsx
294
+ var import_lucide_react2 = require("lucide-react");
295
+ var import_react4 = require("react");
296
+
297
+ // components/chatbot/RichTextEditor.tsx
298
+ var import_react2 = require("@tiptap/react");
299
+ var import_starter_kit = __toESM(require("@tiptap/starter-kit"));
300
+ var import_extension_placeholder = __toESM(require("@tiptap/extension-placeholder"));
301
+ var import_lucide_react = require("lucide-react");
302
+ var import_react3 = require("react");
303
+ var import_jsx_runtime3 = require("react/jsx-runtime");
304
+ function RichTextEditor({
305
+ content,
306
+ onChange,
307
+ placeholder = "Start writing your article..."
308
+ }) {
309
+ const editor = (0, import_react2.useEditor)({
310
+ immediatelyRender: false,
311
+ // Prevents SSR hydration mismatch
312
+ extensions: [
313
+ import_starter_kit.default.configure({
314
+ heading: {
315
+ levels: [1, 2]
316
+ }
317
+ }),
318
+ import_extension_placeholder.default.configure({
319
+ placeholder
320
+ })
321
+ ],
322
+ content,
323
+ editorProps: {
324
+ attributes: {
325
+ class: "prose prose-sm max-w-none focus:outline-none min-h-[350px] p-4"
326
+ }
327
+ },
328
+ onUpdate: ({ editor: editor2 }) => {
329
+ onChange(editor2.getHTML());
330
+ }
331
+ });
332
+ (0, import_react3.useEffect)(() => {
333
+ if (editor && content !== editor.getHTML()) {
334
+ editor.commands.setContent(content);
335
+ }
336
+ }, [content, editor]);
337
+ if (!editor) {
338
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "w-full h-[400px] border border-gray-300 rounded-lg flex items-center justify-center text-gray-400", children: "Loading editor..." });
339
+ }
340
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "w-full border border-gray-300 rounded-lg overflow-hidden", children: [
341
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex flex-wrap items-center gap-1 p-2 border-b border-gray-200 bg-gray-50", children: [
342
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
343
+ ToolbarButton,
344
+ {
345
+ onClick: () => editor.chain().focus().toggleHeading({ level: 1 }).run(),
346
+ isActive: editor.isActive("heading", { level: 1 }),
347
+ title: "Heading 1",
348
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react.Heading1, { size: 18 })
349
+ }
350
+ ),
351
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
352
+ ToolbarButton,
353
+ {
354
+ onClick: () => editor.chain().focus().toggleHeading({ level: 2 }).run(),
355
+ isActive: editor.isActive("heading", { level: 2 }),
356
+ title: "Heading 2",
357
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react.Heading2, { size: 18 })
358
+ }
359
+ ),
360
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
361
+ ToolbarButton,
362
+ {
363
+ onClick: () => editor.chain().focus().setParagraph().run(),
364
+ isActive: editor.isActive("paragraph"),
365
+ title: "Paragraph",
366
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react.Pilcrow, { size: 18 })
367
+ }
368
+ ),
369
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "w-px h-6 bg-gray-300 mx-1" }),
370
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
371
+ ToolbarButton,
372
+ {
373
+ onClick: () => editor.chain().focus().toggleBold().run(),
374
+ isActive: editor.isActive("bold"),
375
+ title: "Bold",
376
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react.Bold, { size: 18 })
377
+ }
378
+ ),
379
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
380
+ ToolbarButton,
381
+ {
382
+ onClick: () => editor.chain().focus().toggleItalic().run(),
383
+ isActive: editor.isActive("italic"),
384
+ title: "Italic",
385
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react.Italic, { size: 18 })
386
+ }
387
+ ),
388
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "w-px h-6 bg-gray-300 mx-1" }),
389
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
390
+ ToolbarButton,
391
+ {
392
+ onClick: () => editor.chain().focus().toggleBulletList().run(),
393
+ isActive: editor.isActive("bulletList"),
394
+ title: "Bullet List",
395
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react.List, { size: 18 })
396
+ }
397
+ ),
398
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
399
+ ToolbarButton,
400
+ {
401
+ onClick: () => editor.chain().focus().toggleOrderedList().run(),
402
+ isActive: editor.isActive("orderedList"),
403
+ title: "Numbered List",
404
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react.ListOrdered, { size: 18 })
405
+ }
406
+ ),
407
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "w-px h-6 bg-gray-300 mx-1" }),
408
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
409
+ ToolbarButton,
410
+ {
411
+ onClick: () => editor.chain().focus().undo().run(),
412
+ disabled: !editor.can().undo(),
413
+ title: "Undo",
414
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react.Undo, { size: 18 })
415
+ }
416
+ ),
417
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
418
+ ToolbarButton,
419
+ {
420
+ onClick: () => editor.chain().focus().redo().run(),
421
+ disabled: !editor.can().redo(),
422
+ title: "Redo",
423
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react.Redo, { size: 18 })
424
+ }
425
+ )
426
+ ] }),
427
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react2.EditorContent, { editor, className: "min-h-[350px]" })
428
+ ] });
429
+ }
430
+ function ToolbarButton({
431
+ onClick,
432
+ isActive,
433
+ disabled,
434
+ title,
435
+ children
436
+ }) {
437
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
438
+ "button",
439
+ {
440
+ onClick,
441
+ disabled,
442
+ title,
443
+ className: `p-2 rounded hover:bg-gray-200 transition-colors ${isActive ? "bg-gray-200 text-emerald-600" : "text-gray-600"} ${disabled ? "opacity-40 cursor-not-allowed" : ""}`,
444
+ children
445
+ }
446
+ );
447
+ }
448
+
449
+ // hooks/useTheme.ts
450
+ function useTheme() {
451
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
452
+ const { preferences, loading } = usePreferences();
453
+ return {
454
+ loading,
455
+ primaryColor: ((_a = preferences == null ? void 0 : preferences.chatbot) == null ? void 0 : _a.primaryColor) || "#10b981",
456
+ secondaryColor: ((_b = preferences == null ? void 0 : preferences.chatbot) == null ? void 0 : _b.secondaryColor) || "#064e3b",
457
+ botName: ((_c = preferences == null ? void 0 : preferences.chatbot) == null ? void 0 : _c.name) || "AI News Assistant",
458
+ logoUrl: (_d = preferences == null ? void 0 : preferences.chatbot) == null ? void 0 : _d.logoUrl,
459
+ theme: ((_e = preferences == null ? void 0 : preferences.chatbot) == null ? void 0 : _e.theme) || "system",
460
+ showSidebar: (_g = (_f = preferences == null ? void 0 : preferences.appearance) == null ? void 0 : _f.showSidebar) != null ? _g : true,
461
+ showHeader: (_i = (_h = preferences == null ? void 0 : preferences.appearance) == null ? void 0 : _h.showHeader) != null ? _i : true,
462
+ activeThemePreset: (_k = (_j = preferences == null ? void 0 : preferences.appearance) == null ? void 0 : _j.activeThemePreset) != null ? _k : null,
463
+ showNewsPanel: (_m = (_l = preferences == null ? void 0 : preferences.appearance) == null ? void 0 : _l.showNewsPanel) != null ? _m : true
464
+ };
465
+ }
466
+
467
+ // components/chatbot/EditModal.tsx
468
+ var import_jsx_runtime4 = require("react/jsx-runtime");
469
+ function EditModal({
470
+ isOpen,
471
+ initialContent,
472
+ metaKeywords,
473
+ onClose,
474
+ onSaveDraft,
475
+ onPost
476
+ }) {
477
+ const { primaryColor } = useTheme();
478
+ const [content, setContent] = (0, import_react4.useState)("");
479
+ const [keywords, setKeywords] = (0, import_react4.useState)(metaKeywords);
480
+ const [newKeyword, setNewKeyword] = (0, import_react4.useState)("");
481
+ const markdownToHtml = (text) => {
482
+ const lines = text.split("\n").filter((line) => line.trim());
483
+ let html = "";
484
+ lines.forEach((line) => {
485
+ const trimmed = line.trim();
486
+ if (trimmed.startsWith("# ")) {
487
+ html += `<h1>${trimmed.replace(/^#\s+/, "")}</h1>`;
488
+ } else if (trimmed.startsWith("## ")) {
489
+ html += `<h2>${trimmed.replace(/^##\s+/, "")}</h2>`;
490
+ } else {
491
+ html += `<p>${trimmed}</p>`;
492
+ }
493
+ });
494
+ return html;
495
+ };
496
+ (0, import_react4.useEffect)(() => {
497
+ const htmlContent = markdownToHtml(initialContent);
498
+ setContent(htmlContent);
499
+ setKeywords(metaKeywords);
500
+ }, [initialContent, metaKeywords]);
501
+ (0, import_react4.useEffect)(() => {
502
+ if (isOpen) {
503
+ document.body.style.overflow = "hidden";
504
+ } else {
505
+ document.body.style.overflow = "";
506
+ }
507
+ return () => {
508
+ document.body.style.overflow = "";
509
+ };
510
+ }, [isOpen]);
511
+ const handleAddKeyword = () => {
512
+ if (newKeyword.trim() && !keywords.includes(newKeyword.trim())) {
513
+ setKeywords([...keywords, newKeyword.trim()]);
514
+ setNewKeyword("");
515
+ }
516
+ };
517
+ const handleRemoveKeyword = (index) => {
518
+ setKeywords(keywords.filter((_, i) => i !== index));
519
+ };
520
+ const handleKeyDown = (e) => {
521
+ if (e.key === "Enter") {
522
+ e.preventDefault();
523
+ handleAddKeyword();
524
+ }
525
+ };
526
+ if (!isOpen) return null;
527
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
528
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
529
+ "div",
530
+ {
531
+ className: "absolute inset-0 bg-black/50",
532
+ onClick: onClose
533
+ }
534
+ ),
535
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "relative w-full h-full max-w-5xl max-h-[90vh] m-4 bg-white rounded-xl shadow-2xl flex flex-col overflow-hidden", children: [
536
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center justify-between px-6 py-4 border-b", children: [
537
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { className: "text-xl font-semibold text-gray-900", children: "Edit Article" }),
538
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
539
+ "button",
540
+ {
541
+ onClick: onClose,
542
+ className: "p-2 rounded-full hover:bg-gray-100 transition-colors",
543
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.X, { size: 20, className: "text-gray-500" })
544
+ }
545
+ )
546
+ ] }),
547
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex-1 overflow-y-auto p-6 space-y-6", children: [
548
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
549
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Article Content" }),
550
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
551
+ RichTextEditor,
552
+ {
553
+ content,
554
+ onChange: setContent,
555
+ placeholder: "Start writing your article..."
556
+ }
557
+ )
558
+ ] }),
559
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
560
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Meta Keywords" }),
561
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex flex-wrap gap-2 mb-3", children: keywords.map((keyword, index) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
562
+ "span",
563
+ {
564
+ className: "inline-flex items-center gap-1 px-3 py-1 bg-gray-100 text-gray-700 rounded-full text-sm",
565
+ children: [
566
+ "#",
567
+ keyword,
568
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
569
+ "button",
570
+ {
571
+ onClick: () => handleRemoveKeyword(index),
572
+ className: "ml-1 text-gray-400 hover:text-gray-600",
573
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.X, { size: 14 })
574
+ }
575
+ )
576
+ ]
577
+ },
578
+ index
579
+ )) }),
580
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex gap-2", children: [
581
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
582
+ "input",
583
+ {
584
+ type: "text",
585
+ value: newKeyword,
586
+ onChange: (e) => setNewKeyword(e.target.value),
587
+ onKeyDown: handleKeyDown,
588
+ placeholder: "Add a keyword...",
589
+ className: "flex-1 px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-emerald-500 focus:border-emerald-500"
590
+ }
591
+ ),
592
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
593
+ "button",
594
+ {
595
+ onClick: handleAddKeyword,
596
+ className: "px-4 py-2 bg-gray-100 text-gray-700 rounded-lg text-sm font-medium hover:bg-gray-200 transition-colors",
597
+ children: "Add"
598
+ }
599
+ )
600
+ ] })
601
+ ] })
602
+ ] }),
603
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center justify-end gap-3 px-6 py-4 border-t bg-gray-50", children: [
604
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
605
+ "button",
606
+ {
607
+ onClick: onClose,
608
+ className: "px-4 py-2 text-gray-700 text-sm font-medium hover:bg-gray-100 rounded-lg transition-colors",
609
+ children: "Cancel"
610
+ }
611
+ ),
612
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
613
+ "button",
614
+ {
615
+ onClick: () => onSaveDraft(content, keywords),
616
+ className: "px-4 py-2 bg-gray-200 text-gray-800 text-sm font-medium rounded-lg hover:bg-gray-300 transition-colors",
617
+ children: "Save Draft"
618
+ }
619
+ ),
620
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
621
+ "button",
622
+ {
623
+ onClick: () => onPost(content, keywords),
624
+ className: "px-4 py-2 bg-emerald-600 text-white text-sm font-medium rounded-lg hover:bg-emerald-700 transition-colors",
625
+ style: { backgroundColor: primaryColor, color: "#fff" },
626
+ children: "Post"
627
+ }
628
+ )
629
+ ] })
630
+ ] })
631
+ ] });
632
+ }
633
+
634
+ // components/news/NewsList.tsx
635
+ var import_react5 = require("react");
636
+ var import_lucide_react3 = require("lucide-react");
637
+ var import_jsx_runtime5 = require("react/jsx-runtime");
638
+ var dateFormatter = new Intl.DateTimeFormat("en-GB", {
639
+ day: "2-digit",
640
+ month: "short",
641
+ year: "numeric"
642
+ });
643
+ var timeFormatter = new Intl.DateTimeFormat("en-GB", {
644
+ hour: "2-digit",
645
+ minute: "2-digit",
646
+ hour12: false
647
+ });
648
+ function extractLinksFromContent(html) {
649
+ if (!html) return [];
650
+ const parser = new DOMParser();
651
+ const doc = parser.parseFromString(html, "text/html");
652
+ return Array.from(doc.querySelectorAll("li")).map((li) => {
653
+ const anchor = li.querySelector("a");
654
+ const source = li.querySelector("font");
655
+ return {
656
+ title: (anchor == null ? void 0 : anchor.textContent) || "",
657
+ url: (anchor == null ? void 0 : anchor.getAttribute("href")) || "#",
658
+ source: (source == null ? void 0 : source.textContent) || ""
659
+ };
660
+ });
661
+ }
662
+ function NewsList({
663
+ news,
664
+ onView,
665
+ onRecreate
666
+ }) {
667
+ const { primaryColor } = useTheme();
668
+ const [primaryColorState] = (0, import_react5.useState)(primaryColor);
669
+ const lightenColor = (hex, percent = 85) => {
670
+ hex = hex.replace("#", "");
671
+ let r = parseInt(hex.substring(0, 2), 16);
672
+ let g = parseInt(hex.substring(2, 4), 16);
673
+ let b = parseInt(hex.substring(4, 6), 16);
674
+ r = Math.round(r + (255 - r) * (percent / 100));
675
+ g = Math.round(g + (255 - g) * (percent / 100));
676
+ b = Math.round(b + (255 - b) * (percent / 100));
677
+ return `rgb(${r}, ${g}, ${b})`;
678
+ };
679
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "p-4 space-y-3", children: news.flatMap((item) => {
680
+ const publishedDate = new Date(item.publishedAt);
681
+ const links = extractLinksFromContent(item.content);
682
+ if (links.length > 0) {
683
+ return links.map((link, idx) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
684
+ "div",
685
+ {
686
+ className: "flex items-start justify-between gap-3 rounded-lg border border-gray-200 bg-white p-3 text-sm hover:bg-gray-50 transition",
687
+ children: [
688
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "min-w-0", children: [
689
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "font-bold text-gray-900 line-clamp-2", children: link.title }),
690
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-wrap items-center gap-2 mt-2 text-xs text-gray-500", children: [
691
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
692
+ "span",
693
+ {
694
+ className: "inline-flex items-center rounded-full px-2 py-0.5 font-medium border",
695
+ style: {
696
+ backgroundColor: lightenColor(primaryColorState, 95),
697
+ color: primaryColor,
698
+ borderColor: lightenColor(primaryColorState, 95)
699
+ },
700
+ children: link.source || item.sourceName
701
+ }
702
+ ),
703
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "\u2022" }),
704
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "capitalize", children: item.category }),
705
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "\u2022" }),
706
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { children: [
707
+ dateFormatter.format(publishedDate),
708
+ " ",
709
+ timeFormatter.format(publishedDate)
710
+ ] })
711
+ ] })
712
+ ] }),
713
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-2 shrink-0", children: [
714
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
715
+ "button",
716
+ {
717
+ onClick: () => window.open(link.url, "_blank"),
718
+ className: "p-1 text-gray-400 hover:text-gray-700",
719
+ title: "View",
720
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react3.Eye, { size: 16 })
721
+ }
722
+ ),
723
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
724
+ "button",
725
+ {
726
+ onClick: () => onRecreate({
727
+ title: link.title,
728
+ content: link.title,
729
+ id: item._id
730
+ // Include article ID
731
+ }),
732
+ className: "p-1 text-gray-400 hover:text-gray-700",
733
+ title: "Recreate",
734
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react3.RefreshCcw, { size: 16 })
735
+ }
736
+ )
737
+ ] })
738
+ ]
739
+ },
740
+ `${item._id}-link-${idx}`
741
+ ));
742
+ }
743
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
744
+ "div",
745
+ {
746
+ className: "flex items-start justify-between gap-3 rounded-lg border border-gray-200 bg-white p-3 text-sm hover:bg-gray-50 transition",
747
+ children: [
748
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "min-w-0", children: [
749
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "font-bold text-gray-900 line-clamp-2", children: item.title }),
750
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-wrap items-center gap-2 mt-2 text-xs text-gray-500", children: [
751
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
752
+ "span",
753
+ {
754
+ className: "inline-flex items-center rounded-full px-2 py-0.5 font-medium border",
755
+ style: {
756
+ backgroundColor: lightenColor(primaryColorState, 95),
757
+ color: primaryColor,
758
+ borderColor: lightenColor(primaryColorState, 95)
759
+ },
760
+ children: item.sourceName
761
+ }
762
+ ),
763
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "\u2022" }),
764
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "capitalize", children: item.category }),
765
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "\u2022" }),
766
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { children: [
767
+ dateFormatter.format(publishedDate),
768
+ " ",
769
+ timeFormatter.format(publishedDate)
770
+ ] })
771
+ ] })
772
+ ] }),
773
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-2 shrink-0", children: [
774
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
775
+ "button",
776
+ {
777
+ onClick: () => onView(item),
778
+ className: "p-1 text-gray-400 hover:text-gray-700",
779
+ title: "View",
780
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react3.Eye, { size: 16 })
781
+ }
782
+ ),
783
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
784
+ "button",
785
+ {
786
+ onClick: () => onRecreate({
787
+ title: item.title,
788
+ content: item.content || item.title,
789
+ id: item._id
790
+ // Include article ID
791
+ }),
792
+ className: "p-1 text-gray-400 hover:text-gray-700",
793
+ title: "Recreate",
794
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react3.RefreshCcw, { size: 16 })
795
+ }
796
+ )
797
+ ] })
798
+ ]
799
+ },
800
+ item._id
801
+ );
802
+ }) });
803
+ }
804
+
805
+ // services/news.service.ts
806
+ var getTrendingNews = async () => {
807
+ const { data } = await api_default.get("/news/trending");
808
+ return data.data;
809
+ };
810
+ var getNewsSources = async () => {
811
+ const { data } = await api_default.get("/news/sources");
812
+ return data.data;
813
+ };
814
+ var scrapeNewsSource = async (sourceId) => {
815
+ const { data } = await api_default.post("/news/scrape-source", { sourceId });
816
+ return data.data;
817
+ };
818
+ var getNewsBySource = async (sourceId) => {
819
+ const { data } = await api_default.get(`/news/by-source/${sourceId}`);
820
+ return data.data;
821
+ };
822
+
823
+ // components/chatbot/ChatWindow.tsx
824
+ var import_react_select = __toESM(require("react-select"));
825
+ var import_jsx_runtime6 = require("react/jsx-runtime");
826
+ var ACTION_ICONS = {
827
+ recreate_article: import_lucide_react4.RefreshCcw,
828
+ create_summary: import_lucide_react4.ListChecks,
829
+ create_social_posts: import_lucide_react4.Share2,
830
+ create_blog_post: import_lucide_react4.BookOpen,
831
+ create_newsletter: import_lucide_react4.Mail,
832
+ extract_key_points: import_lucide_react4.Key
833
+ };
834
+ function ChatWindow({
835
+ messages,
836
+ onSend,
837
+ onSelectNews,
838
+ isStreaming = false,
839
+ analyzedData,
840
+ onSelectAction
841
+ }) {
842
+ var _a, _b;
843
+ const { loading, showNewsPanel } = useTheme();
844
+ const bottomRef = (0, import_react6.useRef)(null);
845
+ const textareaRef = (0, import_react6.useRef)(null);
846
+ const dropdownRef = (0, import_react6.useRef)(null);
847
+ const [input, setInput] = (0, import_react6.useState)("");
848
+ const [copiedId, setCopiedId] = (0, import_react6.useState)(null);
849
+ const [showNewsDropdown, setShowNewsDropdown] = (0, import_react6.useState)(false);
850
+ const [trendingNews, setTrendingNews] = (0, import_react6.useState)([]);
851
+ const [loadingNews, setLoadingNews] = (0, import_react6.useState)(false);
852
+ const [sources, setSources] = (0, import_react6.useState)([]);
853
+ const [selectedSource, setSelectedSource] = (0, import_react6.useState)(null);
854
+ const [loadingSources, setLoadingSources] = (0, import_react6.useState)(false);
855
+ const [scraping, setScraping] = (0, import_react6.useState)(false);
856
+ const [editModal, setEditModal] = (0, import_react6.useState)({ isOpen: false, content: "", metaKeywords: [], messageId: "" });
857
+ const { primaryColor } = useTheme();
858
+ const { preferences } = usePreferences();
859
+ const preferredLanguage = (_a = preferences == null ? void 0 : preferences.localization) == null ? void 0 : _a.language;
860
+ const handleCopy = async (blocks, messageId) => {
861
+ try {
862
+ const formattedText = blocksToFormattedText(blocks);
863
+ await navigator.clipboard.writeText(formattedText);
864
+ setCopiedId(messageId);
865
+ setTimeout(() => setCopiedId(null), 2e3);
866
+ } catch (err) {
867
+ console.error("Failed to copy:", err);
868
+ }
869
+ };
870
+ const blocksToFormattedText = (blocks) => {
871
+ return blocks.map((block) => {
872
+ if (block.type === "h1") return `# ${block.text}`;
873
+ if (block.type === "h2") return `## ${block.text}`;
874
+ return block.text;
875
+ }).join("\n\n");
876
+ };
877
+ const handleEdit = (blocks, metaKeywords, messageId) => {
878
+ const formattedContent = blocksToFormattedText(blocks);
879
+ setEditModal({ isOpen: true, content: formattedContent, metaKeywords, messageId });
880
+ };
881
+ const handleCloseModal = () => {
882
+ setEditModal({ isOpen: false, content: "", metaKeywords: [], messageId: "" });
883
+ };
884
+ const handleSaveDraft = (content, keywords) => {
885
+ console.log("Saving draft:", { content, keywords });
886
+ handleCloseModal();
887
+ };
888
+ const handlePost = (content, keywords) => {
889
+ console.log("Posting:", { content, keywords });
890
+ handleCloseModal();
891
+ };
892
+ (0, import_react6.useLayoutEffect)(() => {
893
+ var _a2;
894
+ (_a2 = bottomRef.current) == null ? void 0 : _a2.scrollIntoView({ block: "end" });
895
+ }, [messages]);
896
+ const handleSend = () => {
897
+ if (!input.trim()) return;
898
+ onSend(input);
899
+ setInput("");
900
+ if (textareaRef.current) {
901
+ textareaRef.current.style.height = "auto";
902
+ }
903
+ };
904
+ (0, import_react6.useEffect)(() => {
905
+ const textarea = textareaRef.current;
906
+ if (textarea) {
907
+ textarea.style.height = "auto";
908
+ textarea.style.height = `${textarea.scrollHeight}px`;
909
+ }
910
+ }, [input]);
911
+ const fetchSources = async () => {
912
+ setLoadingSources(true);
913
+ try {
914
+ const data = await getNewsSources();
915
+ setSources(data || []);
916
+ } catch (err) {
917
+ console.error("Failed to fetch sources:", err);
918
+ } finally {
919
+ setLoadingSources(false);
920
+ }
921
+ };
922
+ const fetchNews = async (sourceId) => {
923
+ setLoadingNews(true);
924
+ try {
925
+ let data;
926
+ if (sourceId) {
927
+ data = await getNewsBySource(sourceId);
928
+ } else {
929
+ data = await getTrendingNews();
930
+ }
931
+ setTrendingNews(data || []);
932
+ } catch (err) {
933
+ console.error("Failed to fetch news:", err);
934
+ } finally {
935
+ setLoadingNews(false);
936
+ }
937
+ };
938
+ const handleScrape = async () => {
939
+ if (!selectedSource) return;
940
+ setScraping(true);
941
+ try {
942
+ await scrapeNewsSource(selectedSource);
943
+ await fetchNews(selectedSource);
944
+ } catch (err) {
945
+ console.error("Failed to scrape:", err);
946
+ } finally {
947
+ setScraping(false);
948
+ }
949
+ };
950
+ const handleSourceSelect = (option) => {
951
+ var _a2;
952
+ const sourceId = (_a2 = option == null ? void 0 : option.value) != null ? _a2 : null;
953
+ console.log("Selected source:", option);
954
+ setSelectedSource(sourceId);
955
+ fetchNews(sourceId);
956
+ };
957
+ const handleOpenNewsDropdown = () => {
958
+ setShowNewsDropdown(true);
959
+ if (sources.length === 0) {
960
+ fetchSources();
961
+ }
962
+ if (trendingNews.length === 0) {
963
+ fetchNews(selectedSource);
964
+ }
965
+ };
966
+ (0, import_react6.useEffect)(() => {
967
+ const handleClickOutside = (event) => {
968
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
969
+ setShowNewsDropdown(false);
970
+ }
971
+ };
972
+ if (showNewsDropdown) {
973
+ document.addEventListener("mousedown", handleClickOutside);
974
+ }
975
+ return () => {
976
+ document.removeEventListener("mousedown", handleClickOutside);
977
+ };
978
+ }, [showNewsDropdown]);
979
+ function formatAIContent(raw) {
980
+ const extracted = extractArticleContent(raw);
981
+ if (!extracted || !extracted.article) {
982
+ return { blocks: [], metaKeywords: [] };
983
+ }
984
+ const { article, metaKeywords } = extracted;
985
+ 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");
986
+ const lines = normalized.split("\n").map((line) => line.replace(/<[^>]+>/g, "").trim()).filter(Boolean);
987
+ const blocks = [];
988
+ lines.forEach((line, index) => {
989
+ if (index === 0 && line.length < 120) {
990
+ blocks.push({ type: "h1", text: line });
991
+ return;
992
+ }
993
+ if (line.length < 90 && !line.endsWith("\u0964") && !line.endsWith(".")) {
994
+ blocks.push({ type: "h2", text: line });
995
+ return;
996
+ }
997
+ blocks.push({ type: "p", text: line });
998
+ });
999
+ return { blocks, metaKeywords };
1000
+ }
1001
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "h-full flex w-full flex-col", children: [
1002
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex-1 px-4 py-6", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "max-w-3xl chat-scroll mx-auto space-y-6", children: [
1003
+ messages.map((msg) => {
1004
+ var _a2;
1005
+ const parsed = formatAIContent(msg.content);
1006
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex gap-3", children: [
1007
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex-shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1008
+ "div",
1009
+ {
1010
+ className: "h-8 w-8 rounded-full flex items-center justify-center text-xs font-semibold text-white",
1011
+ style: {
1012
+ backgroundColor: msg.role === "assistant" ? primaryColor : "#1f2937"
1013
+ },
1014
+ children: msg.role === "assistant" ? "AI" : "You"
1015
+ }
1016
+ ) }),
1017
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex-1 text-sm space-y-3 leading-relaxed", children: [
1018
+ msg.role === "assistant" && parsed.blocks.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex justify-end -mt-1 mb-2", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1019
+ "button",
1020
+ {
1021
+ onClick: () => handleCopy(parsed.blocks, msg.id),
1022
+ className: "p-1.5 rounded-md hover:bg-gray-100 transition-colors text-gray-400 hover:text-gray-600",
1023
+ title: "Copy to clipboard",
1024
+ children: copiedId === msg.id ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react4.Check, { size: 16, className: "text-emerald-600" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react4.Copy, { size: 16 })
1025
+ }
1026
+ ) }),
1027
+ parsed.blocks.map((block, idx) => {
1028
+ if (block.type === "h1") {
1029
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1030
+ "h1",
1031
+ {
1032
+ className: "text-sm font-semibold",
1033
+ children: block.text
1034
+ },
1035
+ idx
1036
+ );
1037
+ }
1038
+ if (block.type === "h2") {
1039
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1040
+ "h2",
1041
+ {
1042
+ className: "text-base font-semibold mt-4",
1043
+ children: block.text
1044
+ },
1045
+ idx
1046
+ );
1047
+ }
1048
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-gray-800", children: block.text }, idx);
1049
+ }),
1050
+ parsed.metaKeywords.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "pt-4 border-t mt-6", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex flex-wrap gap-2", children: parsed.metaKeywords.map((tag, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1051
+ "span",
1052
+ {
1053
+ className: "text-xs px-3 py-1 rounded-full bg-gray-100 text-gray-700",
1054
+ children: [
1055
+ "#",
1056
+ tag
1057
+ ]
1058
+ },
1059
+ i
1060
+ )) }) }),
1061
+ msg.role === "assistant" && (analyzedData == null ? void 0 : analyzedData.messageId) === msg.id && analyzedData.options.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex flex-wrap gap-2 pt-4 mt-2", children: analyzedData.options.map((option) => {
1062
+ const IconComponent = ACTION_ICONS[option.id] || import_lucide_react4.FileText;
1063
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1064
+ "button",
1065
+ {
1066
+ onClick: () => onSelectAction == null ? void 0 : onSelectAction(option.id, analyzedData.url, analyzedData.content),
1067
+ className: "flex items-center gap-2 px-4 py-2 border border-gray-200 rounded-lg text-sm font-medium text-gray-700 hover:bg-gray-50 hover:border-gray-300 transition-colors",
1068
+ children: [
1069
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(IconComponent, { size: 16 }),
1070
+ option.name
1071
+ ]
1072
+ },
1073
+ option.id
1074
+ );
1075
+ }) }),
1076
+ 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_runtime6.jsxs)("div", { className: "flex gap-3 pt-4 mt-2", children: [
1077
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1078
+ "button",
1079
+ {
1080
+ onClick: () => handleEdit(parsed.blocks, parsed.metaKeywords, msg.id),
1081
+ className: "flex items-center gap-2 px-4 py-2 border border-gray-300 rounded-lg text-sm font-medium text-gray-700 hover:bg-gray-50 transition-colors",
1082
+ children: [
1083
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react4.Edit3, { size: 16 }),
1084
+ "Edit"
1085
+ ]
1086
+ }
1087
+ ),
1088
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1089
+ "button",
1090
+ {
1091
+ onClick: () => handlePost(msg.content, parsed.metaKeywords),
1092
+ className: "flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium text-white hover:bg-emerald-700 transition-colors",
1093
+ style: { backgroundColor: primaryColor, color: "#fff" },
1094
+ children: [
1095
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react4.Send, { size: 16 }),
1096
+ "Post"
1097
+ ]
1098
+ }
1099
+ )
1100
+ ] })
1101
+ ] })
1102
+ ] }, msg.id);
1103
+ }),
1104
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { ref: bottomRef })
1105
+ ] }) }),
1106
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "shrink-0 bg-white px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "max-w-3xl mx-auto relative flex gap-2 items-end", children: [
1107
+ !showNewsPanel && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { ref: dropdownRef, className: "absolute left-3 top-1/2 -translate-y-1/2 z-10", children: [
1108
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1109
+ "button",
1110
+ {
1111
+ onClick: handleOpenNewsDropdown,
1112
+ className: "flex h-8 w-8 items-center justify-center rounded-full bg-gradient-to-r from-purple-500 to-pink-500 text-white hover:from-purple-600 hover:to-pink-600 transition-all animate-pulse hover:animate-none",
1113
+ title: "Select from trending news",
1114
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react4.Zap, { size: 16 })
1115
+ }
1116
+ ),
1117
+ showNewsDropdown && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "absolute bottom-full left-0 mb-2 w-[600px] bg-white border border-gray-200 rounded-lg shadow-xl max-h-[600px] overflow-hidden", children: [
1118
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center justify-between px-3 py-2 border-b border-gray-100", style: { backgroundColor: primaryColor, color: "#fff" }, children: [
1119
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-sm font-medium text-white", children: "Select News" }),
1120
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1121
+ "button",
1122
+ {
1123
+ onClick: () => setShowNewsDropdown(false),
1124
+ className: "p-1 hover:bg-gray-200 rounded transition",
1125
+ style: { backgroundColor: primaryColor, color: "#fff" },
1126
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react4.X, { size: 14, className: "text-white" })
1127
+ }
1128
+ )
1129
+ ] }),
1130
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "px-3 py-2 border-b border-gray-100 flex items-center gap-2", children: [
1131
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex-1 min-w-[180px]", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1132
+ import_react_select.default,
1133
+ {
1134
+ options: [
1135
+ { value: null, label: "All Sources (Trending)" },
1136
+ ...sources.filter((source) => !preferredLanguage || source.language === preferredLanguage).map((source) => ({
1137
+ value: source.id,
1138
+ label: source.name
1139
+ }))
1140
+ ],
1141
+ value: selectedSource ? { value: selectedSource, label: ((_b = sources.find((s) => s.id === selectedSource)) == null ? void 0 : _b.name) || selectedSource } : { value: null, label: "All Sources (Trending)" },
1142
+ onChange: handleSourceSelect,
1143
+ isLoading: loadingSources,
1144
+ isDisabled: loadingSources,
1145
+ placeholder: "Select source...",
1146
+ classNamePrefix: "react-select",
1147
+ menuPlacement: "bottom",
1148
+ menuPosition: "fixed",
1149
+ menuShouldScrollIntoView: false,
1150
+ maxMenuHeight: 220,
1151
+ menuPortalTarget: typeof window !== "undefined" ? document.body : null,
1152
+ styles: {
1153
+ container: (base) => __spreadProps(__spreadValues({}, base), {
1154
+ width: "100%"
1155
+ }),
1156
+ control: (base) => __spreadProps(__spreadValues({}, base), {
1157
+ minHeight: "38px",
1158
+ borderColor: "#e5e7eb",
1159
+ boxShadow: "none",
1160
+ width: "100%"
1161
+ }),
1162
+ menu: (base) => __spreadProps(__spreadValues({}, base), {
1163
+ width: "100%",
1164
+ zIndex: 999999
1165
+ }),
1166
+ menuPortal: (base) => __spreadProps(__spreadValues({}, base), {
1167
+ zIndex: 999999
1168
+ }),
1169
+ option: (base, state) => __spreadProps(__spreadValues({}, base), {
1170
+ backgroundColor: state.isSelected ? primaryColor : state.isFocused ? "#f3f4f6" : "white",
1171
+ color: state.isSelected ? "white" : "#374151",
1172
+ cursor: "pointer"
1173
+ })
1174
+ }
1175
+ }
1176
+ ) }),
1177
+ selectedSource && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1178
+ "button",
1179
+ {
1180
+ onClick: handleScrape,
1181
+ disabled: scraping,
1182
+ className: "flex items-center gap-1.5 px-3 py-2 bg-emerald-600 text-white rounded-lg text-sm font-medium hover:bg-emerald-700 transition disabled:opacity-50",
1183
+ title: "Fetch latest news",
1184
+ children: [
1185
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react4.RefreshCcw, { size: 14, className: scraping ? "animate-spin" : "" }),
1186
+ scraping ? "Scraping..." : "Scrape"
1187
+ ]
1188
+ }
1189
+ ),
1190
+ !selectedSource && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1191
+ "button",
1192
+ {
1193
+ onClick: () => fetchNews(null),
1194
+ disabled: loadingNews,
1195
+ className: "flex items-center gap-1 px-3 py-2 text-sm text-gray-600 hover:text-gray-900 border border-gray-200 rounded-lg transition",
1196
+ children: [
1197
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react4.RefreshCcw, { size: 14, className: loadingNews ? "animate-spin" : "" }),
1198
+ "Refresh"
1199
+ ]
1200
+ }
1201
+ )
1202
+ ] }),
1203
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "overflow-y-auto max-h-[320px]", children: loadingNews ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "p-4 text-center text-sm text-gray-500", children: "Loading news..." }) : trendingNews.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "p-4 text-center text-sm text-gray-500", children: [
1204
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: "No news found." }),
1205
+ selectedSource && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mt-1 text-xs", children: 'Click "Scrape" to fetch latest news.' })
1206
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1207
+ NewsList,
1208
+ {
1209
+ news: trendingNews.slice(0, 10),
1210
+ onView: (item) => window.open(item.sourceUrl || item.link, "_blank"),
1211
+ onRecreate: (payload) => {
1212
+ setShowNewsDropdown(false);
1213
+ onSelectNews == null ? void 0 : onSelectNews(payload);
1214
+ }
1215
+ }
1216
+ ) })
1217
+ ] })
1218
+ ] }),
1219
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1220
+ "textarea",
1221
+ {
1222
+ ref: textareaRef,
1223
+ value: input,
1224
+ onChange: (e) => setInput(e.target.value),
1225
+ onKeyDown: (e) => {
1226
+ if (e.key === "Enter" && !e.shiftKey) {
1227
+ e.preventDefault();
1228
+ handleSend();
1229
+ }
1230
+ },
1231
+ rows: 1,
1232
+ placeholder: "Ask AI something\u2026",
1233
+ className: `flex-1 resize-none overflow-hidden border border-gray-300 ${!showNewsPanel ? "!pl-14" : "!pl-5"} !pr-[50px] input_box_textarea`,
1234
+ style: { maxHeight: "200px", overflowY: input.split("\n").length > 6 ? "auto" : "hidden" }
1235
+ }
1236
+ ),
1237
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1238
+ "button",
1239
+ {
1240
+ onClick: handleSend,
1241
+ className: "flex h-10 w-10 absolute right-[10px] top-[5px] items-center justify-center rounded-full bg-emerald-600 text-white",
1242
+ style: { backgroundColor: primaryColor, color: "#fff" },
1243
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react4.SendHorizontal, { size: 18 })
1244
+ }
1245
+ )
1246
+ ] }) }),
1247
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1248
+ EditModal,
1249
+ {
1250
+ isOpen: editModal.isOpen,
1251
+ initialContent: editModal.content,
1252
+ metaKeywords: editModal.metaKeywords,
1253
+ onClose: handleCloseModal,
1254
+ onSaveDraft: handleSaveDraft,
1255
+ onPost: handlePost
1256
+ }
1257
+ )
1258
+ ] });
1259
+ }
1260
+
1261
+ // components/chatbot/UserMenu.tsx
1262
+ var import_react7 = require("react");
1263
+ var import_lucide_react5 = require("lucide-react");
1264
+ var import_react_hot_toast = __toESM(require("react-hot-toast"));
1265
+
1266
+ // services/auth.service.ts
1267
+ var logoutUser = async () => {
1268
+ const { data } = await api_default.post("/auth/logout");
1269
+ return data;
1270
+ };
1271
+
1272
+ // components/chatbot/UserMenu.tsx
1273
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1274
+ function UserMenu({
1275
+ onNavigate,
1276
+ onLogout
1277
+ }) {
1278
+ const [open, setOpen] = (0, import_react7.useState)(false);
1279
+ const ref = (0, import_react7.useRef)(null);
1280
+ (0, import_react7.useEffect)(() => {
1281
+ const handler = (e) => {
1282
+ if (ref.current && !ref.current.contains(e.target)) {
1283
+ setOpen(false);
1284
+ }
1285
+ };
1286
+ document.addEventListener("mousedown", handler);
1287
+ return () => document.removeEventListener("mousedown", handler);
1288
+ }, []);
1289
+ function go(path) {
1290
+ setOpen(false);
1291
+ onNavigate == null ? void 0 : onNavigate(path);
1292
+ }
1293
+ async function logout() {
1294
+ setOpen(false);
1295
+ if (onLogout) {
1296
+ onLogout();
1297
+ return;
1298
+ }
1299
+ try {
1300
+ await logoutUser();
1301
+ import_react_hot_toast.default.success("Logged out successfully");
1302
+ onNavigate == null ? void 0 : onNavigate("/login");
1303
+ } catch (err) {
1304
+ import_react_hot_toast.default.error(err || "Logout failed");
1305
+ }
1306
+ }
1307
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { ref, className: "relative", children: [
1308
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1309
+ "button",
1310
+ {
1311
+ onClick: () => setOpen((v) => !v),
1312
+ className: "p-2 rounded-full hover:bg-gray-100 transition",
1313
+ "aria-label": "Settings",
1314
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react5.Settings, { size: 20, className: "text-gray-600" })
1315
+ }
1316
+ ),
1317
+ open && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "absolute right-0 top-full mb-2 w-52 bg-white border border-gray-200 rounded-lg shadow-lg z-50", children: [
1318
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MenuItem, { icon: import_lucide_react5.User, label: "Account", onClick: () => go("/dashboard/account") }),
1319
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MenuItem, { icon: import_lucide_react5.Settings, label: "Preferences", onClick: () => go("/dashboard/preferences") }),
1320
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MenuItem, { icon: import_lucide_react5.Palette, label: "Appearance", onClick: () => go("/dashboard/appearance") }),
1321
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MenuItem, { icon: import_lucide_react5.HelpCircle, label: "Settings", onClick: () => go("/dashboard/settings") }),
1322
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MenuItem, { icon: import_lucide_react5.HelpCircle, label: "Help & Support", onClick: () => go("/dashboard/help") }),
1323
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "border-t border-gray-300 my-1" }),
1324
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1325
+ MenuItem,
1326
+ {
1327
+ icon: import_lucide_react5.LogOut,
1328
+ label: "Logout",
1329
+ danger: true,
1330
+ onClick: logout
1331
+ }
1332
+ )
1333
+ ] })
1334
+ ] });
1335
+ }
1336
+ function MenuItem({
1337
+ icon: Icon,
1338
+ label,
1339
+ onClick,
1340
+ danger = false
1341
+ }) {
1342
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1343
+ "button",
1344
+ {
1345
+ onClick,
1346
+ className: `w-full flex items-center gap-2 px-4 py-2 text-sm transition ${danger ? "text-red-600 hover:bg-red-50" : "text-gray-700 hover:bg-gray-100"}`,
1347
+ children: [
1348
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Icon, { size: 16 }),
1349
+ label
1350
+ ]
1351
+ }
1352
+ );
1353
+ }
1354
+
1355
+ // components/chatbot/headerBar.tsx
1356
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1357
+ function HeaderBar({
1358
+ onNavigate,
1359
+ onLogout
1360
+ }) {
1361
+ const { primaryColor, botName, logoUrl } = useTheme();
1362
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("header", { className: "sticky top-0 z-40 bg-white border-b border-gray-200", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mx-auto flex h-14 items-center justify-between px-4", children: [
1363
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-3 cursor-pointer", onClick: () => onNavigate == null ? void 0 : onNavigate("/"), children: [
1364
+ logoUrl ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("img", { src: logoUrl, alt: "Logo", className: "h-8 w-8 rounded-md object-cover" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1365
+ "div",
1366
+ {
1367
+ className: "h-8 w-8 rounded-md text-white flex items-center justify-center font-bold text-sm",
1368
+ style: { backgroundColor: primaryColor },
1369
+ children: botName.charAt(0).toUpperCase()
1370
+ }
1371
+ ),
1372
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "font-semibold text-gray-900 text-base", children: botName.toUpperCase() })
1373
+ ] }),
1374
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex items-center gap-3", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(UserMenu, { onNavigate, onLogout }) })
1375
+ ] }) });
1376
+ }
1377
+
1378
+ // components/news/TrendingNews.tsx
1379
+ var import_react8 = require("react");
1380
+ var import_react_hot_toast2 = __toESM(require("react-hot-toast"));
1381
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1382
+ function TrendingNews({
1383
+ onRecreate,
1384
+ selectedSource,
1385
+ refreshKey,
1386
+ news: externalNews,
1387
+ isLoading: externalLoading
1388
+ }) {
1389
+ const [news, setNews] = (0, import_react8.useState)([]);
1390
+ const [loading, setLoading] = (0, import_react8.useState)(false);
1391
+ const displayNews = externalNews !== void 0 ? externalNews : news;
1392
+ const isLoading = externalLoading !== void 0 ? externalLoading : loading;
1393
+ const fetchNews = (0, import_react8.useCallback)(async () => {
1394
+ if (externalNews !== void 0) {
1395
+ return;
1396
+ }
1397
+ setLoading(true);
1398
+ try {
1399
+ let data;
1400
+ if (selectedSource) {
1401
+ data = await getNewsBySource(selectedSource);
1402
+ } else {
1403
+ data = await getTrendingNews();
1404
+ }
1405
+ setNews(data || []);
1406
+ } catch (e) {
1407
+ import_react_hot_toast2.default.error("Failed to load news");
1408
+ } finally {
1409
+ setLoading(false);
1410
+ }
1411
+ }, [selectedSource, externalNews]);
1412
+ (0, import_react8.useEffect)(() => {
1413
+ fetchNews();
1414
+ }, [fetchNews, refreshKey]);
1415
+ function handleView(item) {
1416
+ window.open(item.sourceUrl || item.link, "_blank");
1417
+ }
1418
+ if (isLoading) {
1419
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "p-4 text-sm text-gray-500", children: "Loading news..." });
1420
+ }
1421
+ if (!displayNews || displayNews.length === 0) {
1422
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "p-4 text-sm text-gray-500 text-center", children: [
1423
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { children: "No news found for this source." }),
1424
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "mt-1 text-xs", children: 'Click "Scrape" to fetch latest news.' })
1425
+ ] });
1426
+ }
1427
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1428
+ NewsList,
1429
+ {
1430
+ news: displayNews,
1431
+ onView: handleView,
1432
+ onRecreate
1433
+ }
1434
+ );
1435
+ }
1436
+
1437
+ // components/news/SourceSelector.tsx
1438
+ var import_react9 = require("react");
1439
+ var import_lucide_react6 = require("lucide-react");
1440
+ var import_react_select2 = __toESM(require("react-select"));
1441
+ var import_react_hot_toast3 = __toESM(require("react-hot-toast"));
1442
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1443
+ var ALL_SOURCES_VALUE = "__all__";
1444
+ function SourceSelector({
1445
+ selectedSource,
1446
+ onSourceChange,
1447
+ onScrapeComplete,
1448
+ onNewsLoaded,
1449
+ onLoadingChange
1450
+ }) {
1451
+ const [sources, setSources] = (0, import_react9.useState)([]);
1452
+ const [loading, setLoading] = (0, import_react9.useState)(true);
1453
+ const [scraping, setScraping] = (0, import_react9.useState)(false);
1454
+ const [fetchingNews, setFetchingNews] = (0, import_react9.useState)(true);
1455
+ const { primaryColor } = useTheme();
1456
+ (0, import_react9.useEffect)(() => {
1457
+ onLoadingChange == null ? void 0 : onLoadingChange(fetchingNews);
1458
+ }, [fetchingNews, onLoadingChange]);
1459
+ (0, import_react9.useEffect)(() => {
1460
+ const fetchSources = async () => {
1461
+ try {
1462
+ const data = await getNewsSources();
1463
+ setSources(data || []);
1464
+ } catch (e) {
1465
+ import_react_hot_toast3.default.error("Failed to load news sources");
1466
+ } finally {
1467
+ setLoading(false);
1468
+ }
1469
+ };
1470
+ fetchSources();
1471
+ }, []);
1472
+ const fetchNews = (0, import_react9.useCallback)(async (sourceId) => {
1473
+ setFetchingNews(true);
1474
+ try {
1475
+ let news;
1476
+ if (sourceId) {
1477
+ news = await getNewsBySource(sourceId);
1478
+ } else {
1479
+ news = await getTrendingNews();
1480
+ }
1481
+ onNewsLoaded == null ? void 0 : onNewsLoaded(news || []);
1482
+ } catch (err) {
1483
+ console.error("Failed to fetch news:", err);
1484
+ } finally {
1485
+ setFetchingNews(false);
1486
+ }
1487
+ }, []);
1488
+ (0, import_react9.useEffect)(() => {
1489
+ fetchNews(selectedSource);
1490
+ }, [selectedSource, fetchNews]);
1491
+ const handleSourceSelect = (option) => {
1492
+ var _a;
1493
+ const value = (option == null ? void 0 : option.value) === ALL_SOURCES_VALUE ? null : (_a = option == null ? void 0 : option.value) != null ? _a : null;
1494
+ onSourceChange(value);
1495
+ };
1496
+ const handleScrape = async () => {
1497
+ if (!selectedSource) {
1498
+ import_react_hot_toast3.default.error("Please select a source first");
1499
+ return;
1500
+ }
1501
+ setScraping(true);
1502
+ try {
1503
+ await scrapeNewsSource(selectedSource);
1504
+ import_react_hot_toast3.default.success("News scraped successfully!");
1505
+ onScrapeComplete == null ? void 0 : onScrapeComplete();
1506
+ const news = await getNewsBySource(selectedSource);
1507
+ onNewsLoaded == null ? void 0 : onNewsLoaded(news || []);
1508
+ } catch (err) {
1509
+ import_react_hot_toast3.default.error(err || "Failed to scrape news");
1510
+ } finally {
1511
+ setScraping(false);
1512
+ }
1513
+ };
1514
+ const options = [
1515
+ { value: ALL_SOURCES_VALUE, label: "All Sources (Trending)" },
1516
+ ...sources.map((source) => ({
1517
+ value: source.id,
1518
+ label: source.name
1519
+ }))
1520
+ ];
1521
+ const selectedOption = options.find(
1522
+ (opt) => selectedSource === null ? opt.value === ALL_SOURCES_VALUE : opt.value === selectedSource
1523
+ ) || options[0];
1524
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "px-3 py-2 border-b border-gray-100 flex items-center gap-2", children: [
1525
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex-1 min-w-[180px]", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1526
+ import_react_select2.default,
1527
+ {
1528
+ options,
1529
+ value: selectedOption,
1530
+ onChange: handleSourceSelect,
1531
+ isLoading: loading,
1532
+ isDisabled: loading,
1533
+ placeholder: "Select source...",
1534
+ classNamePrefix: "react-select",
1535
+ menuPlacement: "bottom",
1536
+ menuPosition: "fixed",
1537
+ menuShouldScrollIntoView: false,
1538
+ maxMenuHeight: 220,
1539
+ menuPortalTarget: typeof window !== "undefined" ? document.body : null,
1540
+ styles: {
1541
+ container: (base) => __spreadProps(__spreadValues({}, base), {
1542
+ width: "100%"
1543
+ }),
1544
+ control: (base) => __spreadProps(__spreadValues({}, base), {
1545
+ minHeight: "38px",
1546
+ borderColor: "#e5e7eb",
1547
+ boxShadow: "none",
1548
+ width: "100%"
1549
+ }),
1550
+ menu: (base) => __spreadProps(__spreadValues({}, base), {
1551
+ width: "100%",
1552
+ zIndex: 999999
1553
+ }),
1554
+ menuPortal: (base) => __spreadProps(__spreadValues({}, base), {
1555
+ zIndex: 999999
1556
+ }),
1557
+ option: (base, state) => __spreadProps(__spreadValues({}, base), {
1558
+ backgroundColor: state.isSelected ? primaryColor : state.isFocused ? "#f3f4f6" : "white",
1559
+ color: state.isSelected ? "white" : "#374151",
1560
+ cursor: "pointer"
1561
+ })
1562
+ }
1563
+ }
1564
+ ) }),
1565
+ selectedSource && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1566
+ "button",
1567
+ {
1568
+ onClick: handleScrape,
1569
+ disabled: scraping,
1570
+ className: "flex items-center gap-1.5 px-3 py-2 bg-emerald-600 text-white rounded-lg text-sm font-medium hover:bg-emerald-700 transition disabled:opacity-50",
1571
+ title: "Fetch latest news",
1572
+ children: [
1573
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react6.RefreshCcw, { size: 14, className: scraping ? "animate-spin" : "" }),
1574
+ scraping ? "Scraping..." : "Scrape"
1575
+ ]
1576
+ }
1577
+ ),
1578
+ !selectedSource && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1579
+ "button",
1580
+ {
1581
+ onClick: () => fetchNews(null),
1582
+ disabled: fetchingNews,
1583
+ className: "flex items-center gap-1 px-3 py-2 text-sm text-gray-600 hover:text-gray-900 border border-gray-200 rounded-lg transition",
1584
+ children: [
1585
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react6.RefreshCcw, { size: 14, className: fetchingNews ? "animate-spin" : "" }),
1586
+ "Refresh"
1587
+ ]
1588
+ }
1589
+ )
1590
+ ] });
1591
+ }
1592
+
1593
+ // services/chat.service.ts
1594
+ var analyzeInputApi = async (input) => {
1595
+ const { data } = await api_default.post("/chat/analyze-input", { input });
1596
+ return data.data;
1597
+ };
1598
+ var createContentApi = async (payload) => {
1599
+ const { data } = await api_default.post("/chat/create-content", payload);
1600
+ return data.data;
1601
+ };
1602
+ var createContentStreamApi = async ({
1603
+ url,
1604
+ content,
1605
+ actionId,
1606
+ settings,
1607
+ onChunk,
1608
+ onComplete,
1609
+ onError
1610
+ }) => {
1611
+ var _a;
1612
+ const API_BASE_URL = getApiBaseUrl();
1613
+ const apiKey = process.env.NEXT_PUBLIC_API_KEY;
1614
+ const headers = {
1615
+ "Content-Type": "application/json"
1616
+ };
1617
+ if (apiKey) {
1618
+ headers["x-api-key"] = apiKey;
1619
+ }
1620
+ const response = await fetch(`${API_BASE_URL}/chat/create-content/stream`, {
1621
+ method: "POST",
1622
+ headers,
1623
+ credentials: "include",
1624
+ body: JSON.stringify({ url, content, actionId, settings })
1625
+ });
1626
+ if (!response.ok) {
1627
+ const errorText = await response.text();
1628
+ throw new Error(`HTTP error! status: ${response.status} - ${errorText}`);
1629
+ }
1630
+ const reader = (_a = response.body) == null ? void 0 : _a.getReader();
1631
+ const decoder = new TextDecoder();
1632
+ if (!reader) {
1633
+ throw new Error("No reader available");
1634
+ }
1635
+ let buffer = "";
1636
+ let hasReceivedData = false;
1637
+ while (true) {
1638
+ const { done, value } = await reader.read();
1639
+ if (done) {
1640
+ if (hasReceivedData) onComplete == null ? void 0 : onComplete();
1641
+ break;
1642
+ }
1643
+ const chunk = decoder.decode(value, { stream: true });
1644
+ buffer += chunk;
1645
+ const lines = buffer.split("\n");
1646
+ buffer = lines.pop() || "";
1647
+ for (const line of lines) {
1648
+ const trimmedLine = line.trim();
1649
+ if (trimmedLine === "") continue;
1650
+ hasReceivedData = true;
1651
+ if (trimmedLine.startsWith("data: ")) {
1652
+ const data = trimmedLine.slice(6).trim();
1653
+ if (data === "[DONE]") {
1654
+ onComplete == null ? void 0 : onComplete();
1655
+ return;
1656
+ }
1657
+ try {
1658
+ const parsed = JSON.parse(data);
1659
+ if (parsed.content) onChunk(parsed.content);
1660
+ else if (parsed.delta) onChunk(parsed.delta);
1661
+ else if (parsed.text) onChunk(parsed.text);
1662
+ else if (parsed.chunk) onChunk(parsed.chunk);
1663
+ else if (typeof parsed === "string") onChunk(parsed);
1664
+ } catch (e) {
1665
+ if (data) onChunk(data);
1666
+ }
1667
+ } else if (trimmedLine) {
1668
+ onChunk(trimmedLine + "\n");
1669
+ }
1670
+ }
1671
+ }
1672
+ if (!hasReceivedData) {
1673
+ throw new Error("No data received from stream");
1674
+ }
1675
+ };
1676
+ var rewriteNewsStreamApi = async ({
1677
+ article,
1678
+ language = "English",
1679
+ articleId,
1680
+ tone,
1681
+ style,
1682
+ wordCount,
1683
+ includeQuotes,
1684
+ includeFAQ,
1685
+ targetAudience,
1686
+ onChunk,
1687
+ onComplete,
1688
+ onError
1689
+ }) => {
1690
+ var _a;
1691
+ const payload = {
1692
+ article,
1693
+ language
1694
+ };
1695
+ if (articleId) {
1696
+ payload.articleId = articleId;
1697
+ }
1698
+ if (tone) payload.tone = tone;
1699
+ if (style) payload.style = style;
1700
+ if (wordCount) payload.wordCount = wordCount;
1701
+ if (includeQuotes !== void 0) payload.includeQuotes = includeQuotes;
1702
+ if (includeFAQ !== void 0) payload.includeFAQ = includeFAQ;
1703
+ if (targetAudience) payload.targetAudience = targetAudience;
1704
+ try {
1705
+ const API_BASE_URL = getApiBaseUrl();
1706
+ const apiKey = process.env.NEXT_PUBLIC_API_KEY;
1707
+ console.log("\u{1F680} Starting stream request to:", `${API_BASE_URL}/chat/rewrite/stream`);
1708
+ console.log("\u{1F4E6} Payload:", payload);
1709
+ const headers = {
1710
+ "Content-Type": "application/json"
1711
+ };
1712
+ if (apiKey) {
1713
+ headers["x-api-key"] = apiKey;
1714
+ }
1715
+ const response = await fetch(`${API_BASE_URL}/chat/rewrite/stream`, {
1716
+ method: "POST",
1717
+ headers,
1718
+ credentials: "include",
1719
+ // Include cookies for authentication
1720
+ body: JSON.stringify(payload)
1721
+ });
1722
+ console.log("\u{1F4E1} Response status:", response.status);
1723
+ console.log("\u{1F4E1} Response headers:", Object.fromEntries(response.headers.entries()));
1724
+ if (!response.ok) {
1725
+ const errorText = await response.text();
1726
+ console.error("\u274C API Error:", response.status, errorText);
1727
+ throw new Error(`HTTP error! status: ${response.status} - ${errorText}`);
1728
+ }
1729
+ const reader = (_a = response.body) == null ? void 0 : _a.getReader();
1730
+ const decoder = new TextDecoder();
1731
+ if (!reader) {
1732
+ throw new Error("No reader available");
1733
+ }
1734
+ let buffer = "";
1735
+ let hasReceivedData = false;
1736
+ while (true) {
1737
+ const { done, value } = await reader.read();
1738
+ if (done) {
1739
+ if (hasReceivedData) {
1740
+ onComplete == null ? void 0 : onComplete();
1741
+ }
1742
+ break;
1743
+ }
1744
+ const chunk = decoder.decode(value, { stream: true });
1745
+ buffer += chunk;
1746
+ const lines = buffer.split("\n");
1747
+ buffer = lines.pop() || "";
1748
+ for (const line of lines) {
1749
+ const trimmedLine = line.trim();
1750
+ if (trimmedLine === "") continue;
1751
+ hasReceivedData = true;
1752
+ console.log("\u{1F4E8} Received line:", trimmedLine.substring(0, 100) + (trimmedLine.length > 100 ? "..." : ""));
1753
+ if (trimmedLine.startsWith("data: ")) {
1754
+ const data = trimmedLine.slice(6).trim();
1755
+ if (data === "[DONE]") {
1756
+ console.log("\u2705 Stream completed with [DONE] signal");
1757
+ onComplete == null ? void 0 : onComplete();
1758
+ return;
1759
+ }
1760
+ try {
1761
+ const parsed = JSON.parse(data);
1762
+ console.log("\u{1F4CB} Parsed JSON:", parsed);
1763
+ if (parsed.content) {
1764
+ onChunk(parsed.content);
1765
+ } else if (parsed.delta) {
1766
+ onChunk(parsed.delta);
1767
+ } else if (parsed.text) {
1768
+ onChunk(parsed.text);
1769
+ } else if (parsed.chunk) {
1770
+ onChunk(parsed.chunk);
1771
+ } else if (typeof parsed === "string") {
1772
+ onChunk(parsed);
1773
+ } else {
1774
+ console.warn("\u26A0\uFE0F Unknown JSON format:", parsed);
1775
+ }
1776
+ } catch (e) {
1777
+ console.log("\u{1F4DD} Non-JSON data, treating as plain text");
1778
+ if (data) {
1779
+ onChunk(data);
1780
+ }
1781
+ }
1782
+ } else {
1783
+ console.log("\u{1F4C4} Plain text chunk");
1784
+ if (trimmedLine) {
1785
+ onChunk(trimmedLine + "\n");
1786
+ }
1787
+ }
1788
+ }
1789
+ }
1790
+ if (!hasReceivedData) {
1791
+ console.error("\u274C No data received from stream");
1792
+ throw new Error("No data received from stream");
1793
+ }
1794
+ console.log("\u2705 Stream completed successfully");
1795
+ } catch (error) {
1796
+ console.error("\u274C Streaming error:", error);
1797
+ console.error("Error details:", {
1798
+ message: error.message,
1799
+ name: error.name,
1800
+ stack: error.stack
1801
+ });
1802
+ onError == null ? void 0 : onError(error);
1803
+ throw error;
1804
+ }
1805
+ };
1806
+ var rewriteNewsApi = async ({
1807
+ article,
1808
+ language = "English",
1809
+ articleId,
1810
+ tone,
1811
+ style,
1812
+ wordCount,
1813
+ includeQuotes,
1814
+ includeFAQ,
1815
+ targetAudience
1816
+ }) => {
1817
+ const payload = {
1818
+ article,
1819
+ language
1820
+ };
1821
+ if (articleId) {
1822
+ payload.articleId = articleId;
1823
+ }
1824
+ if (tone) payload.tone = tone;
1825
+ if (style) payload.style = style;
1826
+ if (wordCount) payload.wordCount = wordCount;
1827
+ if (includeQuotes !== void 0) payload.includeQuotes = includeQuotes;
1828
+ if (includeFAQ !== void 0) payload.includeFAQ = includeFAQ;
1829
+ if (targetAudience) payload.targetAudience = targetAudience;
1830
+ const { data } = await api_default.post("/chat/rewrite", payload);
1831
+ return data.data;
1832
+ };
1833
+
1834
+ // components/chatbot/ChatBot.tsx
1835
+ var import_react_hot_toast4 = __toESM(require("react-hot-toast"));
1836
+ var import_lucide_react7 = require("lucide-react");
1837
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1838
+ function ChatBot({
1839
+ onNavigate,
1840
+ onLogout
1841
+ } = {}) {
1842
+ const { loading, showNewsPanel } = useTheme();
1843
+ const { preferences } = usePreferences();
1844
+ const [selectedSource, setSelectedSource] = (0, import_react10.useState)(null);
1845
+ const [news, setNews] = (0, import_react10.useState)([]);
1846
+ const [newsLoading, setNewsLoading] = (0, import_react10.useState)(true);
1847
+ const [selectedArticle, setSelectedArticle] = (0, import_react10.useState)(null);
1848
+ const [messages, setMessages] = (0, import_react10.useState)([]);
1849
+ const [showChatMobile, setShowChatMobile] = (0, import_react10.useState)(false);
1850
+ const [isStreaming, setIsStreaming] = (0, import_react10.useState)(false);
1851
+ const [analyzedData, setAnalyzedData] = (0, import_react10.useState)(null);
1852
+ const handleNewsLoaded = (0, import_react10.useCallback)((loadedNews) => {
1853
+ setNews(loadedNews);
1854
+ }, []);
1855
+ const handleLoadingChange = (0, import_react10.useCallback)((isLoading) => {
1856
+ setNewsLoading(isLoading);
1857
+ }, []);
1858
+ const handleRecreate = async ({ title, content, id }) => {
1859
+ var _a;
1860
+ setShowChatMobile(true);
1861
+ const assistantId = crypto.randomUUID();
1862
+ setMessages((prev) => [
1863
+ ...prev,
1864
+ {
1865
+ id: crypto.randomUUID(),
1866
+ role: "user",
1867
+ content: `Recreate this news:
1868
+ ${title}`
1869
+ },
1870
+ {
1871
+ id: assistantId,
1872
+ role: "assistant",
1873
+ content: "\u23F3 Creating article..."
1874
+ }
1875
+ ]);
1876
+ try {
1877
+ let accumulatedContent = "";
1878
+ let hasStartedStreaming = false;
1879
+ const contentPrefs = preferences == null ? void 0 : preferences.content;
1880
+ const lang = ((_a = preferences == null ? void 0 : preferences.localization) == null ? void 0 : _a.language) === "hi" ? "Hindi" : "English";
1881
+ try {
1882
+ await rewriteNewsStreamApi({
1883
+ article: content,
1884
+ language: lang,
1885
+ articleId: id,
1886
+ tone: contentPrefs == null ? void 0 : contentPrefs.tone,
1887
+ style: contentPrefs == null ? void 0 : contentPrefs.style,
1888
+ wordCount: contentPrefs == null ? void 0 : contentPrefs.wordCount,
1889
+ includeQuotes: contentPrefs == null ? void 0 : contentPrefs.includeQuotes,
1890
+ includeFAQ: contentPrefs == null ? void 0 : contentPrefs.includeFAQ,
1891
+ targetAudience: contentPrefs == null ? void 0 : contentPrefs.targetAudience,
1892
+ onChunk: (chunk) => {
1893
+ hasStartedStreaming = true;
1894
+ accumulatedContent += chunk;
1895
+ setMessages(
1896
+ (prev) => prev.map(
1897
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: accumulatedContent }) : m
1898
+ )
1899
+ );
1900
+ },
1901
+ onComplete: () => {
1902
+ console.log("Streaming completed successfully");
1903
+ import_react_hot_toast4.default.success("Article created successfully!");
1904
+ },
1905
+ onError: async (error) => {
1906
+ console.error("Streaming error:", error);
1907
+ if (!hasStartedStreaming) {
1908
+ console.log("Falling back to regular API...");
1909
+ throw error;
1910
+ } else {
1911
+ import_react_hot_toast4.default.error("Failed to complete article generation");
1912
+ setMessages(
1913
+ (prev) => prev.map(
1914
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: accumulatedContent || "\u274C Failed to create article. Please try again." }) : m
1915
+ )
1916
+ );
1917
+ }
1918
+ }
1919
+ });
1920
+ } catch (streamError) {
1921
+ console.log("Streaming failed, using regular API...", streamError);
1922
+ setMessages(
1923
+ (prev) => prev.map(
1924
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "\u23F3 Generating with regular API..." }) : m
1925
+ )
1926
+ );
1927
+ const result = await rewriteNewsApi({
1928
+ article: content,
1929
+ language: lang,
1930
+ articleId: id,
1931
+ tone: contentPrefs == null ? void 0 : contentPrefs.tone,
1932
+ style: contentPrefs == null ? void 0 : contentPrefs.style,
1933
+ wordCount: contentPrefs == null ? void 0 : contentPrefs.wordCount,
1934
+ includeQuotes: contentPrefs == null ? void 0 : contentPrefs.includeQuotes,
1935
+ includeFAQ: contentPrefs == null ? void 0 : contentPrefs.includeFAQ,
1936
+ targetAudience: contentPrefs == null ? void 0 : contentPrefs.targetAudience
1937
+ });
1938
+ setMessages(
1939
+ (prev) => prev.map(
1940
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: result.article || result }) : m
1941
+ )
1942
+ );
1943
+ import_react_hot_toast4.default.success("Article created successfully!");
1944
+ }
1945
+ } catch (err) {
1946
+ console.error("Complete Error:", err);
1947
+ import_react_hot_toast4.default.error((err == null ? void 0 : err.message) || "An error occurred while creating the article");
1948
+ setMessages(
1949
+ (prev) => prev.map(
1950
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "\u274C Failed to create article. Please try again." }) : m
1951
+ )
1952
+ );
1953
+ }
1954
+ };
1955
+ const handleSendMessage = async (text) => {
1956
+ var _a, _b;
1957
+ const userMsgId = crypto.randomUUID();
1958
+ const assistantId = crypto.randomUUID();
1959
+ setMessages((prev) => [
1960
+ ...prev,
1961
+ { id: userMsgId, role: "user", content: text }
1962
+ ]);
1963
+ const urlRegex = /https?:\/\/[^\s]+/;
1964
+ const hasUrl = urlRegex.test(text);
1965
+ if (hasUrl) {
1966
+ setMessages((prev) => [
1967
+ ...prev,
1968
+ { id: assistantId, role: "assistant", content: "\u{1F50D} Analyzing your link..." }
1969
+ ]);
1970
+ try {
1971
+ const result = await analyzeInputApi(text);
1972
+ if (result.hasUrl && ((_a = result.options) == null ? void 0 : _a.length) > 0) {
1973
+ setAnalyzedData({
1974
+ url: result.url || "",
1975
+ content: result.content || "",
1976
+ options: result.options,
1977
+ messageId: assistantId
1978
+ });
1979
+ const optionsList = result.options.map((opt) => `\u2022 **${opt.name}**${opt.description ? ` \u2013 ${opt.description}` : ""}`).join("\n");
1980
+ setMessages(
1981
+ (prev) => prev.map(
1982
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), {
1983
+ content: `I found an article (${result.wordCount || 0} words). What would you like to do?
1984
+
1985
+ ${optionsList}`
1986
+ }) : m
1987
+ )
1988
+ );
1989
+ } else {
1990
+ setMessages(
1991
+ (prev) => prev.map(
1992
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "Could not extract content from that URL. Please try a different link." }) : m
1993
+ )
1994
+ );
1995
+ }
1996
+ } catch (err) {
1997
+ console.error("Analyze input error:", err);
1998
+ import_react_hot_toast4.default.error("Failed to analyze the link");
1999
+ setMessages(
2000
+ (prev) => prev.map(
2001
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "\u274C Failed to analyze the link. Please try again." }) : m
2002
+ )
2003
+ );
2004
+ }
2005
+ } else {
2006
+ setMessages((prev) => [
2007
+ ...prev,
2008
+ { id: assistantId, role: "assistant", content: "\u23F3 Generating content..." }
2009
+ ]);
2010
+ setIsStreaming(true);
2011
+ try {
2012
+ const contentPrefs = preferences == null ? void 0 : preferences.content;
2013
+ const lang = ((_b = preferences == null ? void 0 : preferences.localization) == null ? void 0 : _b.language) === "hi" ? "Hindi" : "English";
2014
+ let accumulatedContent = "";
2015
+ await rewriteNewsStreamApi({
2016
+ article: text,
2017
+ language: lang,
2018
+ tone: contentPrefs == null ? void 0 : contentPrefs.tone,
2019
+ style: contentPrefs == null ? void 0 : contentPrefs.style,
2020
+ wordCount: contentPrefs == null ? void 0 : contentPrefs.wordCount,
2021
+ includeQuotes: contentPrefs == null ? void 0 : contentPrefs.includeQuotes,
2022
+ includeFAQ: contentPrefs == null ? void 0 : contentPrefs.includeFAQ,
2023
+ targetAudience: contentPrefs == null ? void 0 : contentPrefs.targetAudience,
2024
+ onChunk: (chunk) => {
2025
+ accumulatedContent += chunk;
2026
+ setMessages(
2027
+ (prev) => prev.map(
2028
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: accumulatedContent }) : m
2029
+ )
2030
+ );
2031
+ },
2032
+ onComplete: () => {
2033
+ setIsStreaming(false);
2034
+ import_react_hot_toast4.default.success("Content generated!");
2035
+ },
2036
+ onError: (error) => {
2037
+ console.error("Stream error:", error);
2038
+ setIsStreaming(false);
2039
+ import_react_hot_toast4.default.error("Failed to generate content");
2040
+ }
2041
+ });
2042
+ } catch (err) {
2043
+ console.error("Send message error:", err);
2044
+ setIsStreaming(false);
2045
+ setMessages(
2046
+ (prev) => prev.map(
2047
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "\u274C Failed to generate content. Please try again." }) : m
2048
+ )
2049
+ );
2050
+ }
2051
+ }
2052
+ };
2053
+ const handleSelectAction = async (actionId, url, content) => {
2054
+ var _a;
2055
+ const assistantId = crypto.randomUUID();
2056
+ setAnalyzedData(null);
2057
+ const actionName = ((_a = analyzedData == null ? void 0 : analyzedData.options.find((o) => o.id === actionId)) == null ? void 0 : _a.name) || actionId;
2058
+ setMessages((prev) => [
2059
+ ...prev,
2060
+ { id: crypto.randomUUID(), role: "user", content: `Action: ${actionName}` },
2061
+ { id: assistantId, role: "assistant", content: "\u23F3 Creating content..." }
2062
+ ]);
2063
+ setIsStreaming(true);
2064
+ try {
2065
+ const contentPrefs = preferences == null ? void 0 : preferences.content;
2066
+ let accumulatedContent = "";
2067
+ try {
2068
+ await createContentStreamApi({
2069
+ url,
2070
+ content,
2071
+ actionId,
2072
+ settings: {
2073
+ tone: contentPrefs == null ? void 0 : contentPrefs.tone,
2074
+ style: contentPrefs == null ? void 0 : contentPrefs.style,
2075
+ wordCount: contentPrefs == null ? void 0 : contentPrefs.wordCount
2076
+ },
2077
+ onChunk: (chunk) => {
2078
+ accumulatedContent += chunk;
2079
+ setMessages(
2080
+ (prev) => prev.map(
2081
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: accumulatedContent }) : m
2082
+ )
2083
+ );
2084
+ },
2085
+ onComplete: () => {
2086
+ setIsStreaming(false);
2087
+ import_react_hot_toast4.default.success("Content created!");
2088
+ },
2089
+ onError: (error) => {
2090
+ console.error("Stream error:", error);
2091
+ setIsStreaming(false);
2092
+ import_react_hot_toast4.default.error("Failed to create content");
2093
+ }
2094
+ });
2095
+ } catch (e) {
2096
+ const result = await createContentApi({ url, content, actionId, settings: {
2097
+ tone: contentPrefs == null ? void 0 : contentPrefs.tone,
2098
+ style: contentPrefs == null ? void 0 : contentPrefs.style,
2099
+ wordCount: contentPrefs == null ? void 0 : contentPrefs.wordCount
2100
+ } });
2101
+ setMessages(
2102
+ (prev) => prev.map(
2103
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: result.article || result.content || JSON.stringify(result) }) : m
2104
+ )
2105
+ );
2106
+ setIsStreaming(false);
2107
+ import_react_hot_toast4.default.success("Content created!");
2108
+ }
2109
+ } catch (err) {
2110
+ console.error("Create content error:", err);
2111
+ setIsStreaming(false);
2112
+ setMessages(
2113
+ (prev) => prev.map(
2114
+ (m) => m.id === assistantId ? __spreadProps(__spreadValues({}, m), { content: "\u274C Failed to create content. Please try again." }) : m
2115
+ )
2116
+ );
2117
+ }
2118
+ };
2119
+ if (loading) {
2120
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "w-full h-screen flex items-center justify-center bg-white", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col items-center gap-3", children: [
2121
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react7.Loader2, { className: "h-8 w-8 animate-spin text-gray-400" }),
2122
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-sm text-gray-500", children: "Loading..." })
2123
+ ] }) });
2124
+ }
2125
+ return (
2126
+ // ✅ FULL SCREEN
2127
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "w-full h-screen flex flex-col bg-white", children: [
2128
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(HeaderBar, { onNavigate, onLogout }),
2129
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: `flex-1 min-h-0 md:grid ${showNewsPanel ? "md:grid-cols-12" : ""}`, children: [
2130
+ showNewsPanel && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("aside", { className: "relative col-span-4 bg-gray-50/60 border-r border-gray-300 h-[90vh] overflow-y-auto", children: [
2131
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2132
+ SourceSelector,
2133
+ {
2134
+ selectedSource,
2135
+ onSourceChange: (value) => {
2136
+ console.log("Selected source:", value);
2137
+ setSelectedSource(value);
2138
+ },
2139
+ onNewsLoaded: handleNewsLoaded,
2140
+ onLoadingChange: handleLoadingChange
2141
+ }
2142
+ ) }),
2143
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2144
+ TrendingNews,
2145
+ {
2146
+ onRecreate: handleRecreate,
2147
+ news,
2148
+ isLoading: newsLoading
2149
+ }
2150
+ )
2151
+ ] }),
2152
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
2153
+ "section",
2154
+ {
2155
+ className: `
2156
+ ${showNewsPanel ? "md:col-span-8" : "w-full"} bg-white flex flex-col h-full relative
2157
+ ${showChatMobile ? "block" : "hidden md:block"}
2158
+ `,
2159
+ children: [
2160
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "md:hidden border-b border-gray-300 p-3", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2161
+ "button",
2162
+ {
2163
+ onClick: () => setShowChatMobile(false),
2164
+ className: "text-sm text-gray-600",
2165
+ children: "\u2190 Back to news"
2166
+ }
2167
+ ) }),
2168
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "flex-1 min-h-0", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2169
+ ChatWindow,
2170
+ {
2171
+ messages,
2172
+ onSend: handleSendMessage,
2173
+ onSelectNews: handleRecreate,
2174
+ isStreaming,
2175
+ analyzedData,
2176
+ onSelectAction: handleSelectAction
2177
+ }
2178
+ ) }),
2179
+ !messages.length && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center text-center text-gray-400 text-sm px-4", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
2180
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "font-medium", children: "AI News Assistant" }),
2181
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "mt-1", children: "Select a news article or type a message to begin" })
2182
+ ] }) })
2183
+ ]
2184
+ }
2185
+ )
2186
+ ] }),
2187
+ selectedArticle && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2188
+ ArticleModal,
2189
+ {
2190
+ article: selectedArticle,
2191
+ onClose: () => setSelectedArticle(null),
2192
+ onRecreate: handleRecreate
2193
+ }
2194
+ )
2195
+ ] })
2196
+ );
2197
+ }
2198
+
2199
+ // src/index.tsx
2200
+ var import_jsx_runtime12 = require("react/jsx-runtime");
2201
+ function ContenifyChatBot({ onNavigate, onLogout }) {
2202
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PreferencesProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ChatBot, { onNavigate, onLogout }) });
2203
+ }
2204
+ var index_default = ContenifyChatBot;
2205
+ // Annotate the CommonJS export names for ESM import in node:
2206
+ 0 && (module.exports = {
2207
+ ContenifyChatBot
2208
+ });
2209
+ //# sourceMappingURL=index.js.map