@apteva/apteva-kit 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 +147 -0
- package/dist/index.d.mts +310 -0
- package/dist/index.d.ts +310 -0
- package/dist/index.js +1229 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1190 -0
- package/dist/index.mjs.map +1 -0
- package/dist/styles.css +2 -0
- package/package.json +65 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1190 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/components/Chat/Chat.tsx
|
|
4
|
+
import { useState as useState2, useEffect as useEffect3 } from "react";
|
|
5
|
+
|
|
6
|
+
// src/components/Chat/MessageList.tsx
|
|
7
|
+
import { useEffect as useEffect2, useRef } from "react";
|
|
8
|
+
|
|
9
|
+
// src/utils/cn.ts
|
|
10
|
+
import { clsx } from "clsx";
|
|
11
|
+
import { twMerge } from "tailwind-merge";
|
|
12
|
+
function cn(...inputs) {
|
|
13
|
+
return twMerge(clsx(inputs));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// src/utils/mock-data.ts
|
|
17
|
+
var mockMessages = [
|
|
18
|
+
{
|
|
19
|
+
id: "msg-1",
|
|
20
|
+
role: "assistant",
|
|
21
|
+
content: "Hello! I'm your AI assistant. How can I help you today?",
|
|
22
|
+
timestamp: new Date(Date.now() - 36e5)
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: "msg-2",
|
|
26
|
+
role: "user",
|
|
27
|
+
content: "I want to plan a trip to Europe",
|
|
28
|
+
timestamp: new Date(Date.now() - 35e5)
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: "msg-3",
|
|
32
|
+
role: "assistant",
|
|
33
|
+
content: "Great choice! Europe has amazing destinations. What's your budget and how many days do you have?",
|
|
34
|
+
timestamp: new Date(Date.now() - 34e5)
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: "msg-4",
|
|
38
|
+
role: "user",
|
|
39
|
+
content: "Around $2000 for 5 days",
|
|
40
|
+
timestamp: new Date(Date.now() - 33e5)
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: "msg-5",
|
|
44
|
+
role: "assistant",
|
|
45
|
+
content: "Perfect! I found some great destinations that fit your budget:",
|
|
46
|
+
widgets: [
|
|
47
|
+
{
|
|
48
|
+
type: "list",
|
|
49
|
+
id: "destinations-1",
|
|
50
|
+
props: {
|
|
51
|
+
items: [
|
|
52
|
+
{
|
|
53
|
+
id: "paris",
|
|
54
|
+
title: "Paris, France",
|
|
55
|
+
subtitle: "5 days \u2022 $1,850",
|
|
56
|
+
description: "The City of Light with iconic landmarks",
|
|
57
|
+
metadata: { city: "Paris", country: "France", lat: 48.8566, lng: 2.3522, price: 1850, days: 5 }
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: "rome",
|
|
61
|
+
title: "Rome, Italy",
|
|
62
|
+
subtitle: "5 days \u2022 $1,650",
|
|
63
|
+
description: "Ancient history meets modern culture",
|
|
64
|
+
metadata: { city: "Rome", country: "Italy", lat: 41.9028, lng: 12.4964, price: 1650, days: 5 }
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: "barcelona",
|
|
68
|
+
title: "Barcelona, Spain",
|
|
69
|
+
subtitle: "5 days \u2022 $1,450",
|
|
70
|
+
description: "Beautiful beaches and Gaud\xED architecture",
|
|
71
|
+
metadata: { city: "Barcelona", country: "Spain", lat: 41.3851, lng: 2.1734, price: 1450, days: 5 }
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
actions: [
|
|
76
|
+
{
|
|
77
|
+
type: "select_destination",
|
|
78
|
+
label: "Select",
|
|
79
|
+
handler: "client",
|
|
80
|
+
payload: {}
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
type: "view_details",
|
|
84
|
+
label: "Details",
|
|
85
|
+
handler: "server",
|
|
86
|
+
payload: {}
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
],
|
|
91
|
+
timestamp: new Date(Date.now() - 32e5)
|
|
92
|
+
}
|
|
93
|
+
];
|
|
94
|
+
var mockThreads = [
|
|
95
|
+
{
|
|
96
|
+
id: "thread-1",
|
|
97
|
+
title: "Trip to Europe",
|
|
98
|
+
preview: "Planning a 5-day trip...",
|
|
99
|
+
createdAt: new Date(Date.now() - 864e5),
|
|
100
|
+
updatedAt: new Date(Date.now() - 36e5),
|
|
101
|
+
messageCount: 12
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
id: "thread-2",
|
|
105
|
+
title: "Restaurant Recommendations",
|
|
106
|
+
preview: "Looking for good places...",
|
|
107
|
+
createdAt: new Date(Date.now() - 1728e5),
|
|
108
|
+
updatedAt: new Date(Date.now() - 864e5),
|
|
109
|
+
messageCount: 8
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
id: "thread-3",
|
|
113
|
+
title: "Budget Planning",
|
|
114
|
+
preview: "Help with monthly budget",
|
|
115
|
+
createdAt: new Date(Date.now() - 2592e5),
|
|
116
|
+
updatedAt: new Date(Date.now() - 1728e5),
|
|
117
|
+
messageCount: 15
|
|
118
|
+
}
|
|
119
|
+
];
|
|
120
|
+
var mockWidgets = [
|
|
121
|
+
{
|
|
122
|
+
type: "card",
|
|
123
|
+
id: "card-1",
|
|
124
|
+
props: {
|
|
125
|
+
title: "Paris, France",
|
|
126
|
+
description: "5-day adventure in the City of Light",
|
|
127
|
+
image: "https://images.unsplash.com/photo-1502602898657-3e91760cbb34",
|
|
128
|
+
footer: "Total: $1,850"
|
|
129
|
+
},
|
|
130
|
+
actions: [
|
|
131
|
+
{
|
|
132
|
+
type: "book_trip",
|
|
133
|
+
label: "Book Now",
|
|
134
|
+
handler: "client",
|
|
135
|
+
payload: { tripId: "trip-paris" }
|
|
136
|
+
}
|
|
137
|
+
]
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
type: "card",
|
|
141
|
+
id: "card-2",
|
|
142
|
+
props: {
|
|
143
|
+
title: "Rome, Italy",
|
|
144
|
+
description: "Explore ancient wonders",
|
|
145
|
+
image: "https://images.unsplash.com/photo-1552832230-c0197dd311b5",
|
|
146
|
+
footer: "Total: $1,650"
|
|
147
|
+
},
|
|
148
|
+
actions: [
|
|
149
|
+
{
|
|
150
|
+
type: "book_trip",
|
|
151
|
+
label: "Book Now",
|
|
152
|
+
handler: "client",
|
|
153
|
+
payload: { tripId: "trip-rome" }
|
|
154
|
+
}
|
|
155
|
+
]
|
|
156
|
+
}
|
|
157
|
+
];
|
|
158
|
+
function generateMockResponse(delay = 1e3) {
|
|
159
|
+
return new Promise((resolve) => {
|
|
160
|
+
setTimeout(() => {
|
|
161
|
+
resolve({
|
|
162
|
+
id: `msg-${Date.now()}`,
|
|
163
|
+
role: "assistant",
|
|
164
|
+
content: "This is a mock response. In production, this would come from your AI agent API.",
|
|
165
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
166
|
+
});
|
|
167
|
+
}, delay);
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
function generateMockStreamingResponse(text, onChunk, typingSpeed = 30) {
|
|
171
|
+
return new Promise((resolve) => {
|
|
172
|
+
const words = text.split(" ");
|
|
173
|
+
let currentIndex = 0;
|
|
174
|
+
const interval = setInterval(() => {
|
|
175
|
+
if (currentIndex < words.length) {
|
|
176
|
+
onChunk(words[currentIndex] + " ");
|
|
177
|
+
currentIndex++;
|
|
178
|
+
} else {
|
|
179
|
+
clearInterval(interval);
|
|
180
|
+
resolve();
|
|
181
|
+
}
|
|
182
|
+
}, typingSpeed);
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// src/components/Widgets/Widgets.tsx
|
|
187
|
+
import { useEffect } from "react";
|
|
188
|
+
|
|
189
|
+
// src/components/Widgets/widget-library/Card.tsx
|
|
190
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
191
|
+
function Card({ widget, onAction }) {
|
|
192
|
+
const { title, description, image, footer } = widget.props;
|
|
193
|
+
return /* @__PURE__ */ jsxs("div", { className: "apteva-widget-card", children: [
|
|
194
|
+
image && /* @__PURE__ */ jsx("img", { src: image, alt: title, className: "w-full h-48 object-cover" }),
|
|
195
|
+
/* @__PURE__ */ jsxs("div", { className: "p-4", children: [
|
|
196
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-gray-900 dark:text-white", children: title }),
|
|
197
|
+
description && /* @__PURE__ */ jsx("p", { className: "text-gray-600 dark:text-gray-400 mt-2", children: description })
|
|
198
|
+
] }),
|
|
199
|
+
(footer || widget.actions && widget.actions.length > 0) && /* @__PURE__ */ jsxs("div", { className: "border-t border-gray-200 dark:border-gray-700 p-4 flex justify-between items-center", children: [
|
|
200
|
+
footer && /* @__PURE__ */ jsx("span", { className: "text-sm text-gray-600 dark:text-gray-400", children: footer }),
|
|
201
|
+
widget.actions && widget.actions.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex gap-2", children: widget.actions.map((action, idx) => /* @__PURE__ */ jsx(
|
|
202
|
+
"button",
|
|
203
|
+
{
|
|
204
|
+
onClick: () => onAction?.({
|
|
205
|
+
type: action.type,
|
|
206
|
+
payload: action.payload,
|
|
207
|
+
widgetId: widget.id,
|
|
208
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
209
|
+
}),
|
|
210
|
+
className: "apteva-widget-button",
|
|
211
|
+
children: action.label
|
|
212
|
+
},
|
|
213
|
+
idx
|
|
214
|
+
)) })
|
|
215
|
+
] })
|
|
216
|
+
] });
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// src/components/Widgets/widget-library/List.tsx
|
|
220
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
221
|
+
function List({ widget, onAction }) {
|
|
222
|
+
const { items } = widget.props;
|
|
223
|
+
return /* @__PURE__ */ jsx2("div", { className: "apteva-widget-list", children: items.map((item) => /* @__PURE__ */ jsxs2("div", { className: "flex items-center p-4 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors", children: [
|
|
224
|
+
item.image && /* @__PURE__ */ jsx2("img", { src: item.image, alt: item.title, className: "w-16 h-16 rounded object-cover" }),
|
|
225
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex-1 ml-4", children: [
|
|
226
|
+
/* @__PURE__ */ jsx2("h4", { className: "font-semibold text-gray-900 dark:text-white", children: item.title }),
|
|
227
|
+
item.subtitle && /* @__PURE__ */ jsx2("p", { className: "text-sm text-gray-600 dark:text-gray-400", children: item.subtitle }),
|
|
228
|
+
item.description && /* @__PURE__ */ jsx2("p", { className: "text-xs text-gray-500 dark:text-gray-500 mt-1", children: item.description })
|
|
229
|
+
] }),
|
|
230
|
+
widget.actions && widget.actions.length > 0 && /* @__PURE__ */ jsx2("div", { className: "flex gap-2", children: widget.actions.map((action, idx) => /* @__PURE__ */ jsx2(
|
|
231
|
+
"button",
|
|
232
|
+
{
|
|
233
|
+
onClick: () => onAction?.({
|
|
234
|
+
type: action.type,
|
|
235
|
+
payload: item.metadata || item,
|
|
236
|
+
widgetId: widget.id,
|
|
237
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
238
|
+
}),
|
|
239
|
+
className: "px-3 py-1.5 text-sm rounded-lg font-medium transition-colors bg-apteva-500 text-white hover:bg-apteva-600",
|
|
240
|
+
children: action.label
|
|
241
|
+
},
|
|
242
|
+
idx
|
|
243
|
+
)) })
|
|
244
|
+
] }, item.id)) });
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// src/components/Widgets/widget-library/Button.tsx
|
|
248
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
249
|
+
function Button({ widget, onAction }) {
|
|
250
|
+
const { label, variant = "primary", disabled = false } = widget.props;
|
|
251
|
+
const variantClasses = {
|
|
252
|
+
primary: "bg-apteva-500 text-white hover:bg-apteva-600",
|
|
253
|
+
secondary: "bg-gray-500 text-white hover:bg-gray-600",
|
|
254
|
+
outline: "border-2 border-apteva-500 text-apteva-500 hover:bg-apteva-50",
|
|
255
|
+
ghost: "text-apteva-500 hover:bg-apteva-50"
|
|
256
|
+
};
|
|
257
|
+
return /* @__PURE__ */ jsx3(
|
|
258
|
+
"button",
|
|
259
|
+
{
|
|
260
|
+
onClick: () => widget.actions?.[0] && onAction?.({
|
|
261
|
+
type: widget.actions[0].type,
|
|
262
|
+
payload: widget.actions[0].payload,
|
|
263
|
+
widgetId: widget.id,
|
|
264
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
265
|
+
}),
|
|
266
|
+
disabled,
|
|
267
|
+
className: cn("px-4 py-2 rounded-lg font-medium transition-colors", variantClasses[variant], {
|
|
268
|
+
"opacity-50 cursor-not-allowed": disabled
|
|
269
|
+
}),
|
|
270
|
+
children: label
|
|
271
|
+
}
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// src/components/Widgets/WidgetRenderer.tsx
|
|
276
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
277
|
+
function WidgetRenderer({ widget, onAction }) {
|
|
278
|
+
switch (widget.type) {
|
|
279
|
+
case "card":
|
|
280
|
+
return /* @__PURE__ */ jsx4(Card, { widget, onAction });
|
|
281
|
+
case "list":
|
|
282
|
+
return /* @__PURE__ */ jsx4(List, { widget, onAction });
|
|
283
|
+
case "button":
|
|
284
|
+
return /* @__PURE__ */ jsx4(Button, { widget, onAction });
|
|
285
|
+
default:
|
|
286
|
+
return /* @__PURE__ */ jsxs3("div", { className: "p-4 border border-yellow-300 bg-yellow-50 rounded-lg", children: [
|
|
287
|
+
/* @__PURE__ */ jsxs3("p", { className: "text-sm text-yellow-800", children: [
|
|
288
|
+
"Unknown widget type: ",
|
|
289
|
+
widget.type
|
|
290
|
+
] }),
|
|
291
|
+
/* @__PURE__ */ jsx4("pre", { className: "text-xs mt-2 overflow-auto", children: JSON.stringify(widget, null, 2) })
|
|
292
|
+
] });
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// src/components/Widgets/Widgets.tsx
|
|
297
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
298
|
+
function Widgets({
|
|
299
|
+
widgets,
|
|
300
|
+
onAction,
|
|
301
|
+
onWidgetMount,
|
|
302
|
+
layout = "stack",
|
|
303
|
+
spacing = "normal",
|
|
304
|
+
columns = 3,
|
|
305
|
+
className
|
|
306
|
+
}) {
|
|
307
|
+
useEffect(() => {
|
|
308
|
+
widgets.forEach((widget) => {
|
|
309
|
+
onWidgetMount?.(widget.id);
|
|
310
|
+
});
|
|
311
|
+
}, [widgets, onWidgetMount]);
|
|
312
|
+
const layoutClasses = {
|
|
313
|
+
stack: "flex flex-col",
|
|
314
|
+
grid: `grid grid-cols-1 md:grid-cols-${columns}`,
|
|
315
|
+
masonry: "columns-1 md:columns-2 lg:columns-3"
|
|
316
|
+
};
|
|
317
|
+
const spacingClasses = {
|
|
318
|
+
tight: "gap-2",
|
|
319
|
+
normal: "gap-4",
|
|
320
|
+
loose: "gap-6"
|
|
321
|
+
};
|
|
322
|
+
return /* @__PURE__ */ jsx5("div", { className: cn(layoutClasses[layout], spacingClasses[spacing], className), children: widgets.map((widget) => /* @__PURE__ */ jsx5(WidgetRenderer, { widget, onAction }, widget.id)) });
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// src/components/Chat/Message.tsx
|
|
326
|
+
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
327
|
+
function Message({ message, onAction }) {
|
|
328
|
+
const isUser = message.role === "user";
|
|
329
|
+
return /* @__PURE__ */ jsxs4("div", { className: cn("apteva-message", isUser ? "apteva-message-user" : "apteva-message-assistant"), children: [
|
|
330
|
+
/* @__PURE__ */ jsx6("div", { className: "whitespace-pre-wrap", children: message.content }),
|
|
331
|
+
message.widgets && message.widgets.length > 0 && /* @__PURE__ */ jsx6("div", { className: "mt-4", children: /* @__PURE__ */ jsx6(Widgets, { widgets: message.widgets, onAction, layout: "stack" }) }),
|
|
332
|
+
/* @__PURE__ */ jsx6("div", { className: "text-xs opacity-70 mt-2", children: message.timestamp.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) })
|
|
333
|
+
] });
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// src/components/Chat/MessageList.tsx
|
|
337
|
+
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
338
|
+
function MessageList({ messages, onAction }) {
|
|
339
|
+
const listRef = useRef(null);
|
|
340
|
+
useEffect2(() => {
|
|
341
|
+
if (listRef.current) {
|
|
342
|
+
listRef.current.scrollTop = listRef.current.scrollHeight;
|
|
343
|
+
}
|
|
344
|
+
}, [messages]);
|
|
345
|
+
return /* @__PURE__ */ jsx7("div", { ref: listRef, className: "apteva-message-list", children: messages.length === 0 ? /* @__PURE__ */ jsx7("div", { className: "flex items-center justify-center h-full text-gray-500", children: /* @__PURE__ */ jsxs5("div", { className: "text-center space-y-2", children: [
|
|
346
|
+
/* @__PURE__ */ jsx7("div", { className: "text-4xl", children: "\u{1F4AC}" }),
|
|
347
|
+
/* @__PURE__ */ jsx7("p", { children: "No messages yet. Start a conversation!" })
|
|
348
|
+
] }) }) : messages.map((message) => /* @__PURE__ */ jsx7(Message, { message, onAction }, message.id)) });
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// src/components/Chat/Composer.tsx
|
|
352
|
+
import { useState, useRef as useRef2 } from "react";
|
|
353
|
+
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
354
|
+
function Composer({ onSendMessage, placeholder = "Type a message...", disabled = false }) {
|
|
355
|
+
const [text, setText] = useState("");
|
|
356
|
+
const textareaRef = useRef2(null);
|
|
357
|
+
const handleKeyDown = (e) => {
|
|
358
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
359
|
+
e.preventDefault();
|
|
360
|
+
handleSend();
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
const handleSend = () => {
|
|
364
|
+
if (text.trim() && !disabled) {
|
|
365
|
+
onSendMessage(text.trim());
|
|
366
|
+
setText("");
|
|
367
|
+
if (textareaRef.current) {
|
|
368
|
+
textareaRef.current.style.height = "auto";
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
const handleChange = (e) => {
|
|
373
|
+
setText(e.target.value);
|
|
374
|
+
e.target.style.height = "auto";
|
|
375
|
+
e.target.style.height = `${e.target.scrollHeight}px`;
|
|
376
|
+
};
|
|
377
|
+
return /* @__PURE__ */ jsxs6("div", { className: "apteva-composer", children: [
|
|
378
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex gap-2", children: [
|
|
379
|
+
/* @__PURE__ */ jsx8(
|
|
380
|
+
"textarea",
|
|
381
|
+
{
|
|
382
|
+
ref: textareaRef,
|
|
383
|
+
value: text,
|
|
384
|
+
onChange: handleChange,
|
|
385
|
+
onKeyDown: handleKeyDown,
|
|
386
|
+
placeholder,
|
|
387
|
+
disabled,
|
|
388
|
+
className: "apteva-composer-input",
|
|
389
|
+
rows: 1,
|
|
390
|
+
style: { maxHeight: "200px" }
|
|
391
|
+
}
|
|
392
|
+
),
|
|
393
|
+
/* @__PURE__ */ jsx8(
|
|
394
|
+
"button",
|
|
395
|
+
{
|
|
396
|
+
onClick: handleSend,
|
|
397
|
+
disabled: !text.trim() || disabled,
|
|
398
|
+
className: "px-6 py-2 bg-apteva-500 text-white rounded-lg hover:bg-apteva-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors font-medium",
|
|
399
|
+
children: "Send"
|
|
400
|
+
}
|
|
401
|
+
)
|
|
402
|
+
] }),
|
|
403
|
+
/* @__PURE__ */ jsx8("div", { className: "text-xs text-gray-500 mt-2", children: "Press Enter to send, Shift+Enter for new line" })
|
|
404
|
+
] });
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// src/components/Chat/Chat.tsx
|
|
408
|
+
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
409
|
+
function Chat({
|
|
410
|
+
agentId,
|
|
411
|
+
threadId,
|
|
412
|
+
initialMessages = [],
|
|
413
|
+
onThreadChange,
|
|
414
|
+
onMessageSent,
|
|
415
|
+
onAction,
|
|
416
|
+
placeholder = "Type a message...",
|
|
417
|
+
showHeader = true,
|
|
418
|
+
headerTitle = "Chat",
|
|
419
|
+
className
|
|
420
|
+
}) {
|
|
421
|
+
const [messages, setMessages] = useState2(initialMessages.length > 0 ? initialMessages : mockMessages);
|
|
422
|
+
const [isLoading, setIsLoading] = useState2(false);
|
|
423
|
+
useEffect3(() => {
|
|
424
|
+
if (threadId) {
|
|
425
|
+
console.log("Loading thread:", threadId);
|
|
426
|
+
onThreadChange?.(threadId);
|
|
427
|
+
}
|
|
428
|
+
}, [threadId, onThreadChange]);
|
|
429
|
+
const handleSendMessage = async (text) => {
|
|
430
|
+
const userMessage = {
|
|
431
|
+
id: `msg-${Date.now()}`,
|
|
432
|
+
role: "user",
|
|
433
|
+
content: text,
|
|
434
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
435
|
+
};
|
|
436
|
+
setMessages((prev) => [...prev, userMessage]);
|
|
437
|
+
onMessageSent?.(userMessage);
|
|
438
|
+
setIsLoading(true);
|
|
439
|
+
try {
|
|
440
|
+
const response = await generateMockResponse(1e3);
|
|
441
|
+
setMessages((prev) => [...prev, response]);
|
|
442
|
+
} catch (error) {
|
|
443
|
+
console.error("Error generating response:", error);
|
|
444
|
+
} finally {
|
|
445
|
+
setIsLoading(false);
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
return /* @__PURE__ */ jsxs7("div", { className: cn("apteva-chat-container", className), children: [
|
|
449
|
+
showHeader && /* @__PURE__ */ jsxs7("div", { className: "border-b border-gray-200 dark:border-gray-700 px-4 py-3", children: [
|
|
450
|
+
/* @__PURE__ */ jsx9("h2", { className: "text-lg font-semibold text-gray-900 dark:text-white", children: headerTitle }),
|
|
451
|
+
/* @__PURE__ */ jsxs7("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: [
|
|
452
|
+
"Agent: ",
|
|
453
|
+
agentId
|
|
454
|
+
] })
|
|
455
|
+
] }),
|
|
456
|
+
/* @__PURE__ */ jsx9(MessageList, { messages, onAction }),
|
|
457
|
+
isLoading && /* @__PURE__ */ jsx9("div", { className: "px-4 py-2 text-sm text-gray-500 italic", children: "AI is thinking..." }),
|
|
458
|
+
/* @__PURE__ */ jsx9(Composer, { onSendMessage: handleSendMessage, placeholder, disabled: isLoading })
|
|
459
|
+
] });
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// src/components/Command/Command.tsx
|
|
463
|
+
import { useState as useState3, useEffect as useEffect4 } from "react";
|
|
464
|
+
import { Fragment, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
465
|
+
function Command({
|
|
466
|
+
agentId,
|
|
467
|
+
command: initialCommand,
|
|
468
|
+
context,
|
|
469
|
+
autoExecute = false,
|
|
470
|
+
allowInput = true,
|
|
471
|
+
placeholder = "Enter your command...",
|
|
472
|
+
submitButtonText = "Execute",
|
|
473
|
+
variant = "default",
|
|
474
|
+
onStart,
|
|
475
|
+
onProgress,
|
|
476
|
+
onChunk,
|
|
477
|
+
onComplete,
|
|
478
|
+
onError,
|
|
479
|
+
loadingText = "Processing...",
|
|
480
|
+
showProgress = true,
|
|
481
|
+
enableStreaming = false,
|
|
482
|
+
resultRenderer,
|
|
483
|
+
className
|
|
484
|
+
}) {
|
|
485
|
+
const [state, setState] = useState3("idle");
|
|
486
|
+
const [result, setResult] = useState3(null);
|
|
487
|
+
const [error, setError] = useState3(null);
|
|
488
|
+
const [progress, setProgress] = useState3(0);
|
|
489
|
+
const [command, setCommand] = useState3(initialCommand || "");
|
|
490
|
+
const [streamedContent, setStreamedContent] = useState3("");
|
|
491
|
+
useEffect4(() => {
|
|
492
|
+
if (autoExecute && state === "idle" && command) {
|
|
493
|
+
executeCommand();
|
|
494
|
+
}
|
|
495
|
+
}, [autoExecute]);
|
|
496
|
+
const executeCommand = async () => {
|
|
497
|
+
if (!command.trim()) {
|
|
498
|
+
setError(new Error("Please enter a command"));
|
|
499
|
+
setState("error");
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
setState("loading");
|
|
503
|
+
setError(null);
|
|
504
|
+
setProgress(0);
|
|
505
|
+
setStreamedContent("");
|
|
506
|
+
onStart?.();
|
|
507
|
+
try {
|
|
508
|
+
if (enableStreaming) {
|
|
509
|
+
const mockStreamChunks = [
|
|
510
|
+
"Initializing...",
|
|
511
|
+
"Connecting to agent...",
|
|
512
|
+
"Processing your request...",
|
|
513
|
+
"Analyzing data sources...",
|
|
514
|
+
"Gathering information...",
|
|
515
|
+
"Generating response...",
|
|
516
|
+
"Finalizing results..."
|
|
517
|
+
];
|
|
518
|
+
for (let i = 0; i < mockStreamChunks.length; i++) {
|
|
519
|
+
await new Promise((resolve) => setTimeout(resolve, 600 + Math.random() * 400));
|
|
520
|
+
const chunk = mockStreamChunks[i];
|
|
521
|
+
setStreamedContent(chunk);
|
|
522
|
+
onChunk?.(chunk);
|
|
523
|
+
setProgress(Math.round((i + 1) / mockStreamChunks.length * 100));
|
|
524
|
+
onProgress?.(Math.round((i + 1) / mockStreamChunks.length * 100));
|
|
525
|
+
}
|
|
526
|
+
const mockResult = {
|
|
527
|
+
success: true,
|
|
528
|
+
data: {
|
|
529
|
+
summary: `Successfully processed: "${command}"`,
|
|
530
|
+
agentId,
|
|
531
|
+
context,
|
|
532
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
533
|
+
},
|
|
534
|
+
message: "Command executed successfully"
|
|
535
|
+
};
|
|
536
|
+
setResult(mockResult);
|
|
537
|
+
setState("success");
|
|
538
|
+
setProgress(100);
|
|
539
|
+
onComplete?.(mockResult);
|
|
540
|
+
} else {
|
|
541
|
+
const progressInterval = setInterval(() => {
|
|
542
|
+
setProgress((prev) => {
|
|
543
|
+
const next = Math.min(prev + 10, 90);
|
|
544
|
+
onProgress?.(next);
|
|
545
|
+
return next;
|
|
546
|
+
});
|
|
547
|
+
}, 200);
|
|
548
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
549
|
+
clearInterval(progressInterval);
|
|
550
|
+
const mockResult = {
|
|
551
|
+
success: true,
|
|
552
|
+
data: {
|
|
553
|
+
summary: `Command "${command}" executed successfully`,
|
|
554
|
+
agentId,
|
|
555
|
+
context,
|
|
556
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
557
|
+
},
|
|
558
|
+
message: "Execution complete"
|
|
559
|
+
};
|
|
560
|
+
setResult(mockResult);
|
|
561
|
+
setState("success");
|
|
562
|
+
setProgress(100);
|
|
563
|
+
onComplete?.(mockResult);
|
|
564
|
+
}
|
|
565
|
+
} catch (err) {
|
|
566
|
+
const error2 = err instanceof Error ? err : new Error("Unknown error");
|
|
567
|
+
setError(error2);
|
|
568
|
+
setState("error");
|
|
569
|
+
onError?.(error2);
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
const resetCommand = () => {
|
|
573
|
+
setState("idle");
|
|
574
|
+
setResult(null);
|
|
575
|
+
setError(null);
|
|
576
|
+
setProgress(0);
|
|
577
|
+
setCommand("");
|
|
578
|
+
};
|
|
579
|
+
const isCompact = variant === "compact";
|
|
580
|
+
return /* @__PURE__ */ jsxs8(
|
|
581
|
+
"div",
|
|
582
|
+
{
|
|
583
|
+
className: cn(
|
|
584
|
+
"relative border-2 rounded-xl bg-white dark:bg-gray-900 transition-all duration-300 flex flex-col",
|
|
585
|
+
state === "loading" && "animate-pulse-border",
|
|
586
|
+
state === "idle" && "border-gray-300 dark:border-gray-700",
|
|
587
|
+
state === "loading" && "border-apteva-500",
|
|
588
|
+
state === "success" && "border-green-500",
|
|
589
|
+
state === "error" && "border-red-500",
|
|
590
|
+
className
|
|
591
|
+
),
|
|
592
|
+
style: { minHeight: isCompact ? "auto" : "180px" },
|
|
593
|
+
children: [
|
|
594
|
+
/* @__PURE__ */ jsxs8("div", { className: cn("flex-1 flex", isCompact ? "flex-row items-center p-3 gap-3" : "flex-col p-4"), children: [
|
|
595
|
+
state === "idle" && allowInput && !isCompact && /* @__PURE__ */ jsx10(
|
|
596
|
+
"textarea",
|
|
597
|
+
{
|
|
598
|
+
value: command,
|
|
599
|
+
onChange: (e) => setCommand(e.target.value),
|
|
600
|
+
onKeyDown: (e) => {
|
|
601
|
+
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
602
|
+
e.preventDefault();
|
|
603
|
+
executeCommand();
|
|
604
|
+
}
|
|
605
|
+
},
|
|
606
|
+
placeholder,
|
|
607
|
+
className: "flex-1 w-full resize-none bg-transparent border-none focus:outline-none text-gray-900 dark:text-white placeholder-gray-400",
|
|
608
|
+
rows: 6
|
|
609
|
+
}
|
|
610
|
+
),
|
|
611
|
+
state === "idle" && allowInput && isCompact && /* @__PURE__ */ jsxs8(Fragment, { children: [
|
|
612
|
+
/* @__PURE__ */ jsx10(
|
|
613
|
+
"input",
|
|
614
|
+
{
|
|
615
|
+
type: "text",
|
|
616
|
+
value: command,
|
|
617
|
+
onChange: (e) => setCommand(e.target.value),
|
|
618
|
+
onKeyDown: (e) => {
|
|
619
|
+
if (e.key === "Enter") {
|
|
620
|
+
e.preventDefault();
|
|
621
|
+
executeCommand();
|
|
622
|
+
}
|
|
623
|
+
},
|
|
624
|
+
placeholder,
|
|
625
|
+
className: "flex-1 bg-transparent border-none focus:outline-none text-gray-900 dark:text-white placeholder-gray-400 py-1"
|
|
626
|
+
}
|
|
627
|
+
),
|
|
628
|
+
/* @__PURE__ */ jsx10(
|
|
629
|
+
"button",
|
|
630
|
+
{
|
|
631
|
+
onClick: executeCommand,
|
|
632
|
+
disabled: !command.trim(),
|
|
633
|
+
className: cn(
|
|
634
|
+
"w-8 h-8 rounded-lg flex items-center justify-center font-bold transition-all flex-shrink-0",
|
|
635
|
+
"border border-gray-300 dark:border-gray-600",
|
|
636
|
+
"bg-white dark:bg-gray-800",
|
|
637
|
+
"text-gray-700 dark:text-gray-300",
|
|
638
|
+
"hover:bg-gray-50 dark:hover:bg-gray-700",
|
|
639
|
+
"disabled:opacity-30 disabled:cursor-not-allowed",
|
|
640
|
+
!command.trim() && "border-gray-200 dark:border-gray-700 text-gray-400 dark:text-gray-600"
|
|
641
|
+
),
|
|
642
|
+
title: "Execute",
|
|
643
|
+
children: "\u2191"
|
|
644
|
+
}
|
|
645
|
+
)
|
|
646
|
+
] }),
|
|
647
|
+
state === "loading" && !isCompact && /* @__PURE__ */ jsxs8("div", { className: "flex-1 flex flex-col items-center justify-center space-y-4 py-8", children: [
|
|
648
|
+
/* @__PURE__ */ jsx10("div", { className: "w-6 h-6 border-2 border-gray-300 border-t-apteva-500 rounded-full animate-spin" }),
|
|
649
|
+
/* @__PURE__ */ jsx10("div", { className: "text-gray-600 dark:text-gray-400 text-sm text-center max-w-md", children: enableStreaming && streamedContent ? streamedContent : loadingText }),
|
|
650
|
+
showProgress && /* @__PURE__ */ jsxs8("div", { className: "w-full max-w-sm", children: [
|
|
651
|
+
/* @__PURE__ */ jsx10("div", { className: "w-full bg-gray-200 dark:bg-gray-700 rounded-full h-1.5", children: /* @__PURE__ */ jsx10(
|
|
652
|
+
"div",
|
|
653
|
+
{
|
|
654
|
+
className: "bg-apteva-500 h-1.5 rounded-full transition-all duration-300",
|
|
655
|
+
style: { width: `${progress}%` }
|
|
656
|
+
}
|
|
657
|
+
) }),
|
|
658
|
+
/* @__PURE__ */ jsxs8("p", { className: "text-xs text-gray-500 mt-2 text-center", children: [
|
|
659
|
+
progress,
|
|
660
|
+
"%"
|
|
661
|
+
] })
|
|
662
|
+
] })
|
|
663
|
+
] }),
|
|
664
|
+
state === "loading" && isCompact && /* @__PURE__ */ jsxs8(Fragment, { children: [
|
|
665
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex-1 flex items-center gap-3 py-1", children: [
|
|
666
|
+
/* @__PURE__ */ jsx10("div", { className: "w-4 h-4 border-2 border-gray-300 border-t-apteva-500 rounded-full animate-spin" }),
|
|
667
|
+
/* @__PURE__ */ jsx10("div", { className: "text-gray-600 dark:text-gray-400 text-sm truncate", children: enableStreaming && streamedContent ? streamedContent : loadingText })
|
|
668
|
+
] }),
|
|
669
|
+
/* @__PURE__ */ jsx10(
|
|
670
|
+
"button",
|
|
671
|
+
{
|
|
672
|
+
disabled: true,
|
|
673
|
+
className: cn(
|
|
674
|
+
"w-8 h-8 rounded-lg flex items-center justify-center font-bold transition-all flex-shrink-0",
|
|
675
|
+
"border border-gray-200 dark:border-gray-700",
|
|
676
|
+
"bg-white dark:bg-gray-800",
|
|
677
|
+
"text-gray-400 dark:text-gray-600",
|
|
678
|
+
"opacity-30 cursor-not-allowed"
|
|
679
|
+
),
|
|
680
|
+
children: "\u2191"
|
|
681
|
+
}
|
|
682
|
+
)
|
|
683
|
+
] }),
|
|
684
|
+
state === "error" && /* @__PURE__ */ jsxs8("div", { className: "flex-1 flex flex-col", children: [
|
|
685
|
+
/* @__PURE__ */ jsx10("div", { className: "mb-4 p-3 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg", children: /* @__PURE__ */ jsxs8("div", { className: "flex items-start gap-2", children: [
|
|
686
|
+
/* @__PURE__ */ jsx10("svg", { className: "w-5 h-5 text-red-600 mt-0.5 flex-shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx10("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" }) }),
|
|
687
|
+
/* @__PURE__ */ jsxs8("div", { children: [
|
|
688
|
+
/* @__PURE__ */ jsx10("h3", { className: "text-sm font-semibold text-red-800 dark:text-red-400", children: "Error" }),
|
|
689
|
+
/* @__PURE__ */ jsx10("p", { className: "text-red-700 dark:text-red-300 text-sm mt-1", children: error?.message })
|
|
690
|
+
] })
|
|
691
|
+
] }) }),
|
|
692
|
+
allowInput && /* @__PURE__ */ jsx10(
|
|
693
|
+
"textarea",
|
|
694
|
+
{
|
|
695
|
+
value: command,
|
|
696
|
+
onChange: (e) => setCommand(e.target.value),
|
|
697
|
+
onKeyDown: (e) => {
|
|
698
|
+
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
699
|
+
e.preventDefault();
|
|
700
|
+
executeCommand();
|
|
701
|
+
}
|
|
702
|
+
},
|
|
703
|
+
placeholder,
|
|
704
|
+
className: "flex-1 w-full resize-none bg-transparent border-none focus:outline-none text-gray-900 dark:text-white placeholder-gray-400",
|
|
705
|
+
rows: 4
|
|
706
|
+
}
|
|
707
|
+
)
|
|
708
|
+
] }),
|
|
709
|
+
state === "success" && result && !isCompact && /* @__PURE__ */ jsx10("div", { className: "flex-1 overflow-auto", children: resultRenderer ? resultRenderer(result.data) : /* @__PURE__ */ jsxs8("div", { children: [
|
|
710
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex items-start gap-3 mb-3 p-3 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg", children: [
|
|
711
|
+
/* @__PURE__ */ jsx10("svg", { className: "w-5 h-5 text-green-600 mt-0.5 flex-shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx10("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" }) }),
|
|
712
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex-1", children: [
|
|
713
|
+
/* @__PURE__ */ jsx10("h3", { className: "text-sm font-semibold text-green-800 dark:text-green-400 mb-1", children: "Success" }),
|
|
714
|
+
/* @__PURE__ */ jsx10("p", { className: "text-green-700 dark:text-green-300 text-sm", children: result.message || "Command executed successfully" })
|
|
715
|
+
] })
|
|
716
|
+
] }),
|
|
717
|
+
result.data?.summary && /* @__PURE__ */ jsx10("div", { className: "text-gray-700 dark:text-gray-300 text-sm leading-relaxed", children: result.data.summary })
|
|
718
|
+
] }) }),
|
|
719
|
+
state === "success" && result && isCompact && /* @__PURE__ */ jsxs8(Fragment, { children: [
|
|
720
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex-1 flex items-center gap-2 py-1", children: [
|
|
721
|
+
/* @__PURE__ */ jsx10("svg", { className: "w-4 h-4 text-green-600 flex-shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx10("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" }) }),
|
|
722
|
+
/* @__PURE__ */ jsx10("div", { className: "text-green-700 dark:text-green-300 text-sm truncate", children: resultRenderer ? resultRenderer(result.data) : result.message || "Command executed successfully" })
|
|
723
|
+
] }),
|
|
724
|
+
/* @__PURE__ */ jsx10(
|
|
725
|
+
"button",
|
|
726
|
+
{
|
|
727
|
+
disabled: true,
|
|
728
|
+
className: cn(
|
|
729
|
+
"w-8 h-8 rounded-lg flex items-center justify-center font-bold transition-all flex-shrink-0",
|
|
730
|
+
"border border-gray-200 dark:border-gray-700",
|
|
731
|
+
"bg-white dark:bg-gray-800",
|
|
732
|
+
"text-gray-400 dark:text-gray-600",
|
|
733
|
+
"opacity-30 cursor-not-allowed"
|
|
734
|
+
),
|
|
735
|
+
children: "\u2191"
|
|
736
|
+
}
|
|
737
|
+
)
|
|
738
|
+
] })
|
|
739
|
+
] }),
|
|
740
|
+
!isCompact && /* @__PURE__ */ jsxs8("div", { className: "p-3 flex items-center justify-end gap-2", children: [
|
|
741
|
+
(state === "success" || state === "error") && allowInput && /* @__PURE__ */ jsx10(
|
|
742
|
+
"button",
|
|
743
|
+
{
|
|
744
|
+
onClick: resetCommand,
|
|
745
|
+
className: "px-3 py-1.5 text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white transition-colors",
|
|
746
|
+
children: "Reset"
|
|
747
|
+
}
|
|
748
|
+
),
|
|
749
|
+
(state === "idle" || state === "error") && /* @__PURE__ */ jsx10(
|
|
750
|
+
"button",
|
|
751
|
+
{
|
|
752
|
+
onClick: executeCommand,
|
|
753
|
+
disabled: !command.trim(),
|
|
754
|
+
className: cn(
|
|
755
|
+
"w-8 h-8 rounded-lg flex items-center justify-center font-bold transition-all",
|
|
756
|
+
"border border-gray-300 dark:border-gray-600",
|
|
757
|
+
"bg-white dark:bg-gray-800",
|
|
758
|
+
"text-gray-700 dark:text-gray-300",
|
|
759
|
+
"hover:bg-gray-50 dark:hover:bg-gray-700",
|
|
760
|
+
"disabled:opacity-30 disabled:cursor-not-allowed",
|
|
761
|
+
!command.trim() && "border-gray-200 dark:border-gray-700 text-gray-400 dark:text-gray-600"
|
|
762
|
+
),
|
|
763
|
+
title: state === "error" ? "Retry" : "Execute",
|
|
764
|
+
children: state === "error" ? "\u21BB" : "\u2191"
|
|
765
|
+
}
|
|
766
|
+
)
|
|
767
|
+
] }),
|
|
768
|
+
/* @__PURE__ */ jsx10("style", { dangerouslySetInnerHTML: {
|
|
769
|
+
__html: `
|
|
770
|
+
@keyframes pulse-border {
|
|
771
|
+
0%, 100% {
|
|
772
|
+
border-color: rgb(59, 130, 246);
|
|
773
|
+
}
|
|
774
|
+
50% {
|
|
775
|
+
border-color: rgb(147, 197, 253);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
.animate-pulse-border {
|
|
779
|
+
animation: pulse-border 2s ease-in-out infinite;
|
|
780
|
+
}
|
|
781
|
+
`
|
|
782
|
+
} })
|
|
783
|
+
]
|
|
784
|
+
}
|
|
785
|
+
);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// src/components/Prompt/Prompt.tsx
|
|
789
|
+
import { useState as useState4 } from "react";
|
|
790
|
+
import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
791
|
+
function Prompt({
|
|
792
|
+
agentId,
|
|
793
|
+
placeholder = "Enter your prompt...",
|
|
794
|
+
initialValue = "",
|
|
795
|
+
submitOn = "button",
|
|
796
|
+
debounceMs = 0,
|
|
797
|
+
minLength = 0,
|
|
798
|
+
maxLength,
|
|
799
|
+
onSubmit,
|
|
800
|
+
onResult,
|
|
801
|
+
onChange,
|
|
802
|
+
variant = "inline",
|
|
803
|
+
showSuggestions = false,
|
|
804
|
+
className
|
|
805
|
+
}) {
|
|
806
|
+
const [value, setValue] = useState4(initialValue);
|
|
807
|
+
const [isLoading, setIsLoading] = useState4(false);
|
|
808
|
+
const [suggestions] = useState4(["Plan a trip", "Write a description", "Analyze data"]);
|
|
809
|
+
const handleChange = (e) => {
|
|
810
|
+
const newValue = e.target.value;
|
|
811
|
+
if (!maxLength || newValue.length <= maxLength) {
|
|
812
|
+
setValue(newValue);
|
|
813
|
+
onChange?.(newValue);
|
|
814
|
+
}
|
|
815
|
+
};
|
|
816
|
+
const handleSubmit = async () => {
|
|
817
|
+
if (value.length < minLength) return;
|
|
818
|
+
onSubmit?.(value);
|
|
819
|
+
setIsLoading(true);
|
|
820
|
+
try {
|
|
821
|
+
await new Promise((resolve) => setTimeout(resolve, 1500));
|
|
822
|
+
const mockResult = `Enhanced version: ${value} [AI-generated content]`;
|
|
823
|
+
onResult?.(mockResult);
|
|
824
|
+
setValue("");
|
|
825
|
+
} catch (error) {
|
|
826
|
+
console.error("Error processing prompt:", error);
|
|
827
|
+
} finally {
|
|
828
|
+
setIsLoading(false);
|
|
829
|
+
}
|
|
830
|
+
};
|
|
831
|
+
const handleKeyDown = (e) => {
|
|
832
|
+
if (submitOn === "enter" && e.key === "Enter" && !e.shiftKey) {
|
|
833
|
+
e.preventDefault();
|
|
834
|
+
handleSubmit();
|
|
835
|
+
}
|
|
836
|
+
};
|
|
837
|
+
const handleBlur = () => {
|
|
838
|
+
if (submitOn === "blur" && value.trim()) {
|
|
839
|
+
handleSubmit();
|
|
840
|
+
}
|
|
841
|
+
};
|
|
842
|
+
return /* @__PURE__ */ jsxs9("div", { className: cn("space-y-2", className), children: [
|
|
843
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex gap-2", children: [
|
|
844
|
+
/* @__PURE__ */ jsx11(
|
|
845
|
+
"input",
|
|
846
|
+
{
|
|
847
|
+
type: "text",
|
|
848
|
+
value,
|
|
849
|
+
onChange: handleChange,
|
|
850
|
+
onKeyDown: handleKeyDown,
|
|
851
|
+
onBlur: handleBlur,
|
|
852
|
+
placeholder,
|
|
853
|
+
disabled: isLoading,
|
|
854
|
+
className: "flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-apteva-500 dark:bg-gray-800 dark:border-gray-600 dark:text-white"
|
|
855
|
+
}
|
|
856
|
+
),
|
|
857
|
+
submitOn === "button" && /* @__PURE__ */ jsx11(
|
|
858
|
+
"button",
|
|
859
|
+
{
|
|
860
|
+
onClick: handleSubmit,
|
|
861
|
+
disabled: isLoading || value.length < minLength,
|
|
862
|
+
className: "px-6 py-2 bg-apteva-500 text-white rounded-lg hover:bg-apteva-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors font-medium",
|
|
863
|
+
children: isLoading ? "Processing..." : "Generate"
|
|
864
|
+
}
|
|
865
|
+
)
|
|
866
|
+
] }),
|
|
867
|
+
maxLength && /* @__PURE__ */ jsxs9("p", { className: "text-xs text-gray-500", children: [
|
|
868
|
+
value.length,
|
|
869
|
+
" / ",
|
|
870
|
+
maxLength,
|
|
871
|
+
" characters"
|
|
872
|
+
] }),
|
|
873
|
+
showSuggestions && !value && /* @__PURE__ */ jsx11("div", { className: "flex flex-wrap gap-2", children: suggestions.map((suggestion, idx) => /* @__PURE__ */ jsx11(
|
|
874
|
+
"button",
|
|
875
|
+
{
|
|
876
|
+
onClick: () => setValue(suggestion),
|
|
877
|
+
className: "px-3 py-1 text-sm bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-full transition-colors",
|
|
878
|
+
children: suggestion
|
|
879
|
+
},
|
|
880
|
+
idx
|
|
881
|
+
)) }),
|
|
882
|
+
isLoading && /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2 text-sm text-gray-500", children: [
|
|
883
|
+
/* @__PURE__ */ jsx11("div", { className: "w-4 h-4 border-2 border-apteva-500 border-t-transparent rounded-full animate-spin" }),
|
|
884
|
+
/* @__PURE__ */ jsx11("span", { children: "AI is processing your request..." })
|
|
885
|
+
] })
|
|
886
|
+
] });
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
// src/components/Stream/Stream.tsx
|
|
890
|
+
import { useState as useState5, useEffect as useEffect5 } from "react";
|
|
891
|
+
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
892
|
+
function Stream({
|
|
893
|
+
agentId,
|
|
894
|
+
prompt,
|
|
895
|
+
context,
|
|
896
|
+
autoStart = false,
|
|
897
|
+
onStart,
|
|
898
|
+
onChunk,
|
|
899
|
+
onComplete,
|
|
900
|
+
onError,
|
|
901
|
+
variant = "prose",
|
|
902
|
+
showCursor = true,
|
|
903
|
+
typingSpeed = 30,
|
|
904
|
+
className
|
|
905
|
+
}) {
|
|
906
|
+
const [text, setText] = useState5("");
|
|
907
|
+
const [isStreaming, setIsStreaming] = useState5(false);
|
|
908
|
+
const [isComplete, setIsComplete] = useState5(false);
|
|
909
|
+
useEffect5(() => {
|
|
910
|
+
if (autoStart && !isStreaming && !isComplete) {
|
|
911
|
+
startStreaming();
|
|
912
|
+
}
|
|
913
|
+
}, [autoStart]);
|
|
914
|
+
const startStreaming = async () => {
|
|
915
|
+
setIsStreaming(true);
|
|
916
|
+
onStart?.();
|
|
917
|
+
const mockText = "This is a simulated streaming response from the AI agent. In a real implementation, this would stream data from your backend API. The text appears word by word to simulate the streaming effect. You can customize the typing speed and styling based on your needs.";
|
|
918
|
+
try {
|
|
919
|
+
await generateMockStreamingResponse(
|
|
920
|
+
mockText,
|
|
921
|
+
(chunk) => {
|
|
922
|
+
setText((prev) => prev + chunk);
|
|
923
|
+
onChunk?.(chunk);
|
|
924
|
+
},
|
|
925
|
+
typingSpeed
|
|
926
|
+
);
|
|
927
|
+
setIsComplete(true);
|
|
928
|
+
setIsStreaming(false);
|
|
929
|
+
onComplete?.(text + mockText);
|
|
930
|
+
} catch (error) {
|
|
931
|
+
const err = error instanceof Error ? error : new Error("Streaming error");
|
|
932
|
+
onError?.(err);
|
|
933
|
+
setIsStreaming(false);
|
|
934
|
+
}
|
|
935
|
+
};
|
|
936
|
+
const variantClasses = {
|
|
937
|
+
prose: "prose prose-sm max-w-none dark:prose-invert",
|
|
938
|
+
code: "font-mono text-sm bg-gray-900 text-green-400 p-4 rounded-lg",
|
|
939
|
+
plain: "text-gray-900 dark:text-gray-100"
|
|
940
|
+
};
|
|
941
|
+
if (!isStreaming && !isComplete) {
|
|
942
|
+
return /* @__PURE__ */ jsx12("div", { className: cn("p-4", className), children: /* @__PURE__ */ jsx12(
|
|
943
|
+
"button",
|
|
944
|
+
{
|
|
945
|
+
onClick: startStreaming,
|
|
946
|
+
className: "px-6 py-3 bg-apteva-500 text-white rounded-lg hover:bg-apteva-600 transition-colors font-medium",
|
|
947
|
+
children: "Start Streaming"
|
|
948
|
+
}
|
|
949
|
+
) });
|
|
950
|
+
}
|
|
951
|
+
return /* @__PURE__ */ jsxs10("div", { className: cn(variantClasses[variant], className), children: [
|
|
952
|
+
text,
|
|
953
|
+
isStreaming && showCursor && /* @__PURE__ */ jsx12("span", { className: "apteva-stream-cursor" })
|
|
954
|
+
] });
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
// src/components/Threads/ThreadList.tsx
|
|
958
|
+
import { useState as useState6 } from "react";
|
|
959
|
+
|
|
960
|
+
// src/components/Threads/ThreadItem.tsx
|
|
961
|
+
import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
962
|
+
function ThreadItem({ thread, isActive = false, onSelect, onDelete }) {
|
|
963
|
+
return /* @__PURE__ */ jsxs11(
|
|
964
|
+
"div",
|
|
965
|
+
{
|
|
966
|
+
className: cn("apteva-thread-item", {
|
|
967
|
+
"apteva-thread-item-active": isActive
|
|
968
|
+
}),
|
|
969
|
+
onClick: onSelect,
|
|
970
|
+
children: [
|
|
971
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex-1 min-w-0", children: [
|
|
972
|
+
/* @__PURE__ */ jsx13("h4", { className: "font-semibold text-gray-900 dark:text-white truncate", children: thread.title }),
|
|
973
|
+
thread.preview && /* @__PURE__ */ jsx13("p", { className: "text-sm text-gray-600 dark:text-gray-400 truncate", children: thread.preview }),
|
|
974
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2 mt-1 text-xs text-gray-500", children: [
|
|
975
|
+
/* @__PURE__ */ jsxs11("span", { children: [
|
|
976
|
+
thread.messageCount,
|
|
977
|
+
" messages"
|
|
978
|
+
] }),
|
|
979
|
+
/* @__PURE__ */ jsx13("span", { children: "\u2022" }),
|
|
980
|
+
/* @__PURE__ */ jsx13("span", { children: formatRelativeTime(thread.updatedAt) })
|
|
981
|
+
] })
|
|
982
|
+
] }),
|
|
983
|
+
onDelete && /* @__PURE__ */ jsx13(
|
|
984
|
+
"button",
|
|
985
|
+
{
|
|
986
|
+
onClick: (e) => {
|
|
987
|
+
e.stopPropagation();
|
|
988
|
+
onDelete();
|
|
989
|
+
},
|
|
990
|
+
className: "p-2 text-gray-400 hover:text-red-500 hover:bg-red-50 rounded transition-colors",
|
|
991
|
+
title: "Delete thread",
|
|
992
|
+
children: "\u{1F5D1}\uFE0F"
|
|
993
|
+
}
|
|
994
|
+
)
|
|
995
|
+
]
|
|
996
|
+
}
|
|
997
|
+
);
|
|
998
|
+
}
|
|
999
|
+
function formatRelativeTime(date) {
|
|
1000
|
+
const now = /* @__PURE__ */ new Date();
|
|
1001
|
+
const diff = now.getTime() - date.getTime();
|
|
1002
|
+
const minutes = Math.floor(diff / 6e4);
|
|
1003
|
+
const hours = Math.floor(diff / 36e5);
|
|
1004
|
+
const days = Math.floor(diff / 864e5);
|
|
1005
|
+
if (minutes < 1) return "Just now";
|
|
1006
|
+
if (minutes < 60) return `${minutes}m ago`;
|
|
1007
|
+
if (hours < 24) return `${hours}h ago`;
|
|
1008
|
+
if (days < 7) return `${days}d ago`;
|
|
1009
|
+
return date.toLocaleDateString();
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
// src/components/Threads/ThreadList.tsx
|
|
1013
|
+
import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1014
|
+
function ThreadList({
|
|
1015
|
+
threads,
|
|
1016
|
+
currentThreadId,
|
|
1017
|
+
onThreadSelect,
|
|
1018
|
+
onThreadDelete,
|
|
1019
|
+
showSearch = false,
|
|
1020
|
+
groupBy = "none"
|
|
1021
|
+
}) {
|
|
1022
|
+
const [searchQuery, setSearchQuery] = useState6("");
|
|
1023
|
+
const filteredThreads = threads.filter(
|
|
1024
|
+
(thread) => thread.title.toLowerCase().includes(searchQuery.toLowerCase()) || thread.preview?.toLowerCase().includes(searchQuery.toLowerCase())
|
|
1025
|
+
);
|
|
1026
|
+
const groupedThreads = groupBy === "date" ? groupThreadsByDate(filteredThreads) : { All: filteredThreads };
|
|
1027
|
+
return /* @__PURE__ */ jsxs12("div", { className: "flex flex-col h-full", children: [
|
|
1028
|
+
showSearch && /* @__PURE__ */ jsx14("div", { className: "p-3 border-b border-gray-200 dark:border-gray-700", children: /* @__PURE__ */ jsx14(
|
|
1029
|
+
"input",
|
|
1030
|
+
{
|
|
1031
|
+
type: "text",
|
|
1032
|
+
placeholder: "Search conversations...",
|
|
1033
|
+
value: searchQuery,
|
|
1034
|
+
onChange: (e) => setSearchQuery(e.target.value),
|
|
1035
|
+
className: "w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-apteva-500 dark:bg-gray-800 dark:border-gray-600 dark:text-white"
|
|
1036
|
+
}
|
|
1037
|
+
) }),
|
|
1038
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex-1 overflow-y-auto", children: [
|
|
1039
|
+
Object.entries(groupedThreads).map(([group, groupThreads]) => /* @__PURE__ */ jsxs12("div", { children: [
|
|
1040
|
+
groupBy !== "none" && /* @__PURE__ */ jsx14("div", { className: "px-3 py-2 text-xs font-semibold text-gray-500 uppercase", children: group }),
|
|
1041
|
+
groupThreads.map((thread) => /* @__PURE__ */ jsx14(
|
|
1042
|
+
ThreadItem,
|
|
1043
|
+
{
|
|
1044
|
+
thread,
|
|
1045
|
+
isActive: thread.id === currentThreadId,
|
|
1046
|
+
onSelect: () => onThreadSelect?.(thread.id),
|
|
1047
|
+
onDelete: () => onThreadDelete?.(thread.id)
|
|
1048
|
+
},
|
|
1049
|
+
thread.id
|
|
1050
|
+
))
|
|
1051
|
+
] }, group)),
|
|
1052
|
+
filteredThreads.length === 0 && /* @__PURE__ */ jsxs12("div", { className: "p-8 text-center text-gray-500", children: [
|
|
1053
|
+
/* @__PURE__ */ jsx14("div", { className: "text-4xl mb-2", children: "\u{1F4AC}" }),
|
|
1054
|
+
/* @__PURE__ */ jsx14("p", { children: "No conversations found" })
|
|
1055
|
+
] })
|
|
1056
|
+
] })
|
|
1057
|
+
] });
|
|
1058
|
+
}
|
|
1059
|
+
function groupThreadsByDate(threads) {
|
|
1060
|
+
const now = /* @__PURE__ */ new Date();
|
|
1061
|
+
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
1062
|
+
const yesterday = new Date(today);
|
|
1063
|
+
yesterday.setDate(yesterday.getDate() - 1);
|
|
1064
|
+
const lastWeek = new Date(today);
|
|
1065
|
+
lastWeek.setDate(lastWeek.getDate() - 7);
|
|
1066
|
+
return threads.reduce(
|
|
1067
|
+
(groups, thread) => {
|
|
1068
|
+
const threadDate = new Date(thread.updatedAt);
|
|
1069
|
+
let group = "Older";
|
|
1070
|
+
if (threadDate >= today) {
|
|
1071
|
+
group = "Today";
|
|
1072
|
+
} else if (threadDate >= yesterday) {
|
|
1073
|
+
group = "Yesterday";
|
|
1074
|
+
} else if (threadDate >= lastWeek) {
|
|
1075
|
+
group = "Last 7 Days";
|
|
1076
|
+
}
|
|
1077
|
+
if (!groups[group]) groups[group] = [];
|
|
1078
|
+
groups[group].push(thread);
|
|
1079
|
+
return groups;
|
|
1080
|
+
},
|
|
1081
|
+
{}
|
|
1082
|
+
);
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
// src/components/Threads/Threads.tsx
|
|
1086
|
+
import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1087
|
+
function Threads({
|
|
1088
|
+
threads,
|
|
1089
|
+
currentThreadId,
|
|
1090
|
+
onThreadSelect,
|
|
1091
|
+
onThreadDelete,
|
|
1092
|
+
onNewThread,
|
|
1093
|
+
variant = "sidebar",
|
|
1094
|
+
showSearch = false,
|
|
1095
|
+
showNewButton = true,
|
|
1096
|
+
groupBy = "none",
|
|
1097
|
+
className
|
|
1098
|
+
}) {
|
|
1099
|
+
const variantClasses = {
|
|
1100
|
+
sidebar: "h-full border-r border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900",
|
|
1101
|
+
dropdown: "absolute top-full left-0 right-0 mt-2 bg-white dark:bg-gray-800 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 max-h-96 overflow-hidden",
|
|
1102
|
+
tabs: "flex gap-2 border-b border-gray-200 dark:border-gray-700 overflow-x-auto"
|
|
1103
|
+
};
|
|
1104
|
+
if (variant === "tabs") {
|
|
1105
|
+
return /* @__PURE__ */ jsxs13("div", { className: cn(variantClasses[variant], className), children: [
|
|
1106
|
+
threads.slice(0, 5).map((thread) => /* @__PURE__ */ jsx15(
|
|
1107
|
+
"button",
|
|
1108
|
+
{
|
|
1109
|
+
onClick: () => onThreadSelect?.(thread.id),
|
|
1110
|
+
className: cn(
|
|
1111
|
+
"px-4 py-2 whitespace-nowrap font-medium transition-colors",
|
|
1112
|
+
thread.id === currentThreadId ? "border-b-2 border-apteva-500 text-apteva-500" : "text-gray-600 hover:text-gray-900"
|
|
1113
|
+
),
|
|
1114
|
+
children: thread.title
|
|
1115
|
+
},
|
|
1116
|
+
thread.id
|
|
1117
|
+
)),
|
|
1118
|
+
showNewButton && onNewThread && /* @__PURE__ */ jsx15(
|
|
1119
|
+
"button",
|
|
1120
|
+
{
|
|
1121
|
+
onClick: onNewThread,
|
|
1122
|
+
className: "px-4 py-2 text-gray-600 hover:text-apteva-500 transition-colors font-medium",
|
|
1123
|
+
children: "+ New"
|
|
1124
|
+
}
|
|
1125
|
+
)
|
|
1126
|
+
] });
|
|
1127
|
+
}
|
|
1128
|
+
return /* @__PURE__ */ jsxs13("div", { className: cn(variantClasses[variant], "flex flex-col", className), children: [
|
|
1129
|
+
showNewButton && onNewThread && /* @__PURE__ */ jsx15("div", { className: "p-3 border-b border-gray-200 dark:border-gray-700", children: /* @__PURE__ */ jsx15(
|
|
1130
|
+
"button",
|
|
1131
|
+
{
|
|
1132
|
+
onClick: onNewThread,
|
|
1133
|
+
className: "w-full px-4 py-2 bg-apteva-500 text-white rounded-lg hover:bg-apteva-600 transition-colors font-medium",
|
|
1134
|
+
children: "+ New Conversation"
|
|
1135
|
+
}
|
|
1136
|
+
) }),
|
|
1137
|
+
/* @__PURE__ */ jsx15(
|
|
1138
|
+
ThreadList,
|
|
1139
|
+
{
|
|
1140
|
+
threads,
|
|
1141
|
+
currentThreadId,
|
|
1142
|
+
onThreadSelect,
|
|
1143
|
+
onThreadDelete,
|
|
1144
|
+
showSearch,
|
|
1145
|
+
groupBy
|
|
1146
|
+
}
|
|
1147
|
+
)
|
|
1148
|
+
] });
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
// src/utils/theme-script.ts
|
|
1152
|
+
var themeScript = `
|
|
1153
|
+
(function() {
|
|
1154
|
+
try {
|
|
1155
|
+
// Get system preference
|
|
1156
|
+
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
1157
|
+
const colorMode = isDark ? 'dark' : 'light';
|
|
1158
|
+
|
|
1159
|
+
// Set attributes before render
|
|
1160
|
+
document.documentElement.setAttribute('data-color-mode', colorMode);
|
|
1161
|
+
|
|
1162
|
+
// Add dark class for Tailwind
|
|
1163
|
+
if (isDark) {
|
|
1164
|
+
document.documentElement.classList.add('dark');
|
|
1165
|
+
}
|
|
1166
|
+
} catch (e) {
|
|
1167
|
+
console.error('Failed to initialize theme:', e);
|
|
1168
|
+
}
|
|
1169
|
+
})();
|
|
1170
|
+
`;
|
|
1171
|
+
function getThemeScript() {
|
|
1172
|
+
return themeScript;
|
|
1173
|
+
}
|
|
1174
|
+
export {
|
|
1175
|
+
Button,
|
|
1176
|
+
Card,
|
|
1177
|
+
Chat,
|
|
1178
|
+
Command,
|
|
1179
|
+
List,
|
|
1180
|
+
Prompt,
|
|
1181
|
+
Stream,
|
|
1182
|
+
Threads,
|
|
1183
|
+
Widgets,
|
|
1184
|
+
cn,
|
|
1185
|
+
getThemeScript,
|
|
1186
|
+
mockMessages,
|
|
1187
|
+
mockThreads,
|
|
1188
|
+
mockWidgets
|
|
1189
|
+
};
|
|
1190
|
+
//# sourceMappingURL=index.mjs.map
|