@burdenoff/microfe-botmanor 2026.821.1 → 2026.925.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/botlit/BotlitRoutes.js.map +1 -1
- package/dist/botlit/BotmanorRoot.js.map +1 -1
- package/dist/botlit/BotmanorRootInner.js +4 -4
- package/dist/botlit/BotmanorRootInner.js.map +1 -1
- package/dist/botlit/components/ErrorState.js +33 -29
- package/dist/botlit/components/ErrorState.js.map +1 -1
- package/dist/botlit/components/RouteLoader.js +52 -48
- package/dist/botlit/components/RouteLoader.js.map +1 -1
- package/dist/botlit/components/VersionHistoryPanel.js +74 -77
- package/dist/botlit/components/VersionHistoryPanel.js.map +1 -1
- package/dist/botlit/components/WorkspaceRequired.js +31 -30
- package/dist/botlit/components/WorkspaceRequired.js.map +1 -1
- package/dist/botlit/hooks/internal/workspaceGateway.js.map +1 -1
- package/dist/botlit/hooks/useConversations.js.map +1 -1
- package/dist/botlit/hooks/useLLMProviders.js.map +1 -1
- package/dist/botlit/pages/A2AAgentDetailPage.js +1 -1
- package/dist/botlit/pages/A2AAgentDetailPage.js.map +1 -1
- package/dist/botlit/pages/A2ARegistryPage.js +116 -124
- package/dist/botlit/pages/A2ARegistryPage.js.map +1 -1
- package/dist/botlit/pages/AgentDetailPage.js +169 -172
- package/dist/botlit/pages/AgentDetailPage.js.map +1 -1
- package/dist/botlit/pages/AgentsListPage.js +96 -90
- package/dist/botlit/pages/AgentsListPage.js.map +1 -1
- package/dist/botlit/pages/BotChannelsTab.js.map +1 -1
- package/dist/botlit/pages/BotDetailPage.js +228 -231
- package/dist/botlit/pages/BotDetailPage.js.map +1 -1
- package/dist/botlit/pages/BotWorkflowTab.js +84 -87
- package/dist/botlit/pages/BotWorkflowTab.js.map +1 -1
- package/dist/botlit/pages/BotsListPage.js +144 -147
- package/dist/botlit/pages/BotsListPage.js.map +1 -1
- package/dist/botlit/pages/BucketsPage.js +3 -3
- package/dist/botlit/pages/BucketsPage.js.map +1 -1
- package/dist/botlit/pages/ChannelDetailPage.js +1 -1
- package/dist/botlit/pages/ChannelDetailPage.js.map +1 -1
- package/dist/botlit/pages/ChannelsPage.js +156 -149
- package/dist/botlit/pages/ChannelsPage.js.map +1 -1
- package/dist/botlit/pages/ChatPage.js +126 -129
- package/dist/botlit/pages/ChatPage.js.map +1 -1
- package/dist/botlit/pages/DashboardPage.js +111 -114
- package/dist/botlit/pages/DashboardPage.js.map +1 -1
- package/dist/botlit/pages/ExecutionsPage.js +163 -159
- package/dist/botlit/pages/ExecutionsPage.js.map +1 -1
- package/dist/botlit/pages/InstalledPackagesPage.js +3 -3
- package/dist/botlit/pages/InstalledPackagesPage.js.map +1 -1
- package/dist/botlit/pages/KnowledgeBasesPage.js +84 -78
- package/dist/botlit/pages/KnowledgeBasesPage.js.map +1 -1
- package/dist/botlit/pages/MCPServersPage.js +159 -154
- package/dist/botlit/pages/MCPServersPage.js.map +1 -1
- package/dist/botlit/pages/PoliciesPage.js +119 -114
- package/dist/botlit/pages/PoliciesPage.js.map +1 -1
- package/dist/botlit/pages/RetrievalProfilesPage.js +102 -109
- package/dist/botlit/pages/RetrievalProfilesPage.js.map +1 -1
- package/dist/botlit/pages/SettingsPage.js +144 -159
- package/dist/botlit/pages/SettingsPage.js.map +1 -1
- package/dist/botlit/pages/SkillsPage.js +122 -105
- package/dist/botlit/pages/SkillsPage.js.map +1 -1
- package/dist/botlit/pages/StorePage.js +176 -175
- package/dist/botlit/pages/StorePage.js.map +1 -1
- package/dist/botlit/providers/BotlitProvider.js.map +1 -1
- package/dist/botlit/store/botlitStore.js +1 -1
- package/dist/botlit/store/botlitStore.js.map +1 -1
- package/dist/botlit/utils/navigation.js.map +1 -1
- package/dist/shared/hooks/useTr.js +14 -0
- package/dist/shared/hooks/useTr.js.map +1 -0
- package/package.json +33 -9
|
@@ -1,55 +1,52 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
1
|
+
import { useTr as e } from "../../shared/hooks/useTr.js";
|
|
2
|
+
import { useBotlit as t } from "../providers/BotlitProvider.js";
|
|
3
|
+
import { useAgents as n } from "../hooks/useAgents.js";
|
|
4
|
+
import { useSendMessage as r } from "../hooks/useConversations.js";
|
|
5
|
+
import { cn as i } from "../utils/cn.js";
|
|
6
|
+
import { PageLayout as a } from "../components/PageLayout.js";
|
|
7
|
+
import { Card as o, CardContent as s } from "../components/Card.js";
|
|
8
|
+
import { Button as c } from "../components/Button.js";
|
|
9
|
+
import { Badge as l } from "../components/Badge.js";
|
|
10
|
+
import { Textarea as u } from "../components/Input.js";
|
|
11
|
+
import { QuotaExhaustedDialog as d } from "../components/QuotaExhaustedDialog.js";
|
|
12
|
+
import { useEffect as f, useRef as p, useState as m } from "react";
|
|
13
|
+
import { IllustratedEmptyState as h, PagePurpose as g } from "@burdenoff/fe-libs/ui";
|
|
14
|
+
import { jsx as _, jsxs as v } from "react/jsx-runtime";
|
|
15
|
+
import { useNavigate as y, useParams as b } from "react-router";
|
|
16
|
+
import { isQuotaExhaustedError as x } from "@burdenoff/fe-libs/shared/utils";
|
|
17
|
+
import { ArrowLeft as S, Bot as C, Loader2 as w, RefreshCw as T, Send as E, Settings as D, User as O } from "lucide-react";
|
|
18
18
|
//#region src/botlit/pages/ChatPage.tsx
|
|
19
19
|
var k = () => {
|
|
20
|
-
let { t:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}, { agentId: j } = y(), M = v(), { navigate: N } = e(), { data: P } = t(), { sendMessage: F, isSending: I } = n(), [L, R] = p(j || ""), [z, B] = p(""), [V, H] = p([]), [U, W] = p(), [G, K] = p(!1), q = f(null), J = () => {
|
|
24
|
-
if (N) {
|
|
25
|
-
N("/billing");
|
|
20
|
+
let k = e(), { agentId: A } = b(), j = y(), { navigate: M } = t(), { data: N } = n(), { sendMessage: P, isSending: F } = r(), [I, L] = m(A || ""), [R, z] = m(""), [B, V] = m([]), [H, U] = m(), [W, G] = m(!1), K = p(null), q = () => {
|
|
21
|
+
if (M) {
|
|
22
|
+
M("/billing");
|
|
26
23
|
return;
|
|
27
24
|
}
|
|
28
25
|
window.location.assign("/billing");
|
|
29
|
-
},
|
|
30
|
-
|
|
26
|
+
}, J = () => {
|
|
27
|
+
K.current?.scrollIntoView({ behavior: "smooth" });
|
|
31
28
|
};
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}, [
|
|
35
|
-
|
|
36
|
-
}, [
|
|
37
|
-
let
|
|
38
|
-
if (!
|
|
29
|
+
f(() => {
|
|
30
|
+
J();
|
|
31
|
+
}, [B]), f(() => {
|
|
32
|
+
A && L(A);
|
|
33
|
+
}, [A]);
|
|
34
|
+
let Y = async () => {
|
|
35
|
+
if (!R.trim() || !I) return;
|
|
39
36
|
let e = {
|
|
40
37
|
id: `user-${Date.now()}`,
|
|
41
38
|
role: "user",
|
|
42
|
-
content:
|
|
39
|
+
content: R,
|
|
43
40
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
44
41
|
};
|
|
45
|
-
|
|
42
|
+
V((t) => [...t, e]), z("");
|
|
46
43
|
try {
|
|
47
|
-
let e = await
|
|
48
|
-
conversationId:
|
|
49
|
-
agentId:
|
|
50
|
-
content:
|
|
44
|
+
let e = await P({
|
|
45
|
+
conversationId: H,
|
|
46
|
+
agentId: I,
|
|
47
|
+
content: R
|
|
51
48
|
});
|
|
52
|
-
e.conversationId &&
|
|
49
|
+
e.conversationId && U(e.conversationId);
|
|
53
50
|
let t = {
|
|
54
51
|
id: `assistant-${e.executionId}`,
|
|
55
52
|
role: "assistant",
|
|
@@ -58,82 +55,82 @@ var k = () => {
|
|
|
58
55
|
model: e.model ?? void 0,
|
|
59
56
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
60
57
|
};
|
|
61
|
-
|
|
58
|
+
V((e) => [...e, t]);
|
|
62
59
|
} catch (e) {
|
|
63
|
-
if (
|
|
64
|
-
|
|
60
|
+
if (x(e)) {
|
|
61
|
+
G(!0);
|
|
65
62
|
return;
|
|
66
63
|
}
|
|
67
64
|
let t = {
|
|
68
65
|
id: `error-${Date.now()}`,
|
|
69
66
|
role: "assistant",
|
|
70
|
-
content: `${
|
|
67
|
+
content: `${k("botlit.chat.send.errorPrefix", "Error")}: ${e instanceof Error ? e.message : k("botlit.chat.send.errorFallback", "Failed to send message")}`,
|
|
71
68
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
72
69
|
};
|
|
73
|
-
|
|
70
|
+
V((e) => [...e, t]), console.error("Failed to send message:", e);
|
|
74
71
|
}
|
|
75
|
-
},
|
|
76
|
-
e.key === "Enter" && !e.shiftKey && (e.preventDefault(),
|
|
77
|
-
},
|
|
78
|
-
|
|
79
|
-
},
|
|
80
|
-
return /* @__PURE__ */
|
|
81
|
-
title: /* @__PURE__ */
|
|
72
|
+
}, X = (e) => {
|
|
73
|
+
e.key === "Enter" && !e.shiftKey && (e.preventDefault(), Y());
|
|
74
|
+
}, Z = () => {
|
|
75
|
+
V([]), U(void 0);
|
|
76
|
+
}, Q = N?.find((e) => e.id === I);
|
|
77
|
+
return /* @__PURE__ */ _(a, {
|
|
78
|
+
title: /* @__PURE__ */ v("div", {
|
|
82
79
|
className: "flex items-center gap-3",
|
|
83
|
-
children: [/* @__PURE__ */
|
|
80
|
+
children: [/* @__PURE__ */ _(c, {
|
|
84
81
|
variant: "ghost",
|
|
85
82
|
size: "icon",
|
|
86
|
-
onClick: () =>
|
|
87
|
-
children: /* @__PURE__ */
|
|
88
|
-
}),
|
|
89
|
-
|
|
83
|
+
onClick: () => j(-1),
|
|
84
|
+
children: /* @__PURE__ */ _(S, { size: 20 })
|
|
85
|
+
}), Q ? /* @__PURE__ */ v("span", { children: [
|
|
86
|
+
k("botlit.chat.title.withAgent", "Chat with"),
|
|
90
87
|
" ",
|
|
91
|
-
|
|
92
|
-
] }) : /* @__PURE__ */
|
|
88
|
+
Q.name
|
|
89
|
+
] }) : /* @__PURE__ */ _("span", { children: k("botlit.chat.title.default", "AI Chat") })]
|
|
93
90
|
}),
|
|
94
|
-
actions: /* @__PURE__ */
|
|
91
|
+
actions: /* @__PURE__ */ v("div", {
|
|
95
92
|
className: "flex gap-2",
|
|
96
|
-
children: [/* @__PURE__ */
|
|
93
|
+
children: [/* @__PURE__ */ _(c, {
|
|
97
94
|
variant: "outline",
|
|
98
95
|
size: "sm",
|
|
99
|
-
leftIcon: /* @__PURE__ */
|
|
100
|
-
onClick:
|
|
101
|
-
children:
|
|
102
|
-
}),
|
|
96
|
+
leftIcon: /* @__PURE__ */ _(T, { size: 14 }),
|
|
97
|
+
onClick: Z,
|
|
98
|
+
children: k("botlit.chat.actions.newChat", "New Chat")
|
|
99
|
+
}), I && /* @__PURE__ */ _(c, {
|
|
103
100
|
variant: "outline",
|
|
104
101
|
size: "sm",
|
|
105
|
-
leftIcon: /* @__PURE__ */
|
|
106
|
-
children: /* @__PURE__ */
|
|
107
|
-
href: `/agents/${
|
|
108
|
-
children:
|
|
102
|
+
leftIcon: /* @__PURE__ */ _(D, { size: 14 }),
|
|
103
|
+
children: /* @__PURE__ */ _("a", {
|
|
104
|
+
href: `/agents/${I}`,
|
|
105
|
+
children: k("botlit.chat.actions.agentSettings", "Agent Settings")
|
|
109
106
|
})
|
|
110
107
|
})]
|
|
111
108
|
}),
|
|
112
|
-
children: /* @__PURE__ */
|
|
109
|
+
children: /* @__PURE__ */ v("div", {
|
|
113
110
|
className: "flex flex-col h-[calc(100vh-220px)] max-w-4xl mx-auto",
|
|
114
111
|
children: [
|
|
115
|
-
/* @__PURE__ */ g
|
|
112
|
+
/* @__PURE__ */ _(g, {
|
|
116
113
|
className: "mb-4",
|
|
117
|
-
icon: /* @__PURE__ */
|
|
118
|
-
children:
|
|
114
|
+
icon: /* @__PURE__ */ _(C, { size: 18 }),
|
|
115
|
+
children: k("botlit.chat.purpose", "This is a live playground for talking to your agents. Pick an agent and chat with it exactly as a real user would — to sanity-check its answers, tone and tool use before you connect it to a bot or channel. Conversations here are throwaway tests, so experiment freely.")
|
|
119
116
|
}),
|
|
120
|
-
!
|
|
117
|
+
!A && /* @__PURE__ */ _(o, {
|
|
121
118
|
className: "mb-4",
|
|
122
|
-
children: /* @__PURE__ */
|
|
119
|
+
children: /* @__PURE__ */ _(s, {
|
|
123
120
|
className: "py-4",
|
|
124
|
-
children: /* @__PURE__ */
|
|
121
|
+
children: /* @__PURE__ */ v("div", {
|
|
125
122
|
className: "flex items-center gap-4",
|
|
126
|
-
children: [/* @__PURE__ */
|
|
123
|
+
children: [/* @__PURE__ */ _("label", {
|
|
127
124
|
className: "text-sm font-medium",
|
|
128
|
-
children:
|
|
129
|
-
}), /* @__PURE__ */
|
|
125
|
+
children: k("botlit.chat.selector.label", "Select Agent:")
|
|
126
|
+
}), /* @__PURE__ */ v("select", {
|
|
130
127
|
className: "flex h-10 flex-1 max-w-md rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm",
|
|
131
|
-
value:
|
|
132
|
-
onChange: (e) =>
|
|
133
|
-
children: [/* @__PURE__ */
|
|
128
|
+
value: I,
|
|
129
|
+
onChange: (e) => L(e.target.value),
|
|
130
|
+
children: [/* @__PURE__ */ _("option", {
|
|
134
131
|
value: "",
|
|
135
|
-
children:
|
|
136
|
-
}),
|
|
132
|
+
children: k("botlit.chat.selector.placeholder", "Choose an agent...")
|
|
133
|
+
}), N?.filter((e) => e.status === "ACTIVE").map((e) => /* @__PURE__ */ _("option", {
|
|
137
134
|
value: e.id,
|
|
138
135
|
children: e.name
|
|
139
136
|
}, e.id))]
|
|
@@ -141,101 +138,101 @@ var k = () => {
|
|
|
141
138
|
})
|
|
142
139
|
})
|
|
143
140
|
}),
|
|
144
|
-
/* @__PURE__ */
|
|
141
|
+
/* @__PURE__ */ _(o, {
|
|
145
142
|
treatment: "glass",
|
|
146
143
|
glow: !0,
|
|
147
144
|
className: "flex-1 overflow-hidden",
|
|
148
|
-
children: /* @__PURE__ */
|
|
145
|
+
children: /* @__PURE__ */ v(s, {
|
|
149
146
|
className: "h-full flex flex-col p-0",
|
|
150
|
-
children: [/* @__PURE__ */
|
|
147
|
+
children: [/* @__PURE__ */ v("div", {
|
|
151
148
|
className: "flex-1 overflow-y-auto p-4 space-y-4",
|
|
152
149
|
children: [
|
|
153
|
-
|
|
150
|
+
B.length === 0 ? /* @__PURE__ */ _("div", {
|
|
154
151
|
className: "flex h-full items-center justify-center",
|
|
155
|
-
children: /* @__PURE__ */
|
|
152
|
+
children: /* @__PURE__ */ _(h, {
|
|
156
153
|
illustration: "onboarding",
|
|
157
|
-
title:
|
|
158
|
-
description:
|
|
154
|
+
title: k("botlit.chat.empty.title", "Start a Conversation"),
|
|
155
|
+
description: I ? `${k("botlit.chat.empty.withAgentPrefix", "Type a message below to start chatting with")} ${Q?.name || k("botlit.chat.empty.withAgentFallbackName", "the agent")}.` : k("botlit.chat.empty.noAgent", "Select an agent above to start chatting.")
|
|
159
156
|
})
|
|
160
|
-
}) :
|
|
161
|
-
className:
|
|
157
|
+
}) : B.map((e) => /* @__PURE__ */ v("div", {
|
|
158
|
+
className: i("flex gap-3", e.role === "user" ? "justify-end" : "justify-start"),
|
|
162
159
|
children: [
|
|
163
|
-
e.role !== "user" && /* @__PURE__ */
|
|
160
|
+
e.role !== "user" && /* @__PURE__ */ _("div", {
|
|
164
161
|
className: "w-8 h-8 rounded-full bg-action-primary-bg/10 flex items-center justify-center flex-shrink-0",
|
|
165
|
-
children: /* @__PURE__ */
|
|
162
|
+
children: /* @__PURE__ */ _(C, { size: 16 })
|
|
166
163
|
}),
|
|
167
|
-
/* @__PURE__ */
|
|
168
|
-
className:
|
|
169
|
-
children: [/* @__PURE__ */
|
|
164
|
+
/* @__PURE__ */ v("div", {
|
|
165
|
+
className: i("max-w-[80%] rounded-lg px-4 py-3", e.role === "user" ? "bg-action-primary-bg text-action-primary-text" : "bg-bg-sunken"),
|
|
166
|
+
children: [/* @__PURE__ */ _("div", {
|
|
170
167
|
className: "whitespace-pre-wrap text-sm",
|
|
171
168
|
children: e.content
|
|
172
|
-
}), e.tokensUsed != null && e.tokensUsed > 0 && /* @__PURE__ */
|
|
169
|
+
}), e.tokensUsed != null && e.tokensUsed > 0 && /* @__PURE__ */ v("div", {
|
|
173
170
|
className: "mt-2 flex items-center gap-2 text-xs opacity-70",
|
|
174
|
-
children: [/* @__PURE__ */
|
|
171
|
+
children: [/* @__PURE__ */ v(l, {
|
|
175
172
|
variant: "outline",
|
|
176
173
|
className: "text-[10px]",
|
|
177
174
|
children: [
|
|
178
175
|
e.tokensUsed,
|
|
179
176
|
" ",
|
|
180
|
-
|
|
177
|
+
k("botlit.chat.tokensLabel", "tokens")
|
|
181
178
|
]
|
|
182
|
-
}), e.model && /* @__PURE__ */
|
|
179
|
+
}), e.model && /* @__PURE__ */ _(l, {
|
|
183
180
|
variant: "outline",
|
|
184
181
|
className: "text-[10px]",
|
|
185
182
|
children: e.model
|
|
186
183
|
})]
|
|
187
184
|
})]
|
|
188
185
|
}),
|
|
189
|
-
e.role === "user" && /* @__PURE__ */
|
|
186
|
+
e.role === "user" && /* @__PURE__ */ _("div", {
|
|
190
187
|
className: "w-8 h-8 rounded-full bg-secondary flex items-center justify-center flex-shrink-0",
|
|
191
|
-
children: /* @__PURE__ */
|
|
188
|
+
children: /* @__PURE__ */ _(O, { size: 16 })
|
|
192
189
|
})
|
|
193
190
|
]
|
|
194
191
|
}, e.id)),
|
|
195
|
-
|
|
192
|
+
F && /* @__PURE__ */ v("div", {
|
|
196
193
|
className: "flex gap-3",
|
|
197
|
-
children: [/* @__PURE__ */
|
|
194
|
+
children: [/* @__PURE__ */ _("div", {
|
|
198
195
|
className: "w-8 h-8 rounded-full bg-action-primary-bg/10 flex items-center justify-center",
|
|
199
|
-
children: /* @__PURE__ */
|
|
200
|
-
}), /* @__PURE__ */
|
|
196
|
+
children: /* @__PURE__ */ _(C, { size: 16 })
|
|
197
|
+
}), /* @__PURE__ */ _("div", {
|
|
201
198
|
className: "bg-bg-sunken rounded-lg px-4 py-3",
|
|
202
|
-
children: /* @__PURE__ */
|
|
199
|
+
children: /* @__PURE__ */ _(w, { className: "h-5 w-5 animate-spin text-text-secondary" })
|
|
203
200
|
})]
|
|
204
201
|
}),
|
|
205
|
-
/* @__PURE__ */
|
|
202
|
+
/* @__PURE__ */ _("div", { ref: K })
|
|
206
203
|
]
|
|
207
|
-
}), /* @__PURE__ */
|
|
204
|
+
}), /* @__PURE__ */ v("div", {
|
|
208
205
|
className: "border-t border-border-subtle p-4",
|
|
209
|
-
children: [/* @__PURE__ */
|
|
206
|
+
children: [/* @__PURE__ */ v("div", {
|
|
210
207
|
className: "flex gap-2",
|
|
211
|
-
children: [/* @__PURE__ */
|
|
212
|
-
placeholder:
|
|
213
|
-
value:
|
|
214
|
-
onChange: (e) =>
|
|
215
|
-
onKeyDown:
|
|
216
|
-
disabled: !
|
|
208
|
+
children: [/* @__PURE__ */ _(u, {
|
|
209
|
+
placeholder: I ? k("botlit.chat.input.placeholder", "Type your message...") : k("botlit.chat.input.disabledPlaceholder", "Select an agent to start chatting"),
|
|
210
|
+
value: R,
|
|
211
|
+
onChange: (e) => z(e.target.value),
|
|
212
|
+
onKeyDown: X,
|
|
213
|
+
disabled: !I || F,
|
|
217
214
|
className: "min-h-[48px] max-h-[200px] resize-none",
|
|
218
215
|
rows: 1
|
|
219
|
-
}), /* @__PURE__ */
|
|
220
|
-
onClick:
|
|
221
|
-
disabled: !
|
|
216
|
+
}), /* @__PURE__ */ _(c, {
|
|
217
|
+
onClick: Y,
|
|
218
|
+
disabled: !R.trim() || !I || F,
|
|
222
219
|
size: "icon",
|
|
223
220
|
className: "h-12 w-12",
|
|
224
|
-
children:
|
|
221
|
+
children: F ? /* @__PURE__ */ _(w, { className: "h-5 w-5 animate-spin" }) : /* @__PURE__ */ _(E, { size: 20 })
|
|
225
222
|
})]
|
|
226
|
-
}), /* @__PURE__ */
|
|
223
|
+
}), /* @__PURE__ */ _("p", {
|
|
227
224
|
className: "text-xs text-text-secondary mt-2",
|
|
228
|
-
children:
|
|
225
|
+
children: k("botlit.chat.input.helper", "Press Enter to send, Shift+Enter for new line")
|
|
229
226
|
})]
|
|
230
227
|
})]
|
|
231
228
|
})
|
|
232
229
|
}),
|
|
233
|
-
/* @__PURE__ */
|
|
234
|
-
open:
|
|
235
|
-
title:
|
|
236
|
-
description:
|
|
237
|
-
onClose: () =>
|
|
238
|
-
onViewBilling:
|
|
230
|
+
/* @__PURE__ */ _(d, {
|
|
231
|
+
open: W,
|
|
232
|
+
title: k("botlit.usage.runQuota.title", "Bot Run Limit Reached"),
|
|
233
|
+
description: k("botlit.usage.runQuota.description", "You have reached your bot run limit for this month."),
|
|
234
|
+
onClose: () => G(!1),
|
|
235
|
+
onViewBilling: q
|
|
239
236
|
})
|
|
240
237
|
]
|
|
241
238
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatPage.js","names":[],"sources":["../../../src/botlit/pages/ChatPage.tsx"],"sourcesContent":["/**\n * Chat Page\n * @module botlit/pages\n */\n\nimport type { FC } from 'react';\nimport { useState, useRef, useEffect } from 'react';\nimport { isQuotaExhaustedError } from '@burdenoff/fe-libs/shared/utils';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useParams, useNavigate } from 'react-router';\nimport { ArrowLeft, Send, Bot, User, Loader2, RefreshCw, Settings } from 'lucide-react';\nimport { PagePurpose, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { Card, CardContent } from '../components/Card';\nimport { Button } from '../components/Button';\nimport { Textarea } from '../components/Input';\nimport { Badge } from '../components/Badge';\nimport { QuotaExhaustedDialog } from '../components/QuotaExhaustedDialog';\nimport { useAgents } from '../hooks/useAgents';\nimport { useSendMessage } from '../hooks/useConversations';\nimport { useBotlit } from '../providers/BotlitProvider';\nimport { cn } from '../utils/cn';\n\ninterface ChatMessage {\n id: string;\n role: 'user' | 'assistant';\n content: string;\n tokensUsed?: number;\n model?: string;\n createdAt: string;\n}\n\nexport const ChatPage: FC = () => {\n const { t: translate } = useI18n();\n const t = (key: string, fallback: string) => {\n const value = translate(key);\n return value === key ? fallback : value;\n };\n const { agentId } = useParams();\n const navigate = useNavigate();\n const { navigate: shellNavigate } = useBotlit();\n const { data: agents } = useAgents();\n const { sendMessage, isSending } = useSendMessage();\n\n const [selectedAgentId, setSelectedAgentId] = useState(agentId || '');\n const [inputValue, setInputValue] = useState('');\n const [messages, setMessages] = useState<ChatMessage[]>([]);\n const [conversationId, setConversationId] = useState<string | undefined>();\n const [showQuotaDialog, setShowQuotaDialog] = useState(false);\n const messagesEndRef = useRef<HTMLDivElement>(null);\n\n const handleViewBilling = () => {\n if (shellNavigate) {\n shellNavigate('/billing');\n return;\n }\n window.location.assign('/billing');\n };\n\n const scrollToBottom = () => {\n messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });\n };\n\n useEffect(() => {\n scrollToBottom();\n }, [messages]);\n\n useEffect(() => {\n if (agentId) {\n setSelectedAgentId(agentId);\n }\n }, [agentId]);\n\n const handleSend = async () => {\n if (!inputValue.trim() || !selectedAgentId) return;\n\n const userMessage: ChatMessage = {\n id: `user-${Date.now()}`,\n role: 'user',\n content: inputValue,\n createdAt: new Date().toISOString(),\n };\n\n setMessages((prev) => [...prev, userMessage]);\n setInputValue('');\n\n try {\n const result = await sendMessage({\n conversationId,\n agentId: selectedAgentId,\n content: inputValue,\n });\n\n // Track conversation ID for follow-up messages\n if (result.conversationId) {\n setConversationId(result.conversationId);\n }\n\n const assistantMessage: ChatMessage = {\n id: `assistant-${result.executionId}`,\n role: 'assistant',\n content: result.message,\n tokensUsed: result.tokensUsed ?? undefined,\n model: result.model ?? undefined,\n createdAt: new Date().toISOString(),\n };\n\n setMessages((prev) => [...prev, assistantMessage]);\n } catch (error) {\n if (isQuotaExhaustedError(error)) {\n setShowQuotaDialog(true);\n return;\n }\n // Add error message to chat\n const errorMessage: ChatMessage = {\n id: `error-${Date.now()}`,\n role: 'assistant',\n content: `${t('botlit.chat.send.errorPrefix', 'Error')}: ${error instanceof Error ? error.message : t('botlit.chat.send.errorFallback', 'Failed to send message')}`,\n createdAt: new Date().toISOString(),\n };\n setMessages((prev) => [...prev, errorMessage]);\n console.error('Failed to send message:', error);\n }\n };\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n handleSend();\n }\n };\n\n const handleReset = () => {\n setMessages([]);\n setConversationId(undefined);\n };\n\n const selectedAgent = agents?.find((a: { id: string }) => a.id === selectedAgentId);\n\n return (\n <PageLayout\n title={\n <div className=\"flex items-center gap-3\">\n <Button variant=\"ghost\" size=\"icon\" onClick={() => navigate(-1)}>\n <ArrowLeft size={20} />\n </Button>\n {selectedAgent ? (\n <span>\n {t('botlit.chat.title.withAgent', 'Chat with')} {selectedAgent.name}\n </span>\n ) : (\n <span>{t('botlit.chat.title.default', 'AI Chat')}</span>\n )}\n </div>\n }\n actions={\n <div className=\"flex gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n leftIcon={<RefreshCw size={14} />}\n onClick={handleReset}\n >\n {t('botlit.chat.actions.newChat', 'New Chat')}\n </Button>\n {selectedAgentId && (\n <Button variant=\"outline\" size=\"sm\" leftIcon={<Settings size={14} />}>\n <a href={`/agents/${selectedAgentId}`}>\n {t('botlit.chat.actions.agentSettings', 'Agent Settings')}\n </a>\n </Button>\n )}\n </div>\n }\n >\n <div className=\"flex flex-col h-[calc(100vh-220px)] max-w-4xl mx-auto\">\n <PagePurpose className=\"mb-4\" icon={<Bot size={18} />}>\n {t(\n 'botlit.chat.purpose',\n 'This is a live playground for talking to your agents. Pick an agent and chat with it exactly as a real user would — to sanity-check its answers, tone and tool use before you connect it to a bot or channel. Conversations here are throwaway tests, so experiment freely.'\n )}\n </PagePurpose>\n\n {/* Agent Selector */}\n {!agentId && (\n <Card className=\"mb-4\">\n <CardContent className=\"py-4\">\n <div className=\"flex items-center gap-4\">\n <label className=\"text-sm font-medium\">\n {t('botlit.chat.selector.label', 'Select Agent:')}\n </label>\n <select\n className=\"flex h-10 flex-1 max-w-md rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm\"\n value={selectedAgentId}\n onChange={(e) => setSelectedAgentId(e.target.value)}\n >\n <option value=\"\">\n {t('botlit.chat.selector.placeholder', 'Choose an agent...')}\n </option>\n {agents\n ?.filter((a: { status: string }) => a.status === 'ACTIVE')\n .map((a: { id: string; name: string }) => (\n <option key={a.id} value={a.id}>\n {a.name}\n </option>\n ))}\n </select>\n </div>\n </CardContent>\n </Card>\n )}\n\n {/* Messages */}\n <Card treatment=\"glass\" glow className=\"flex-1 overflow-hidden\">\n <CardContent className=\"h-full flex flex-col p-0\">\n <div className=\"flex-1 overflow-y-auto p-4 space-y-4\">\n {messages.length === 0 ? (\n <div className=\"flex h-full items-center justify-center\">\n <IllustratedEmptyState\n illustration=\"onboarding\"\n title={t('botlit.chat.empty.title', 'Start a Conversation')}\n description={\n selectedAgentId\n ? `${t('botlit.chat.empty.withAgentPrefix', 'Type a message below to start chatting with')} ${selectedAgent?.name || t('botlit.chat.empty.withAgentFallbackName', 'the agent')}.`\n : t('botlit.chat.empty.noAgent', 'Select an agent above to start chatting.')\n }\n />\n </div>\n ) : (\n messages.map((message) => (\n <div\n key={message.id}\n className={cn(\n 'flex gap-3',\n message.role === 'user' ? 'justify-end' : 'justify-start'\n )}\n >\n {message.role !== 'user' && (\n <div className=\"w-8 h-8 rounded-full bg-action-primary-bg/10 flex items-center justify-center flex-shrink-0\">\n <Bot size={16} />\n </div>\n )}\n <div\n className={cn(\n 'max-w-[80%] rounded-lg px-4 py-3',\n message.role === 'user'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken'\n )}\n >\n <div className=\"whitespace-pre-wrap text-sm\">{message.content}</div>\n {message.tokensUsed != null && message.tokensUsed > 0 && (\n <div className=\"mt-2 flex items-center gap-2 text-xs opacity-70\">\n <Badge variant=\"outline\" className=\"text-[10px]\">\n {message.tokensUsed} {t('botlit.chat.tokensLabel', 'tokens')}\n </Badge>\n {message.model && (\n <Badge variant=\"outline\" className=\"text-[10px]\">\n {message.model}\n </Badge>\n )}\n </div>\n )}\n </div>\n {message.role === 'user' && (\n <div className=\"w-8 h-8 rounded-full bg-secondary flex items-center justify-center flex-shrink-0\">\n <User size={16} />\n </div>\n )}\n </div>\n ))\n )}\n\n {isSending && (\n <div className=\"flex gap-3\">\n <div className=\"w-8 h-8 rounded-full bg-action-primary-bg/10 flex items-center justify-center\">\n <Bot size={16} />\n </div>\n <div className=\"bg-bg-sunken rounded-lg px-4 py-3\">\n <Loader2 className=\"h-5 w-5 animate-spin text-text-secondary\" />\n </div>\n </div>\n )}\n\n <div ref={messagesEndRef} />\n </div>\n\n {/* Input */}\n <div className=\"border-t border-border-subtle p-4\">\n <div className=\"flex gap-2\">\n <Textarea\n placeholder={\n selectedAgentId\n ? t('botlit.chat.input.placeholder', 'Type your message...')\n : t(\n 'botlit.chat.input.disabledPlaceholder',\n 'Select an agent to start chatting'\n )\n }\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n onKeyDown={handleKeyDown}\n disabled={!selectedAgentId || isSending}\n className=\"min-h-[48px] max-h-[200px] resize-none\"\n rows={1}\n />\n <Button\n onClick={handleSend}\n disabled={!inputValue.trim() || !selectedAgentId || isSending}\n size=\"icon\"\n className=\"h-12 w-12\"\n >\n {isSending ? <Loader2 className=\"h-5 w-5 animate-spin\" /> : <Send size={20} />}\n </Button>\n </div>\n <p className=\"text-xs text-text-secondary mt-2\">\n {t('botlit.chat.input.helper', 'Press Enter to send, Shift+Enter for new line')}\n </p>\n </div>\n </CardContent>\n </Card>\n <QuotaExhaustedDialog\n open={showQuotaDialog}\n title={t('botlit.usage.runQuota.title', 'Bot Run Limit Reached')}\n description={t(\n 'botlit.usage.runQuota.description',\n 'You have reached your bot run limit for this month.'\n )}\n onClose={() => setShowQuotaDialog(false)}\n onViewBilling={handleViewBilling}\n />\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ChatPage.js","names":[],"sources":["../../../src/botlit/pages/ChatPage.tsx"],"sourcesContent":["/**\n * Chat Page\n * @module botlit/pages\n */\n\nimport type { FC } from 'react';\nimport { useState, useRef, useEffect } from 'react';\nimport { isQuotaExhaustedError } from '@burdenoff/fe-libs/shared/utils';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { useParams, useNavigate } from 'react-router';\nimport { ArrowLeft, Send, Bot, User, Loader2, RefreshCw, Settings } from 'lucide-react';\nimport { PagePurpose, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { Card, CardContent } from '../components/Card';\nimport { Button } from '../components/Button';\nimport { Textarea } from '../components/Input';\nimport { Badge } from '../components/Badge';\nimport { QuotaExhaustedDialog } from '../components/QuotaExhaustedDialog';\nimport { useAgents } from '../hooks/useAgents';\nimport { useSendMessage } from '../hooks/useConversations';\nimport { useBotlit } from '../providers/BotlitProvider';\nimport { cn } from '../utils/cn';\n\ninterface ChatMessage {\n id: string;\n role: 'user' | 'assistant';\n content: string;\n tokensUsed?: number;\n model?: string;\n createdAt: string;\n}\n\nexport const ChatPage: FC = () => {\n const t = useTr();\n const { agentId } = useParams();\n const navigate = useNavigate();\n const { navigate: shellNavigate } = useBotlit();\n const { data: agents } = useAgents();\n const { sendMessage, isSending } = useSendMessage();\n\n const [selectedAgentId, setSelectedAgentId] = useState(agentId || '');\n const [inputValue, setInputValue] = useState('');\n const [messages, setMessages] = useState<ChatMessage[]>([]);\n const [conversationId, setConversationId] = useState<string | undefined>();\n const [showQuotaDialog, setShowQuotaDialog] = useState(false);\n const messagesEndRef = useRef<HTMLDivElement>(null);\n\n const handleViewBilling = () => {\n if (shellNavigate) {\n shellNavigate('/billing');\n return;\n }\n window.location.assign('/billing');\n };\n\n const scrollToBottom = () => {\n messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });\n };\n\n useEffect(() => {\n scrollToBottom();\n }, [messages]);\n\n useEffect(() => {\n if (agentId) {\n setSelectedAgentId(agentId);\n }\n }, [agentId]);\n\n const handleSend = async () => {\n if (!inputValue.trim() || !selectedAgentId) return;\n\n const userMessage: ChatMessage = {\n id: `user-${Date.now()}`,\n role: 'user',\n content: inputValue,\n createdAt: new Date().toISOString(),\n };\n\n setMessages((prev) => [...prev, userMessage]);\n setInputValue('');\n\n try {\n const result = await sendMessage({\n conversationId,\n agentId: selectedAgentId,\n content: inputValue,\n });\n\n // Track conversation ID for follow-up messages\n if (result.conversationId) {\n setConversationId(result.conversationId);\n }\n\n const assistantMessage: ChatMessage = {\n id: `assistant-${result.executionId}`,\n role: 'assistant',\n content: result.message,\n tokensUsed: result.tokensUsed ?? undefined,\n model: result.model ?? undefined,\n createdAt: new Date().toISOString(),\n };\n\n setMessages((prev) => [...prev, assistantMessage]);\n } catch (error) {\n if (isQuotaExhaustedError(error)) {\n setShowQuotaDialog(true);\n return;\n }\n // Add error message to chat\n const errorMessage: ChatMessage = {\n id: `error-${Date.now()}`,\n role: 'assistant',\n content: `${t('botlit.chat.send.errorPrefix', 'Error')}: ${error instanceof Error ? error.message : t('botlit.chat.send.errorFallback', 'Failed to send message')}`,\n createdAt: new Date().toISOString(),\n };\n setMessages((prev) => [...prev, errorMessage]);\n console.error('Failed to send message:', error);\n }\n };\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n handleSend();\n }\n };\n\n const handleReset = () => {\n setMessages([]);\n setConversationId(undefined);\n };\n\n const selectedAgent = agents?.find((a: { id: string }) => a.id === selectedAgentId);\n\n return (\n <PageLayout\n title={\n <div className=\"flex items-center gap-3\">\n <Button variant=\"ghost\" size=\"icon\" onClick={() => navigate(-1)}>\n <ArrowLeft size={20} />\n </Button>\n {selectedAgent ? (\n <span>\n {t('botlit.chat.title.withAgent', 'Chat with')} {selectedAgent.name}\n </span>\n ) : (\n <span>{t('botlit.chat.title.default', 'AI Chat')}</span>\n )}\n </div>\n }\n actions={\n <div className=\"flex gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n leftIcon={<RefreshCw size={14} />}\n onClick={handleReset}\n >\n {t('botlit.chat.actions.newChat', 'New Chat')}\n </Button>\n {selectedAgentId && (\n <Button variant=\"outline\" size=\"sm\" leftIcon={<Settings size={14} />}>\n <a href={`/agents/${selectedAgentId}`}>\n {t('botlit.chat.actions.agentSettings', 'Agent Settings')}\n </a>\n </Button>\n )}\n </div>\n }\n >\n <div className=\"flex flex-col h-[calc(100vh-220px)] max-w-4xl mx-auto\">\n <PagePurpose className=\"mb-4\" icon={<Bot size={18} />}>\n {t(\n 'botlit.chat.purpose',\n 'This is a live playground for talking to your agents. Pick an agent and chat with it exactly as a real user would — to sanity-check its answers, tone and tool use before you connect it to a bot or channel. Conversations here are throwaway tests, so experiment freely.'\n )}\n </PagePurpose>\n\n {/* Agent Selector */}\n {!agentId && (\n <Card className=\"mb-4\">\n <CardContent className=\"py-4\">\n <div className=\"flex items-center gap-4\">\n <label className=\"text-sm font-medium\">\n {t('botlit.chat.selector.label', 'Select Agent:')}\n </label>\n <select\n className=\"flex h-10 flex-1 max-w-md rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm\"\n value={selectedAgentId}\n onChange={(e) => setSelectedAgentId(e.target.value)}\n >\n <option value=\"\">\n {t('botlit.chat.selector.placeholder', 'Choose an agent...')}\n </option>\n {agents\n ?.filter((a: { status: string }) => a.status === 'ACTIVE')\n .map((a: { id: string; name: string }) => (\n <option key={a.id} value={a.id}>\n {a.name}\n </option>\n ))}\n </select>\n </div>\n </CardContent>\n </Card>\n )}\n\n {/* Messages */}\n <Card treatment=\"glass\" glow className=\"flex-1 overflow-hidden\">\n <CardContent className=\"h-full flex flex-col p-0\">\n <div className=\"flex-1 overflow-y-auto p-4 space-y-4\">\n {messages.length === 0 ? (\n <div className=\"flex h-full items-center justify-center\">\n <IllustratedEmptyState\n illustration=\"onboarding\"\n title={t('botlit.chat.empty.title', 'Start a Conversation')}\n description={\n selectedAgentId\n ? `${t('botlit.chat.empty.withAgentPrefix', 'Type a message below to start chatting with')} ${selectedAgent?.name || t('botlit.chat.empty.withAgentFallbackName', 'the agent')}.`\n : t('botlit.chat.empty.noAgent', 'Select an agent above to start chatting.')\n }\n />\n </div>\n ) : (\n messages.map((message) => (\n <div\n key={message.id}\n className={cn(\n 'flex gap-3',\n message.role === 'user' ? 'justify-end' : 'justify-start'\n )}\n >\n {message.role !== 'user' && (\n <div className=\"w-8 h-8 rounded-full bg-action-primary-bg/10 flex items-center justify-center flex-shrink-0\">\n <Bot size={16} />\n </div>\n )}\n <div\n className={cn(\n 'max-w-[80%] rounded-lg px-4 py-3',\n message.role === 'user'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken'\n )}\n >\n <div className=\"whitespace-pre-wrap text-sm\">{message.content}</div>\n {message.tokensUsed != null && message.tokensUsed > 0 && (\n <div className=\"mt-2 flex items-center gap-2 text-xs opacity-70\">\n <Badge variant=\"outline\" className=\"text-[10px]\">\n {message.tokensUsed} {t('botlit.chat.tokensLabel', 'tokens')}\n </Badge>\n {message.model && (\n <Badge variant=\"outline\" className=\"text-[10px]\">\n {message.model}\n </Badge>\n )}\n </div>\n )}\n </div>\n {message.role === 'user' && (\n <div className=\"w-8 h-8 rounded-full bg-secondary flex items-center justify-center flex-shrink-0\">\n <User size={16} />\n </div>\n )}\n </div>\n ))\n )}\n\n {isSending && (\n <div className=\"flex gap-3\">\n <div className=\"w-8 h-8 rounded-full bg-action-primary-bg/10 flex items-center justify-center\">\n <Bot size={16} />\n </div>\n <div className=\"bg-bg-sunken rounded-lg px-4 py-3\">\n <Loader2 className=\"h-5 w-5 animate-spin text-text-secondary\" />\n </div>\n </div>\n )}\n\n <div ref={messagesEndRef} />\n </div>\n\n {/* Input */}\n <div className=\"border-t border-border-subtle p-4\">\n <div className=\"flex gap-2\">\n <Textarea\n placeholder={\n selectedAgentId\n ? t('botlit.chat.input.placeholder', 'Type your message...')\n : t(\n 'botlit.chat.input.disabledPlaceholder',\n 'Select an agent to start chatting'\n )\n }\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n onKeyDown={handleKeyDown}\n disabled={!selectedAgentId || isSending}\n className=\"min-h-[48px] max-h-[200px] resize-none\"\n rows={1}\n />\n <Button\n onClick={handleSend}\n disabled={!inputValue.trim() || !selectedAgentId || isSending}\n size=\"icon\"\n className=\"h-12 w-12\"\n >\n {isSending ? <Loader2 className=\"h-5 w-5 animate-spin\" /> : <Send size={20} />}\n </Button>\n </div>\n <p className=\"text-xs text-text-secondary mt-2\">\n {t('botlit.chat.input.helper', 'Press Enter to send, Shift+Enter for new line')}\n </p>\n </div>\n </CardContent>\n </Card>\n <QuotaExhaustedDialog\n open={showQuotaDialog}\n title={t('botlit.usage.runQuota.title', 'Bot Run Limit Reached')}\n description={t(\n 'botlit.usage.runQuota.description',\n 'You have reached your bot run limit for this month.'\n )}\n onClose={() => setShowQuotaDialog(false)}\n onViewBilling={handleViewBilling}\n />\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|