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