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