@bdocs/plugin-ask-ai 0.3.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/LICENSE +21 -0
- package/dist/client/index.d.mts +64 -0
- package/dist/client/index.mjs +776 -0
- package/dist/node/index.d.mts +143 -0
- package/dist/node/index.mjs +387 -0
- package/dist/server/index.d.mts +49 -0
- package/dist/server/index.mjs +103 -0
- package/package.json +51 -0
|
@@ -0,0 +1,776 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { useConfig } from "boltdocs/client";
|
|
3
|
+
import { Streamdown } from "streamdown";
|
|
4
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
//#region src/client/use-ask-ai.ts
|
|
6
|
+
function useAskAi(options = {}) {
|
|
7
|
+
const [messages, setMessages] = useState([]);
|
|
8
|
+
const [input, setInput] = useState("");
|
|
9
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
10
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
11
|
+
const submitAbortRef = useRef(null);
|
|
12
|
+
const pendingTextRef = useRef({ value: "" });
|
|
13
|
+
const pendingRafRef = useRef(null);
|
|
14
|
+
const askAiPluginMeta = useConfig()?.plugins?.find((p) => p.name === "boltdocs-plugin-ask-ai")?.metadata;
|
|
15
|
+
const customEndpoint = options.endpoint || askAiPluginMeta?.endpoint || "/api/ask-ai";
|
|
16
|
+
const devMode = askAiPluginMeta?.devMode ?? false;
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
const handleOpen = () => setIsOpen(true);
|
|
19
|
+
const handleClose = () => setIsOpen(false);
|
|
20
|
+
const handleToggle = () => setIsOpen((prev) => !prev);
|
|
21
|
+
window.addEventListener("boltdocs:ask-ai:open", handleOpen);
|
|
22
|
+
window.addEventListener("boltdocs:ask-ai:close", handleClose);
|
|
23
|
+
window.addEventListener("boltdocs:ask-ai:toggle", handleToggle);
|
|
24
|
+
return () => {
|
|
25
|
+
window.removeEventListener("boltdocs:ask-ai:open", handleOpen);
|
|
26
|
+
window.removeEventListener("boltdocs:ask-ai:close", handleClose);
|
|
27
|
+
window.removeEventListener("boltdocs:ask-ai:toggle", handleToggle);
|
|
28
|
+
};
|
|
29
|
+
}, []);
|
|
30
|
+
const startTimeRef = useRef(0);
|
|
31
|
+
const usageRef = useRef(null);
|
|
32
|
+
const flushPendingText = useCallback(() => {
|
|
33
|
+
if (pendingRafRef.current !== null) {
|
|
34
|
+
cancelAnimationFrame(pendingRafRef.current);
|
|
35
|
+
pendingRafRef.current = null;
|
|
36
|
+
}
|
|
37
|
+
if (!pendingTextRef.current.value) return;
|
|
38
|
+
const chunk = pendingTextRef.current.value;
|
|
39
|
+
pendingTextRef.current.value = "";
|
|
40
|
+
setMessages((prev) => {
|
|
41
|
+
const next = [...prev];
|
|
42
|
+
const last = next[next.length - 1];
|
|
43
|
+
if (last && last.role === "assistant") last.content += chunk;
|
|
44
|
+
return next;
|
|
45
|
+
});
|
|
46
|
+
}, []);
|
|
47
|
+
const scheduleFlush = useCallback(() => {
|
|
48
|
+
if (pendingRafRef.current !== null) return;
|
|
49
|
+
pendingRafRef.current = requestAnimationFrame(() => {
|
|
50
|
+
pendingRafRef.current = null;
|
|
51
|
+
flushPendingText();
|
|
52
|
+
});
|
|
53
|
+
}, [flushPendingText]);
|
|
54
|
+
const finalizeAssistantTerminal = useCallback((opts) => {
|
|
55
|
+
flushPendingText();
|
|
56
|
+
setMessages((prev) => {
|
|
57
|
+
const next = [...prev];
|
|
58
|
+
const last = next[next.length - 1];
|
|
59
|
+
if (last && last.role === "assistant" && last.status !== "done") {
|
|
60
|
+
last.status = last.content ? "done" : "error";
|
|
61
|
+
if (last.status === "error" && opts.message) last.errorMessage = opts.message;
|
|
62
|
+
}
|
|
63
|
+
return next;
|
|
64
|
+
});
|
|
65
|
+
}, [flushPendingText]);
|
|
66
|
+
const stopStreaming = useCallback(() => {
|
|
67
|
+
if (submitAbortRef.current) submitAbortRef.current.abort();
|
|
68
|
+
setIsLoading(false);
|
|
69
|
+
finalizeAssistantTerminal({ kind: "cancel" });
|
|
70
|
+
}, [finalizeAssistantTerminal]);
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
if (!isOpen && isLoading) stopStreaming();
|
|
73
|
+
}, [
|
|
74
|
+
isOpen,
|
|
75
|
+
isLoading,
|
|
76
|
+
stopStreaming
|
|
77
|
+
]);
|
|
78
|
+
const clearChat = useCallback(() => {
|
|
79
|
+
stopStreaming();
|
|
80
|
+
setMessages([]);
|
|
81
|
+
}, [stopStreaming]);
|
|
82
|
+
return {
|
|
83
|
+
messages,
|
|
84
|
+
input,
|
|
85
|
+
setInput,
|
|
86
|
+
isLoading,
|
|
87
|
+
submitQuestion: useCallback(async (text) => {
|
|
88
|
+
const trimmed = text.trim();
|
|
89
|
+
if (!trimmed || isLoading) return;
|
|
90
|
+
pendingTextRef.current = { value: "" };
|
|
91
|
+
pendingRafRef.current = null;
|
|
92
|
+
setMessages((prev) => [
|
|
93
|
+
...prev,
|
|
94
|
+
{
|
|
95
|
+
role: "user",
|
|
96
|
+
content: trimmed
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
role: "assistant",
|
|
100
|
+
content: "",
|
|
101
|
+
status: "reading"
|
|
102
|
+
}
|
|
103
|
+
]);
|
|
104
|
+
setInput("");
|
|
105
|
+
setIsLoading(true);
|
|
106
|
+
const controller = new AbortController();
|
|
107
|
+
submitAbortRef.current = controller;
|
|
108
|
+
try {
|
|
109
|
+
const currentPage = options.currentPage || (typeof window !== "undefined" ? window.location.pathname : "/");
|
|
110
|
+
const response = await fetch(customEndpoint, {
|
|
111
|
+
method: "POST",
|
|
112
|
+
headers: { "Content-Type": "application/json" },
|
|
113
|
+
body: JSON.stringify({
|
|
114
|
+
question: trimmed,
|
|
115
|
+
currentPage
|
|
116
|
+
}),
|
|
117
|
+
signal: controller.signal
|
|
118
|
+
});
|
|
119
|
+
if (!response.ok) throw new Error(`Server returned ${response.status}`);
|
|
120
|
+
if (!response.body) throw new Error("No streaming body from endpoint");
|
|
121
|
+
const reader = response.body.getReader();
|
|
122
|
+
const decoder = new TextDecoder();
|
|
123
|
+
let lineBuffer = "";
|
|
124
|
+
let firstTextSeen = false;
|
|
125
|
+
while (true) {
|
|
126
|
+
const { value, done } = await reader.read();
|
|
127
|
+
if (done) break;
|
|
128
|
+
lineBuffer += decoder.decode(value, { stream: true });
|
|
129
|
+
const lines = lineBuffer.split("\n");
|
|
130
|
+
lineBuffer = lines.pop() || "";
|
|
131
|
+
for (const line of lines) {
|
|
132
|
+
const cleaned = line.trim();
|
|
133
|
+
if (!cleaned.startsWith("data:")) continue;
|
|
134
|
+
const dataStr = cleaned.slice(5).trim();
|
|
135
|
+
if (!dataStr || dataStr === "[DONE]") continue;
|
|
136
|
+
let parsed;
|
|
137
|
+
try {
|
|
138
|
+
parsed = JSON.parse(dataStr);
|
|
139
|
+
} catch {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (parsed.context) setMessages((prev) => {
|
|
143
|
+
const next = [...prev];
|
|
144
|
+
const last = next[next.length - 1];
|
|
145
|
+
if (last && last.role === "assistant") {
|
|
146
|
+
last.contextChip = parsed.context;
|
|
147
|
+
startTimeRef.current = Date.now();
|
|
148
|
+
}
|
|
149
|
+
return next;
|
|
150
|
+
});
|
|
151
|
+
else if (parsed.usage) usageRef.current = parsed.usage;
|
|
152
|
+
else if (typeof parsed.text === "string") {
|
|
153
|
+
if (!firstTextSeen) {
|
|
154
|
+
firstTextSeen = true;
|
|
155
|
+
setMessages((prev) => {
|
|
156
|
+
const next = [...prev];
|
|
157
|
+
const last = next[next.length - 1];
|
|
158
|
+
if (last && last.role === "assistant") last.status = "streaming";
|
|
159
|
+
return next;
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
pendingTextRef.current.value += parsed.text;
|
|
163
|
+
scheduleFlush();
|
|
164
|
+
} else if (parsed.error) throw new Error(parsed.error);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
flushPendingText();
|
|
168
|
+
setMessages((prev) => {
|
|
169
|
+
const next = [...prev];
|
|
170
|
+
const last = next[next.length - 1];
|
|
171
|
+
if (last && last.role === "assistant") {
|
|
172
|
+
last.status = last.content ? "done" : "error";
|
|
173
|
+
if (last.status === "error" && !last.errorMessage) last.errorMessage = "No response received.";
|
|
174
|
+
if (usageRef.current && devMode) last.usage = {
|
|
175
|
+
...usageRef.current,
|
|
176
|
+
elapsedMs: startTimeRef.current ? Date.now() - startTimeRef.current : usageRef.current.elapsedMs
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
return next;
|
|
180
|
+
});
|
|
181
|
+
} catch (error) {
|
|
182
|
+
if (error instanceof DOMException && error.name === "AbortError") finalizeAssistantTerminal({
|
|
183
|
+
kind: "cancel",
|
|
184
|
+
message: "Request cancelled."
|
|
185
|
+
});
|
|
186
|
+
else {
|
|
187
|
+
finalizeAssistantTerminal({
|
|
188
|
+
kind: "error",
|
|
189
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
190
|
+
});
|
|
191
|
+
console.error("[Ask AI] failed:", error);
|
|
192
|
+
}
|
|
193
|
+
} finally {
|
|
194
|
+
if (pendingRafRef.current !== null) {
|
|
195
|
+
cancelAnimationFrame(pendingRafRef.current);
|
|
196
|
+
pendingRafRef.current = null;
|
|
197
|
+
}
|
|
198
|
+
if (submitAbortRef.current === controller) submitAbortRef.current = null;
|
|
199
|
+
setIsLoading(false);
|
|
200
|
+
}
|
|
201
|
+
}, [
|
|
202
|
+
customEndpoint,
|
|
203
|
+
options.currentPage,
|
|
204
|
+
isLoading,
|
|
205
|
+
scheduleFlush,
|
|
206
|
+
finalizeAssistantTerminal
|
|
207
|
+
]),
|
|
208
|
+
stopStreaming,
|
|
209
|
+
clearChat,
|
|
210
|
+
isOpen,
|
|
211
|
+
setIsOpen,
|
|
212
|
+
devMode
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
//#endregion
|
|
216
|
+
//#region src/client/render-markdown.tsx
|
|
217
|
+
function MarkdownRenderer({ content, className, parseIncompleteMarkdown = true }) {
|
|
218
|
+
if (!content) return null;
|
|
219
|
+
return /* @__PURE__ */ jsx("div", {
|
|
220
|
+
className,
|
|
221
|
+
children: /* @__PURE__ */ jsx(Streamdown, {
|
|
222
|
+
parseIncompleteMarkdown,
|
|
223
|
+
children: content
|
|
224
|
+
})
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
//#endregion
|
|
228
|
+
//#region src/client/components/ask-ai-bubble.tsx
|
|
229
|
+
function AskAiBubble() {
|
|
230
|
+
const { messages, input, setInput, isLoading, submitQuestion, stopStreaming, clearChat, isOpen, setIsOpen, devMode } = useAskAi();
|
|
231
|
+
const messagesEndRef = useRef(null);
|
|
232
|
+
useEffect(() => {
|
|
233
|
+
if (isOpen) messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
234
|
+
}, [messages, isOpen]);
|
|
235
|
+
const handleSubmit = (e) => {
|
|
236
|
+
e.preventDefault();
|
|
237
|
+
if (!input.trim()) return;
|
|
238
|
+
submitQuestion(input);
|
|
239
|
+
};
|
|
240
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
241
|
+
className: "fixed bottom-6 right-6 z-50 flex flex-col items-end",
|
|
242
|
+
children: [isOpen && /* @__PURE__ */ jsxs("div", {
|
|
243
|
+
className: "mb-4 w-[380px] max-w-[calc(100vw-2rem)] h-[min(520px,calc(100vh-8rem))] bg-main/90 backdrop-blur-md border border-subtle rounded-2xl shadow-xl flex flex-col overflow-hidden animate-in fade-in slide-in-from-bottom-5 duration-200",
|
|
244
|
+
children: [
|
|
245
|
+
/* @__PURE__ */ jsxs("div", {
|
|
246
|
+
className: "px-4 py-3 border-b border-subtle flex items-center justify-between bg-surface/50",
|
|
247
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
248
|
+
className: "flex items-center gap-2",
|
|
249
|
+
children: [/* @__PURE__ */ jsx("svg", {
|
|
250
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
251
|
+
width: "16",
|
|
252
|
+
height: "16",
|
|
253
|
+
viewBox: "0 0 24 24",
|
|
254
|
+
fill: "none",
|
|
255
|
+
stroke: "currentColor",
|
|
256
|
+
strokeWidth: "2.5",
|
|
257
|
+
strokeLinecap: "round",
|
|
258
|
+
strokeLinejoin: "round",
|
|
259
|
+
className: "text-primary-500",
|
|
260
|
+
children: /* @__PURE__ */ jsx("path", { d: "m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z" })
|
|
261
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
262
|
+
className: "text-sm font-semibold text-body",
|
|
263
|
+
children: "Ask Assistant"
|
|
264
|
+
})]
|
|
265
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
266
|
+
className: "flex items-center gap-1",
|
|
267
|
+
children: [messages.length > 0 && /* @__PURE__ */ jsx("button", {
|
|
268
|
+
onClick: clearChat,
|
|
269
|
+
className: "p-1 text-muted hover:text-red-500 hover:bg-surface rounded-lg transition-colors cursor-pointer",
|
|
270
|
+
title: "Clear chat",
|
|
271
|
+
"aria-label": "Clear chat",
|
|
272
|
+
children: /* @__PURE__ */ jsxs("svg", {
|
|
273
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
274
|
+
width: "14",
|
|
275
|
+
height: "14",
|
|
276
|
+
viewBox: "0 0 24 24",
|
|
277
|
+
fill: "none",
|
|
278
|
+
stroke: "currentColor",
|
|
279
|
+
strokeWidth: "2",
|
|
280
|
+
strokeLinecap: "round",
|
|
281
|
+
strokeLinejoin: "round",
|
|
282
|
+
children: [
|
|
283
|
+
/* @__PURE__ */ jsx("path", { d: "M3 6h18" }),
|
|
284
|
+
/* @__PURE__ */ jsx("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }),
|
|
285
|
+
/* @__PURE__ */ jsx("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" })
|
|
286
|
+
]
|
|
287
|
+
})
|
|
288
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
289
|
+
onClick: () => setIsOpen(false),
|
|
290
|
+
className: "p-1 text-muted hover:text-body hover:bg-surface rounded-lg transition-colors cursor-pointer",
|
|
291
|
+
title: "Close assistant",
|
|
292
|
+
"aria-label": "Close assistant",
|
|
293
|
+
children: /* @__PURE__ */ jsxs("svg", {
|
|
294
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
295
|
+
width: "16",
|
|
296
|
+
height: "16",
|
|
297
|
+
viewBox: "0 0 24 24",
|
|
298
|
+
fill: "none",
|
|
299
|
+
stroke: "currentColor",
|
|
300
|
+
strokeWidth: "2",
|
|
301
|
+
strokeLinecap: "round",
|
|
302
|
+
strokeLinejoin: "round",
|
|
303
|
+
children: [/* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }), /* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })]
|
|
304
|
+
})
|
|
305
|
+
})]
|
|
306
|
+
})]
|
|
307
|
+
}),
|
|
308
|
+
/* @__PURE__ */ jsxs("div", {
|
|
309
|
+
className: "flex-1 overflow-y-auto p-4 flex flex-col gap-4",
|
|
310
|
+
children: [
|
|
311
|
+
messages.length === 0 && /* @__PURE__ */ jsxs("div", {
|
|
312
|
+
className: "flex-1 flex flex-col items-center justify-center text-center p-6",
|
|
313
|
+
children: [
|
|
314
|
+
/* @__PURE__ */ jsx("div", {
|
|
315
|
+
className: "w-12 h-12 rounded-full bg-primary-500/10 flex items-center justify-center text-primary-500 mb-3",
|
|
316
|
+
children: /* @__PURE__ */ jsx("svg", {
|
|
317
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
318
|
+
width: "24",
|
|
319
|
+
height: "24",
|
|
320
|
+
viewBox: "0 0 24 24",
|
|
321
|
+
fill: "none",
|
|
322
|
+
stroke: "currentColor",
|
|
323
|
+
strokeWidth: "2",
|
|
324
|
+
strokeLinecap: "round",
|
|
325
|
+
strokeLinejoin: "round",
|
|
326
|
+
children: /* @__PURE__ */ jsx("path", { d: "m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z" })
|
|
327
|
+
})
|
|
328
|
+
}),
|
|
329
|
+
/* @__PURE__ */ jsx("h3", {
|
|
330
|
+
className: "text-sm font-semibold text-body mb-1",
|
|
331
|
+
children: "How can I help you today?"
|
|
332
|
+
}),
|
|
333
|
+
/* @__PURE__ */ jsx("p", {
|
|
334
|
+
className: "text-xs text-muted max-w-[240px]",
|
|
335
|
+
children: "Ask questions about the current documentation page. The assistant only answers using the page you're viewing."
|
|
336
|
+
})
|
|
337
|
+
]
|
|
338
|
+
}),
|
|
339
|
+
messages.map((msg, i) => /* @__PURE__ */ jsxs("div", {
|
|
340
|
+
className: `flex flex-col max-w-[85%] ${msg.role === "user" ? "align-self-end items-end ml-auto" : "align-self-start items-start"}`,
|
|
341
|
+
children: [
|
|
342
|
+
msg.role === "assistant" && msg.usage && devMode && /* @__PURE__ */ jsxs("div", {
|
|
343
|
+
className: "flex items-center gap-1.5 px-2 py-1 mb-1 text-[11px] text-muted font-mono",
|
|
344
|
+
children: [
|
|
345
|
+
/* @__PURE__ */ jsx("span", {
|
|
346
|
+
className: "px-1 py-0.5 rounded bg-amber-500/10 text-amber-600 dark:text-amber-400 text-[10px] font-bold uppercase tracking-wide",
|
|
347
|
+
children: "DEV"
|
|
348
|
+
}),
|
|
349
|
+
/* @__PURE__ */ jsxs("span", {
|
|
350
|
+
title: "Provider / model",
|
|
351
|
+
children: [
|
|
352
|
+
msg.usage.provider,
|
|
353
|
+
"/",
|
|
354
|
+
msg.usage.model
|
|
355
|
+
]
|
|
356
|
+
}),
|
|
357
|
+
/* @__PURE__ */ jsx("span", {
|
|
358
|
+
className: "text-muted",
|
|
359
|
+
children: "·"
|
|
360
|
+
}),
|
|
361
|
+
/* @__PURE__ */ jsxs("span", {
|
|
362
|
+
title: "Prompt tokens",
|
|
363
|
+
children: [msg.usage.promptTokens, "↑"]
|
|
364
|
+
}),
|
|
365
|
+
/* @__PURE__ */ jsxs("span", {
|
|
366
|
+
title: "Completion tokens",
|
|
367
|
+
children: [msg.usage.completionTokens, "↓"]
|
|
368
|
+
}),
|
|
369
|
+
/* @__PURE__ */ jsx("span", {
|
|
370
|
+
className: "text-muted",
|
|
371
|
+
children: "·"
|
|
372
|
+
}),
|
|
373
|
+
/* @__PURE__ */ jsxs("span", {
|
|
374
|
+
title: "Total tokens",
|
|
375
|
+
className: "text-primary-500 font-semibold",
|
|
376
|
+
children: [msg.usage.totalTokens, " tok"]
|
|
377
|
+
}),
|
|
378
|
+
/* @__PURE__ */ jsx("span", {
|
|
379
|
+
className: "text-muted",
|
|
380
|
+
children: "·"
|
|
381
|
+
}),
|
|
382
|
+
/* @__PURE__ */ jsxs("span", {
|
|
383
|
+
title: "Elapsed",
|
|
384
|
+
children: [msg.usage.elapsedMs, "ms"]
|
|
385
|
+
})
|
|
386
|
+
]
|
|
387
|
+
}),
|
|
388
|
+
msg.role === "assistant" && msg.contextChip && /* @__PURE__ */ jsx("div", {
|
|
389
|
+
className: "flex items-center gap-1.5 px-2 py-1 mb-1 text-[11px] text-muted",
|
|
390
|
+
children: msg.contextChip.missing ? /* @__PURE__ */ jsx("span", { children: "No docs page in scope" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
391
|
+
/* @__PURE__ */ jsxs("svg", {
|
|
392
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
393
|
+
width: "12",
|
|
394
|
+
height: "12",
|
|
395
|
+
viewBox: "0 0 24 24",
|
|
396
|
+
fill: "none",
|
|
397
|
+
stroke: "currentColor",
|
|
398
|
+
strokeWidth: "2",
|
|
399
|
+
strokeLinecap: "round",
|
|
400
|
+
strokeLinejoin: "round",
|
|
401
|
+
children: [/* @__PURE__ */ jsx("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }), /* @__PURE__ */ jsx("polyline", { points: "14 2 14 8 20 8" })]
|
|
402
|
+
}),
|
|
403
|
+
/* @__PURE__ */ jsx("span", { children: "Reading" }),
|
|
404
|
+
/* @__PURE__ */ jsx("code", {
|
|
405
|
+
className: "px-1 py-0.5 rounded bg-surface text-primary-500 text-[10px] font-mono",
|
|
406
|
+
children: msg.contextChip.page
|
|
407
|
+
}),
|
|
408
|
+
/* @__PURE__ */ jsx("span", {
|
|
409
|
+
className: "text-muted",
|
|
410
|
+
children: "·"
|
|
411
|
+
}),
|
|
412
|
+
/* @__PURE__ */ jsxs("span", { children: [msg.contextChip.chars, " chars"] }),
|
|
413
|
+
typeof msg.contextChip.elapsedMs === "number" && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
|
|
414
|
+
className: "text-muted",
|
|
415
|
+
children: "·"
|
|
416
|
+
}), /* @__PURE__ */ jsxs("span", { children: [msg.contextChip.elapsedMs, "ms"] })] })
|
|
417
|
+
] })
|
|
418
|
+
}),
|
|
419
|
+
/* @__PURE__ */ jsx("div", {
|
|
420
|
+
className: `px-3 py-2 rounded-xl ${msg.role === "user" ? "bg-primary-500 text-white rounded-br-none" : msg.status === "error" ? "bg-red-500/10 border border-red-500/30 text-red-600 dark:text-red-400 rounded-bl-none" : "bg-surface border border-subtle text-body rounded-bl-none"}`,
|
|
421
|
+
children: msg.role === "user" ? /* @__PURE__ */ jsx("p", {
|
|
422
|
+
className: "whitespace-pre-wrap text-sm",
|
|
423
|
+
children: msg.content
|
|
424
|
+
}) : msg.status === "error" ? /* @__PURE__ */ jsxs("p", {
|
|
425
|
+
className: "text-sm",
|
|
426
|
+
children: [
|
|
427
|
+
/* @__PURE__ */ jsx("strong", { children: "Error:" }),
|
|
428
|
+
" ",
|
|
429
|
+
msg.errorMessage || "Something went wrong."
|
|
430
|
+
]
|
|
431
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
432
|
+
className: "ask-ai-streamdown",
|
|
433
|
+
children: msg.content ? /* @__PURE__ */ jsx(MarkdownRenderer, { content: msg.content }) : msg.status === "reading" ? /* @__PURE__ */ jsxs("span", {
|
|
434
|
+
className: "inline-flex items-center gap-1.5 text-xs text-muted",
|
|
435
|
+
children: [/* @__PURE__ */ jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-primary-500 animate-pulse" }), /* @__PURE__ */ jsx("span", { children: "Reading page…" })]
|
|
436
|
+
}) : /* @__PURE__ */ jsxs("span", {
|
|
437
|
+
className: "inline-flex items-center gap-1.5 text-xs text-muted",
|
|
438
|
+
children: [/* @__PURE__ */ jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-primary-500 animate-pulse" }), /* @__PURE__ */ jsx("span", { children: "Waiting…" })]
|
|
439
|
+
})
|
|
440
|
+
})
|
|
441
|
+
})
|
|
442
|
+
]
|
|
443
|
+
}, i)),
|
|
444
|
+
/* @__PURE__ */ jsx("div", { ref: messagesEndRef })
|
|
445
|
+
]
|
|
446
|
+
}),
|
|
447
|
+
/* @__PURE__ */ jsxs("form", {
|
|
448
|
+
onSubmit: handleSubmit,
|
|
449
|
+
className: "p-3 border-t border-subtle bg-surface/30 flex gap-2",
|
|
450
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
451
|
+
type: "text",
|
|
452
|
+
value: input,
|
|
453
|
+
onChange: (e) => setInput(e.target.value),
|
|
454
|
+
placeholder: "Ask about this page…",
|
|
455
|
+
className: "flex-1 bg-surface border border-subtle rounded-xl px-3 py-1.5 text-sm outline-none text-body focus-visible:border-primary-500 transition-colors",
|
|
456
|
+
disabled: isLoading
|
|
457
|
+
}), isLoading ? /* @__PURE__ */ jsx("button", {
|
|
458
|
+
type: "button",
|
|
459
|
+
onClick: stopStreaming,
|
|
460
|
+
className: "px-3 py-1.5 bg-red-500 hover:bg-red-600 text-white text-sm font-semibold rounded-xl flex items-center justify-center transition-colors cursor-pointer select-none",
|
|
461
|
+
title: "Stop generating",
|
|
462
|
+
"aria-label": "Stop generating",
|
|
463
|
+
children: /* @__PURE__ */ jsx("svg", {
|
|
464
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
465
|
+
width: "16",
|
|
466
|
+
height: "16",
|
|
467
|
+
viewBox: "0 0 24 24",
|
|
468
|
+
fill: "currentColor",
|
|
469
|
+
children: /* @__PURE__ */ jsx("rect", {
|
|
470
|
+
x: "6",
|
|
471
|
+
y: "6",
|
|
472
|
+
width: "12",
|
|
473
|
+
height: "12",
|
|
474
|
+
rx: "2"
|
|
475
|
+
})
|
|
476
|
+
})
|
|
477
|
+
}) : /* @__PURE__ */ jsx("button", {
|
|
478
|
+
type: "submit",
|
|
479
|
+
disabled: !input.trim(),
|
|
480
|
+
className: "px-3 py-1.5 bg-primary-500 hover:bg-primary-600 disabled:opacity-50 text-white text-sm font-semibold rounded-xl flex items-center justify-center transition-colors cursor-pointer select-none",
|
|
481
|
+
"aria-label": "Send question",
|
|
482
|
+
children: /* @__PURE__ */ jsxs("svg", {
|
|
483
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
484
|
+
width: "16",
|
|
485
|
+
height: "16",
|
|
486
|
+
viewBox: "0 0 24 24",
|
|
487
|
+
fill: "none",
|
|
488
|
+
stroke: "currentColor",
|
|
489
|
+
strokeWidth: "2.5",
|
|
490
|
+
strokeLinecap: "round",
|
|
491
|
+
strokeLinejoin: "round",
|
|
492
|
+
children: [/* @__PURE__ */ jsx("path", { d: "m22 2-7 20-4-9-9-4Z" }), /* @__PURE__ */ jsx("path", { d: "M22 2 11 13" })]
|
|
493
|
+
})
|
|
494
|
+
})]
|
|
495
|
+
})
|
|
496
|
+
]
|
|
497
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
498
|
+
onClick: () => setIsOpen((prev) => !prev),
|
|
499
|
+
className: "w-12 h-12 bg-primary-500 hover:bg-primary-600 active:scale-95 text-white rounded-full flex items-center justify-center shadow-lg transition-all cursor-pointer select-none",
|
|
500
|
+
title: "Ask AI assistant",
|
|
501
|
+
"aria-label": "Ask AI assistant",
|
|
502
|
+
children: isOpen ? /* @__PURE__ */ jsxs("svg", {
|
|
503
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
504
|
+
width: "20",
|
|
505
|
+
height: "20",
|
|
506
|
+
viewBox: "0 0 24 24",
|
|
507
|
+
fill: "none",
|
|
508
|
+
stroke: "currentColor",
|
|
509
|
+
strokeWidth: "2.5",
|
|
510
|
+
strokeLinecap: "round",
|
|
511
|
+
strokeLinejoin: "round",
|
|
512
|
+
children: [/* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }), /* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })]
|
|
513
|
+
}) : /* @__PURE__ */ jsx("svg", {
|
|
514
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
515
|
+
width: "20",
|
|
516
|
+
height: "20",
|
|
517
|
+
viewBox: "0 0 24 24",
|
|
518
|
+
fill: "none",
|
|
519
|
+
stroke: "currentColor",
|
|
520
|
+
strokeWidth: "2",
|
|
521
|
+
strokeLinecap: "round",
|
|
522
|
+
strokeLinejoin: "round",
|
|
523
|
+
children: /* @__PURE__ */ jsx("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" })
|
|
524
|
+
})
|
|
525
|
+
})]
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
//#endregion
|
|
529
|
+
//#region src/client/components/ask-ai-dialog.tsx
|
|
530
|
+
function AskAiDialog() {
|
|
531
|
+
const { messages, input, setInput, isLoading, submitQuestion, stopStreaming, clearChat, isOpen, setIsOpen, devMode } = useAskAi();
|
|
532
|
+
const messagesEndRef = useRef(null);
|
|
533
|
+
const inputRef = useRef(null);
|
|
534
|
+
useEffect(() => {
|
|
535
|
+
if (isOpen) {
|
|
536
|
+
window.dispatchEvent(new CustomEvent("boltdocs:ask-ai:open"));
|
|
537
|
+
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
538
|
+
inputRef.current?.focus();
|
|
539
|
+
} else window.dispatchEvent(new CustomEvent("boltdocs:ask-ai:close"));
|
|
540
|
+
}, [isOpen]);
|
|
541
|
+
const handleSubmit = (e) => {
|
|
542
|
+
e.preventDefault();
|
|
543
|
+
if (!input.trim() || isLoading) return;
|
|
544
|
+
submitQuestion(input);
|
|
545
|
+
};
|
|
546
|
+
if (!isOpen) return null;
|
|
547
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
548
|
+
className: "hidden xl:flex flex-col shrink-0 w-[320px] border-l border-subtle bg-main overflow-hidden",
|
|
549
|
+
children: [
|
|
550
|
+
/* @__PURE__ */ jsxs("div", {
|
|
551
|
+
className: "px-4 py-3 border-b border-subtle flex items-center justify-between bg-surface/50 shrink-0",
|
|
552
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
553
|
+
className: "flex items-center gap-2",
|
|
554
|
+
children: [/* @__PURE__ */ jsx("svg", {
|
|
555
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
556
|
+
width: "14",
|
|
557
|
+
height: "14",
|
|
558
|
+
viewBox: "0 0 24 24",
|
|
559
|
+
fill: "none",
|
|
560
|
+
stroke: "currentColor",
|
|
561
|
+
strokeWidth: "2.5",
|
|
562
|
+
strokeLinecap: "round",
|
|
563
|
+
strokeLinejoin: "round",
|
|
564
|
+
className: "text-primary-500 shrink-0",
|
|
565
|
+
children: /* @__PURE__ */ jsx("path", { d: "m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z" })
|
|
566
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
567
|
+
className: "text-xs font-bold text-body",
|
|
568
|
+
children: "AI Assistant"
|
|
569
|
+
})]
|
|
570
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
571
|
+
className: "flex items-center gap-1",
|
|
572
|
+
children: [messages.length > 0 && /* @__PURE__ */ jsx("button", {
|
|
573
|
+
onClick: clearChat,
|
|
574
|
+
className: "p-1 text-muted hover:text-red-500 hover:bg-surface rounded-lg transition-colors cursor-pointer",
|
|
575
|
+
title: "Clear chat",
|
|
576
|
+
"aria-label": "Clear chat",
|
|
577
|
+
children: /* @__PURE__ */ jsxs("svg", {
|
|
578
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
579
|
+
width: "12",
|
|
580
|
+
height: "12",
|
|
581
|
+
viewBox: "0 0 24 24",
|
|
582
|
+
fill: "none",
|
|
583
|
+
stroke: "currentColor",
|
|
584
|
+
strokeWidth: "2",
|
|
585
|
+
strokeLinecap: "round",
|
|
586
|
+
strokeLinejoin: "round",
|
|
587
|
+
children: [
|
|
588
|
+
/* @__PURE__ */ jsx("path", { d: "M3 6h18" }),
|
|
589
|
+
/* @__PURE__ */ jsx("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }),
|
|
590
|
+
/* @__PURE__ */ jsx("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" })
|
|
591
|
+
]
|
|
592
|
+
})
|
|
593
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
594
|
+
onClick: () => setIsOpen(false),
|
|
595
|
+
className: "p-1 text-muted hover:text-body hover:bg-surface rounded-lg transition-colors cursor-pointer",
|
|
596
|
+
title: "Close assistant",
|
|
597
|
+
"aria-label": "Close assistant",
|
|
598
|
+
children: /* @__PURE__ */ jsxs("svg", {
|
|
599
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
600
|
+
width: "12",
|
|
601
|
+
height: "12",
|
|
602
|
+
viewBox: "0 0 24 24",
|
|
603
|
+
fill: "none",
|
|
604
|
+
stroke: "currentColor",
|
|
605
|
+
strokeWidth: "2",
|
|
606
|
+
strokeLinecap: "round",
|
|
607
|
+
strokeLinejoin: "round",
|
|
608
|
+
children: [/* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }), /* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })]
|
|
609
|
+
})
|
|
610
|
+
})]
|
|
611
|
+
})]
|
|
612
|
+
}),
|
|
613
|
+
/* @__PURE__ */ jsxs("div", {
|
|
614
|
+
className: "flex-1 overflow-y-auto p-4 flex flex-col gap-3 min-h-0",
|
|
615
|
+
children: [
|
|
616
|
+
messages.length === 0 && /* @__PURE__ */ jsxs("div", {
|
|
617
|
+
className: "flex-1 flex flex-col items-center justify-center text-center p-2",
|
|
618
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
619
|
+
className: "w-10 h-10 rounded-xl bg-primary-500/10 flex items-center justify-center text-primary-500 mb-3",
|
|
620
|
+
children: /* @__PURE__ */ jsx("svg", {
|
|
621
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
622
|
+
width: "20",
|
|
623
|
+
height: "20",
|
|
624
|
+
viewBox: "0 0 24 24",
|
|
625
|
+
fill: "none",
|
|
626
|
+
stroke: "currentColor",
|
|
627
|
+
strokeWidth: "2",
|
|
628
|
+
strokeLinecap: "round",
|
|
629
|
+
strokeLinejoin: "round",
|
|
630
|
+
children: /* @__PURE__ */ jsx("path", { d: "m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z" })
|
|
631
|
+
})
|
|
632
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
633
|
+
className: "text-xs text-muted leading-relaxed",
|
|
634
|
+
children: "Ask anything about the current documentation page"
|
|
635
|
+
})]
|
|
636
|
+
}),
|
|
637
|
+
messages.map((msg, i) => /* @__PURE__ */ jsxs("div", {
|
|
638
|
+
className: `flex flex-col max-w-full ${msg.role === "user" ? "items-end" : "items-start"}`,
|
|
639
|
+
children: [
|
|
640
|
+
msg.role === "assistant" && msg.usage && devMode && /* @__PURE__ */ jsxs("div", {
|
|
641
|
+
className: "flex items-center gap-1.5 px-2 py-1 mb-1 text-[11px] text-muted font-mono",
|
|
642
|
+
children: [
|
|
643
|
+
/* @__PURE__ */ jsx("span", {
|
|
644
|
+
className: "px-1 py-0.5 rounded bg-amber-500/10 text-amber-600 dark:text-amber-400 text-[10px] font-bold uppercase tracking-wide",
|
|
645
|
+
children: "DEV"
|
|
646
|
+
}),
|
|
647
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
648
|
+
msg.usage.provider,
|
|
649
|
+
"/",
|
|
650
|
+
msg.usage.model
|
|
651
|
+
] }),
|
|
652
|
+
/* @__PURE__ */ jsx("span", { children: "·" }),
|
|
653
|
+
/* @__PURE__ */ jsxs("span", { children: [msg.usage.promptTokens, "↑"] }),
|
|
654
|
+
/* @__PURE__ */ jsxs("span", { children: [msg.usage.completionTokens, "↓"] }),
|
|
655
|
+
/* @__PURE__ */ jsx("span", { children: "·" }),
|
|
656
|
+
/* @__PURE__ */ jsxs("span", {
|
|
657
|
+
className: "text-primary-500 font-semibold",
|
|
658
|
+
children: [msg.usage.totalTokens, " tok"]
|
|
659
|
+
}),
|
|
660
|
+
/* @__PURE__ */ jsx("span", { children: "·" }),
|
|
661
|
+
/* @__PURE__ */ jsxs("span", { children: [msg.usage.elapsedMs, "ms"] })
|
|
662
|
+
]
|
|
663
|
+
}),
|
|
664
|
+
msg.role === "assistant" && msg.contextChip && /* @__PURE__ */ jsx("div", {
|
|
665
|
+
className: "flex items-center gap-1.5 px-2 py-1 mb-1 text-[11px] text-muted",
|
|
666
|
+
children: msg.contextChip.missing ? /* @__PURE__ */ jsx("span", { children: "No docs in scope" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
667
|
+
/* @__PURE__ */ jsxs("svg", {
|
|
668
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
669
|
+
width: "12",
|
|
670
|
+
height: "12",
|
|
671
|
+
viewBox: "0 0 24 24",
|
|
672
|
+
fill: "none",
|
|
673
|
+
stroke: "currentColor",
|
|
674
|
+
strokeWidth: "2",
|
|
675
|
+
strokeLinecap: "round",
|
|
676
|
+
strokeLinejoin: "round",
|
|
677
|
+
children: [/* @__PURE__ */ jsx("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }), /* @__PURE__ */ jsx("polyline", { points: "14 2 14 8 20 8" })]
|
|
678
|
+
}),
|
|
679
|
+
/* @__PURE__ */ jsx("span", { children: "Reading" }),
|
|
680
|
+
/* @__PURE__ */ jsx("code", {
|
|
681
|
+
className: "px-1 py-0.5 rounded bg-surface text-primary-500 text-[10px] font-mono",
|
|
682
|
+
children: msg.contextChip.page
|
|
683
|
+
}),
|
|
684
|
+
/* @__PURE__ */ jsx("span", {
|
|
685
|
+
className: "text-muted",
|
|
686
|
+
children: "·"
|
|
687
|
+
}),
|
|
688
|
+
/* @__PURE__ */ jsxs("span", { children: [msg.contextChip.chars, "c"] })
|
|
689
|
+
] })
|
|
690
|
+
}),
|
|
691
|
+
/* @__PURE__ */ jsx("div", {
|
|
692
|
+
className: `px-3 py-2 rounded-xl ${msg.role === "user" ? "bg-primary-500 text-white rounded-br-none max-w-[90%]" : msg.status === "error" ? "bg-red-500/10 border border-red-500/30 text-red-600 dark:text-red-400 rounded-bl-none" : "bg-surface border border-subtle text-body rounded-bl-none"}`,
|
|
693
|
+
children: msg.role === "user" ? /* @__PURE__ */ jsx("p", {
|
|
694
|
+
className: "whitespace-pre-wrap text-xs",
|
|
695
|
+
children: msg.content
|
|
696
|
+
}) : msg.status === "error" ? /* @__PURE__ */ jsxs("p", {
|
|
697
|
+
className: "text-xs",
|
|
698
|
+
children: [
|
|
699
|
+
/* @__PURE__ */ jsx("strong", { children: "Error:" }),
|
|
700
|
+
" ",
|
|
701
|
+
msg.errorMessage || "Something went wrong."
|
|
702
|
+
]
|
|
703
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
704
|
+
className: "ask-ai-streamdown",
|
|
705
|
+
children: msg.content ? /* @__PURE__ */ jsx(MarkdownRenderer, { content: msg.content }) : msg.status === "reading" ? /* @__PURE__ */ jsxs("span", {
|
|
706
|
+
className: "inline-flex items-center gap-1.5 text-xs text-muted",
|
|
707
|
+
children: [/* @__PURE__ */ jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-primary-500 animate-pulse" }), /* @__PURE__ */ jsx("span", { children: "Reading page…" })]
|
|
708
|
+
}) : /* @__PURE__ */ jsxs("span", {
|
|
709
|
+
className: "inline-flex items-center gap-1.5 text-xs text-muted",
|
|
710
|
+
children: [/* @__PURE__ */ jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-primary-500 animate-pulse" }), /* @__PURE__ */ jsx("span", { children: "Waiting…" })]
|
|
711
|
+
})
|
|
712
|
+
})
|
|
713
|
+
})
|
|
714
|
+
]
|
|
715
|
+
}, i)),
|
|
716
|
+
/* @__PURE__ */ jsx("div", { ref: messagesEndRef })
|
|
717
|
+
]
|
|
718
|
+
}),
|
|
719
|
+
/* @__PURE__ */ jsxs("form", {
|
|
720
|
+
onSubmit: handleSubmit,
|
|
721
|
+
className: "p-3 border-t border-subtle bg-surface/30 flex gap-2 shrink-0",
|
|
722
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
723
|
+
ref: inputRef,
|
|
724
|
+
type: "text",
|
|
725
|
+
value: input,
|
|
726
|
+
onChange: (e) => setInput(e.target.value),
|
|
727
|
+
placeholder: "Ask about this page…",
|
|
728
|
+
className: "flex-1 bg-surface border border-subtle rounded-lg px-2.5 py-1.5 text-xs outline-none text-body focus-within:border-primary-500 transition-colors min-w-0",
|
|
729
|
+
disabled: isLoading
|
|
730
|
+
}), isLoading ? /* @__PURE__ */ jsx("button", {
|
|
731
|
+
type: "button",
|
|
732
|
+
onClick: stopStreaming,
|
|
733
|
+
className: "px-2.5 py-1.5 bg-red-500 hover:bg-red-600 text-white text-xs font-semibold rounded-lg flex items-center justify-center transition-colors cursor-pointer shrink-0",
|
|
734
|
+
title: "Stop generating",
|
|
735
|
+
"aria-label": "Stop generating",
|
|
736
|
+
children: /* @__PURE__ */ jsx("svg", {
|
|
737
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
738
|
+
width: "12",
|
|
739
|
+
height: "12",
|
|
740
|
+
viewBox: "0 0 24 24",
|
|
741
|
+
fill: "currentColor",
|
|
742
|
+
children: /* @__PURE__ */ jsx("rect", {
|
|
743
|
+
x: "6",
|
|
744
|
+
y: "6",
|
|
745
|
+
width: "12",
|
|
746
|
+
height: "12",
|
|
747
|
+
rx: "2"
|
|
748
|
+
})
|
|
749
|
+
})
|
|
750
|
+
}) : /* @__PURE__ */ jsx("button", {
|
|
751
|
+
type: "submit",
|
|
752
|
+
disabled: !input.trim(),
|
|
753
|
+
className: "px-2.5 py-1.5 bg-primary-500 hover:bg-primary-600 disabled:opacity-50 text-white text-xs font-semibold rounded-lg flex items-center justify-center transition-colors cursor-pointer shrink-0",
|
|
754
|
+
"aria-label": "Send question",
|
|
755
|
+
children: /* @__PURE__ */ jsxs("svg", {
|
|
756
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
757
|
+
width: "12",
|
|
758
|
+
height: "12",
|
|
759
|
+
viewBox: "0 0 24 24",
|
|
760
|
+
fill: "none",
|
|
761
|
+
stroke: "currentColor",
|
|
762
|
+
strokeWidth: "2.5",
|
|
763
|
+
strokeLinecap: "round",
|
|
764
|
+
strokeLinejoin: "round",
|
|
765
|
+
children: [/* @__PURE__ */ jsx("path", { d: "m22 2-7 20-4-9-9-4Z" }), /* @__PURE__ */ jsx("path", { d: "M22 2 11 13" })]
|
|
766
|
+
})
|
|
767
|
+
})]
|
|
768
|
+
})
|
|
769
|
+
]
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
//#endregion
|
|
773
|
+
//#region src/client/index.ts
|
|
774
|
+
var client_default = null;
|
|
775
|
+
//#endregion
|
|
776
|
+
export { AskAiBubble, AskAiDialog, MarkdownRenderer, client_default as default, useAskAi };
|