@timbal-ai/timbal-react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,1731 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ AuthGuard: () => AuthGuard,
34
+ Avatar: () => Avatar,
35
+ AvatarFallback: () => AvatarFallback,
36
+ AvatarImage: () => AvatarImage,
37
+ Button: () => Button,
38
+ ComposerAddAttachment: () => ComposerAddAttachment,
39
+ ComposerAttachments: () => ComposerAttachments,
40
+ Dialog: () => Dialog,
41
+ DialogClose: () => DialogClose,
42
+ DialogContent: () => DialogContent,
43
+ DialogOverlay: () => DialogOverlay,
44
+ DialogPortal: () => DialogPortal,
45
+ DialogTitle: () => DialogTitle,
46
+ DialogTrigger: () => DialogTrigger,
47
+ MarkdownText: () => MarkdownText,
48
+ SessionProvider: () => SessionProvider,
49
+ Shimmer: () => Shimmer,
50
+ SyntaxHighlighter: () => syntax_highlighter_default,
51
+ Thread: () => Thread,
52
+ TimbalRuntimeProvider: () => TimbalRuntimeProvider,
53
+ ToolFallback: () => ToolFallback,
54
+ Tooltip: () => Tooltip,
55
+ TooltipContent: () => TooltipContent,
56
+ TooltipIconButton: () => TooltipIconButton,
57
+ TooltipProvider: () => TooltipProvider,
58
+ TooltipTrigger: () => TooltipTrigger,
59
+ UserMessageAttachments: () => UserMessageAttachments,
60
+ authFetch: () => authFetch,
61
+ buttonVariants: () => buttonVariants,
62
+ clearTokens: () => clearTokens,
63
+ cn: () => cn,
64
+ fetchCurrentUser: () => fetchCurrentUser,
65
+ getAccessToken: () => getAccessToken,
66
+ getRefreshToken: () => getRefreshToken,
67
+ refreshAccessToken: () => refreshAccessToken,
68
+ useSession: () => useSession
69
+ });
70
+ module.exports = __toCommonJS(index_exports);
71
+
72
+ // src/runtime/provider.tsx
73
+ var import_react = require("react");
74
+ var import_react2 = require("@assistant-ui/react");
75
+ var import_timbal_sdk = require("@timbal-ai/timbal-sdk");
76
+
77
+ // src/auth/tokens.ts
78
+ var ACCESS_TOKEN_KEY = "timbal_project_access_token";
79
+ var REFRESH_TOKEN_KEY = "timbal_project_refresh_token";
80
+ var getAccessToken = () => localStorage.getItem(ACCESS_TOKEN_KEY);
81
+ var getRefreshToken = () => localStorage.getItem(REFRESH_TOKEN_KEY);
82
+ var clearTokens = () => {
83
+ localStorage.removeItem(ACCESS_TOKEN_KEY);
84
+ localStorage.removeItem(REFRESH_TOKEN_KEY);
85
+ };
86
+ var refreshPromise = null;
87
+ var refreshAccessToken = async () => {
88
+ const refreshToken = getRefreshToken();
89
+ if (!refreshToken) return false;
90
+ if (refreshPromise) return refreshPromise;
91
+ refreshPromise = (async () => {
92
+ try {
93
+ const res = await fetch("/api/auth/refresh", {
94
+ method: "POST",
95
+ headers: { "Content-Type": "application/json" },
96
+ body: JSON.stringify({ refresh_token: refreshToken })
97
+ });
98
+ if (!res.ok) {
99
+ clearTokens();
100
+ return false;
101
+ }
102
+ const data = await res.json();
103
+ if (data.access_token) {
104
+ localStorage.setItem(ACCESS_TOKEN_KEY, data.access_token);
105
+ }
106
+ if (data.refresh_token) {
107
+ localStorage.setItem(REFRESH_TOKEN_KEY, data.refresh_token);
108
+ }
109
+ return true;
110
+ } catch {
111
+ clearTokens();
112
+ return false;
113
+ } finally {
114
+ refreshPromise = null;
115
+ }
116
+ })();
117
+ return refreshPromise;
118
+ };
119
+ var authFetch = async (url, options) => {
120
+ const token = getAccessToken();
121
+ let res = await fetch(url, {
122
+ ...options,
123
+ headers: {
124
+ ...options?.headers,
125
+ ...token ? { Authorization: `Bearer ${token}` } : {}
126
+ }
127
+ });
128
+ if (res.status === 401 && getRefreshToken()) {
129
+ const refreshed = await refreshAccessToken();
130
+ if (refreshed) {
131
+ const newToken = getAccessToken();
132
+ res = await fetch(url, {
133
+ ...options,
134
+ headers: {
135
+ ...options?.headers,
136
+ ...newToken ? { Authorization: `Bearer ${newToken}` } : {}
137
+ }
138
+ });
139
+ }
140
+ }
141
+ return res;
142
+ };
143
+ var fetchCurrentUser = async () => {
144
+ try {
145
+ const token = getAccessToken();
146
+ const res = await fetch("/api/me", {
147
+ headers: token ? { Authorization: `Bearer ${token}` } : {}
148
+ });
149
+ if (!res.ok) return null;
150
+ return await res.json();
151
+ } catch {
152
+ return null;
153
+ }
154
+ };
155
+
156
+ // src/runtime/provider.tsx
157
+ var import_jsx_runtime = require("react/jsx-runtime");
158
+ var parseLine = import_timbal_sdk.parseSSELine;
159
+ var convertMessage = (message) => ({
160
+ role: message.role,
161
+ content: message.content,
162
+ id: message.id
163
+ });
164
+ function findParentId(messages, beforeIndex) {
165
+ const slice = beforeIndex !== void 0 ? messages.slice(0, beforeIndex) : messages;
166
+ for (let i = slice.length - 1; i >= 0; i--) {
167
+ if (slice[i].role === "assistant" && slice[i].runId) return slice[i].runId;
168
+ }
169
+ return null;
170
+ }
171
+ function isTopLevelStart(event) {
172
+ return event.type === "START" && typeof event.run_id === "string" && typeof event.path === "string" && !event.path.includes(".");
173
+ }
174
+ function getTextFromMessage(message) {
175
+ const part = message.content.find((c) => c.type === "text");
176
+ return part?.type === "text" ? part.text : null;
177
+ }
178
+ function waitWithAbort(ms, signal) {
179
+ if (signal.aborted) throw new DOMException("The operation was aborted.", "AbortError");
180
+ return new Promise((resolve, reject) => {
181
+ const timeoutId = setTimeout(() => {
182
+ signal.removeEventListener("abort", onAbort);
183
+ resolve();
184
+ }, ms);
185
+ const onAbort = () => {
186
+ clearTimeout(timeoutId);
187
+ reject(new DOMException("The operation was aborted.", "AbortError"));
188
+ };
189
+ signal.addEventListener("abort", onAbort, { once: true });
190
+ });
191
+ }
192
+ function buildFakeLongResponse(input) {
193
+ const safeInput = input.trim() || "your request";
194
+ const base = [
195
+ `Fake streaming fallback enabled. You asked: "${safeInput}".`,
196
+ "",
197
+ "This is a deliberately long response used to test rendering, scrolling, cancellation, and streaming UX behavior.",
198
+ "",
199
+ "What this stream is exercising:",
200
+ "- Frequent tiny token updates",
201
+ "- Long markdown paragraphs",
202
+ "- Bullet list rendering",
203
+ "- UI action bar behavior while running",
204
+ "- Stop button and abort flow",
205
+ "",
206
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse vitae mi at augue pulvinar porta. Praesent ullamcorper felis at nibh tincidunt, id sagittis mauris interdum. Integer nec semper dui. Curabitur sed fermentum libero. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.",
207
+ "",
208
+ "Aliquam luctus purus non bibendum faucibus. Donec at elit eget massa feugiat ultricies. Quisque condimentum, libero in egestas varius, purus justo aliquam sem, vitae feugiat nunc lorem a justo. Sed non tempor est. In hac habitasse platea dictumst.",
209
+ "",
210
+ "If you can read this arriving progressively, the fallback is working as intended."
211
+ ].join("\n");
212
+ return `${base}
213
+
214
+ ---
215
+
216
+ ${base}`;
217
+ }
218
+ async function streamFakeLongResponse(input, delayMs, signal, onDelta) {
219
+ const fullResponse = buildFakeLongResponse(input);
220
+ let cursor = 0;
221
+ while (cursor < fullResponse.length) {
222
+ if (signal.aborted) throw new DOMException("The operation was aborted.", "AbortError");
223
+ const chunkSize = Math.min(fullResponse.length - cursor, Math.floor(Math.random() * 12) + 2);
224
+ const delta = fullResponse.slice(cursor, cursor + chunkSize);
225
+ cursor += chunkSize;
226
+ onDelta(delta);
227
+ await waitWithAbort(delayMs, signal);
228
+ }
229
+ }
230
+ function TimbalRuntimeProvider({
231
+ workforceId,
232
+ children,
233
+ baseUrl = "/api",
234
+ fetch: fetchFn,
235
+ devFakeStream = false,
236
+ devFakeStreamDelayMs = 75
237
+ }) {
238
+ const [messages, setMessages] = (0, import_react.useState)([]);
239
+ const [isRunning, setIsRunning] = (0, import_react.useState)(false);
240
+ const abortRef = (0, import_react.useRef)(null);
241
+ const messagesRef = (0, import_react.useRef)([]);
242
+ const fetchFnRef = (0, import_react.useRef)(fetchFn ?? authFetch);
243
+ (0, import_react.useEffect)(() => {
244
+ fetchFnRef.current = fetchFn ?? authFetch;
245
+ }, [fetchFn]);
246
+ (0, import_react.useEffect)(() => {
247
+ messagesRef.current = messages;
248
+ }, [messages]);
249
+ const streamAssistantResponse = (0, import_react.useCallback)(
250
+ async (input, userId, assistantId, parentId, signal) => {
251
+ const parts = [];
252
+ const toolIndexById = /* @__PURE__ */ new Map();
253
+ const lastTextPart = () => {
254
+ const last = parts[parts.length - 1];
255
+ if (last?.type === "text") return last;
256
+ const next = { type: "text", text: "" };
257
+ parts.push(next);
258
+ return next;
259
+ };
260
+ const flush = () => {
261
+ setMessages(
262
+ (prev) => prev.map((m) => m.id === assistantId ? { ...m, content: [...parts] } : m)
263
+ );
264
+ };
265
+ const stampRunId = (runId) => {
266
+ setMessages(
267
+ (prev) => prev.map((m) => m.id === userId || m.id === assistantId ? { ...m, runId } : m)
268
+ );
269
+ };
270
+ try {
271
+ if (devFakeStream) {
272
+ const fakeId = `call_${crypto.randomUUID().replace(/-/g, "").slice(0, 24)}`;
273
+ parts.push({ type: "tool-call", toolCallId: fakeId, toolName: "get_datetime", argsText: "{}" });
274
+ flush();
275
+ await waitWithAbort(2e3, signal);
276
+ parts[0].result = `Current datetime (from tool): ${(/* @__PURE__ */ new Date()).toISOString()}`;
277
+ flush();
278
+ await waitWithAbort(300, signal);
279
+ await streamFakeLongResponse(input, devFakeStreamDelayMs, signal, (delta) => {
280
+ lastTextPart().text += delta;
281
+ flush();
282
+ });
283
+ return;
284
+ }
285
+ const res = await fetchFnRef.current(`${baseUrl}/workforce/${workforceId}/stream`, {
286
+ method: "POST",
287
+ headers: { "Content-Type": "application/json" },
288
+ body: JSON.stringify({
289
+ prompt: input,
290
+ context: { parent_id: parentId }
291
+ }),
292
+ signal
293
+ });
294
+ if (!res.ok || !res.body) throw new Error(`Request failed: ${res.status}`);
295
+ const reader = res.body.getReader();
296
+ const decoder = new TextDecoder();
297
+ let buffer = "";
298
+ let capturedRunId = null;
299
+ while (true) {
300
+ const { done, value } = await reader.read();
301
+ if (done) break;
302
+ buffer += decoder.decode(value, { stream: true });
303
+ const lines = buffer.split("\n");
304
+ buffer = lines.pop() ?? "";
305
+ for (const line of lines) {
306
+ const event = parseLine(line);
307
+ if (!event) continue;
308
+ if (!capturedRunId && isTopLevelStart(event)) {
309
+ capturedRunId = event.run_id;
310
+ stampRunId(capturedRunId);
311
+ }
312
+ switch (event.type) {
313
+ case "DELTA": {
314
+ const item = event.item;
315
+ if (!item) break;
316
+ if (item.type === "text_delta" && typeof item.text_delta === "string") {
317
+ lastTextPart().text += item.text_delta;
318
+ flush();
319
+ } else if (item.type === "tool_use") {
320
+ const toolCallId = item.id || `tool-${crypto.randomUUID()}`;
321
+ const inputStr = typeof item.input === "string" ? item.input : JSON.stringify(item.input ?? {});
322
+ parts.push({
323
+ type: "tool-call",
324
+ toolCallId,
325
+ toolName: item.name || "unknown",
326
+ argsText: inputStr
327
+ });
328
+ toolIndexById.set(toolCallId, parts.length - 1);
329
+ flush();
330
+ } else if (item.type === "tool_use_delta") {
331
+ const idx = toolIndexById.get(item.id);
332
+ if (idx !== void 0 && typeof item.input_delta === "string") {
333
+ parts[idx].argsText += item.input_delta;
334
+ flush();
335
+ }
336
+ }
337
+ break;
338
+ }
339
+ case "OUTPUT": {
340
+ const output = event.output;
341
+ if (!output) break;
342
+ if (typeof output === "object" && Array.isArray(output.content)) {
343
+ for (const block of output.content) {
344
+ if (block.type === "tool_use") {
345
+ const id = block.id || `tool-${crypto.randomUUID()}`;
346
+ const idx = toolIndexById.get(id);
347
+ if (idx !== void 0) {
348
+ parts[idx].result = "Tool executed";
349
+ } else {
350
+ const inputStr = typeof block.input === "string" ? block.input : JSON.stringify(block.input ?? {});
351
+ parts.push({
352
+ type: "tool-call",
353
+ toolCallId: id,
354
+ toolName: block.name || "unknown",
355
+ argsText: inputStr,
356
+ result: "Tool executed"
357
+ });
358
+ toolIndexById.set(id, parts.length - 1);
359
+ }
360
+ } else if (block.type === "text" && typeof block.text === "string" && !lastTextPart().text) {
361
+ lastTextPart().text = block.text;
362
+ }
363
+ }
364
+ flush();
365
+ } else if (parts.length === 0) {
366
+ const text = typeof output === "string" ? output : JSON.stringify(output);
367
+ parts.push({ type: "text", text });
368
+ flush();
369
+ }
370
+ break;
371
+ }
372
+ }
373
+ }
374
+ }
375
+ if (buffer.trim()) {
376
+ const event = parseLine(buffer);
377
+ if (event?.type === "OUTPUT" && parts.length === 0 && event.output) {
378
+ const text = typeof event.output === "string" ? event.output : JSON.stringify(event.output);
379
+ parts.push({ type: "text", text });
380
+ flush();
381
+ }
382
+ }
383
+ } catch (err) {
384
+ if (err.name !== "AbortError") {
385
+ if (parts.length === 0) parts.push({ type: "text", text: "Something went wrong." });
386
+ flush();
387
+ }
388
+ } finally {
389
+ setIsRunning(false);
390
+ abortRef.current = null;
391
+ }
392
+ },
393
+ [workforceId, baseUrl, devFakeStream, devFakeStreamDelayMs]
394
+ );
395
+ const onNew = (0, import_react.useCallback)(
396
+ async (message) => {
397
+ const textPart = message.content.find((c) => c.type === "text");
398
+ if (!textPart || textPart.type !== "text") return;
399
+ const input = textPart.text;
400
+ const userId = crypto.randomUUID();
401
+ const assistantId = crypto.randomUUID();
402
+ let base = messagesRef.current;
403
+ if (message.parentId !== null) {
404
+ const parentIdx = base.findIndex((m) => m.id === message.parentId);
405
+ if (parentIdx >= 0) {
406
+ base = base.slice(0, parentIdx + 1);
407
+ }
408
+ }
409
+ const parentId = findParentId(base);
410
+ setMessages([
411
+ ...base,
412
+ { id: userId, role: "user", content: [{ type: "text", text: input }] }
413
+ ]);
414
+ setIsRunning(true);
415
+ setMessages((prev) => [
416
+ ...prev,
417
+ { id: assistantId, role: "assistant", content: [] }
418
+ ]);
419
+ const controller = new AbortController();
420
+ abortRef.current = controller;
421
+ await streamAssistantResponse(input, userId, assistantId, parentId, controller.signal);
422
+ },
423
+ [streamAssistantResponse]
424
+ );
425
+ const onReload = (0, import_react.useCallback)(
426
+ async (messageId) => {
427
+ const current = messagesRef.current;
428
+ const idx = messageId ? current.findIndex((m) => m.id === messageId) : current.length - 2;
429
+ const userMessage = idx >= 0 ? current[idx] : null;
430
+ if (!userMessage || userMessage.role !== "user") return;
431
+ const input = getTextFromMessage(userMessage);
432
+ if (!input) return;
433
+ const assistantId = crypto.randomUUID();
434
+ const parentId = findParentId(current, idx);
435
+ setMessages((prev) => [
436
+ ...prev.slice(0, idx + 1),
437
+ { id: assistantId, role: "assistant", content: [] }
438
+ ]);
439
+ setIsRunning(true);
440
+ const controller = new AbortController();
441
+ abortRef.current = controller;
442
+ await streamAssistantResponse(input, userMessage.id, assistantId, parentId, controller.signal);
443
+ },
444
+ [streamAssistantResponse]
445
+ );
446
+ const onCancel = (0, import_react.useCallback)(async () => {
447
+ abortRef.current?.abort();
448
+ }, []);
449
+ const runtime = (0, import_react2.useExternalStoreRuntime)({
450
+ isRunning,
451
+ messages,
452
+ convertMessage,
453
+ onNew,
454
+ onEdit: onNew,
455
+ onReload,
456
+ onCancel
457
+ });
458
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.AssistantRuntimeProvider, { runtime, children });
459
+ }
460
+
461
+ // src/components/attachment.tsx
462
+ var import_react4 = require("react");
463
+ var import_lucide_react2 = require("lucide-react");
464
+ var import_react5 = require("@assistant-ui/react");
465
+ var import_shallow = require("zustand/shallow");
466
+
467
+ // src/ui/tooltip.tsx
468
+ var import_radix_ui = require("radix-ui");
469
+
470
+ // src/utils.ts
471
+ var import_clsx = require("clsx");
472
+ var import_tailwind_merge = require("tailwind-merge");
473
+ function cn(...inputs) {
474
+ return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
475
+ }
476
+
477
+ // src/ui/tooltip.tsx
478
+ var import_jsx_runtime2 = require("react/jsx-runtime");
479
+ function TooltipProvider({
480
+ delayDuration = 0,
481
+ ...props
482
+ }) {
483
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
484
+ import_radix_ui.Tooltip.Provider,
485
+ {
486
+ "data-slot": "tooltip-provider",
487
+ delayDuration,
488
+ ...props
489
+ }
490
+ );
491
+ }
492
+ function Tooltip({
493
+ ...props
494
+ }) {
495
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_radix_ui.Tooltip.Root, { "data-slot": "tooltip", ...props });
496
+ }
497
+ function TooltipTrigger({
498
+ ...props
499
+ }) {
500
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_radix_ui.Tooltip.Trigger, { "data-slot": "tooltip-trigger", ...props });
501
+ }
502
+ function TooltipContent({
503
+ className,
504
+ sideOffset = 0,
505
+ children,
506
+ ...props
507
+ }) {
508
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_radix_ui.Tooltip.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
509
+ import_radix_ui.Tooltip.Content,
510
+ {
511
+ "data-slot": "tooltip-content",
512
+ sideOffset,
513
+ className: cn(
514
+ "bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
515
+ className
516
+ ),
517
+ ...props,
518
+ children: [
519
+ children,
520
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_radix_ui.Tooltip.Arrow, { className: "bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" })
521
+ ]
522
+ }
523
+ ) });
524
+ }
525
+
526
+ // src/ui/dialog.tsx
527
+ var import_lucide_react = require("lucide-react");
528
+ var import_radix_ui2 = require("radix-ui");
529
+ var import_jsx_runtime3 = require("react/jsx-runtime");
530
+ function Dialog({
531
+ ...props
532
+ }) {
533
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_radix_ui2.Dialog.Root, { "data-slot": "dialog", ...props });
534
+ }
535
+ function DialogTrigger({
536
+ ...props
537
+ }) {
538
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_radix_ui2.Dialog.Trigger, { "data-slot": "dialog-trigger", ...props });
539
+ }
540
+ function DialogPortal({
541
+ ...props
542
+ }) {
543
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_radix_ui2.Dialog.Portal, { "data-slot": "dialog-portal", ...props });
544
+ }
545
+ function DialogClose({
546
+ ...props
547
+ }) {
548
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_radix_ui2.Dialog.Close, { "data-slot": "dialog-close", ...props });
549
+ }
550
+ function DialogOverlay({
551
+ className,
552
+ ...props
553
+ }) {
554
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
555
+ import_radix_ui2.Dialog.Overlay,
556
+ {
557
+ "data-slot": "dialog-overlay",
558
+ className: cn(
559
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
560
+ className
561
+ ),
562
+ ...props
563
+ }
564
+ );
565
+ }
566
+ function DialogContent({
567
+ className,
568
+ children,
569
+ showCloseButton = true,
570
+ ...props
571
+ }) {
572
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(DialogPortal, { "data-slot": "dialog-portal", children: [
573
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(DialogOverlay, {}),
574
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
575
+ import_radix_ui2.Dialog.Content,
576
+ {
577
+ "data-slot": "dialog-content",
578
+ className: cn(
579
+ "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 outline-none sm:max-w-lg",
580
+ className
581
+ ),
582
+ ...props,
583
+ children: [
584
+ children,
585
+ showCloseButton && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
586
+ import_radix_ui2.Dialog.Close,
587
+ {
588
+ "data-slot": "dialog-close",
589
+ className: "ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
590
+ children: [
591
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react.XIcon, {}),
592
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "sr-only", children: "Close" })
593
+ ]
594
+ }
595
+ )
596
+ ]
597
+ }
598
+ )
599
+ ] });
600
+ }
601
+ function DialogTitle({
602
+ className,
603
+ ...props
604
+ }) {
605
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
606
+ import_radix_ui2.Dialog.Title,
607
+ {
608
+ "data-slot": "dialog-title",
609
+ className: cn("text-lg leading-none font-semibold", className),
610
+ ...props
611
+ }
612
+ );
613
+ }
614
+
615
+ // src/ui/avatar.tsx
616
+ var import_radix_ui3 = require("radix-ui");
617
+ var import_jsx_runtime4 = require("react/jsx-runtime");
618
+ function Avatar({
619
+ className,
620
+ size = "default",
621
+ ...props
622
+ }) {
623
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
624
+ import_radix_ui3.Avatar.Root,
625
+ {
626
+ "data-slot": "avatar",
627
+ "data-size": size,
628
+ className: cn(
629
+ "group/avatar relative flex size-8 shrink-0 overflow-hidden rounded-full select-none data-[size=lg]:size-10 data-[size=sm]:size-6",
630
+ className
631
+ ),
632
+ ...props
633
+ }
634
+ );
635
+ }
636
+ function AvatarImage({
637
+ className,
638
+ ...props
639
+ }) {
640
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
641
+ import_radix_ui3.Avatar.Image,
642
+ {
643
+ "data-slot": "avatar-image",
644
+ className: cn("aspect-square size-full", className),
645
+ ...props
646
+ }
647
+ );
648
+ }
649
+ function AvatarFallback({
650
+ className,
651
+ ...props
652
+ }) {
653
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
654
+ import_radix_ui3.Avatar.Fallback,
655
+ {
656
+ "data-slot": "avatar-fallback",
657
+ className: cn(
658
+ "bg-muted text-muted-foreground flex size-full items-center justify-center rounded-full text-sm group-data-[size=sm]/avatar:text-xs",
659
+ className
660
+ ),
661
+ ...props
662
+ }
663
+ );
664
+ }
665
+
666
+ // src/components/tooltip-icon-button.tsx
667
+ var import_react3 = require("react");
668
+ var import_react_slot = require("@radix-ui/react-slot");
669
+
670
+ // src/ui/button.tsx
671
+ var import_class_variance_authority = require("class-variance-authority");
672
+ var import_radix_ui4 = require("radix-ui");
673
+ var import_jsx_runtime5 = require("react/jsx-runtime");
674
+ var buttonVariants = (0, import_class_variance_authority.cva)(
675
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
676
+ {
677
+ variants: {
678
+ variant: {
679
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
680
+ destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
681
+ outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
682
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
683
+ ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
684
+ link: "text-primary underline-offset-4 hover:underline"
685
+ },
686
+ size: {
687
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
688
+ xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
689
+ sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
690
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
691
+ icon: "size-9",
692
+ "icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
693
+ "icon-sm": "size-8",
694
+ "icon-lg": "size-10"
695
+ }
696
+ },
697
+ defaultVariants: {
698
+ variant: "default",
699
+ size: "default"
700
+ }
701
+ }
702
+ );
703
+ function Button({
704
+ className,
705
+ variant = "default",
706
+ size = "default",
707
+ asChild = false,
708
+ ...props
709
+ }) {
710
+ const Comp = asChild ? import_radix_ui4.Slot.Root : "button";
711
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
712
+ Comp,
713
+ {
714
+ "data-slot": "button",
715
+ "data-variant": variant,
716
+ "data-size": size,
717
+ className: cn(buttonVariants({ variant, size, className })),
718
+ ...props
719
+ }
720
+ );
721
+ }
722
+
723
+ // src/components/tooltip-icon-button.tsx
724
+ var import_jsx_runtime6 = require("react/jsx-runtime");
725
+ var TooltipIconButton = (0, import_react3.forwardRef)(({ children, tooltip, side = "bottom", className, ...rest }, ref) => {
726
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Tooltip, { children: [
727
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
728
+ Button,
729
+ {
730
+ variant: "ghost",
731
+ size: "icon",
732
+ ...rest,
733
+ className: cn("aui-button-icon size-6 p-1", className),
734
+ ref,
735
+ children: [
736
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_slot.Slottable, { children }),
737
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "aui-sr-only sr-only", children: tooltip })
738
+ ]
739
+ }
740
+ ) }),
741
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(TooltipContent, { side, children: tooltip })
742
+ ] });
743
+ });
744
+ TooltipIconButton.displayName = "TooltipIconButton";
745
+
746
+ // src/components/attachment.tsx
747
+ var import_jsx_runtime7 = require("react/jsx-runtime");
748
+ var useFileSrc = (file) => {
749
+ const [src, setSrc] = (0, import_react4.useState)(void 0);
750
+ (0, import_react4.useEffect)(() => {
751
+ if (!file) {
752
+ setSrc(void 0);
753
+ return;
754
+ }
755
+ const objectUrl = URL.createObjectURL(file);
756
+ setSrc(objectUrl);
757
+ return () => {
758
+ URL.revokeObjectURL(objectUrl);
759
+ };
760
+ }, [file]);
761
+ return src;
762
+ };
763
+ var useAttachmentSrc = () => {
764
+ const { file, src } = (0, import_react5.useAuiState)(
765
+ (0, import_shallow.useShallow)((s) => {
766
+ if (s.attachment.type !== "image") return {};
767
+ if (s.attachment.file) return { file: s.attachment.file };
768
+ const src2 = s.attachment.content?.filter((c) => c.type === "image")[0]?.image;
769
+ if (!src2) return {};
770
+ return { src: src2 };
771
+ })
772
+ );
773
+ return useFileSrc(file) ?? src;
774
+ };
775
+ var AttachmentPreview = ({ src }) => {
776
+ const [isLoaded, setIsLoaded] = (0, import_react4.useState)(false);
777
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
778
+ "img",
779
+ {
780
+ src,
781
+ alt: "Image Preview",
782
+ className: cn(
783
+ "block h-auto max-h-[80vh] w-auto max-w-full object-contain",
784
+ isLoaded ? "aui-attachment-preview-image-loaded" : "aui-attachment-preview-image-loading invisible"
785
+ ),
786
+ onLoad: () => setIsLoaded(true)
787
+ }
788
+ );
789
+ };
790
+ var AttachmentPreviewDialog = ({ children }) => {
791
+ const src = useAttachmentSrc();
792
+ if (!src) return children;
793
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Dialog, { children: [
794
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
795
+ DialogTrigger,
796
+ {
797
+ className: "aui-attachment-preview-trigger cursor-pointer transition-colors hover:bg-accent/50",
798
+ asChild: true,
799
+ children
800
+ }
801
+ ),
802
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(DialogContent, { className: "aui-attachment-preview-dialog-content p-2 sm:max-w-3xl [&>button]:rounded-full [&>button]:bg-foreground/60 [&>button]:p-1 [&>button]:opacity-100 [&>button]:ring-0! [&_svg]:text-background [&>button]:hover:[&_svg]:text-destructive", children: [
803
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(DialogTitle, { className: "aui-sr-only sr-only", children: "Image Attachment Preview" }),
804
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "aui-attachment-preview relative mx-auto flex max-h-[80dvh] w-full items-center justify-center overflow-hidden bg-background", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AttachmentPreview, { src }) })
805
+ ] })
806
+ ] });
807
+ };
808
+ var AttachmentThumb = () => {
809
+ const isImage = (0, import_react5.useAuiState)((s) => s.attachment.type === "image");
810
+ const src = useAttachmentSrc();
811
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Avatar, { className: "aui-attachment-tile-avatar h-full w-full rounded-none", children: [
812
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
813
+ AvatarImage,
814
+ {
815
+ src,
816
+ alt: "Attachment preview",
817
+ className: "aui-attachment-tile-image object-cover"
818
+ }
819
+ ),
820
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AvatarFallback, { delayMs: isImage ? 200 : 0, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react2.FileText, { className: "aui-attachment-tile-fallback-icon size-8 text-muted-foreground" }) })
821
+ ] });
822
+ };
823
+ var AttachmentUI = () => {
824
+ const aui = (0, import_react5.useAui)();
825
+ const isComposer = aui.attachment.source === "composer";
826
+ const isImage = (0, import_react5.useAuiState)((s) => s.attachment.type === "image");
827
+ const typeLabel = (0, import_react5.useAuiState)((s) => {
828
+ const type = s.attachment.type;
829
+ switch (type) {
830
+ case "image":
831
+ return "Image";
832
+ case "document":
833
+ return "Document";
834
+ case "file":
835
+ return "File";
836
+ default:
837
+ throw new Error(`Unknown attachment type: ${type}`);
838
+ }
839
+ });
840
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Tooltip, { children: [
841
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
842
+ import_react5.AttachmentPrimitive.Root,
843
+ {
844
+ className: cn(
845
+ "aui-attachment-root relative",
846
+ isImage && "aui-attachment-root-composer only:[&>#attachment-tile]:size-24"
847
+ ),
848
+ children: [
849
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AttachmentPreviewDialog, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
850
+ "div",
851
+ {
852
+ className: cn(
853
+ "aui-attachment-tile size-14 cursor-pointer overflow-hidden rounded-[14px] border bg-muted transition-opacity hover:opacity-75",
854
+ isComposer && "aui-attachment-tile-composer border-foreground/20"
855
+ ),
856
+ role: "button",
857
+ id: "attachment-tile",
858
+ "aria-label": `${typeLabel} attachment`,
859
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AttachmentThumb, {})
860
+ }
861
+ ) }) }),
862
+ isComposer && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AttachmentRemove, {})
863
+ ]
864
+ }
865
+ ),
866
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TooltipContent, { side: "top", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react5.AttachmentPrimitive.Name, {}) })
867
+ ] });
868
+ };
869
+ var AttachmentRemove = () => {
870
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react5.AttachmentPrimitive.Remove, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
871
+ TooltipIconButton,
872
+ {
873
+ tooltip: "Remove file",
874
+ className: "aui-attachment-tile-remove absolute top-1.5 right-1.5 size-3.5 rounded-full bg-white text-muted-foreground opacity-100 shadow-sm hover:bg-white! [&_svg]:text-black hover:[&_svg]:text-destructive",
875
+ side: "top",
876
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react2.XIcon, { className: "aui-attachment-remove-icon size-3 dark:stroke-[2.5px]" })
877
+ }
878
+ ) });
879
+ };
880
+ var UserMessageAttachments = () => {
881
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "aui-user-message-attachments-end col-span-full col-start-1 row-start-1 flex w-full flex-row justify-end gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react5.MessagePrimitive.Attachments, { components: { Attachment: AttachmentUI } }) });
882
+ };
883
+ var ComposerAttachments = () => {
884
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "aui-composer-attachments mb-2 flex w-full flex-row items-center gap-2 overflow-x-auto px-1.5 pt-0.5 pb-1 empty:hidden", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
885
+ import_react5.ComposerPrimitive.Attachments,
886
+ {
887
+ components: { Attachment: AttachmentUI }
888
+ }
889
+ ) });
890
+ };
891
+ var ComposerAddAttachment = () => {
892
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react5.ComposerPrimitive.AddAttachment, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
893
+ TooltipIconButton,
894
+ {
895
+ tooltip: "Add Attachment",
896
+ side: "bottom",
897
+ variant: "ghost",
898
+ size: "icon",
899
+ className: "aui-composer-add-attachment size-8.5 rounded-full p-1 font-semibold text-xs hover:bg-muted-foreground/15 dark:border-muted-foreground/15 dark:hover:bg-muted-foreground/30",
900
+ "aria-label": "Add Attachment",
901
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react2.PlusIcon, { className: "aui-attachment-add-icon size-5 stroke-[1.5px]" })
902
+ }
903
+ ) });
904
+ };
905
+
906
+ // src/components/markdown-text.tsx
907
+ var import_dot = require("@assistant-ui/react-markdown/styles/dot.css");
908
+ var import_katex_min = require("katex/dist/katex.min.css");
909
+ var import_react_markdown = require("@assistant-ui/react-markdown");
910
+ var import_remark_gfm = __toESM(require("remark-gfm"), 1);
911
+ var import_remark_math = __toESM(require("remark-math"), 1);
912
+ var import_rehype_katex = __toESM(require("rehype-katex"), 1);
913
+ var import_react7 = require("react");
914
+ var import_lucide_react3 = require("lucide-react");
915
+
916
+ // src/components/syntax-highlighter.tsx
917
+ var import_react6 = require("react");
918
+ var import_core = require("shiki/core");
919
+ var import_javascript = require("shiki/engine/javascript");
920
+ var import_javascript2 = __toESM(require("shiki/langs/javascript.mjs"), 1);
921
+ var import_typescript = __toESM(require("shiki/langs/typescript.mjs"), 1);
922
+ var import_python = __toESM(require("shiki/langs/python.mjs"), 1);
923
+ var import_html = __toESM(require("shiki/langs/html.mjs"), 1);
924
+ var import_css = __toESM(require("shiki/langs/css.mjs"), 1);
925
+ var import_json = __toESM(require("shiki/langs/json.mjs"), 1);
926
+ var import_bash = __toESM(require("shiki/langs/bash.mjs"), 1);
927
+ var import_markdown = __toESM(require("shiki/langs/markdown.mjs"), 1);
928
+ var import_jsx = __toESM(require("shiki/langs/jsx.mjs"), 1);
929
+ var import_tsx = __toESM(require("shiki/langs/tsx.mjs"), 1);
930
+ var import_sql = __toESM(require("shiki/langs/sql.mjs"), 1);
931
+ var import_yaml = __toESM(require("shiki/langs/yaml.mjs"), 1);
932
+ var import_rust = __toESM(require("shiki/langs/rust.mjs"), 1);
933
+ var import_go = __toESM(require("shiki/langs/go.mjs"), 1);
934
+ var import_java = __toESM(require("shiki/langs/java.mjs"), 1);
935
+ var import_c = __toESM(require("shiki/langs/c.mjs"), 1);
936
+ var import_cpp = __toESM(require("shiki/langs/cpp.mjs"), 1);
937
+ var import_vitesse_dark = __toESM(require("shiki/themes/vitesse-dark.mjs"), 1);
938
+ var import_vitesse_light = __toESM(require("shiki/themes/vitesse-light.mjs"), 1);
939
+ var import_jsx_runtime8 = require("react/jsx-runtime");
940
+ var SHIKI_THEME_DARK = "vitesse-dark";
941
+ var SHIKI_THEME_LIGHT = "vitesse-light";
942
+ var highlighterPromise = null;
943
+ function getHighlighter() {
944
+ if (!highlighterPromise) {
945
+ highlighterPromise = (0, import_core.createHighlighterCore)({
946
+ themes: [import_vitesse_dark.default, import_vitesse_light.default],
947
+ langs: [
948
+ import_javascript2.default,
949
+ import_typescript.default,
950
+ import_python.default,
951
+ import_html.default,
952
+ import_css.default,
953
+ import_json.default,
954
+ import_bash.default,
955
+ import_markdown.default,
956
+ import_jsx.default,
957
+ import_tsx.default,
958
+ import_sql.default,
959
+ import_yaml.default,
960
+ import_rust.default,
961
+ import_go.default,
962
+ import_java.default,
963
+ import_c.default,
964
+ import_cpp.default
965
+ ],
966
+ engine: (0, import_javascript.createJavaScriptRegexEngine)()
967
+ });
968
+ }
969
+ return highlighterPromise;
970
+ }
971
+ getHighlighter();
972
+ var ShikiSyntaxHighlighter = ({
973
+ components: { Pre, Code: Code2 },
974
+ language,
975
+ code
976
+ }) => {
977
+ const [html, setHtml] = (0, import_react6.useState)(null);
978
+ (0, import_react6.useEffect)(() => {
979
+ let cancelled = false;
980
+ (async () => {
981
+ try {
982
+ const highlighter = await getHighlighter();
983
+ const loadedLangs = highlighter.getLoadedLanguages();
984
+ if (!loadedLangs.includes(language)) {
985
+ if (!cancelled) setHtml(null);
986
+ return;
987
+ }
988
+ const result = highlighter.codeToHtml(code, {
989
+ lang: language,
990
+ themes: {
991
+ dark: SHIKI_THEME_DARK,
992
+ light: SHIKI_THEME_LIGHT
993
+ }
994
+ });
995
+ if (!cancelled) setHtml(result);
996
+ } catch {
997
+ if (!cancelled) setHtml(null);
998
+ }
999
+ })();
1000
+ return () => {
1001
+ cancelled = true;
1002
+ };
1003
+ }, [code, language]);
1004
+ if (html) {
1005
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1006
+ "div",
1007
+ {
1008
+ className: "shiki-wrapper [&>pre]:!m-0 [&>pre]:!rounded-t-none [&>pre]:!rounded-b-lg [&>pre]:!border [&>pre]:!border-t-0 [&>pre]:!border-border/50 [&>pre]:!p-3 [&>pre]:!text-xs [&>pre]:!leading-relaxed [&>pre]:overflow-x-auto",
1009
+ dangerouslySetInnerHTML: { __html: html }
1010
+ }
1011
+ );
1012
+ }
1013
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Pre, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Code2, { children: code }) });
1014
+ };
1015
+ var syntax_highlighter_default = ShikiSyntaxHighlighter;
1016
+
1017
+ // src/components/markdown-text.tsx
1018
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1019
+ var MarkdownTextImpl = () => {
1020
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1021
+ import_react_markdown.MarkdownTextPrimitive,
1022
+ {
1023
+ remarkPlugins: [import_remark_gfm.default, import_remark_math.default],
1024
+ rehypePlugins: [import_rehype_katex.default],
1025
+ className: "aui-md",
1026
+ components: {
1027
+ ...defaultComponents,
1028
+ SyntaxHighlighter: syntax_highlighter_default
1029
+ }
1030
+ }
1031
+ );
1032
+ };
1033
+ var MarkdownText = (0, import_react7.memo)(MarkdownTextImpl);
1034
+ var CodeHeader = ({ language, code }) => {
1035
+ const { isCopied, copyToClipboard } = useCopyToClipboard();
1036
+ const onCopy = () => {
1037
+ if (!code || isCopied) return;
1038
+ copyToClipboard(code);
1039
+ };
1040
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "aui-code-header flex items-center justify-between rounded-t-lg border border-b-0 border-border/50 bg-zinc-100 px-4 py-2 dark:bg-zinc-800/80", children: [
1041
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "flex items-center gap-2 text-xs font-semibold tracking-wide text-muted-foreground/80 uppercase", children: [
1042
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "inline-block h-2 w-2 rounded-full bg-primary/40" }),
1043
+ language
1044
+ ] }),
1045
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1046
+ TooltipIconButton,
1047
+ {
1048
+ tooltip: isCopied ? "Copied!" : "Copy",
1049
+ onClick: onCopy,
1050
+ className: "transition-colors hover:text-foreground",
1051
+ children: [
1052
+ !isCopied && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react3.CopyIcon, { className: "h-3.5 w-3.5" }),
1053
+ isCopied && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react3.CheckIcon, { className: "h-3.5 w-3.5 text-emerald-500" })
1054
+ ]
1055
+ }
1056
+ )
1057
+ ] });
1058
+ };
1059
+ var useCopyToClipboard = ({
1060
+ copiedDuration = 3e3
1061
+ } = {}) => {
1062
+ const [isCopied, setIsCopied] = (0, import_react7.useState)(false);
1063
+ const copyToClipboard = (value) => {
1064
+ if (!value) return;
1065
+ navigator.clipboard.writeText(value).then(() => {
1066
+ setIsCopied(true);
1067
+ setTimeout(() => setIsCopied(false), copiedDuration);
1068
+ });
1069
+ };
1070
+ return { isCopied, copyToClipboard };
1071
+ };
1072
+ var defaultComponents = (0, import_react_markdown.unstable_memoizeMarkdownComponents)({
1073
+ h1: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1074
+ "h1",
1075
+ {
1076
+ className: cn(
1077
+ "aui-md-h1 mb-3 mt-6 scroll-m-20 text-xl font-bold tracking-tight first:mt-0 last:mb-0",
1078
+ className
1079
+ ),
1080
+ ...props
1081
+ }
1082
+ ),
1083
+ h2: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1084
+ "h2",
1085
+ {
1086
+ className: cn(
1087
+ "aui-md-h2 mb-2.5 mt-5 scroll-m-20 border-b border-border/30 pb-1.5 text-lg font-semibold tracking-tight first:mt-0 last:mb-0",
1088
+ className
1089
+ ),
1090
+ ...props
1091
+ }
1092
+ ),
1093
+ h3: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1094
+ "h3",
1095
+ {
1096
+ className: cn(
1097
+ "aui-md-h3 mb-2 mt-4 scroll-m-20 text-base font-semibold tracking-tight first:mt-0 last:mb-0",
1098
+ className
1099
+ ),
1100
+ ...props
1101
+ }
1102
+ ),
1103
+ h4: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1104
+ "h4",
1105
+ {
1106
+ className: cn(
1107
+ "aui-md-h4 mb-1.5 mt-3 scroll-m-20 text-sm font-semibold first:mt-0 last:mb-0",
1108
+ className
1109
+ ),
1110
+ ...props
1111
+ }
1112
+ ),
1113
+ h5: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1114
+ "h5",
1115
+ {
1116
+ className: cn(
1117
+ "aui-md-h5 mb-1 mt-2.5 text-sm font-medium first:mt-0 last:mb-0",
1118
+ className
1119
+ ),
1120
+ ...props
1121
+ }
1122
+ ),
1123
+ h6: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1124
+ "h6",
1125
+ {
1126
+ className: cn(
1127
+ "aui-md-h6 mb-1 mt-2 text-sm font-medium text-muted-foreground first:mt-0 last:mb-0",
1128
+ className
1129
+ ),
1130
+ ...props
1131
+ }
1132
+ ),
1133
+ p: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1134
+ "p",
1135
+ {
1136
+ className: cn(
1137
+ "aui-md-p my-3 leading-[1.7] first:mt-0 last:mb-0",
1138
+ className
1139
+ ),
1140
+ ...props
1141
+ }
1142
+ ),
1143
+ a: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1144
+ "a",
1145
+ {
1146
+ className: cn(
1147
+ "aui-md-a font-medium text-primary underline decoration-primary/30 underline-offset-[3px] transition-colors hover:decoration-primary/80",
1148
+ className
1149
+ ),
1150
+ target: "_blank",
1151
+ rel: "noopener noreferrer",
1152
+ ...props
1153
+ }
1154
+ ),
1155
+ blockquote: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1156
+ "blockquote",
1157
+ {
1158
+ className: cn(
1159
+ "aui-md-blockquote my-3 border-l-[3px] border-primary/30 bg-muted/30 py-1 pl-4 pr-2 text-muted-foreground italic [&>p]:my-1",
1160
+ className
1161
+ ),
1162
+ ...props
1163
+ }
1164
+ ),
1165
+ ul: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1166
+ "ul",
1167
+ {
1168
+ className: cn(
1169
+ "aui-md-ul my-3 ml-1 list-none space-y-1.5 [&>li]:relative [&>li]:pl-5 [&>li]:before:absolute [&>li]:before:left-0 [&>li]:before:top-[0.6em] [&>li]:before:h-1.5 [&>li]:before:w-1.5 [&>li]:before:rounded-full [&>li]:before:bg-primary/30 [&>li]:before:content-['']",
1170
+ className
1171
+ ),
1172
+ ...props
1173
+ }
1174
+ ),
1175
+ ol: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1176
+ "ol",
1177
+ {
1178
+ className: cn(
1179
+ "aui-md-ol my-3 ml-1 list-none space-y-1.5 [counter-reset:list-counter] [&>li]:relative [&>li]:pl-7 [&>li]:[counter-increment:list-counter] [&>li]:before:absolute [&>li]:before:left-0 [&>li]:before:top-0 [&>li]:before:flex [&>li]:before:h-[1.7em] [&>li]:before:w-5 [&>li]:before:items-center [&>li]:before:justify-center [&>li]:before:rounded-md [&>li]:before:bg-primary/[0.07] [&>li]:before:text-xs [&>li]:before:font-semibold [&>li]:before:text-primary/60 [&>li]:before:content-[counter(list-counter)]",
1180
+ className
1181
+ ),
1182
+ ...props
1183
+ }
1184
+ ),
1185
+ hr: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1186
+ "hr",
1187
+ {
1188
+ className: cn(
1189
+ "aui-md-hr my-6 border-none h-px bg-gradient-to-r from-transparent via-border to-transparent",
1190
+ className
1191
+ ),
1192
+ ...props
1193
+ }
1194
+ ),
1195
+ table: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "my-4 w-full overflow-x-auto rounded-lg border border-border/50", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1196
+ "table",
1197
+ {
1198
+ className: cn("aui-md-table w-full border-collapse text-sm", className),
1199
+ ...props
1200
+ }
1201
+ ) }),
1202
+ th: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1203
+ "th",
1204
+ {
1205
+ className: cn(
1206
+ "aui-md-th border-b border-border/50 bg-muted/60 px-3 py-2 text-left text-xs font-semibold uppercase tracking-wider text-muted-foreground [[align=center]]:text-center [[align=right]]:text-right",
1207
+ className
1208
+ ),
1209
+ ...props
1210
+ }
1211
+ ),
1212
+ td: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1213
+ "td",
1214
+ {
1215
+ className: cn(
1216
+ "aui-md-td border-b border-border/30 px-3 py-2 [[align=center]]:text-center [[align=right]]:text-right",
1217
+ className
1218
+ ),
1219
+ ...props
1220
+ }
1221
+ ),
1222
+ tr: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1223
+ "tr",
1224
+ {
1225
+ className: cn(
1226
+ "aui-md-tr transition-colors hover:bg-muted/30 [&:last-child>td]:border-b-0",
1227
+ className
1228
+ ),
1229
+ ...props
1230
+ }
1231
+ ),
1232
+ li: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("li", { className: cn("aui-md-li leading-[1.7]", className), ...props }),
1233
+ sup: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1234
+ "sup",
1235
+ {
1236
+ className: cn(
1237
+ "aui-md-sup [&>a]:text-[0.7em] [&>a]:font-semibold [&>a]:text-primary/70 [&>a]:no-underline [&>a]:transition-colors [&>a]:hover:text-primary",
1238
+ className
1239
+ ),
1240
+ ...props
1241
+ }
1242
+ ),
1243
+ pre: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1244
+ "pre",
1245
+ {
1246
+ className: cn(
1247
+ "aui-md-pre overflow-x-auto rounded-t-none rounded-b-lg border border-t-0 border-border/50 bg-zinc-50 p-4 text-[13px] leading-relaxed dark:bg-zinc-900/80",
1248
+ className
1249
+ ),
1250
+ ...props
1251
+ }
1252
+ ),
1253
+ code: function Code({ className, ...props }) {
1254
+ const isCodeBlock = (0, import_react_markdown.useIsMarkdownCodeBlock)();
1255
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1256
+ "code",
1257
+ {
1258
+ className: cn(
1259
+ !isCodeBlock && "aui-md-inline-code rounded-[5px] border border-border/60 bg-muted/60 px-[0.4em] py-[0.15em] font-mono text-[0.85em] font-medium text-foreground/90 dark:bg-muted/40",
1260
+ className
1261
+ ),
1262
+ ...props
1263
+ }
1264
+ );
1265
+ },
1266
+ strong: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("strong", { className: cn("font-semibold text-foreground", className), ...props }),
1267
+ em: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("em", { className: cn("italic", className), ...props }),
1268
+ CodeHeader
1269
+ });
1270
+
1271
+ // src/components/tool-fallback.tsx
1272
+ var import_react10 = require("react");
1273
+ var import_lucide_react4 = require("lucide-react");
1274
+
1275
+ // src/ui/shimmer.tsx
1276
+ var import_react8 = require("motion/react");
1277
+ var import_react9 = require("react");
1278
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1279
+ var ShimmerComponent = ({
1280
+ children,
1281
+ as: Component = "p",
1282
+ className,
1283
+ duration = 2,
1284
+ spread = 2
1285
+ }) => {
1286
+ const MotionComponent = import_react8.motion.create(
1287
+ Component
1288
+ );
1289
+ const dynamicSpread = (0, import_react9.useMemo)(
1290
+ () => (children?.length ?? 0) * spread,
1291
+ [children, spread]
1292
+ );
1293
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1294
+ MotionComponent,
1295
+ {
1296
+ animate: { backgroundPosition: "0% center" },
1297
+ className: cn(
1298
+ "relative inline-block bg-[length:250%_100%,auto] bg-clip-text text-transparent",
1299
+ "[--bg:linear-gradient(90deg,#0000_calc(50%-var(--spread)),var(--color-background),#0000_calc(50%+var(--spread)))] [background-repeat:no-repeat,padding-box]",
1300
+ className
1301
+ ),
1302
+ initial: { backgroundPosition: "100% center" },
1303
+ style: {
1304
+ "--spread": `${dynamicSpread}px`,
1305
+ backgroundImage: "var(--bg), linear-gradient(var(--color-muted-foreground), var(--color-muted-foreground))"
1306
+ },
1307
+ transition: {
1308
+ repeat: Number.POSITIVE_INFINITY,
1309
+ duration,
1310
+ ease: "linear"
1311
+ },
1312
+ children
1313
+ }
1314
+ );
1315
+ };
1316
+ var Shimmer = (0, import_react9.memo)(ShimmerComponent);
1317
+
1318
+ // src/components/tool-fallback.tsx
1319
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1320
+ var ToolFallbackImpl = ({
1321
+ toolName,
1322
+ status
1323
+ }) => {
1324
+ if (status?.type !== "running") return null;
1325
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center gap-2 py-1 text-sm text-muted-foreground", children: [
1326
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react4.WrenchIcon, { className: "size-4" }),
1327
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Shimmer, { as: "span", duration: 1.8, spread: 2.5, children: `Using tool: ${toolName}` })
1328
+ ] });
1329
+ };
1330
+ var ToolFallback = (0, import_react10.memo)(
1331
+ ToolFallbackImpl
1332
+ );
1333
+ ToolFallback.displayName = "ToolFallback";
1334
+
1335
+ // src/components/thread.tsx
1336
+ var import_react11 = require("@assistant-ui/react");
1337
+ var import_lucide_react5 = require("lucide-react");
1338
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1339
+ var Thread = () => {
1340
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1341
+ import_react11.ThreadPrimitive.Root,
1342
+ {
1343
+ className: "aui-root aui-thread-root @container flex h-full flex-col bg-background",
1344
+ style: {
1345
+ ["--thread-max-width"]: "44rem"
1346
+ },
1347
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1348
+ import_react11.ThreadPrimitive.Viewport,
1349
+ {
1350
+ turnAnchor: "bottom",
1351
+ className: "aui-thread-viewport relative flex flex-1 flex-col overflow-x-auto overflow-y-scroll px-4 pt-4",
1352
+ children: [
1353
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.AuiIf, { condition: (s) => s.thread.isEmpty, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ThreadWelcome, {}) }),
1354
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1355
+ import_react11.ThreadPrimitive.Messages,
1356
+ {
1357
+ components: {
1358
+ UserMessage,
1359
+ EditComposer,
1360
+ AssistantMessage
1361
+ }
1362
+ }
1363
+ ),
1364
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react11.ThreadPrimitive.ViewportFooter, { className: "aui-thread-viewport-footer sticky bottom-0 mx-auto mt-auto flex w-full max-w-(--thread-max-width) flex-col gap-4 overflow-visible rounded-t-3xl bg-background pb-4 md:pb-6", children: [
1365
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ThreadScrollToBottom, {}),
1366
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Composer, {})
1367
+ ] })
1368
+ ]
1369
+ }
1370
+ )
1371
+ }
1372
+ );
1373
+ };
1374
+ var ThreadScrollToBottom = () => {
1375
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.ThreadPrimitive.ScrollToBottom, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1376
+ TooltipIconButton,
1377
+ {
1378
+ tooltip: "Scroll to bottom",
1379
+ variant: "outline",
1380
+ className: "aui-thread-scroll-to-bottom absolute -top-12 z-10 self-center rounded-full p-4 disabled:invisible dark:bg-background dark:hover:bg-accent",
1381
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react5.ArrowDownIcon, {})
1382
+ }
1383
+ ) });
1384
+ };
1385
+ var ThreadWelcome = () => {
1386
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "aui-thread-welcome-root mx-auto my-auto flex w-full max-w-(--thread-max-width) grow flex-col", children: [
1387
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "aui-thread-welcome-center flex w-full grow flex-col items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "aui-thread-welcome-message flex size-full flex-col items-center justify-center px-4 text-center", children: [
1388
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "fade-in animate-in fill-mode-both relative mb-6 flex size-14 items-center justify-center duration-300", children: [
1389
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "animate-ai-ring-glow absolute inset-0 rounded-2xl bg-gradient-to-br from-primary/15 to-primary/5 ring-1 ring-primary/15" }),
1390
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "animate-ai-pulse-ring absolute inset-0" }),
1391
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1392
+ "svg",
1393
+ {
1394
+ xmlns: "http://www.w3.org/2000/svg",
1395
+ viewBox: "0 0 24 24",
1396
+ fill: "none",
1397
+ stroke: "currentColor",
1398
+ strokeWidth: "1.5",
1399
+ strokeLinecap: "round",
1400
+ strokeLinejoin: "round",
1401
+ className: "animate-ai-breathe relative size-7 text-primary/75",
1402
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z" })
1403
+ }
1404
+ )
1405
+ ] }),
1406
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h1", { className: "aui-thread-welcome-message-inner fade-in slide-in-from-bottom-1 animate-in fill-mode-both font-semibold text-2xl duration-200", children: "How can I help you today?" }),
1407
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "aui-thread-welcome-message-inner fade-in slide-in-from-bottom-1 animate-in fill-mode-both text-muted-foreground mt-2 delay-75 duration-200", children: "Send a message to start a conversation." })
1408
+ ] }) }),
1409
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ThreadSuggestions, {})
1410
+ ] });
1411
+ };
1412
+ var ThreadSuggestions = () => {
1413
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "aui-thread-welcome-suggestions grid w-full @md:grid-cols-2 gap-2 pb-4", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1414
+ import_react11.ThreadPrimitive.Suggestions,
1415
+ {
1416
+ components: {
1417
+ Suggestion: ThreadSuggestionItem
1418
+ }
1419
+ }
1420
+ ) });
1421
+ };
1422
+ var ThreadSuggestionItem = () => {
1423
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "aui-thread-welcome-suggestion-display fade-in slide-in-from-bottom-2 @md:nth-[n+3]:block nth-[n+3]:hidden animate-in fill-mode-both duration-200", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.SuggestionPrimitive.Trigger, { send: true, asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1424
+ Button,
1425
+ {
1426
+ variant: "ghost",
1427
+ className: "aui-thread-welcome-suggestion h-auto w-full @md:flex-col flex-wrap items-start justify-start gap-1 rounded-2xl border px-4 py-3 text-left text-sm transition-colors hover:bg-muted",
1428
+ children: [
1429
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "aui-thread-welcome-suggestion-text-1 font-medium", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.SuggestionPrimitive.Title, {}) }),
1430
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "aui-thread-welcome-suggestion-text-2 text-muted-foreground", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.SuggestionPrimitive.Description, {}) })
1431
+ ]
1432
+ }
1433
+ ) }) });
1434
+ };
1435
+ var Composer = () => {
1436
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.ComposerPrimitive.Root, { className: "aui-composer-root relative mt-3 flex w-full flex-col", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react11.ComposerPrimitive.AttachmentDropzone, { className: "aui-composer-attachment-dropzone flex w-full flex-col rounded-2xl border border-input bg-background px-1 pt-2 outline-none transition-shadow has-[textarea:focus-visible]:border-ring has-[textarea:focus-visible]:ring-2 has-[textarea:focus-visible]:ring-ring/20 data-[dragging=true]:border-ring data-[dragging=true]:border-dashed data-[dragging=true]:bg-accent/50", children: [
1437
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ComposerAttachments, {}),
1438
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1439
+ import_react11.ComposerPrimitive.Input,
1440
+ {
1441
+ placeholder: "Send a message...",
1442
+ className: "aui-composer-input mb-1 max-h-32 min-h-14 w-full resize-none bg-transparent px-4 pt-2 pb-3 text-sm outline-none placeholder:text-muted-foreground focus-visible:ring-0",
1443
+ rows: 1,
1444
+ autoFocus: true,
1445
+ "aria-label": "Message input"
1446
+ }
1447
+ ),
1448
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ComposerAction, {})
1449
+ ] }) });
1450
+ };
1451
+ var ComposerAction = () => {
1452
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "aui-composer-action-wrapper relative mx-2 mb-2 flex items-center justify-end", children: [
1453
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.AuiIf, { condition: (s) => !s.thread.isRunning, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.ComposerPrimitive.Send, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1454
+ TooltipIconButton,
1455
+ {
1456
+ tooltip: "Send message",
1457
+ side: "bottom",
1458
+ type: "submit",
1459
+ variant: "default",
1460
+ size: "icon",
1461
+ className: "aui-composer-send size-8 rounded-full",
1462
+ "aria-label": "Send message",
1463
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react5.ArrowUpIcon, { className: "aui-composer-send-icon size-4" })
1464
+ }
1465
+ ) }) }),
1466
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.AuiIf, { condition: (s) => s.thread.isRunning, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.ComposerPrimitive.Cancel, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1467
+ Button,
1468
+ {
1469
+ type: "button",
1470
+ variant: "default",
1471
+ size: "icon",
1472
+ className: "aui-composer-cancel size-8 rounded-full",
1473
+ "aria-label": "Stop generating",
1474
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react5.SquareIcon, { className: "aui-composer-cancel-icon size-3 fill-current" })
1475
+ }
1476
+ ) }) })
1477
+ ] });
1478
+ };
1479
+ var MessageError = () => {
1480
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.MessagePrimitive.Error, { children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.ErrorPrimitive.Root, { className: "aui-message-error-root mt-2 rounded-md border border-destructive bg-destructive/10 p-3 text-destructive text-sm dark:bg-destructive/5 dark:text-red-200", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.ErrorPrimitive.Message, { className: "aui-message-error-message line-clamp-2" }) }) });
1481
+ };
1482
+ var AssistantMessage = () => {
1483
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1484
+ import_react11.MessagePrimitive.Root,
1485
+ {
1486
+ className: "aui-assistant-message-root fade-in slide-in-from-bottom-1 relative mx-auto w-full max-w-(--thread-max-width) animate-in py-3 duration-150",
1487
+ "data-role": "assistant",
1488
+ children: [
1489
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "aui-assistant-message-content wrap-break-word px-2 text-foreground leading-relaxed", children: [
1490
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1491
+ import_react11.MessagePrimitive.Parts,
1492
+ {
1493
+ components: {
1494
+ Text: MarkdownText,
1495
+ tools: { Fallback: ToolFallback }
1496
+ }
1497
+ }
1498
+ ),
1499
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(MessageError, {})
1500
+ ] }),
1501
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "aui-assistant-message-footer mt-1 ml-2 flex", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(AssistantActionBar, {}) })
1502
+ ]
1503
+ }
1504
+ );
1505
+ };
1506
+ var AssistantActionBar = () => {
1507
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1508
+ import_react11.ActionBarPrimitive.Root,
1509
+ {
1510
+ hideWhenRunning: true,
1511
+ autohide: "not-last",
1512
+ autohideFloat: "single-branch",
1513
+ className: "aui-assistant-action-bar-root col-start-3 row-start-2 -ml-1 flex gap-1 text-muted-foreground data-floating:absolute data-floating:rounded-md data-floating:border data-floating:bg-background data-floating:p-1 data-floating:shadow-sm",
1514
+ children: [
1515
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.ActionBarPrimitive.Copy, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(TooltipIconButton, { tooltip: "Copy", children: [
1516
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.AuiIf, { condition: (s) => s.message.isCopied, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react5.CheckIcon, {}) }),
1517
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.AuiIf, { condition: (s) => !s.message.isCopied, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react5.CopyIcon, {}) })
1518
+ ] }) }),
1519
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.ActionBarPrimitive.Reload, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TooltipIconButton, { tooltip: "Refresh", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react5.RefreshCwIcon, {}) }) }),
1520
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react11.ActionBarMorePrimitive.Root, { children: [
1521
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.ActionBarMorePrimitive.Trigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1522
+ TooltipIconButton,
1523
+ {
1524
+ tooltip: "More",
1525
+ className: "data-[state=open]:bg-accent",
1526
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react5.MoreHorizontalIcon, {})
1527
+ }
1528
+ ) }),
1529
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1530
+ import_react11.ActionBarMorePrimitive.Content,
1531
+ {
1532
+ side: "bottom",
1533
+ align: "start",
1534
+ className: "aui-action-bar-more-content z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
1535
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.ActionBarPrimitive.ExportMarkdown, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react11.ActionBarMorePrimitive.Item, { className: "aui-action-bar-more-item flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground", children: [
1536
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react5.DownloadIcon, { className: "size-4" }),
1537
+ "Export as Markdown"
1538
+ ] }) })
1539
+ }
1540
+ )
1541
+ ] })
1542
+ ]
1543
+ }
1544
+ );
1545
+ };
1546
+ var UserMessage = () => {
1547
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1548
+ import_react11.MessagePrimitive.Root,
1549
+ {
1550
+ className: "aui-user-message-root fade-in slide-in-from-bottom-1 mx-auto grid w-full max-w-(--thread-max-width) animate-in auto-rows-auto grid-cols-[minmax(72px,1fr)_auto] content-start gap-y-2 px-2 py-3 duration-150 [&:where(>*)]:col-start-2",
1551
+ "data-role": "user",
1552
+ children: [
1553
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(UserMessageAttachments, {}),
1554
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "aui-user-message-content-wrapper relative col-start-2 min-w-0", children: [
1555
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "aui-user-message-content wrap-break-word rounded-2xl bg-muted px-4 py-2.5 text-foreground", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.MessagePrimitive.Parts, {}) }),
1556
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "aui-user-action-bar-wrapper absolute top-1/2 left-0 -translate-x-full -translate-y-1/2 pr-2", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(UserActionBar, {}) })
1557
+ ] })
1558
+ ]
1559
+ }
1560
+ );
1561
+ };
1562
+ var UserActionBar = () => {
1563
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1564
+ import_react11.ActionBarPrimitive.Root,
1565
+ {
1566
+ hideWhenRunning: true,
1567
+ autohide: "not-last",
1568
+ className: "aui-user-action-bar-root flex flex-col items-end",
1569
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.ActionBarPrimitive.Edit, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TooltipIconButton, { tooltip: "Edit", className: "aui-user-action-edit p-4", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react5.PencilIcon, {}) }) })
1570
+ }
1571
+ );
1572
+ };
1573
+ var EditComposer = () => {
1574
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.MessagePrimitive.Root, { className: "aui-edit-composer-wrapper mx-auto flex w-full max-w-(--thread-max-width) flex-col px-2 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react11.ComposerPrimitive.Root, { className: "aui-edit-composer-root ml-auto flex w-full max-w-[85%] flex-col rounded-2xl bg-muted", children: [
1575
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1576
+ import_react11.ComposerPrimitive.Input,
1577
+ {
1578
+ className: "aui-edit-composer-input min-h-14 w-full resize-none bg-transparent p-4 text-foreground text-sm outline-none",
1579
+ autoFocus: true
1580
+ }
1581
+ ),
1582
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "aui-edit-composer-footer mx-3 mb-3 flex items-center gap-2 self-end", children: [
1583
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.ComposerPrimitive.Cancel, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Button, { variant: "ghost", size: "sm", children: "Cancel" }) }),
1584
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.ComposerPrimitive.Send, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Button, { size: "sm", children: "Update" }) })
1585
+ ] })
1586
+ ] }) });
1587
+ };
1588
+
1589
+ // src/auth/provider.tsx
1590
+ var import_react12 = require("react");
1591
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1592
+ var SessionContext = (0, import_react12.createContext)(void 0);
1593
+ var useSession = () => {
1594
+ const context = (0, import_react12.useContext)(SessionContext);
1595
+ if (context === void 0) {
1596
+ throw new Error("useSession must be used within a SessionProvider");
1597
+ }
1598
+ return context;
1599
+ };
1600
+ var SessionProvider = ({
1601
+ children,
1602
+ enabled = true
1603
+ }) => {
1604
+ const [user, setUser] = (0, import_react12.useState)(null);
1605
+ const [loading, setLoading] = (0, import_react12.useState)(enabled);
1606
+ (0, import_react12.useEffect)(() => {
1607
+ if (!enabled) {
1608
+ setLoading(false);
1609
+ return;
1610
+ }
1611
+ let ignore = false;
1612
+ const restoreSession = async () => {
1613
+ try {
1614
+ const u = await fetchCurrentUser();
1615
+ if (ignore) return;
1616
+ if (u) {
1617
+ setUser(u);
1618
+ setLoading(false);
1619
+ return;
1620
+ }
1621
+ if (getRefreshToken()) {
1622
+ const ok = await refreshAccessToken();
1623
+ if (ignore) return;
1624
+ if (ok) {
1625
+ const refreshedUser = await fetchCurrentUser();
1626
+ if (ignore) return;
1627
+ if (refreshedUser) {
1628
+ setUser(refreshedUser);
1629
+ setLoading(false);
1630
+ return;
1631
+ }
1632
+ }
1633
+ }
1634
+ } catch {
1635
+ if (ignore) return;
1636
+ clearTokens();
1637
+ }
1638
+ setLoading(false);
1639
+ };
1640
+ restoreSession();
1641
+ return () => {
1642
+ ignore = true;
1643
+ };
1644
+ }, [enabled]);
1645
+ const logout = (0, import_react12.useCallback)(() => {
1646
+ clearTokens();
1647
+ setUser(null);
1648
+ const returnTo = encodeURIComponent(
1649
+ window.location.pathname + window.location.search
1650
+ );
1651
+ fetch("/api/auth/logout", { method: "POST" }).finally(
1652
+ () => window.location.href = `/api/auth/login?return_to=${returnTo}`
1653
+ );
1654
+ }, []);
1655
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1656
+ SessionContext.Provider,
1657
+ {
1658
+ value: {
1659
+ user,
1660
+ loading,
1661
+ isAuthenticated: !!user,
1662
+ logout
1663
+ },
1664
+ children
1665
+ }
1666
+ );
1667
+ };
1668
+
1669
+ // src/auth/guard.tsx
1670
+ var import_lucide_react6 = require("lucide-react");
1671
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1672
+ var AuthGuard = ({
1673
+ children,
1674
+ requireAuth = false,
1675
+ enabled = true
1676
+ }) => {
1677
+ const { isAuthenticated, loading } = useSession();
1678
+ if (!enabled) {
1679
+ return children;
1680
+ }
1681
+ if (loading) {
1682
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex items-center justify-center h-screen", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react6.Loader2, { className: "w-8 h-8 animate-spin" }) });
1683
+ }
1684
+ if (requireAuth && !isAuthenticated) {
1685
+ const returnTo = encodeURIComponent(
1686
+ window.location.pathname + window.location.search
1687
+ );
1688
+ window.location.href = `/api/auth/login?return_to=${returnTo}`;
1689
+ return null;
1690
+ }
1691
+ return children;
1692
+ };
1693
+ // Annotate the CommonJS export names for ESM import in node:
1694
+ 0 && (module.exports = {
1695
+ AuthGuard,
1696
+ Avatar,
1697
+ AvatarFallback,
1698
+ AvatarImage,
1699
+ Button,
1700
+ ComposerAddAttachment,
1701
+ ComposerAttachments,
1702
+ Dialog,
1703
+ DialogClose,
1704
+ DialogContent,
1705
+ DialogOverlay,
1706
+ DialogPortal,
1707
+ DialogTitle,
1708
+ DialogTrigger,
1709
+ MarkdownText,
1710
+ SessionProvider,
1711
+ Shimmer,
1712
+ SyntaxHighlighter,
1713
+ Thread,
1714
+ TimbalRuntimeProvider,
1715
+ ToolFallback,
1716
+ Tooltip,
1717
+ TooltipContent,
1718
+ TooltipIconButton,
1719
+ TooltipProvider,
1720
+ TooltipTrigger,
1721
+ UserMessageAttachments,
1722
+ authFetch,
1723
+ buttonVariants,
1724
+ clearTokens,
1725
+ cn,
1726
+ fetchCurrentUser,
1727
+ getAccessToken,
1728
+ getRefreshToken,
1729
+ refreshAccessToken,
1730
+ useSession
1731
+ });