@timbal-ai/timbal-react 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +326 -44
- package/dist/index.cjs +2698 -459
- package/dist/index.d.cts +823 -17
- package/dist/index.d.ts +823 -17
- package/dist/index.esm.js +2671 -461
- package/dist/styles.css +210 -0
- package/package.json +3 -2
package/dist/index.esm.js
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
// src/runtime/provider.tsx
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
createContext,
|
|
4
|
+
useCallback,
|
|
5
|
+
useContext,
|
|
6
|
+
useEffect,
|
|
7
|
+
useMemo,
|
|
8
|
+
useRef,
|
|
9
|
+
useState
|
|
10
|
+
} from "react";
|
|
3
11
|
import {
|
|
4
12
|
useExternalStoreRuntime,
|
|
5
13
|
AssistantRuntimeProvider
|
|
@@ -10,7 +18,9 @@ import { parseSSELine } from "@timbal-ai/timbal-sdk";
|
|
|
10
18
|
var ACCESS_TOKEN_KEY = "timbal_project_access_token";
|
|
11
19
|
var REFRESH_TOKEN_KEY = "timbal_project_refresh_token";
|
|
12
20
|
var getAccessToken = () => localStorage.getItem(ACCESS_TOKEN_KEY);
|
|
21
|
+
var setAccessToken = (token) => localStorage.setItem(ACCESS_TOKEN_KEY, token);
|
|
13
22
|
var getRefreshToken = () => localStorage.getItem(REFRESH_TOKEN_KEY);
|
|
23
|
+
var setRefreshToken = (token) => localStorage.setItem(REFRESH_TOKEN_KEY, token);
|
|
14
24
|
var clearTokens = () => {
|
|
15
25
|
localStorage.removeItem(ACCESS_TOKEN_KEY);
|
|
16
26
|
localStorage.removeItem(REFRESH_TOKEN_KEY);
|
|
@@ -85,13 +95,506 @@ var fetchCurrentUser = async () => {
|
|
|
85
95
|
}
|
|
86
96
|
};
|
|
87
97
|
|
|
98
|
+
// src/artifacts/types.ts
|
|
99
|
+
function isArtifact(value) {
|
|
100
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.type === "string";
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/runtime/reducer.ts
|
|
104
|
+
function createReducerState() {
|
|
105
|
+
return { parts: [], toolIndexById: /* @__PURE__ */ new Map() };
|
|
106
|
+
}
|
|
107
|
+
function reduceSseEvent(state, event) {
|
|
108
|
+
switch (event.type) {
|
|
109
|
+
case "DELTA":
|
|
110
|
+
return reduceDelta(state, event.item);
|
|
111
|
+
case "OUTPUT": {
|
|
112
|
+
const path = event.path;
|
|
113
|
+
const isNested = typeof path === "string" && path.includes(".");
|
|
114
|
+
if (!isNested) {
|
|
115
|
+
const errorMessage = readErrorMessage(event);
|
|
116
|
+
if (errorMessage) {
|
|
117
|
+
state.parts.push({ type: "text", text: `**Error:** ${errorMessage}` });
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (isNested) {
|
|
122
|
+
return reduceNestedOutput(
|
|
123
|
+
state,
|
|
124
|
+
path,
|
|
125
|
+
event.output
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
return reduceOutput(
|
|
129
|
+
state,
|
|
130
|
+
event.output
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
default:
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function reduceDelta(state, item) {
|
|
138
|
+
if (!item) return false;
|
|
139
|
+
if (item.type === "text_delta" && typeof item.text_delta === "string") {
|
|
140
|
+
lastTextPart(state).text += item.text_delta;
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
if (item.type === "thinking_delta" && typeof item.thinking_delta === "string") {
|
|
144
|
+
lastThinkingPart(state).text += item.thinking_delta;
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
if (item.type === "tool_use") {
|
|
148
|
+
const toolCallId = item.id || `tool-${crypto.randomUUID()}`;
|
|
149
|
+
const inputStr = stringifyInput(item.input);
|
|
150
|
+
const part = {
|
|
151
|
+
type: "tool-call",
|
|
152
|
+
toolCallId,
|
|
153
|
+
toolName: item.name || "unknown",
|
|
154
|
+
argsText: inputStr,
|
|
155
|
+
status: "running"
|
|
156
|
+
};
|
|
157
|
+
state.parts.push(part);
|
|
158
|
+
state.toolIndexById.set(toolCallId, state.parts.length - 1);
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
if (item.type === "tool_use_delta") {
|
|
162
|
+
const idx = state.toolIndexById.get(item.id);
|
|
163
|
+
if (idx !== void 0 && typeof item.input_delta === "string") {
|
|
164
|
+
state.parts[idx].argsText += item.input_delta;
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
function reduceNestedOutput(state, path, output) {
|
|
171
|
+
if (!output || typeof output !== "object") return false;
|
|
172
|
+
if (!Array.isArray(output.content) && isArtifact(output)) {
|
|
173
|
+
const toolName = toolNameFromPath(path);
|
|
174
|
+
if (toolName && attachToolResult(state, { toolName, result: output })) {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return reduceOutput(state, output, { toolResultsOnly: true, allowOrphan: false });
|
|
179
|
+
}
|
|
180
|
+
function reduceOutput(state, output, options) {
|
|
181
|
+
if (!output) return false;
|
|
182
|
+
if (typeof output === "string") {
|
|
183
|
+
if (state.parts.length === 0) {
|
|
184
|
+
state.parts.push({ type: "text", text: output });
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
if (Array.isArray(output.content)) {
|
|
190
|
+
let changed = false;
|
|
191
|
+
const blocks = output.content;
|
|
192
|
+
for (const block of blocks) {
|
|
193
|
+
if (block.type === "tool_use") {
|
|
194
|
+
if (!options?.toolResultsOnly && recordToolUse(state, block)) {
|
|
195
|
+
changed = true;
|
|
196
|
+
}
|
|
197
|
+
} else if (block.type === "tool_result") {
|
|
198
|
+
if (recordToolResult(state, block, options)) changed = true;
|
|
199
|
+
} else if (!options?.toolResultsOnly) {
|
|
200
|
+
if (block.type === "text" && typeof block.text === "string" && !lastTextPart(state).text) {
|
|
201
|
+
lastTextPart(state).text = block.text;
|
|
202
|
+
changed = true;
|
|
203
|
+
} else if (block.type === "thinking" && typeof block.thinking === "string" && !lastThinkingPart(state).text) {
|
|
204
|
+
lastThinkingPart(state).text = block.thinking;
|
|
205
|
+
changed = true;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return changed;
|
|
210
|
+
}
|
|
211
|
+
if (state.parts.length === 0) {
|
|
212
|
+
state.parts.push({ type: "text", text: JSON.stringify(output) });
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
function recordToolUse(state, block) {
|
|
218
|
+
const id = block.id || `tool-${crypto.randomUUID()}`;
|
|
219
|
+
if (state.toolIndexById.has(id)) return false;
|
|
220
|
+
const inputStr = stringifyInput(block.input);
|
|
221
|
+
const part = {
|
|
222
|
+
type: "tool-call",
|
|
223
|
+
toolCallId: id,
|
|
224
|
+
toolName: block.name || "unknown",
|
|
225
|
+
argsText: inputStr,
|
|
226
|
+
status: "running"
|
|
227
|
+
};
|
|
228
|
+
state.parts.push(part);
|
|
229
|
+
state.toolIndexById.set(id, state.parts.length - 1);
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
function recordToolResult(state, block, options) {
|
|
233
|
+
const allowOrphan = options?.allowOrphan !== false;
|
|
234
|
+
const id = block.id || block.tool_use_id || "";
|
|
235
|
+
const { result, resultText } = parseToolResultContent(block.content);
|
|
236
|
+
const toolName = block.name || void 0;
|
|
237
|
+
if (id) {
|
|
238
|
+
const idx = state.toolIndexById.get(id);
|
|
239
|
+
if (idx !== void 0) {
|
|
240
|
+
const part2 = state.parts[idx];
|
|
241
|
+
part2.result = result;
|
|
242
|
+
if (resultText) part2.resultText = resultText;
|
|
243
|
+
part2.status = "complete";
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
if (!allowOrphan) return false;
|
|
247
|
+
}
|
|
248
|
+
if (!id && toolName && attachToolResult(state, {
|
|
249
|
+
toolName,
|
|
250
|
+
result,
|
|
251
|
+
resultText
|
|
252
|
+
})) {
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
if (!id || !allowOrphan) return false;
|
|
256
|
+
const part = {
|
|
257
|
+
type: "tool-call",
|
|
258
|
+
toolCallId: id,
|
|
259
|
+
toolName: toolName || "unknown",
|
|
260
|
+
argsText: "",
|
|
261
|
+
result,
|
|
262
|
+
resultText,
|
|
263
|
+
status: "complete"
|
|
264
|
+
};
|
|
265
|
+
state.parts.push(part);
|
|
266
|
+
state.toolIndexById.set(id, state.parts.length - 1);
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
function toolNameFromPath(path) {
|
|
270
|
+
const segment = path.split(".").pop();
|
|
271
|
+
return segment && segment !== "agent" && segment !== "llm" ? segment : null;
|
|
272
|
+
}
|
|
273
|
+
function attachToolResult(state, {
|
|
274
|
+
toolCallId,
|
|
275
|
+
toolName,
|
|
276
|
+
result,
|
|
277
|
+
resultText
|
|
278
|
+
}) {
|
|
279
|
+
if (toolCallId) {
|
|
280
|
+
const idx = state.toolIndexById.get(toolCallId);
|
|
281
|
+
if (idx !== void 0) {
|
|
282
|
+
const part = state.parts[idx];
|
|
283
|
+
part.result = result;
|
|
284
|
+
if (resultText) part.resultText = resultText;
|
|
285
|
+
part.status = "complete";
|
|
286
|
+
return true;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (toolName) {
|
|
290
|
+
for (let i = state.parts.length - 1; i >= 0; i--) {
|
|
291
|
+
const part = state.parts[i];
|
|
292
|
+
if (part.type === "tool-call" && part.toolName === toolName && part.result === void 0) {
|
|
293
|
+
part.result = result;
|
|
294
|
+
if (resultText) part.resultText = resultText;
|
|
295
|
+
part.status = "complete";
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
function parseToolResultContent(content) {
|
|
303
|
+
if (typeof content === "string") {
|
|
304
|
+
return { result: content, resultText: content };
|
|
305
|
+
}
|
|
306
|
+
if (!Array.isArray(content)) {
|
|
307
|
+
return { result: content };
|
|
308
|
+
}
|
|
309
|
+
const textChunks = [];
|
|
310
|
+
for (const item of content) {
|
|
311
|
+
if (typeof item === "string") {
|
|
312
|
+
textChunks.push(item);
|
|
313
|
+
} else if (item && typeof item === "object") {
|
|
314
|
+
const obj = item;
|
|
315
|
+
if (obj.type === "text" && typeof obj.text === "string") {
|
|
316
|
+
textChunks.push(obj.text);
|
|
317
|
+
} else if (obj.type === "thinking" && typeof obj.thinking === "string") {
|
|
318
|
+
textChunks.push(obj.thinking);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return {
|
|
323
|
+
result: content,
|
|
324
|
+
resultText: textChunks.length > 0 ? textChunks.join("\n") : void 0
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
function readErrorMessage(event) {
|
|
328
|
+
const status = event.status;
|
|
329
|
+
const isErrorStatus = status?.code === "error";
|
|
330
|
+
const error = event.error;
|
|
331
|
+
let type = null;
|
|
332
|
+
let message = null;
|
|
333
|
+
if (isErrorStatus && typeof status?.message === "string" && status.message.length > 0) {
|
|
334
|
+
message = status.message;
|
|
335
|
+
}
|
|
336
|
+
if (!message && typeof error === "string" && error.length > 0) {
|
|
337
|
+
message = error;
|
|
338
|
+
} else if (error && typeof error === "object") {
|
|
339
|
+
const obj = error;
|
|
340
|
+
if (typeof obj.type === "string" && obj.type.length > 0) {
|
|
341
|
+
type = obj.type;
|
|
342
|
+
}
|
|
343
|
+
if (!message && typeof obj.message === "string" && obj.message.length > 0) {
|
|
344
|
+
message = obj.message;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
if (!message && !isErrorStatus) return null;
|
|
348
|
+
if (!message) return "The agent failed to generate a response.";
|
|
349
|
+
const compact = compactError(message);
|
|
350
|
+
return type ? `${type}: ${compact}` : compact;
|
|
351
|
+
}
|
|
352
|
+
var ERROR_MAX_CHARS = 480;
|
|
353
|
+
function compactError(message) {
|
|
354
|
+
const trimmed = message.split(/\n\s*Traceback \(most recent call last\):/u)[0].trim();
|
|
355
|
+
if (trimmed.length <= ERROR_MAX_CHARS) return trimmed;
|
|
356
|
+
return `${trimmed.slice(0, ERROR_MAX_CHARS).trimEnd()}\u2026`;
|
|
357
|
+
}
|
|
358
|
+
function stringifyInput(input) {
|
|
359
|
+
if (input === void 0 || input === null) return "{}";
|
|
360
|
+
return typeof input === "string" ? input : JSON.stringify(input);
|
|
361
|
+
}
|
|
362
|
+
function lastTextPart(state) {
|
|
363
|
+
const last = state.parts[state.parts.length - 1];
|
|
364
|
+
if (last?.type === "text") return last;
|
|
365
|
+
const next = { type: "text", text: "" };
|
|
366
|
+
state.parts.push(next);
|
|
367
|
+
return next;
|
|
368
|
+
}
|
|
369
|
+
function lastThinkingPart(state) {
|
|
370
|
+
const last = state.parts[state.parts.length - 1];
|
|
371
|
+
if (last?.type === "thinking") return last;
|
|
372
|
+
const next = { type: "thinking", text: "" };
|
|
373
|
+
state.parts.push(next);
|
|
374
|
+
return next;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// src/runtime/attachments.ts
|
|
378
|
+
async function extractAttachment(attachment) {
|
|
379
|
+
const file = attachment.file;
|
|
380
|
+
let src = null;
|
|
381
|
+
let contentType;
|
|
382
|
+
let name = attachment.name ?? file?.name;
|
|
383
|
+
const content = attachment.content;
|
|
384
|
+
if (content) {
|
|
385
|
+
for (const block of content) {
|
|
386
|
+
if (block.type === "image" && typeof block.image === "string") {
|
|
387
|
+
src = block.image;
|
|
388
|
+
if (typeof block.mimeType === "string") {
|
|
389
|
+
contentType = block.mimeType;
|
|
390
|
+
}
|
|
391
|
+
break;
|
|
392
|
+
}
|
|
393
|
+
if (block.type === "file" && typeof block.data === "string") {
|
|
394
|
+
src = block.data;
|
|
395
|
+
if (typeof block.mimeType === "string") contentType = block.mimeType;
|
|
396
|
+
break;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
if (src === null && file) {
|
|
401
|
+
src = await fileToDataUrl(file);
|
|
402
|
+
if (!contentType) contentType = file.type || void 0;
|
|
403
|
+
if (!name) name = file.name;
|
|
404
|
+
}
|
|
405
|
+
if (!src) return null;
|
|
406
|
+
if (!contentType) contentType = mimeFromDataUrl(src);
|
|
407
|
+
const rawType = String(attachment.type ?? "file");
|
|
408
|
+
const type = rawType === "image" || rawType === "document" ? rawType : "file";
|
|
409
|
+
return {
|
|
410
|
+
id: attachment.id ?? crypto.randomUUID(),
|
|
411
|
+
type,
|
|
412
|
+
...name !== void 0 ? { name } : {},
|
|
413
|
+
...contentType !== void 0 ? { contentType } : {},
|
|
414
|
+
dataUrl: src
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
function fileToDataUrl(file) {
|
|
418
|
+
return new Promise((resolve, reject) => {
|
|
419
|
+
const reader = new FileReader();
|
|
420
|
+
reader.onload = () => resolve(reader.result);
|
|
421
|
+
reader.onerror = () => reject(reader.error);
|
|
422
|
+
reader.readAsDataURL(file);
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
function mimeFromDataUrl(dataUrl) {
|
|
426
|
+
const match = /^data:([^;,]+)[;,]/.exec(dataUrl);
|
|
427
|
+
return match?.[1];
|
|
428
|
+
}
|
|
429
|
+
function buildPromptBody({
|
|
430
|
+
input,
|
|
431
|
+
attachments,
|
|
432
|
+
parentId
|
|
433
|
+
}) {
|
|
434
|
+
const context = { parent_id: parentId };
|
|
435
|
+
const files = attachments ?? [];
|
|
436
|
+
if (files.length === 0) {
|
|
437
|
+
return { prompt: input, context };
|
|
438
|
+
}
|
|
439
|
+
const parts = [];
|
|
440
|
+
if (input) parts.push({ type: "text", text: input });
|
|
441
|
+
for (const attachment of files) {
|
|
442
|
+
parts.push({ type: "file", file: attachment.dataUrl });
|
|
443
|
+
}
|
|
444
|
+
return { prompt: parts, context };
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// src/runtime/upload-adapter.ts
|
|
448
|
+
var DEFAULT_UPLOAD_ACCEPT = "image/*,application/pdf,text/*,.md,.json,.csv,.tsv,.xlsx,.docx";
|
|
449
|
+
function createDefaultAttachmentAdapter({
|
|
450
|
+
baseUrl = "",
|
|
451
|
+
uploadUrl,
|
|
452
|
+
fetch: fetchFn = authFetch,
|
|
453
|
+
accept = DEFAULT_UPLOAD_ACCEPT
|
|
454
|
+
} = {}) {
|
|
455
|
+
const base = baseUrl.replace(/\/$/, "");
|
|
456
|
+
const resolvedUploadUrl = uploadUrl ?? `${base}/files/upload`;
|
|
457
|
+
return {
|
|
458
|
+
accept,
|
|
459
|
+
async add({ file }) {
|
|
460
|
+
const isImage = file.type.startsWith("image/");
|
|
461
|
+
const pending = {
|
|
462
|
+
id: crypto.randomUUID(),
|
|
463
|
+
type: isImage ? "image" : "file",
|
|
464
|
+
name: file.name,
|
|
465
|
+
contentType: file.type || "application/octet-stream",
|
|
466
|
+
file,
|
|
467
|
+
status: { type: "requires-action", reason: "composer-send" }
|
|
468
|
+
};
|
|
469
|
+
return pending;
|
|
470
|
+
},
|
|
471
|
+
async send(attachment) {
|
|
472
|
+
const fd = new FormData();
|
|
473
|
+
fd.append("file", attachment.file);
|
|
474
|
+
const res = await fetchFn(resolvedUploadUrl, { method: "POST", body: fd });
|
|
475
|
+
if (!res.ok) {
|
|
476
|
+
const detail = await res.text().catch(() => "");
|
|
477
|
+
throw new Error(
|
|
478
|
+
`Attachment upload failed (${res.status}): ${detail || res.statusText}`
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
const remoteUrl = await readUploadedUrl(res);
|
|
482
|
+
const mime = attachment.contentType ?? "application/octet-stream";
|
|
483
|
+
const filename = attachment.name;
|
|
484
|
+
const complete = {
|
|
485
|
+
...attachment,
|
|
486
|
+
status: { type: "complete" },
|
|
487
|
+
content: mime.startsWith("image/") ? [{ type: "image", image: remoteUrl, filename }] : [{ type: "file", data: remoteUrl, mimeType: mime, filename }]
|
|
488
|
+
};
|
|
489
|
+
return complete;
|
|
490
|
+
},
|
|
491
|
+
async remove() {
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
var createUploadAttachmentAdapter = createDefaultAttachmentAdapter;
|
|
496
|
+
async function readUploadedUrl(res) {
|
|
497
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
498
|
+
if (contentType.includes("application/json")) {
|
|
499
|
+
const data = await res.json();
|
|
500
|
+
const candidate = data.url ?? data.signed_url ?? data.id;
|
|
501
|
+
if (typeof candidate === "string" && candidate.length > 0) {
|
|
502
|
+
return candidate;
|
|
503
|
+
}
|
|
504
|
+
throw new Error(
|
|
505
|
+
"Attachment upload response did not include a `url`, `signed_url`, or `id` field."
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
const text = (await res.text()).trim();
|
|
509
|
+
if (!text) {
|
|
510
|
+
throw new Error("Attachment upload response was empty.");
|
|
511
|
+
}
|
|
512
|
+
return text;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// src/runtime/resolve-attachments.ts
|
|
516
|
+
function isAttachmentAdapter(value) {
|
|
517
|
+
return typeof value === "object" && value !== null && "accept" in value && typeof value.add === "function" && typeof value.send === "function" && typeof value.remove === "function";
|
|
518
|
+
}
|
|
519
|
+
function resolveAttachmentAdapter(attachments, options = {}) {
|
|
520
|
+
const baseUrl = options.baseUrl ?? "/api";
|
|
521
|
+
const legacyUploadUrl = options.uploadUrl;
|
|
522
|
+
const legacyAccept = options.accept;
|
|
523
|
+
if (attachments === null) return void 0;
|
|
524
|
+
const legacyEnables = legacyUploadUrl !== void 0 || legacyAccept !== void 0;
|
|
525
|
+
if (attachments === void 0) {
|
|
526
|
+
if (!legacyEnables) return void 0;
|
|
527
|
+
return createDefaultAttachmentAdapter({
|
|
528
|
+
baseUrl,
|
|
529
|
+
fetch: options.fetch,
|
|
530
|
+
uploadUrl: legacyUploadUrl,
|
|
531
|
+
accept: legacyAccept
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
if (attachments === true) {
|
|
535
|
+
return createDefaultAttachmentAdapter({
|
|
536
|
+
baseUrl,
|
|
537
|
+
fetch: options.fetch,
|
|
538
|
+
uploadUrl: legacyUploadUrl,
|
|
539
|
+
accept: legacyAccept
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
if (isAttachmentAdapter(attachments)) return attachments;
|
|
543
|
+
const config = attachments;
|
|
544
|
+
return createDefaultAttachmentAdapter({
|
|
545
|
+
baseUrl,
|
|
546
|
+
fetch: options.fetch,
|
|
547
|
+
uploadUrl: config.uploadUrl ?? legacyUploadUrl,
|
|
548
|
+
accept: config.accept ?? legacyAccept
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
|
|
88
552
|
// src/runtime/provider.tsx
|
|
89
553
|
import { jsx } from "react/jsx-runtime";
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
554
|
+
function projectAttachment(attachment) {
|
|
555
|
+
const filename = attachment.name ?? "attachment";
|
|
556
|
+
const mimeType = attachment.contentType ?? "application/octet-stream";
|
|
557
|
+
if (attachment.type === "image") {
|
|
558
|
+
return {
|
|
559
|
+
id: attachment.id,
|
|
560
|
+
type: "image",
|
|
561
|
+
name: filename,
|
|
562
|
+
contentType: mimeType,
|
|
563
|
+
status: { type: "complete" },
|
|
564
|
+
content: [{ type: "image", image: attachment.dataUrl, filename }]
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
return {
|
|
568
|
+
id: attachment.id,
|
|
569
|
+
type: attachment.type,
|
|
570
|
+
name: filename,
|
|
571
|
+
contentType: mimeType,
|
|
572
|
+
status: { type: "complete" },
|
|
573
|
+
content: [
|
|
574
|
+
{ type: "file", data: attachment.dataUrl, mimeType, filename }
|
|
575
|
+
]
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
var convertMessage = (message) => {
|
|
579
|
+
const content = message.content.map((part) => {
|
|
580
|
+
if (part.type === "text") return { type: "text", text: part.text };
|
|
581
|
+
if (part.type === "thinking") return { type: "reasoning", text: part.text };
|
|
582
|
+
return {
|
|
583
|
+
type: "tool-call",
|
|
584
|
+
toolCallId: part.toolCallId,
|
|
585
|
+
toolName: part.toolName,
|
|
586
|
+
argsText: part.argsText,
|
|
587
|
+
...part.result !== void 0 ? { result: part.result } : {}
|
|
588
|
+
};
|
|
589
|
+
});
|
|
590
|
+
const attachments = message.attachments && message.attachments.length > 0 ? message.attachments.map(projectAttachment) : void 0;
|
|
591
|
+
return {
|
|
592
|
+
role: message.role,
|
|
593
|
+
content,
|
|
594
|
+
id: message.id,
|
|
595
|
+
...attachments ? { attachments } : {}
|
|
596
|
+
};
|
|
597
|
+
};
|
|
95
598
|
function findParentId(messages, beforeIndex) {
|
|
96
599
|
const slice = beforeIndex !== void 0 ? messages.slice(0, beforeIndex) : messages;
|
|
97
600
|
for (let i = slice.length - 1; i >= 0; i--) {
|
|
@@ -99,18 +602,18 @@ function findParentId(messages, beforeIndex) {
|
|
|
99
602
|
}
|
|
100
603
|
return null;
|
|
101
604
|
}
|
|
102
|
-
function isTopLevelStart(event) {
|
|
103
|
-
return event.type === "START" && typeof event.run_id === "string" && typeof event.path === "string" && !event.path.includes(".");
|
|
104
|
-
}
|
|
105
605
|
function getTextFromMessage(message) {
|
|
106
606
|
const part = message.content.find((c) => c.type === "text");
|
|
107
|
-
return part?.type === "text" ? part.text :
|
|
607
|
+
return part?.type === "text" ? part.text : "";
|
|
108
608
|
}
|
|
109
|
-
function
|
|
609
|
+
function getAttachmentsFromMessage(message) {
|
|
610
|
+
return message.attachments?.length ? message.attachments : void 0;
|
|
611
|
+
}
|
|
612
|
+
function useTimbalStream({
|
|
110
613
|
workforceId,
|
|
111
|
-
children,
|
|
112
614
|
baseUrl = "/api",
|
|
113
|
-
fetch: fetchFn
|
|
615
|
+
fetch: fetchFn,
|
|
616
|
+
debug = false
|
|
114
617
|
}) {
|
|
115
618
|
const [messages, setMessages] = useState([]);
|
|
116
619
|
const [isRunning, setIsRunning] = useState(false);
|
|
@@ -120,40 +623,41 @@ function TimbalRuntimeProvider({
|
|
|
120
623
|
useEffect(() => {
|
|
121
624
|
fetchFnRef.current = fetchFn ?? authFetch;
|
|
122
625
|
}, [fetchFn]);
|
|
626
|
+
const debugRef = useRef(debug);
|
|
627
|
+
useEffect(() => {
|
|
628
|
+
debugRef.current = debug;
|
|
629
|
+
}, [debug]);
|
|
123
630
|
useEffect(() => {
|
|
124
631
|
messagesRef.current = messages;
|
|
125
632
|
}, [messages]);
|
|
126
633
|
const streamAssistantResponse = useCallback(
|
|
127
|
-
async (input, userId, assistantId, parentId, signal) => {
|
|
128
|
-
const
|
|
129
|
-
const toolIndexById = /* @__PURE__ */ new Map();
|
|
130
|
-
const lastTextPart = () => {
|
|
131
|
-
const last = parts[parts.length - 1];
|
|
132
|
-
if (last?.type === "text") return last;
|
|
133
|
-
const next = { type: "text", text: "" };
|
|
134
|
-
parts.push(next);
|
|
135
|
-
return next;
|
|
136
|
-
};
|
|
634
|
+
async (input, attachments, userId, assistantId, parentId, signal) => {
|
|
635
|
+
const state = createReducerState();
|
|
137
636
|
const flush = () => {
|
|
138
637
|
setMessages(
|
|
139
|
-
(prev) => prev.map(
|
|
638
|
+
(prev) => prev.map(
|
|
639
|
+
(m) => m.id === assistantId ? { ...m, content: [...state.parts] } : m
|
|
640
|
+
)
|
|
140
641
|
);
|
|
141
642
|
};
|
|
142
643
|
const stampRunId = (runId) => {
|
|
143
644
|
setMessages(
|
|
144
|
-
(prev) => prev.map(
|
|
645
|
+
(prev) => prev.map(
|
|
646
|
+
(m) => m.id === userId || m.id === assistantId ? { ...m, runId } : m
|
|
647
|
+
)
|
|
145
648
|
);
|
|
146
649
|
};
|
|
147
650
|
try {
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
651
|
+
const body = buildPromptBody({ input, attachments, parentId });
|
|
652
|
+
const res = await fetchFnRef.current(
|
|
653
|
+
`${baseUrl}/workforce/${workforceId}/stream`,
|
|
654
|
+
{
|
|
655
|
+
method: "POST",
|
|
656
|
+
headers: { "Content-Type": "application/json" },
|
|
657
|
+
body: JSON.stringify(body),
|
|
658
|
+
signal
|
|
659
|
+
}
|
|
660
|
+
);
|
|
157
661
|
if (!res.ok || !res.body) throw new Error(`Request failed: ${res.status}`);
|
|
158
662
|
const reader = res.body.getReader();
|
|
159
663
|
const decoder = new TextDecoder();
|
|
@@ -168,84 +672,34 @@ function TimbalRuntimeProvider({
|
|
|
168
672
|
for (const line of lines) {
|
|
169
673
|
const event = parseSSELine(line);
|
|
170
674
|
if (!event) continue;
|
|
171
|
-
if (
|
|
172
|
-
|
|
173
|
-
stampRunId(capturedRunId);
|
|
675
|
+
if (debugRef.current) {
|
|
676
|
+
console.debug("[timbal]", event.type, event);
|
|
174
677
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
lastTextPart().text += item.text_delta;
|
|
181
|
-
flush();
|
|
182
|
-
} else if (item.type === "tool_use") {
|
|
183
|
-
const toolCallId = item.id || `tool-${crypto.randomUUID()}`;
|
|
184
|
-
const inputStr = typeof item.input === "string" ? item.input : JSON.stringify(item.input ?? {});
|
|
185
|
-
parts.push({
|
|
186
|
-
type: "tool-call",
|
|
187
|
-
toolCallId,
|
|
188
|
-
toolName: item.name || "unknown",
|
|
189
|
-
argsText: inputStr
|
|
190
|
-
});
|
|
191
|
-
toolIndexById.set(toolCallId, parts.length - 1);
|
|
192
|
-
flush();
|
|
193
|
-
} else if (item.type === "tool_use_delta") {
|
|
194
|
-
const idx = toolIndexById.get(item.id);
|
|
195
|
-
if (idx !== void 0 && typeof item.input_delta === "string") {
|
|
196
|
-
parts[idx].argsText += item.input_delta;
|
|
197
|
-
flush();
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
break;
|
|
201
|
-
}
|
|
202
|
-
case "OUTPUT": {
|
|
203
|
-
const output = event.output;
|
|
204
|
-
if (!output) break;
|
|
205
|
-
if (typeof output === "object" && Array.isArray(output.content)) {
|
|
206
|
-
for (const block of output.content) {
|
|
207
|
-
if (block.type === "tool_use") {
|
|
208
|
-
const id = block.id || `tool-${crypto.randomUUID()}`;
|
|
209
|
-
const idx = toolIndexById.get(id);
|
|
210
|
-
if (idx !== void 0) {
|
|
211
|
-
parts[idx].result = "Tool executed";
|
|
212
|
-
} else {
|
|
213
|
-
const inputStr = typeof block.input === "string" ? block.input : JSON.stringify(block.input ?? {});
|
|
214
|
-
parts.push({
|
|
215
|
-
type: "tool-call",
|
|
216
|
-
toolCallId: id,
|
|
217
|
-
toolName: block.name || "unknown",
|
|
218
|
-
argsText: inputStr,
|
|
219
|
-
result: "Tool executed"
|
|
220
|
-
});
|
|
221
|
-
toolIndexById.set(id, parts.length - 1);
|
|
222
|
-
}
|
|
223
|
-
} else if (block.type === "text" && typeof block.text === "string" && !lastTextPart().text) {
|
|
224
|
-
lastTextPart().text = block.text;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
flush();
|
|
228
|
-
} else if (parts.length === 0) {
|
|
229
|
-
const text = typeof output === "string" ? output : JSON.stringify(output);
|
|
230
|
-
parts.push({ type: "text", text });
|
|
231
|
-
flush();
|
|
232
|
-
}
|
|
233
|
-
break;
|
|
678
|
+
if (!capturedRunId) {
|
|
679
|
+
const runId = readTopLevelStartRunId(event);
|
|
680
|
+
if (runId) {
|
|
681
|
+
capturedRunId = runId;
|
|
682
|
+
stampRunId(runId);
|
|
234
683
|
}
|
|
235
684
|
}
|
|
685
|
+
const changed = reduceSseEvent(state, event);
|
|
686
|
+
if (changed) flush();
|
|
236
687
|
}
|
|
237
688
|
}
|
|
238
689
|
if (buffer.trim()) {
|
|
239
690
|
const event = parseSSELine(buffer);
|
|
240
|
-
if (event
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
691
|
+
if (event) {
|
|
692
|
+
if (debugRef.current) {
|
|
693
|
+
console.debug("[timbal]", event.type, event);
|
|
694
|
+
}
|
|
695
|
+
if (reduceSseEvent(state, event)) flush();
|
|
244
696
|
}
|
|
245
697
|
}
|
|
246
698
|
} catch (err) {
|
|
247
699
|
if (err.name !== "AbortError") {
|
|
248
|
-
if (parts.length === 0)
|
|
700
|
+
if (state.parts.length === 0) {
|
|
701
|
+
state.parts.push({ type: "text", text: "Something went wrong." });
|
|
702
|
+
}
|
|
249
703
|
flush();
|
|
250
704
|
}
|
|
251
705
|
} finally {
|
|
@@ -255,44 +709,46 @@ function TimbalRuntimeProvider({
|
|
|
255
709
|
},
|
|
256
710
|
[workforceId, baseUrl]
|
|
257
711
|
);
|
|
258
|
-
const
|
|
259
|
-
async (
|
|
260
|
-
const textPart = message.content.find((c) => c.type === "text");
|
|
261
|
-
if (!textPart || textPart.type !== "text") return;
|
|
262
|
-
const input = textPart.text;
|
|
712
|
+
const send = useCallback(
|
|
713
|
+
async (input, options) => {
|
|
263
714
|
const userId = crypto.randomUUID();
|
|
264
715
|
const assistantId = crypto.randomUUID();
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
|
|
716
|
+
const base = messagesRef.current;
|
|
717
|
+
const parentId = options?.parentId !== void 0 ? options.parentId : findParentId(base);
|
|
718
|
+
const userMessage = {
|
|
719
|
+
id: userId,
|
|
720
|
+
role: "user",
|
|
721
|
+
content: input ? [{ type: "text", text: input }] : [],
|
|
722
|
+
...options?.attachments && options.attachments.length > 0 ? { attachments: options.attachments } : {}
|
|
723
|
+
};
|
|
273
724
|
setMessages([
|
|
274
725
|
...base,
|
|
275
|
-
|
|
276
|
-
]);
|
|
277
|
-
setIsRunning(true);
|
|
278
|
-
setMessages((prev) => [
|
|
279
|
-
...prev,
|
|
726
|
+
userMessage,
|
|
280
727
|
{ id: assistantId, role: "assistant", content: [] }
|
|
281
728
|
]);
|
|
729
|
+
setIsRunning(true);
|
|
282
730
|
const controller = new AbortController();
|
|
283
731
|
abortRef.current = controller;
|
|
284
|
-
await streamAssistantResponse(
|
|
732
|
+
await streamAssistantResponse(
|
|
733
|
+
input,
|
|
734
|
+
options?.attachments,
|
|
735
|
+
userId,
|
|
736
|
+
assistantId,
|
|
737
|
+
parentId,
|
|
738
|
+
controller.signal
|
|
739
|
+
);
|
|
285
740
|
},
|
|
286
741
|
[streamAssistantResponse]
|
|
287
742
|
);
|
|
288
|
-
const
|
|
743
|
+
const reload = useCallback(
|
|
289
744
|
async (messageId) => {
|
|
290
745
|
const current = messagesRef.current;
|
|
291
746
|
const idx = messageId ? current.findIndex((m) => m.id === messageId) : current.length - 2;
|
|
292
747
|
const userMessage = idx >= 0 ? current[idx] : null;
|
|
293
748
|
if (!userMessage || userMessage.role !== "user") return;
|
|
294
749
|
const input = getTextFromMessage(userMessage);
|
|
295
|
-
|
|
750
|
+
const messageAttachments = getAttachmentsFromMessage(userMessage);
|
|
751
|
+
if (!input && !messageAttachments?.length) return;
|
|
296
752
|
const assistantId = crypto.randomUUID();
|
|
297
753
|
const parentId = findParentId(current, idx);
|
|
298
754
|
setMessages((prev) => [
|
|
@@ -302,24 +758,114 @@ function TimbalRuntimeProvider({
|
|
|
302
758
|
setIsRunning(true);
|
|
303
759
|
const controller = new AbortController();
|
|
304
760
|
abortRef.current = controller;
|
|
305
|
-
await streamAssistantResponse(
|
|
761
|
+
await streamAssistantResponse(
|
|
762
|
+
input,
|
|
763
|
+
messageAttachments,
|
|
764
|
+
userMessage.id,
|
|
765
|
+
assistantId,
|
|
766
|
+
parentId,
|
|
767
|
+
controller.signal
|
|
768
|
+
);
|
|
306
769
|
},
|
|
307
770
|
[streamAssistantResponse]
|
|
308
771
|
);
|
|
309
|
-
const
|
|
772
|
+
const cancel = useCallback(() => {
|
|
773
|
+
abortRef.current?.abort();
|
|
774
|
+
}, []);
|
|
775
|
+
const clear = useCallback(() => {
|
|
310
776
|
abortRef.current?.abort();
|
|
777
|
+
setMessages([]);
|
|
311
778
|
}, []);
|
|
779
|
+
return useMemo(
|
|
780
|
+
() => ({ messages, isRunning, send, reload, cancel, clear }),
|
|
781
|
+
[messages, isRunning, send, reload, cancel, clear]
|
|
782
|
+
);
|
|
783
|
+
}
|
|
784
|
+
function readTopLevelStartRunId(event) {
|
|
785
|
+
if (event.type === "START" && typeof event.run_id === "string" && typeof event.path === "string" && !event.path.includes(".")) {
|
|
786
|
+
return event.run_id;
|
|
787
|
+
}
|
|
788
|
+
return null;
|
|
789
|
+
}
|
|
790
|
+
var TimbalStreamContext = createContext(null);
|
|
791
|
+
function useTimbalRuntime() {
|
|
792
|
+
const ctx = useContext(TimbalStreamContext);
|
|
793
|
+
if (!ctx) {
|
|
794
|
+
throw new Error(
|
|
795
|
+
"useTimbalRuntime must be used inside a <TimbalRuntimeProvider>."
|
|
796
|
+
);
|
|
797
|
+
}
|
|
798
|
+
return ctx;
|
|
799
|
+
}
|
|
800
|
+
function TimbalRuntimeProvider({
|
|
801
|
+
workforceId,
|
|
802
|
+
children,
|
|
803
|
+
baseUrl = "/api",
|
|
804
|
+
fetch: fetchFn,
|
|
805
|
+
attachments,
|
|
806
|
+
attachmentsUploadUrl,
|
|
807
|
+
attachmentsAccept,
|
|
808
|
+
debug
|
|
809
|
+
}) {
|
|
810
|
+
const stream = useTimbalStream({
|
|
811
|
+
workforceId,
|
|
812
|
+
baseUrl,
|
|
813
|
+
fetch: fetchFn,
|
|
814
|
+
debug
|
|
815
|
+
});
|
|
816
|
+
const attachmentAdapter = useMemo(
|
|
817
|
+
() => resolveAttachmentAdapter(attachments, {
|
|
818
|
+
baseUrl,
|
|
819
|
+
fetch: fetchFn,
|
|
820
|
+
uploadUrl: attachmentsUploadUrl,
|
|
821
|
+
accept: attachmentsAccept
|
|
822
|
+
}),
|
|
823
|
+
[attachments, attachmentsUploadUrl, attachmentsAccept, baseUrl, fetchFn]
|
|
824
|
+
);
|
|
825
|
+
const onNew = useCallback(
|
|
826
|
+
async (message) => {
|
|
827
|
+
const textPart = message.content.find((c) => c.type === "text");
|
|
828
|
+
const input = textPart && textPart.type === "text" ? textPart.text : "";
|
|
829
|
+
const auiAttachments = message.attachments;
|
|
830
|
+
const attachments2 = auiAttachments ? (await Promise.all(auiAttachments.map(extractAttachment))).filter((a) => a !== null) : [];
|
|
831
|
+
if (!input && attachments2.length === 0) return;
|
|
832
|
+
const parentId = message.parentId !== null && message.parentId !== void 0 ? findParentIdFromAuiParent(stream.messages, message.parentId) : void 0;
|
|
833
|
+
await stream.send(input, {
|
|
834
|
+
attachments: attachments2.length > 0 ? attachments2 : void 0,
|
|
835
|
+
...parentId !== void 0 ? { parentId } : {}
|
|
836
|
+
});
|
|
837
|
+
},
|
|
838
|
+
[stream]
|
|
839
|
+
);
|
|
840
|
+
const onReload = useCallback(
|
|
841
|
+
async (messageId) => {
|
|
842
|
+
await stream.reload(messageId);
|
|
843
|
+
},
|
|
844
|
+
[stream]
|
|
845
|
+
);
|
|
846
|
+
const onCancel = useCallback(async () => {
|
|
847
|
+
stream.cancel();
|
|
848
|
+
}, [stream]);
|
|
312
849
|
const runtime = useExternalStoreRuntime({
|
|
313
|
-
isRunning,
|
|
314
|
-
messages,
|
|
850
|
+
isRunning: stream.isRunning,
|
|
851
|
+
messages: stream.messages,
|
|
315
852
|
convertMessage,
|
|
316
853
|
onNew,
|
|
317
854
|
onEdit: onNew,
|
|
318
855
|
onReload,
|
|
319
|
-
onCancel
|
|
856
|
+
onCancel,
|
|
857
|
+
...attachmentAdapter ? { adapters: { attachments: attachmentAdapter } } : {}
|
|
320
858
|
});
|
|
321
|
-
return /* @__PURE__ */ jsx(AssistantRuntimeProvider, { runtime, children });
|
|
859
|
+
return /* @__PURE__ */ jsx(TimbalStreamContext.Provider, { value: stream, children: /* @__PURE__ */ jsx(AssistantRuntimeProvider, { runtime, children }) });
|
|
322
860
|
}
|
|
861
|
+
function findParentIdFromAuiParent(messages, auiParentId) {
|
|
862
|
+
const idx = messages.findIndex((m) => m.id === auiParentId);
|
|
863
|
+
if (idx < 0) return null;
|
|
864
|
+
return findParentId(messages.slice(0, idx + 1));
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
// src/index.ts
|
|
868
|
+
import { parseSSELine as parseSSELine2 } from "@timbal-ai/timbal-sdk";
|
|
323
869
|
|
|
324
870
|
// src/components/attachment.tsx
|
|
325
871
|
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
@@ -783,73 +1329,1115 @@ import {
|
|
|
783
1329
|
import remarkGfm from "remark-gfm";
|
|
784
1330
|
import remarkMath from "remark-math";
|
|
785
1331
|
import rehypeKatex from "rehype-katex";
|
|
786
|
-
import { memo, useState as
|
|
787
|
-
import { CheckIcon, CopyIcon } from "lucide-react";
|
|
1332
|
+
import { memo, useState as useState5 } from "react";
|
|
1333
|
+
import { CheckIcon as CheckIcon2, CopyIcon } from "lucide-react";
|
|
788
1334
|
|
|
789
1335
|
// src/components/syntax-highlighter.tsx
|
|
790
|
-
import { useEffect as useEffect3, useState as
|
|
1336
|
+
import { useEffect as useEffect3, useState as useState4 } from "react";
|
|
791
1337
|
import { createHighlighterCore } from "shiki/core";
|
|
792
1338
|
import { createJavaScriptRegexEngine } from "shiki/engine/javascript";
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
import
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
import
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
import
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
langMarkdown,
|
|
829
|
-
langJsx,
|
|
830
|
-
langTsx,
|
|
831
|
-
langSql,
|
|
832
|
-
langYaml,
|
|
833
|
-
langRust,
|
|
834
|
-
langGo,
|
|
835
|
-
langJava,
|
|
836
|
-
langC,
|
|
837
|
-
langCpp
|
|
838
|
-
],
|
|
839
|
-
engine: createJavaScriptRegexEngine()
|
|
840
|
-
});
|
|
841
|
-
}
|
|
842
|
-
return highlighterPromise;
|
|
843
|
-
}
|
|
844
|
-
getHighlighter();
|
|
845
|
-
var ShikiSyntaxHighlighter = ({
|
|
846
|
-
components: { Pre, Code: Code2 },
|
|
847
|
-
language,
|
|
848
|
-
code
|
|
1339
|
+
|
|
1340
|
+
// src/artifacts/registry.tsx
|
|
1341
|
+
import { createContext as createContext3, useContext as useContext3, useMemo as useMemo3 } from "react";
|
|
1342
|
+
|
|
1343
|
+
// src/artifacts/chart-artifact.tsx
|
|
1344
|
+
import { useMemo as useMemo2 } from "react";
|
|
1345
|
+
|
|
1346
|
+
// src/artifacts/artifact-card.tsx
|
|
1347
|
+
import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1348
|
+
var ArtifactCard = ({ title, kind, className, bodyClassName, toolbar, children }) => {
|
|
1349
|
+
const hasHeader = Boolean(title || toolbar);
|
|
1350
|
+
return /* @__PURE__ */ jsxs5(
|
|
1351
|
+
"div",
|
|
1352
|
+
{
|
|
1353
|
+
className: cn(
|
|
1354
|
+
"aui-artifact-root my-3 overflow-hidden rounded-xl border border-border/60 bg-background shadow-sm",
|
|
1355
|
+
className
|
|
1356
|
+
),
|
|
1357
|
+
"data-artifact-kind": kind,
|
|
1358
|
+
children: [
|
|
1359
|
+
hasHeader && /* @__PURE__ */ jsxs5("div", { className: "aui-artifact-header flex items-center gap-2 border-b border-border/40 bg-muted/30 px-3 py-1.5", children: [
|
|
1360
|
+
title && /* @__PURE__ */ jsx8("span", { className: "aui-artifact-title flex-1 truncate text-xs font-semibold text-foreground/80", children: title }),
|
|
1361
|
+
!title && /* @__PURE__ */ jsx8("span", { className: "flex-1" }),
|
|
1362
|
+
toolbar
|
|
1363
|
+
] }),
|
|
1364
|
+
/* @__PURE__ */ jsx8("div", { className: cn("aui-artifact-body", bodyClassName), children })
|
|
1365
|
+
]
|
|
1366
|
+
}
|
|
1367
|
+
);
|
|
1368
|
+
};
|
|
1369
|
+
|
|
1370
|
+
// src/artifacts/chart-artifact.tsx
|
|
1371
|
+
import { Fragment, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1372
|
+
var ChartArtifactView = ({
|
|
1373
|
+
artifact
|
|
849
1374
|
}) => {
|
|
850
|
-
const
|
|
851
|
-
|
|
852
|
-
|
|
1375
|
+
const { type: _t, chartType = "bar", data = [] } = artifact;
|
|
1376
|
+
const xKey = artifact.xKey ?? inferXKey(data);
|
|
1377
|
+
const dataKeys = useMemo2(() => {
|
|
1378
|
+
if (Array.isArray(artifact.dataKey)) return artifact.dataKey;
|
|
1379
|
+
if (typeof artifact.dataKey === "string") return [artifact.dataKey];
|
|
1380
|
+
return inferDataKeys(data, xKey);
|
|
1381
|
+
}, [artifact.dataKey, data, xKey]);
|
|
1382
|
+
return /* @__PURE__ */ jsx9(ArtifactCard, { title: artifact.title, kind: "chart", children: /* @__PURE__ */ jsxs6("div", { className: "aui-artifact-chart p-3", children: [
|
|
1383
|
+
/* @__PURE__ */ jsx9(
|
|
1384
|
+
ChartSvg,
|
|
1385
|
+
{
|
|
1386
|
+
chartType,
|
|
1387
|
+
data,
|
|
1388
|
+
xKey,
|
|
1389
|
+
dataKeys,
|
|
1390
|
+
unit: artifact.unit
|
|
1391
|
+
}
|
|
1392
|
+
),
|
|
1393
|
+
dataKeys.length > 1 && /* @__PURE__ */ jsx9(Legend, { dataKeys })
|
|
1394
|
+
] }) });
|
|
1395
|
+
};
|
|
1396
|
+
var COLORS = [
|
|
1397
|
+
"var(--primary, #6366f1)",
|
|
1398
|
+
"#22c55e",
|
|
1399
|
+
"#f59e0b",
|
|
1400
|
+
"#ef4444",
|
|
1401
|
+
"#06b6d4",
|
|
1402
|
+
"#a855f7"
|
|
1403
|
+
];
|
|
1404
|
+
var W = 600;
|
|
1405
|
+
var H = 240;
|
|
1406
|
+
var PAD = { top: 12, right: 16, bottom: 28, left: 36 };
|
|
1407
|
+
var ChartSvg = ({ chartType, data, xKey, dataKeys, unit }) => {
|
|
1408
|
+
if (data.length === 0 || dataKeys.length === 0) {
|
|
1409
|
+
return /* @__PURE__ */ jsx9(EmptyState, {});
|
|
1410
|
+
}
|
|
1411
|
+
if (chartType === "pie") {
|
|
1412
|
+
return /* @__PURE__ */ jsx9(PieChart, { data, xKey, dataKey: dataKeys[0] });
|
|
1413
|
+
}
|
|
1414
|
+
const innerW = W - PAD.left - PAD.right;
|
|
1415
|
+
const innerH = H - PAD.top - PAD.bottom;
|
|
1416
|
+
const all = dataKeys.flatMap((k) => data.map((d) => toNum(d[k])));
|
|
1417
|
+
const maxV = Math.max(0, ...all);
|
|
1418
|
+
const minV = Math.min(0, ...all);
|
|
1419
|
+
const range = maxV - minV || 1;
|
|
1420
|
+
const yScale = (v) => PAD.top + innerH - (v - minV) / range * innerH;
|
|
1421
|
+
const xCount = data.length;
|
|
1422
|
+
const xStep = xCount > 1 ? innerW / (xCount - 1) : innerW;
|
|
1423
|
+
const xPos = (i) => chartType === "bar" ? PAD.left + innerW * (i + 0.5) / xCount : PAD.left + i * xStep;
|
|
1424
|
+
const ticks = niceTicks(minV, maxV);
|
|
1425
|
+
return /* @__PURE__ */ jsxs6(
|
|
1426
|
+
"svg",
|
|
1427
|
+
{
|
|
1428
|
+
viewBox: `0 0 ${W} ${H}`,
|
|
1429
|
+
className: "aui-artifact-chart-svg w-full",
|
|
1430
|
+
role: "img",
|
|
1431
|
+
"aria-label": "Chart",
|
|
1432
|
+
children: [
|
|
1433
|
+
ticks.map((t, i) => /* @__PURE__ */ jsxs6("g", { children: [
|
|
1434
|
+
/* @__PURE__ */ jsx9(
|
|
1435
|
+
"line",
|
|
1436
|
+
{
|
|
1437
|
+
x1: PAD.left,
|
|
1438
|
+
x2: W - PAD.right,
|
|
1439
|
+
y1: yScale(t),
|
|
1440
|
+
y2: yScale(t),
|
|
1441
|
+
stroke: "currentColor",
|
|
1442
|
+
strokeOpacity: 0.08
|
|
1443
|
+
}
|
|
1444
|
+
),
|
|
1445
|
+
/* @__PURE__ */ jsx9(
|
|
1446
|
+
"text",
|
|
1447
|
+
{
|
|
1448
|
+
x: PAD.left - 6,
|
|
1449
|
+
y: yScale(t),
|
|
1450
|
+
textAnchor: "end",
|
|
1451
|
+
dominantBaseline: "middle",
|
|
1452
|
+
className: "fill-muted-foreground text-[10px]",
|
|
1453
|
+
children: formatTick(t, unit)
|
|
1454
|
+
}
|
|
1455
|
+
)
|
|
1456
|
+
] }, i)),
|
|
1457
|
+
data.map((d, i) => /* @__PURE__ */ jsx9(
|
|
1458
|
+
"text",
|
|
1459
|
+
{
|
|
1460
|
+
x: xPos(i),
|
|
1461
|
+
y: H - PAD.bottom + 14,
|
|
1462
|
+
textAnchor: "middle",
|
|
1463
|
+
className: "fill-muted-foreground text-[10px]",
|
|
1464
|
+
children: String(d[xKey] ?? i)
|
|
1465
|
+
},
|
|
1466
|
+
i
|
|
1467
|
+
)),
|
|
1468
|
+
chartType === "bar" && renderBars({ data, dataKeys, xCount, xPos, yScale, minV, innerW }),
|
|
1469
|
+
chartType === "line" && dataKeys.map((k, ki) => /* @__PURE__ */ jsx9(
|
|
1470
|
+
Polyline,
|
|
1471
|
+
{
|
|
1472
|
+
data,
|
|
1473
|
+
dataKey: k,
|
|
1474
|
+
xPos,
|
|
1475
|
+
yScale,
|
|
1476
|
+
color: COLORS[ki % COLORS.length]
|
|
1477
|
+
},
|
|
1478
|
+
k
|
|
1479
|
+
)),
|
|
1480
|
+
chartType === "area" && dataKeys.map((k, ki) => /* @__PURE__ */ jsx9(
|
|
1481
|
+
Area,
|
|
1482
|
+
{
|
|
1483
|
+
data,
|
|
1484
|
+
dataKey: k,
|
|
1485
|
+
xPos,
|
|
1486
|
+
yScale,
|
|
1487
|
+
baseY: yScale(Math.max(0, minV)),
|
|
1488
|
+
color: COLORS[ki % COLORS.length]
|
|
1489
|
+
},
|
|
1490
|
+
k
|
|
1491
|
+
))
|
|
1492
|
+
]
|
|
1493
|
+
}
|
|
1494
|
+
);
|
|
1495
|
+
};
|
|
1496
|
+
function renderBars(args) {
|
|
1497
|
+
const { data, dataKeys, xCount, xPos, yScale, minV, innerW } = args;
|
|
1498
|
+
const groupWidth = innerW / xCount * 0.7;
|
|
1499
|
+
const barWidth = groupWidth / dataKeys.length;
|
|
1500
|
+
const baseY = yScale(Math.max(0, minV));
|
|
1501
|
+
return dataKeys.flatMap(
|
|
1502
|
+
(k, ki) => data.map((d, i) => {
|
|
1503
|
+
const v = toNum(d[k]);
|
|
1504
|
+
const y = yScale(v);
|
|
1505
|
+
const x = xPos(i) - groupWidth / 2 + ki * barWidth;
|
|
1506
|
+
const top = Math.min(y, baseY);
|
|
1507
|
+
const height = Math.abs(y - baseY);
|
|
1508
|
+
return /* @__PURE__ */ jsx9(
|
|
1509
|
+
"rect",
|
|
1510
|
+
{
|
|
1511
|
+
x,
|
|
1512
|
+
y: top,
|
|
1513
|
+
width: Math.max(1, barWidth - 2),
|
|
1514
|
+
height: Math.max(1, height),
|
|
1515
|
+
rx: 2,
|
|
1516
|
+
fill: COLORS[ki % COLORS.length]
|
|
1517
|
+
},
|
|
1518
|
+
`${k}-${i}`
|
|
1519
|
+
);
|
|
1520
|
+
})
|
|
1521
|
+
);
|
|
1522
|
+
}
|
|
1523
|
+
var Polyline = ({ data, dataKey, xPos, yScale, color }) => {
|
|
1524
|
+
const points = data.map((d, i) => `${xPos(i)},${yScale(toNum(d[dataKey]))}`).join(" ");
|
|
1525
|
+
return /* @__PURE__ */ jsx9(
|
|
1526
|
+
"polyline",
|
|
1527
|
+
{
|
|
1528
|
+
points,
|
|
1529
|
+
fill: "none",
|
|
1530
|
+
stroke: color,
|
|
1531
|
+
strokeWidth: 2,
|
|
1532
|
+
strokeLinejoin: "round",
|
|
1533
|
+
strokeLinecap: "round"
|
|
1534
|
+
}
|
|
1535
|
+
);
|
|
1536
|
+
};
|
|
1537
|
+
var Area = ({ data, dataKey, xPos, yScale, baseY, color }) => {
|
|
1538
|
+
if (data.length === 0) return null;
|
|
1539
|
+
const top = data.map((d, i) => `${xPos(i)},${yScale(toNum(d[dataKey]))}`).join(" ");
|
|
1540
|
+
const path = `M ${xPos(0)},${baseY} L ${top} L ${xPos(data.length - 1)},${baseY} Z`;
|
|
1541
|
+
return /* @__PURE__ */ jsxs6(Fragment, { children: [
|
|
1542
|
+
/* @__PURE__ */ jsx9("path", { d: path, fill: color, fillOpacity: 0.18 }),
|
|
1543
|
+
/* @__PURE__ */ jsx9(Polyline, { data, dataKey, xPos, yScale, color })
|
|
1544
|
+
] });
|
|
1545
|
+
};
|
|
1546
|
+
var PieChart = ({ data, xKey, dataKey }) => {
|
|
1547
|
+
const cx = W / 2;
|
|
1548
|
+
const cy = H / 2;
|
|
1549
|
+
const r = Math.min(W, H) / 2 - 16;
|
|
1550
|
+
const total = data.reduce((sum, d) => sum + toNum(d[dataKey]), 0) || 1;
|
|
1551
|
+
let acc = 0;
|
|
1552
|
+
return /* @__PURE__ */ jsx9(
|
|
1553
|
+
"svg",
|
|
1554
|
+
{
|
|
1555
|
+
viewBox: `0 0 ${W} ${H}`,
|
|
1556
|
+
className: "aui-artifact-chart-svg w-full",
|
|
1557
|
+
role: "img",
|
|
1558
|
+
"aria-label": "Pie chart",
|
|
1559
|
+
children: data.map((d, i) => {
|
|
1560
|
+
const value = toNum(d[dataKey]);
|
|
1561
|
+
const start = acc / total * Math.PI * 2;
|
|
1562
|
+
acc += value;
|
|
1563
|
+
const end = acc / total * Math.PI * 2;
|
|
1564
|
+
return /* @__PURE__ */ jsx9(
|
|
1565
|
+
PieSlice,
|
|
1566
|
+
{
|
|
1567
|
+
cx,
|
|
1568
|
+
cy,
|
|
1569
|
+
r,
|
|
1570
|
+
start,
|
|
1571
|
+
end,
|
|
1572
|
+
color: COLORS[i % COLORS.length],
|
|
1573
|
+
label: String(d[xKey] ?? i)
|
|
1574
|
+
},
|
|
1575
|
+
i
|
|
1576
|
+
);
|
|
1577
|
+
})
|
|
1578
|
+
}
|
|
1579
|
+
);
|
|
1580
|
+
};
|
|
1581
|
+
var PieSlice = ({ cx, cy, r, start, end, color, label }) => {
|
|
1582
|
+
const x1 = cx + Math.sin(start) * r;
|
|
1583
|
+
const y1 = cy - Math.cos(start) * r;
|
|
1584
|
+
const x2 = cx + Math.sin(end) * r;
|
|
1585
|
+
const y2 = cy - Math.cos(end) * r;
|
|
1586
|
+
const large = end - start > Math.PI ? 1 : 0;
|
|
1587
|
+
const path = `M ${cx} ${cy} L ${x1} ${y1} A ${r} ${r} 0 ${large} 1 ${x2} ${y2} Z`;
|
|
1588
|
+
const mid = (start + end) / 2;
|
|
1589
|
+
const lx = cx + Math.sin(mid) * (r * 0.65);
|
|
1590
|
+
const ly = cy - Math.cos(mid) * (r * 0.65);
|
|
1591
|
+
return /* @__PURE__ */ jsxs6("g", { children: [
|
|
1592
|
+
/* @__PURE__ */ jsx9("path", { d: path, fill: color, stroke: "var(--background, #fff)", strokeWidth: 1 }),
|
|
1593
|
+
/* @__PURE__ */ jsx9(
|
|
1594
|
+
"text",
|
|
1595
|
+
{
|
|
1596
|
+
x: lx,
|
|
1597
|
+
y: ly,
|
|
1598
|
+
textAnchor: "middle",
|
|
1599
|
+
dominantBaseline: "middle",
|
|
1600
|
+
className: "fill-white text-[10px] font-semibold",
|
|
1601
|
+
children: label
|
|
1602
|
+
}
|
|
1603
|
+
)
|
|
1604
|
+
] });
|
|
1605
|
+
};
|
|
1606
|
+
var Legend = ({ dataKeys }) => /* @__PURE__ */ jsx9("div", { className: "aui-artifact-chart-legend mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: dataKeys.map((k, i) => /* @__PURE__ */ jsxs6("span", { className: "inline-flex items-center gap-1.5", children: [
|
|
1607
|
+
/* @__PURE__ */ jsx9(
|
|
1608
|
+
"span",
|
|
1609
|
+
{
|
|
1610
|
+
className: "inline-block size-2 rounded-sm",
|
|
1611
|
+
style: { background: COLORS[i % COLORS.length] },
|
|
1612
|
+
"aria-hidden": true
|
|
1613
|
+
}
|
|
1614
|
+
),
|
|
1615
|
+
k
|
|
1616
|
+
] }, k)) });
|
|
1617
|
+
var EmptyState = () => /* @__PURE__ */ jsx9("div", { className: "aui-artifact-chart-empty flex h-32 items-center justify-center text-xs text-muted-foreground", children: "No data" });
|
|
1618
|
+
function inferXKey(data) {
|
|
1619
|
+
if (data.length === 0) return "x";
|
|
1620
|
+
for (const k of Object.keys(data[0])) {
|
|
1621
|
+
if (typeof data[0][k] !== "number") return k;
|
|
1622
|
+
}
|
|
1623
|
+
return Object.keys(data[0])[0] ?? "x";
|
|
1624
|
+
}
|
|
1625
|
+
function inferDataKeys(data, xKey) {
|
|
1626
|
+
if (data.length === 0) return [];
|
|
1627
|
+
return Object.keys(data[0]).filter(
|
|
1628
|
+
(k) => k !== xKey && typeof data[0][k] === "number"
|
|
1629
|
+
);
|
|
1630
|
+
}
|
|
1631
|
+
function toNum(value) {
|
|
1632
|
+
const n = typeof value === "number" ? value : Number(value);
|
|
1633
|
+
return Number.isFinite(n) ? n : 0;
|
|
1634
|
+
}
|
|
1635
|
+
function niceTicks(min, max, count = 4) {
|
|
1636
|
+
if (max === min) return [min];
|
|
1637
|
+
const range = max - min;
|
|
1638
|
+
const step = niceStep(range / count);
|
|
1639
|
+
const start = Math.floor(min / step) * step;
|
|
1640
|
+
const out = [];
|
|
1641
|
+
for (let v = start; v <= max + step / 2; v += step) {
|
|
1642
|
+
out.push(round(v));
|
|
1643
|
+
}
|
|
1644
|
+
return out;
|
|
1645
|
+
}
|
|
1646
|
+
function niceStep(raw) {
|
|
1647
|
+
const exp = Math.floor(Math.log10(Math.abs(raw))) || 0;
|
|
1648
|
+
const base = Math.pow(10, exp);
|
|
1649
|
+
const norm = raw / base;
|
|
1650
|
+
let nice = 1;
|
|
1651
|
+
if (norm >= 5) nice = 5;
|
|
1652
|
+
else if (norm >= 2) nice = 2;
|
|
1653
|
+
return nice * base;
|
|
1654
|
+
}
|
|
1655
|
+
function round(v) {
|
|
1656
|
+
return Math.round(v * 1e6) / 1e6;
|
|
1657
|
+
}
|
|
1658
|
+
function formatTick(v, unit) {
|
|
1659
|
+
const s = Math.abs(v) >= 1e3 ? `${(v / 1e3).toFixed(1)}k` : String(round(v));
|
|
1660
|
+
return unit ? `${s}${unit}` : s;
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
// src/artifacts/question-artifact.tsx
|
|
1664
|
+
import { useState as useState3 } from "react";
|
|
1665
|
+
import { useThreadRuntime } from "@assistant-ui/react";
|
|
1666
|
+
import { CheckIcon } from "lucide-react";
|
|
1667
|
+
import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1668
|
+
var QuestionArtifactView = ({
|
|
1669
|
+
artifact
|
|
1670
|
+
}) => {
|
|
1671
|
+
const runtime = useThreadRuntime();
|
|
1672
|
+
const [selected, setSelected] = useState3([]);
|
|
1673
|
+
const [submitted, setSubmitted] = useState3(null);
|
|
1674
|
+
const isMulti = artifact.multi === true;
|
|
1675
|
+
const send = (labels) => {
|
|
1676
|
+
if (labels.length === 0) return;
|
|
1677
|
+
const text = labels.join(", ");
|
|
1678
|
+
setSubmitted(text);
|
|
1679
|
+
runtime.append({ role: "user", content: [{ type: "text", text }] });
|
|
1680
|
+
};
|
|
1681
|
+
const onPick = (option) => {
|
|
1682
|
+
if (submitted) return;
|
|
1683
|
+
if (!isMulti) {
|
|
1684
|
+
send([option.label]);
|
|
1685
|
+
return;
|
|
1686
|
+
}
|
|
1687
|
+
setSelected(
|
|
1688
|
+
(prev) => prev.includes(option.id) ? prev.filter((id) => id !== option.id) : [...prev, option.id]
|
|
1689
|
+
);
|
|
1690
|
+
};
|
|
1691
|
+
const onConfirm = () => {
|
|
1692
|
+
const labels = artifact.options.filter((o) => selected.includes(o.id)).map((o) => o.label);
|
|
1693
|
+
send(labels);
|
|
1694
|
+
};
|
|
1695
|
+
return /* @__PURE__ */ jsx10(ArtifactCard, { kind: "question", children: /* @__PURE__ */ jsxs7("div", { className: "aui-artifact-question p-3", children: [
|
|
1696
|
+
artifact.prompt && /* @__PURE__ */ jsx10("p", { className: "aui-artifact-question-prompt mb-2 text-sm text-foreground/85", children: artifact.prompt }),
|
|
1697
|
+
/* @__PURE__ */ jsx10("div", { className: "aui-artifact-question-options flex flex-col gap-1.5", children: artifact.options.map((option) => {
|
|
1698
|
+
const isSelected = isMulti && selected.includes(option.id);
|
|
1699
|
+
const isDisabled = Boolean(submitted);
|
|
1700
|
+
return /* @__PURE__ */ jsxs7(
|
|
1701
|
+
"button",
|
|
1702
|
+
{
|
|
1703
|
+
type: "button",
|
|
1704
|
+
disabled: isDisabled,
|
|
1705
|
+
onClick: () => onPick(option),
|
|
1706
|
+
className: cn(
|
|
1707
|
+
"aui-artifact-question-option flex items-center gap-2 rounded-lg border px-3 py-2 text-left text-sm transition-colors",
|
|
1708
|
+
"border-border/60 hover:border-primary/40 hover:bg-muted/40",
|
|
1709
|
+
isSelected && "border-primary/60 bg-primary/5",
|
|
1710
|
+
isDisabled && "cursor-not-allowed opacity-60 hover:border-border/60 hover:bg-transparent"
|
|
1711
|
+
),
|
|
1712
|
+
children: [
|
|
1713
|
+
/* @__PURE__ */ jsx10(
|
|
1714
|
+
"span",
|
|
1715
|
+
{
|
|
1716
|
+
className: cn(
|
|
1717
|
+
"flex size-4 shrink-0 items-center justify-center rounded-full border",
|
|
1718
|
+
isSelected ? "border-primary bg-primary text-primary-foreground" : "border-border"
|
|
1719
|
+
),
|
|
1720
|
+
"aria-hidden": true,
|
|
1721
|
+
children: isSelected && /* @__PURE__ */ jsx10(CheckIcon, { className: "size-3" })
|
|
1722
|
+
}
|
|
1723
|
+
),
|
|
1724
|
+
/* @__PURE__ */ jsxs7("div", { className: "aui-artifact-question-option-text flex-1", children: [
|
|
1725
|
+
/* @__PURE__ */ jsx10("div", { className: "font-medium text-foreground/90", children: option.label }),
|
|
1726
|
+
option.description && /* @__PURE__ */ jsx10("div", { className: "text-xs text-muted-foreground", children: option.description })
|
|
1727
|
+
] })
|
|
1728
|
+
]
|
|
1729
|
+
},
|
|
1730
|
+
option.id
|
|
1731
|
+
);
|
|
1732
|
+
}) }),
|
|
1733
|
+
isMulti && !submitted && /* @__PURE__ */ jsx10("div", { className: "mt-2 flex justify-end", children: /* @__PURE__ */ jsx10(
|
|
1734
|
+
Button,
|
|
1735
|
+
{
|
|
1736
|
+
type: "button",
|
|
1737
|
+
size: "sm",
|
|
1738
|
+
disabled: selected.length === 0,
|
|
1739
|
+
onClick: onConfirm,
|
|
1740
|
+
children: "Confirm"
|
|
1741
|
+
}
|
|
1742
|
+
) }),
|
|
1743
|
+
submitted && /* @__PURE__ */ jsxs7("p", { className: "aui-artifact-question-submitted mt-2 text-xs text-muted-foreground", children: [
|
|
1744
|
+
"Sent: ",
|
|
1745
|
+
submitted
|
|
1746
|
+
] })
|
|
1747
|
+
] }) });
|
|
1748
|
+
};
|
|
1749
|
+
|
|
1750
|
+
// src/artifacts/html-artifact.tsx
|
|
1751
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
1752
|
+
var HtmlArtifactView = ({ artifact }) => {
|
|
1753
|
+
const sandboxed = artifact.sandboxed !== false;
|
|
1754
|
+
const sandbox = sandboxed ? "allow-scripts allow-same-origin allow-forms allow-modals allow-popups allow-pointer-lock" : void 0;
|
|
1755
|
+
const height = artifact.height ?? "320px";
|
|
1756
|
+
return /* @__PURE__ */ jsx11(ArtifactCard, { title: artifact.title, kind: "html", children: /* @__PURE__ */ jsx11(
|
|
1757
|
+
"iframe",
|
|
1758
|
+
{
|
|
1759
|
+
title: artifact.title ?? "HTML artifact",
|
|
1760
|
+
srcDoc: artifact.content,
|
|
1761
|
+
sandbox,
|
|
1762
|
+
className: "aui-artifact-html w-full border-0 bg-background",
|
|
1763
|
+
style: { height }
|
|
1764
|
+
}
|
|
1765
|
+
) });
|
|
1766
|
+
};
|
|
1767
|
+
|
|
1768
|
+
// src/artifacts/json-artifact.tsx
|
|
1769
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
1770
|
+
var JsonArtifactView = ({
|
|
1771
|
+
artifact
|
|
1772
|
+
}) => {
|
|
1773
|
+
const data = "data" in artifact ? artifact.data : artifact;
|
|
1774
|
+
const title = artifact.title;
|
|
1775
|
+
let body;
|
|
1776
|
+
try {
|
|
1777
|
+
body = JSON.stringify(data, null, 2);
|
|
1778
|
+
} catch {
|
|
1779
|
+
body = String(data);
|
|
1780
|
+
}
|
|
1781
|
+
return /* @__PURE__ */ jsx12(ArtifactCard, { title, kind: "json", children: /* @__PURE__ */ jsx12("pre", { className: "aui-artifact-json m-0 max-h-[420px] overflow-auto p-3 font-mono text-[12px] leading-relaxed text-foreground/85", children: body }) });
|
|
1782
|
+
};
|
|
1783
|
+
|
|
1784
|
+
// src/artifacts/table-artifact.tsx
|
|
1785
|
+
import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1786
|
+
var TableArtifactView = ({ artifact }) => {
|
|
1787
|
+
const rows = artifact.rows ?? [];
|
|
1788
|
+
const columns = artifact.columns ?? deriveColumns(rows);
|
|
1789
|
+
return /* @__PURE__ */ jsx13(ArtifactCard, { title: artifact.title, kind: "table", children: /* @__PURE__ */ jsx13("div", { className: "aui-artifact-table-wrap overflow-x-auto", children: /* @__PURE__ */ jsxs8("table", { className: "aui-artifact-table w-full border-collapse text-sm", children: [
|
|
1790
|
+
/* @__PURE__ */ jsx13("thead", { children: /* @__PURE__ */ jsx13("tr", { className: "border-b border-border/40 bg-muted/20", children: columns.map((col) => /* @__PURE__ */ jsx13(
|
|
1791
|
+
"th",
|
|
1792
|
+
{
|
|
1793
|
+
className: "px-3 py-2 text-left text-xs font-semibold uppercase tracking-wider text-muted-foreground",
|
|
1794
|
+
children: col.label ?? col.key
|
|
1795
|
+
},
|
|
1796
|
+
col.key
|
|
1797
|
+
)) }) }),
|
|
1798
|
+
/* @__PURE__ */ jsx13("tbody", { children: rows.map((row, i) => /* @__PURE__ */ jsx13(
|
|
1799
|
+
"tr",
|
|
1800
|
+
{
|
|
1801
|
+
className: "border-b border-border/30 transition-colors last:border-b-0 hover:bg-muted/20",
|
|
1802
|
+
children: columns.map((col) => /* @__PURE__ */ jsx13(
|
|
1803
|
+
"td",
|
|
1804
|
+
{
|
|
1805
|
+
className: "px-3 py-2 align-top text-foreground/85",
|
|
1806
|
+
children: formatCell(row[col.key])
|
|
1807
|
+
},
|
|
1808
|
+
col.key
|
|
1809
|
+
))
|
|
1810
|
+
},
|
|
1811
|
+
i
|
|
1812
|
+
)) })
|
|
1813
|
+
] }) }) });
|
|
1814
|
+
};
|
|
1815
|
+
function deriveColumns(rows) {
|
|
1816
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1817
|
+
const cols = [];
|
|
1818
|
+
for (const row of rows) {
|
|
1819
|
+
for (const key of Object.keys(row)) {
|
|
1820
|
+
if (!seen.has(key)) {
|
|
1821
|
+
seen.add(key);
|
|
1822
|
+
cols.push({ key });
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
return cols;
|
|
1827
|
+
}
|
|
1828
|
+
function formatCell(value) {
|
|
1829
|
+
if (value === null || value === void 0) return "";
|
|
1830
|
+
if (typeof value === "object") return JSON.stringify(value);
|
|
1831
|
+
return String(value);
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
// src/artifacts/ui/ui-artifact.tsx
|
|
1835
|
+
import { useReducer } from "react";
|
|
1836
|
+
|
|
1837
|
+
// src/artifacts/ui/types.ts
|
|
1838
|
+
function isUiBinding(value) {
|
|
1839
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.$bind === "string";
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
// src/artifacts/ui/state.ts
|
|
1843
|
+
function uiStateReducer(state, action) {
|
|
1844
|
+
switch (action.type) {
|
|
1845
|
+
case "set":
|
|
1846
|
+
return setPath(state, action.path, action.value);
|
|
1847
|
+
case "toggle": {
|
|
1848
|
+
const current = getPath(state, action.path);
|
|
1849
|
+
return setPath(state, action.path, !current);
|
|
1850
|
+
}
|
|
1851
|
+
case "replace":
|
|
1852
|
+
return action.state;
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
function getPath(state, path) {
|
|
1856
|
+
if (!path) return void 0;
|
|
1857
|
+
const parts = path.split(".");
|
|
1858
|
+
let cursor = state;
|
|
1859
|
+
for (const part of parts) {
|
|
1860
|
+
if (typeof cursor !== "object" || cursor === null) return void 0;
|
|
1861
|
+
cursor = cursor[part];
|
|
1862
|
+
}
|
|
1863
|
+
return cursor;
|
|
1864
|
+
}
|
|
1865
|
+
function setPath(state, path, value) {
|
|
1866
|
+
if (!path) return state;
|
|
1867
|
+
const parts = path.split(".");
|
|
1868
|
+
const next = { ...state };
|
|
1869
|
+
let cursor = next;
|
|
1870
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
1871
|
+
const key = parts[i];
|
|
1872
|
+
const child = cursor[key];
|
|
1873
|
+
const cloned = typeof child === "object" && child !== null && !Array.isArray(child) ? { ...child } : {};
|
|
1874
|
+
cursor[key] = cloned;
|
|
1875
|
+
cursor = cloned;
|
|
1876
|
+
}
|
|
1877
|
+
cursor[parts[parts.length - 1]] = value;
|
|
1878
|
+
return next;
|
|
1879
|
+
}
|
|
1880
|
+
function resolveBindable(value, state) {
|
|
1881
|
+
if (isUiBinding(value)) {
|
|
1882
|
+
return getPath(state, value.$bind);
|
|
1883
|
+
}
|
|
1884
|
+
return value;
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
// src/artifacts/ui/registry.tsx
|
|
1888
|
+
import {
|
|
1889
|
+
createContext as createContext2,
|
|
1890
|
+
useContext as useContext2
|
|
1891
|
+
} from "react";
|
|
1892
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
1893
|
+
var UiStateContext = createContext2({});
|
|
1894
|
+
var UiDispatchContext = createContext2(() => {
|
|
1895
|
+
});
|
|
1896
|
+
var UiStateProvider = ({ state, dispatch, children }) => /* @__PURE__ */ jsx14(UiStateContext.Provider, { value: state, children: /* @__PURE__ */ jsx14(UiDispatchContext.Provider, { value: dispatch, children }) });
|
|
1897
|
+
function useUiState() {
|
|
1898
|
+
return useContext2(UiStateContext);
|
|
1899
|
+
}
|
|
1900
|
+
function useUiDispatch() {
|
|
1901
|
+
return useContext2(UiDispatchContext);
|
|
1902
|
+
}
|
|
1903
|
+
var UiEventContext = createContext2(
|
|
1904
|
+
null
|
|
1905
|
+
);
|
|
1906
|
+
var UiEventProvider = ({ onEvent, children }) => /* @__PURE__ */ jsx14(UiEventContext.Provider, { value: onEvent, children });
|
|
1907
|
+
function useUiEventEmitter() {
|
|
1908
|
+
return useContext2(UiEventContext);
|
|
1909
|
+
}
|
|
1910
|
+
var UiCustomNodeRegistryContext = createContext2({});
|
|
1911
|
+
var UiCustomNodeRegistryProvider = ({ renderers, children }) => /* @__PURE__ */ jsx14(UiCustomNodeRegistryContext.Provider, { value: renderers, children });
|
|
1912
|
+
function useUiCustomNodeRegistry() {
|
|
1913
|
+
return useContext2(UiCustomNodeRegistryContext);
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
// src/artifacts/ui/nodes.tsx
|
|
1917
|
+
import { useCallback as useCallback2 } from "react";
|
|
1918
|
+
import { motion } from "motion/react";
|
|
1919
|
+
import { useThreadRuntime as useThreadRuntime2 } from "@assistant-ui/react";
|
|
1920
|
+
import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1921
|
+
var UiNodeView = ({ node }) => {
|
|
1922
|
+
switch (node.kind) {
|
|
1923
|
+
case "box":
|
|
1924
|
+
return /* @__PURE__ */ jsx15(BoxNode, { node });
|
|
1925
|
+
case "text":
|
|
1926
|
+
return /* @__PURE__ */ jsx15(TextNode, { node });
|
|
1927
|
+
case "heading":
|
|
1928
|
+
return /* @__PURE__ */ jsx15(HeadingNode, { node });
|
|
1929
|
+
case "badge":
|
|
1930
|
+
return /* @__PURE__ */ jsx15(BadgeNode, { node });
|
|
1931
|
+
case "button":
|
|
1932
|
+
return /* @__PURE__ */ jsx15(ButtonNode, { node });
|
|
1933
|
+
case "toggle":
|
|
1934
|
+
return /* @__PURE__ */ jsx15(ToggleNode, { node });
|
|
1935
|
+
case "slider":
|
|
1936
|
+
return /* @__PURE__ */ jsx15(SliderNode, { node });
|
|
1937
|
+
case "tooltip":
|
|
1938
|
+
return /* @__PURE__ */ jsx15(TooltipNode, { node });
|
|
1939
|
+
case "draggable":
|
|
1940
|
+
return /* @__PURE__ */ jsx15(DraggableNode, { node });
|
|
1941
|
+
case "custom":
|
|
1942
|
+
return /* @__PURE__ */ jsx15(CustomNode, { node });
|
|
1943
|
+
default:
|
|
1944
|
+
return null;
|
|
1945
|
+
}
|
|
1946
|
+
};
|
|
1947
|
+
function useActionRunner() {
|
|
1948
|
+
const state = useUiState();
|
|
1949
|
+
const dispatch = useUiDispatch();
|
|
1950
|
+
const runtime = useThreadRuntime2();
|
|
1951
|
+
const emit = useUiEventEmitter();
|
|
1952
|
+
return useCallback2(
|
|
1953
|
+
(actions) => {
|
|
1954
|
+
if (!actions) return;
|
|
1955
|
+
const list = Array.isArray(actions) ? actions : [actions];
|
|
1956
|
+
for (const action of list) {
|
|
1957
|
+
switch (action.kind) {
|
|
1958
|
+
case "message": {
|
|
1959
|
+
const text = resolveBindable(action.text, state);
|
|
1960
|
+
if (typeof text === "string" && text.length > 0) {
|
|
1961
|
+
runtime?.append({
|
|
1962
|
+
role: "user",
|
|
1963
|
+
content: [{ type: "text", text }]
|
|
1964
|
+
});
|
|
1965
|
+
}
|
|
1966
|
+
break;
|
|
1967
|
+
}
|
|
1968
|
+
case "set": {
|
|
1969
|
+
const value = resolveBindable(action.value, state);
|
|
1970
|
+
dispatch({ type: "set", path: action.path, value });
|
|
1971
|
+
break;
|
|
1972
|
+
}
|
|
1973
|
+
case "toggle": {
|
|
1974
|
+
dispatch({ type: "toggle", path: action.path });
|
|
1975
|
+
break;
|
|
1976
|
+
}
|
|
1977
|
+
case "emit": {
|
|
1978
|
+
emit?.({ name: action.name, payload: action.payload });
|
|
1979
|
+
break;
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
},
|
|
1984
|
+
[state, dispatch, runtime, emit]
|
|
1985
|
+
);
|
|
1986
|
+
}
|
|
1987
|
+
var ALIGN_CLS = {
|
|
1988
|
+
start: "items-start",
|
|
1989
|
+
center: "items-center",
|
|
1990
|
+
end: "items-end",
|
|
1991
|
+
stretch: "items-stretch"
|
|
1992
|
+
};
|
|
1993
|
+
var JUSTIFY_CLS = {
|
|
1994
|
+
start: "justify-start",
|
|
1995
|
+
center: "justify-center",
|
|
1996
|
+
end: "justify-end",
|
|
1997
|
+
between: "justify-between",
|
|
1998
|
+
around: "justify-around"
|
|
1999
|
+
};
|
|
2000
|
+
var BoxNode = ({ node }) => {
|
|
2001
|
+
const dir = node.direction ?? "col";
|
|
2002
|
+
return /* @__PURE__ */ jsx15(
|
|
2003
|
+
"div",
|
|
2004
|
+
{
|
|
2005
|
+
className: cn(
|
|
2006
|
+
"aui-ui-box flex",
|
|
2007
|
+
dir === "col" ? "flex-col" : "flex-row",
|
|
2008
|
+
node.wrap && "flex-wrap",
|
|
2009
|
+
node.align && ALIGN_CLS[node.align],
|
|
2010
|
+
node.justify && JUSTIFY_CLS[node.justify],
|
|
2011
|
+
node.className
|
|
2012
|
+
),
|
|
2013
|
+
style: {
|
|
2014
|
+
gap: node.gap !== void 0 ? `${node.gap * 0.25}rem` : void 0,
|
|
2015
|
+
padding: node.padding !== void 0 ? `${node.padding * 0.25}rem` : void 0
|
|
2016
|
+
},
|
|
2017
|
+
children: node.children?.map((child, i) => /* @__PURE__ */ jsx15(UiNodeView, { node: child }, child.id ?? i))
|
|
2018
|
+
}
|
|
2019
|
+
);
|
|
2020
|
+
};
|
|
2021
|
+
var TEXT_SIZE = {
|
|
2022
|
+
xs: "text-xs",
|
|
2023
|
+
sm: "text-sm",
|
|
2024
|
+
base: "text-base",
|
|
2025
|
+
lg: "text-lg"
|
|
2026
|
+
};
|
|
2027
|
+
var TEXT_WEIGHT = {
|
|
2028
|
+
normal: "font-normal",
|
|
2029
|
+
medium: "font-medium",
|
|
2030
|
+
semibold: "font-semibold",
|
|
2031
|
+
bold: "font-bold"
|
|
2032
|
+
};
|
|
2033
|
+
var TextNode = ({ node }) => {
|
|
2034
|
+
const state = useUiState();
|
|
2035
|
+
const value = resolveBindable(node.value, state);
|
|
2036
|
+
return /* @__PURE__ */ jsx15(
|
|
2037
|
+
"span",
|
|
2038
|
+
{
|
|
2039
|
+
className: cn(
|
|
2040
|
+
"aui-ui-text",
|
|
2041
|
+
node.muted && "text-muted-foreground",
|
|
2042
|
+
node.size && TEXT_SIZE[node.size],
|
|
2043
|
+
node.weight && TEXT_WEIGHT[node.weight],
|
|
2044
|
+
node.className
|
|
2045
|
+
),
|
|
2046
|
+
children: value === void 0 || value === null ? "" : String(value)
|
|
2047
|
+
}
|
|
2048
|
+
);
|
|
2049
|
+
};
|
|
2050
|
+
var HEADING_CLS = {
|
|
2051
|
+
1: "text-2xl",
|
|
2052
|
+
2: "text-xl",
|
|
2053
|
+
3: "text-lg",
|
|
2054
|
+
4: "text-base"
|
|
2055
|
+
};
|
|
2056
|
+
var HeadingNode = ({ node }) => {
|
|
2057
|
+
const state = useUiState();
|
|
2058
|
+
const value = String(resolveBindable(node.value, state) ?? "");
|
|
2059
|
+
const level = node.level ?? 2;
|
|
2060
|
+
const cls = cn(
|
|
2061
|
+
"aui-ui-heading font-semibold text-foreground",
|
|
2062
|
+
HEADING_CLS[level],
|
|
2063
|
+
node.className
|
|
2064
|
+
);
|
|
2065
|
+
switch (level) {
|
|
2066
|
+
case 1:
|
|
2067
|
+
return /* @__PURE__ */ jsx15("h1", { className: cls, children: value });
|
|
2068
|
+
case 2:
|
|
2069
|
+
return /* @__PURE__ */ jsx15("h2", { className: cls, children: value });
|
|
2070
|
+
case 3:
|
|
2071
|
+
return /* @__PURE__ */ jsx15("h3", { className: cls, children: value });
|
|
2072
|
+
case 4:
|
|
2073
|
+
return /* @__PURE__ */ jsx15("h4", { className: cls, children: value });
|
|
2074
|
+
}
|
|
2075
|
+
};
|
|
2076
|
+
var BADGE_TONE = {
|
|
2077
|
+
default: "bg-muted text-foreground",
|
|
2078
|
+
primary: "bg-primary/10 text-primary",
|
|
2079
|
+
success: "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400",
|
|
2080
|
+
warn: "bg-amber-500/10 text-amber-600 dark:text-amber-400",
|
|
2081
|
+
danger: "bg-destructive/10 text-destructive"
|
|
2082
|
+
};
|
|
2083
|
+
var BadgeNode = ({ node }) => {
|
|
2084
|
+
const state = useUiState();
|
|
2085
|
+
const value = String(resolveBindable(node.value, state) ?? "");
|
|
2086
|
+
return /* @__PURE__ */ jsx15(
|
|
2087
|
+
"span",
|
|
2088
|
+
{
|
|
2089
|
+
className: cn(
|
|
2090
|
+
"aui-ui-badge inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium",
|
|
2091
|
+
BADGE_TONE[node.tone ?? "default"],
|
|
2092
|
+
node.className
|
|
2093
|
+
),
|
|
2094
|
+
children: value
|
|
2095
|
+
}
|
|
2096
|
+
);
|
|
2097
|
+
};
|
|
2098
|
+
var ButtonNode = ({ node }) => {
|
|
2099
|
+
const state = useUiState();
|
|
2100
|
+
const run = useActionRunner();
|
|
2101
|
+
const label = String(resolveBindable(node.label, state) ?? "");
|
|
2102
|
+
const disabled = node.disabled !== void 0 ? Boolean(resolveBindable(node.disabled, state)) : false;
|
|
2103
|
+
return /* @__PURE__ */ jsx15(
|
|
2104
|
+
Button,
|
|
2105
|
+
{
|
|
2106
|
+
variant: node.variant ?? "default",
|
|
2107
|
+
size: node.size ?? "default",
|
|
2108
|
+
disabled,
|
|
2109
|
+
className: node.className,
|
|
2110
|
+
onClick: () => run(node.onClick),
|
|
2111
|
+
children: label
|
|
2112
|
+
}
|
|
2113
|
+
);
|
|
2114
|
+
};
|
|
2115
|
+
var ToggleNode = ({ node }) => {
|
|
2116
|
+
const state = useUiState();
|
|
2117
|
+
const dispatch = useUiDispatch();
|
|
2118
|
+
const run = useActionRunner();
|
|
2119
|
+
const value = Boolean(getPath(state, node.binding));
|
|
2120
|
+
const label = node.label ? String(resolveBindable(node.label, state) ?? "") : null;
|
|
2121
|
+
const onToggle = () => {
|
|
2122
|
+
dispatch({ type: "toggle", path: node.binding });
|
|
2123
|
+
run(node.onChange);
|
|
2124
|
+
};
|
|
2125
|
+
return /* @__PURE__ */ jsxs9(
|
|
2126
|
+
"label",
|
|
2127
|
+
{
|
|
2128
|
+
className: cn(
|
|
2129
|
+
"aui-ui-toggle inline-flex cursor-pointer items-center gap-2 text-sm select-none",
|
|
2130
|
+
node.className
|
|
2131
|
+
),
|
|
2132
|
+
children: [
|
|
2133
|
+
/* @__PURE__ */ jsx15(
|
|
2134
|
+
"button",
|
|
2135
|
+
{
|
|
2136
|
+
type: "button",
|
|
2137
|
+
role: "switch",
|
|
2138
|
+
"aria-checked": value,
|
|
2139
|
+
onClick: onToggle,
|
|
2140
|
+
className: cn(
|
|
2141
|
+
"relative inline-flex h-5 w-9 shrink-0 items-center rounded-full border transition-colors",
|
|
2142
|
+
value ? "border-primary bg-primary" : "border-border bg-muted hover:bg-muted/80"
|
|
2143
|
+
),
|
|
2144
|
+
children: /* @__PURE__ */ jsx15(
|
|
2145
|
+
"span",
|
|
2146
|
+
{
|
|
2147
|
+
className: cn(
|
|
2148
|
+
"inline-block size-4 transform rounded-full bg-background shadow transition-transform",
|
|
2149
|
+
value ? "translate-x-4" : "translate-x-0.5"
|
|
2150
|
+
),
|
|
2151
|
+
"aria-hidden": true
|
|
2152
|
+
}
|
|
2153
|
+
)
|
|
2154
|
+
}
|
|
2155
|
+
),
|
|
2156
|
+
label && /* @__PURE__ */ jsx15("span", { className: "text-foreground/85", children: label })
|
|
2157
|
+
]
|
|
2158
|
+
}
|
|
2159
|
+
);
|
|
2160
|
+
};
|
|
2161
|
+
var SliderNode = ({ node }) => {
|
|
2162
|
+
const state = useUiState();
|
|
2163
|
+
const dispatch = useUiDispatch();
|
|
2164
|
+
const run = useActionRunner();
|
|
2165
|
+
const min = node.min ?? 0;
|
|
2166
|
+
const max = node.max ?? 100;
|
|
2167
|
+
const step = node.step ?? 1;
|
|
2168
|
+
const raw = getPath(state, node.binding);
|
|
2169
|
+
const value = typeof raw === "number" ? raw : min;
|
|
2170
|
+
const showValue = node.showValue ?? true;
|
|
2171
|
+
const label = node.label ? String(resolveBindable(node.label, state) ?? "") : null;
|
|
2172
|
+
const onChange = (e) => {
|
|
2173
|
+
const next = Number(e.target.value);
|
|
2174
|
+
dispatch({ type: "set", path: node.binding, value: next });
|
|
2175
|
+
};
|
|
2176
|
+
return /* @__PURE__ */ jsxs9("div", { className: cn("aui-ui-slider flex flex-col gap-1", node.className), children: [
|
|
2177
|
+
(label || showValue) && /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between text-xs text-muted-foreground", children: [
|
|
2178
|
+
label && /* @__PURE__ */ jsx15("span", { children: label }),
|
|
2179
|
+
showValue && /* @__PURE__ */ jsx15("span", { className: "font-mono", children: value })
|
|
2180
|
+
] }),
|
|
2181
|
+
/* @__PURE__ */ jsx15(
|
|
2182
|
+
"input",
|
|
2183
|
+
{
|
|
2184
|
+
type: "range",
|
|
2185
|
+
min,
|
|
2186
|
+
max,
|
|
2187
|
+
step,
|
|
2188
|
+
value,
|
|
2189
|
+
onChange,
|
|
2190
|
+
onMouseUp: () => run(node.onChange),
|
|
2191
|
+
onKeyUp: (e) => {
|
|
2192
|
+
if (e.key === "Enter" || e.key === " ") run(node.onChange);
|
|
2193
|
+
},
|
|
2194
|
+
onTouchEnd: () => run(node.onChange),
|
|
2195
|
+
className: "aui-ui-slider-input h-1.5 w-full cursor-pointer appearance-none rounded-full bg-muted accent-primary"
|
|
2196
|
+
}
|
|
2197
|
+
)
|
|
2198
|
+
] });
|
|
2199
|
+
};
|
|
2200
|
+
var TooltipNode = ({ node }) => {
|
|
2201
|
+
const state = useUiState();
|
|
2202
|
+
const content = String(resolveBindable(node.content, state) ?? "");
|
|
2203
|
+
return /* @__PURE__ */ jsx15(TooltipProvider, { children: /* @__PURE__ */ jsxs9(Tooltip, { children: [
|
|
2204
|
+
/* @__PURE__ */ jsx15(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx15("span", { className: cn("aui-ui-tooltip-trigger inline-flex", node.className), children: /* @__PURE__ */ jsx15(UiNodeView, { node: node.child }) }) }),
|
|
2205
|
+
/* @__PURE__ */ jsx15(TooltipContent, { side: node.side ?? "top", children: content })
|
|
2206
|
+
] }) });
|
|
2207
|
+
};
|
|
2208
|
+
var DraggableNode = ({ node }) => {
|
|
2209
|
+
const run = useActionRunner();
|
|
2210
|
+
const snapBack = node.snapBack ?? true;
|
|
2211
|
+
const axis = node.axis ?? "both";
|
|
2212
|
+
const dragProp = axis === "both" ? true : axis;
|
|
2213
|
+
return /* @__PURE__ */ jsx15(
|
|
2214
|
+
motion.div,
|
|
2215
|
+
{
|
|
2216
|
+
drag: dragProp,
|
|
2217
|
+
dragMomentum: false,
|
|
2218
|
+
dragSnapToOrigin: snapBack,
|
|
2219
|
+
whileDrag: { scale: 1.02, cursor: "grabbing" },
|
|
2220
|
+
onDragEnd: () => run(node.onDragEnd),
|
|
2221
|
+
className: cn(
|
|
2222
|
+
"aui-ui-draggable inline-block cursor-grab touch-none",
|
|
2223
|
+
node.className
|
|
2224
|
+
),
|
|
2225
|
+
children: /* @__PURE__ */ jsx15(UiNodeView, { node: node.child })
|
|
2226
|
+
}
|
|
2227
|
+
);
|
|
2228
|
+
};
|
|
2229
|
+
var CustomNode = ({ node }) => {
|
|
2230
|
+
const state = useUiState();
|
|
2231
|
+
const registry = useUiCustomNodeRegistry();
|
|
2232
|
+
const Renderer = registry[node.name];
|
|
2233
|
+
if (!Renderer) return null;
|
|
2234
|
+
const resolvedProps = resolveProps(node.props ?? {}, state);
|
|
2235
|
+
const children = node.children?.map((child, i) => /* @__PURE__ */ jsx15(UiNodeView, { node: child }, child.id ?? i));
|
|
2236
|
+
return /* @__PURE__ */ jsx15(Renderer, { props: resolvedProps, children });
|
|
2237
|
+
};
|
|
2238
|
+
function resolveProps(props, state) {
|
|
2239
|
+
const out = {};
|
|
2240
|
+
for (const [k, v] of Object.entries(props)) {
|
|
2241
|
+
out[k] = resolveBindable(v, state);
|
|
2242
|
+
}
|
|
2243
|
+
return out;
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
// src/artifacts/ui/ui-artifact.tsx
|
|
2247
|
+
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
2248
|
+
var UiArtifactView = ({ artifact }) => {
|
|
2249
|
+
const [state, dispatch] = useReducer(
|
|
2250
|
+
uiStateReducer,
|
|
2251
|
+
artifact.initialState ?? {}
|
|
2252
|
+
);
|
|
2253
|
+
return /* @__PURE__ */ jsx16(ArtifactCard, { title: artifact.title, kind: "ui", children: /* @__PURE__ */ jsx16(UiStateProvider, { state, dispatch, children: /* @__PURE__ */ jsx16("div", { className: "aui-ui-root p-3", children: /* @__PURE__ */ jsx16(UiNodeView, { node: artifact.root }) }) }) });
|
|
2254
|
+
};
|
|
2255
|
+
|
|
2256
|
+
// src/artifacts/registry.tsx
|
|
2257
|
+
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
2258
|
+
var defaultArtifactRenderers = {
|
|
2259
|
+
chart: ChartArtifactView,
|
|
2260
|
+
question: QuestionArtifactView,
|
|
2261
|
+
html: HtmlArtifactView,
|
|
2262
|
+
json: JsonArtifactView,
|
|
2263
|
+
table: TableArtifactView,
|
|
2264
|
+
ui: UiArtifactView
|
|
2265
|
+
};
|
|
2266
|
+
var ArtifactRegistryContext = createContext3(
|
|
2267
|
+
defaultArtifactRenderers
|
|
2268
|
+
);
|
|
2269
|
+
var ArtifactRegistryProvider = ({ renderers, override, children }) => {
|
|
2270
|
+
const merged = useMemo3(() => {
|
|
2271
|
+
if (!renderers) return defaultArtifactRenderers;
|
|
2272
|
+
if (override) return renderers;
|
|
2273
|
+
return { ...defaultArtifactRenderers, ...renderers };
|
|
2274
|
+
}, [renderers, override]);
|
|
2275
|
+
return /* @__PURE__ */ jsx17(ArtifactRegistryContext.Provider, { value: merged, children });
|
|
2276
|
+
};
|
|
2277
|
+
function useArtifactRegistry() {
|
|
2278
|
+
return useContext3(ArtifactRegistryContext);
|
|
2279
|
+
}
|
|
2280
|
+
var ArtifactView = ({ artifact }) => {
|
|
2281
|
+
const registry = useArtifactRegistry();
|
|
2282
|
+
const Renderer = registry[artifact.type] ?? registry.json;
|
|
2283
|
+
if (!Renderer) return null;
|
|
2284
|
+
return /* @__PURE__ */ jsx17(Renderer, { artifact });
|
|
2285
|
+
};
|
|
2286
|
+
|
|
2287
|
+
// src/artifacts/parse.ts
|
|
2288
|
+
var ARTIFACT_FENCE_LANGUAGES = /* @__PURE__ */ new Set([
|
|
2289
|
+
"timbal-artifact",
|
|
2290
|
+
"timbal"
|
|
2291
|
+
]);
|
|
2292
|
+
function isArtifactFenceLanguage(language) {
|
|
2293
|
+
return typeof language === "string" && ARTIFACT_FENCE_LANGUAGES.has(language);
|
|
2294
|
+
}
|
|
2295
|
+
function tryParseStructuredText(text) {
|
|
2296
|
+
const trimmed = text.trim();
|
|
2297
|
+
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return null;
|
|
2298
|
+
try {
|
|
2299
|
+
return JSON.parse(trimmed);
|
|
2300
|
+
} catch {
|
|
2301
|
+
}
|
|
2302
|
+
try {
|
|
2303
|
+
const asJson = trimmed.replace(/\bTrue\b/g, "true").replace(/\bFalse\b/g, "false").replace(/\bNone\b/g, "null").replace(/'/g, '"');
|
|
2304
|
+
return JSON.parse(asJson);
|
|
2305
|
+
} catch {
|
|
2306
|
+
return null;
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
function parseArtifactFromToolResult(result) {
|
|
2310
|
+
if (result === void 0 || result === null) return null;
|
|
2311
|
+
if (typeof result === "string") {
|
|
2312
|
+
const parsed = tryParseStructuredText(result);
|
|
2313
|
+
if (parsed === null) return null;
|
|
2314
|
+
return parseArtifactFromToolResult(parsed);
|
|
2315
|
+
}
|
|
2316
|
+
if (Array.isArray(result)) {
|
|
2317
|
+
for (const item of result) {
|
|
2318
|
+
if (typeof item === "object" && item !== null && "text" in item) {
|
|
2319
|
+
const text = item.text;
|
|
2320
|
+
if (typeof text === "string") {
|
|
2321
|
+
const fromText = parseArtifactFromToolResult(text);
|
|
2322
|
+
if (fromText) return fromText;
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2326
|
+
return null;
|
|
2327
|
+
}
|
|
2328
|
+
if (typeof result === "object") {
|
|
2329
|
+
const obj = result;
|
|
2330
|
+
if (obj.type === "text" && typeof obj.text === "string") {
|
|
2331
|
+
return parseArtifactFromToolResult(obj.text);
|
|
2332
|
+
}
|
|
2333
|
+
if (obj.type === "thinking" && typeof obj.thinking === "string") {
|
|
2334
|
+
return parseArtifactFromToolResult(obj.thinking);
|
|
2335
|
+
}
|
|
2336
|
+
}
|
|
2337
|
+
return isArtifact(result) ? result : null;
|
|
2338
|
+
}
|
|
2339
|
+
var FENCE_RE = /```(?:timbal-artifact|timbal)\s*\n([\s\S]*?)\n```/g;
|
|
2340
|
+
function findMarkdownArtifacts(markdown) {
|
|
2341
|
+
const matches = [];
|
|
2342
|
+
FENCE_RE.lastIndex = 0;
|
|
2343
|
+
let m;
|
|
2344
|
+
while ((m = FENCE_RE.exec(markdown)) !== null) {
|
|
2345
|
+
const raw = m[0];
|
|
2346
|
+
const body = m[1];
|
|
2347
|
+
try {
|
|
2348
|
+
const parsed = JSON.parse(body);
|
|
2349
|
+
if (isArtifact(parsed)) {
|
|
2350
|
+
matches.push({
|
|
2351
|
+
artifact: parsed,
|
|
2352
|
+
raw,
|
|
2353
|
+
start: m.index,
|
|
2354
|
+
end: m.index + raw.length
|
|
2355
|
+
});
|
|
2356
|
+
}
|
|
2357
|
+
} catch {
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
return matches;
|
|
2361
|
+
}
|
|
2362
|
+
function splitMarkdownByArtifacts(markdown) {
|
|
2363
|
+
const matches = findMarkdownArtifacts(markdown);
|
|
2364
|
+
if (matches.length === 0) return [{ kind: "text", text: markdown }];
|
|
2365
|
+
const segments = [];
|
|
2366
|
+
let cursor = 0;
|
|
2367
|
+
for (const match of matches) {
|
|
2368
|
+
if (match.start > cursor) {
|
|
2369
|
+
segments.push({ kind: "text", text: markdown.slice(cursor, match.start) });
|
|
2370
|
+
}
|
|
2371
|
+
segments.push({ kind: "artifact", artifact: match.artifact });
|
|
2372
|
+
cursor = match.end;
|
|
2373
|
+
}
|
|
2374
|
+
if (cursor < markdown.length) {
|
|
2375
|
+
segments.push({ kind: "text", text: markdown.slice(cursor) });
|
|
2376
|
+
}
|
|
2377
|
+
return segments;
|
|
2378
|
+
}
|
|
2379
|
+
|
|
2380
|
+
// src/components/syntax-highlighter.tsx
|
|
2381
|
+
import langJavascript from "shiki/langs/javascript.mjs";
|
|
2382
|
+
import langTypescript from "shiki/langs/typescript.mjs";
|
|
2383
|
+
import langPython from "shiki/langs/python.mjs";
|
|
2384
|
+
import langHtml from "shiki/langs/html.mjs";
|
|
2385
|
+
import langCss from "shiki/langs/css.mjs";
|
|
2386
|
+
import langJson from "shiki/langs/json.mjs";
|
|
2387
|
+
import langBash from "shiki/langs/bash.mjs";
|
|
2388
|
+
import langMarkdown from "shiki/langs/markdown.mjs";
|
|
2389
|
+
import langJsx from "shiki/langs/jsx.mjs";
|
|
2390
|
+
import langTsx from "shiki/langs/tsx.mjs";
|
|
2391
|
+
import langSql from "shiki/langs/sql.mjs";
|
|
2392
|
+
import langYaml from "shiki/langs/yaml.mjs";
|
|
2393
|
+
import langRust from "shiki/langs/rust.mjs";
|
|
2394
|
+
import langGo from "shiki/langs/go.mjs";
|
|
2395
|
+
import langJava from "shiki/langs/java.mjs";
|
|
2396
|
+
import langC from "shiki/langs/c.mjs";
|
|
2397
|
+
import langCpp from "shiki/langs/cpp.mjs";
|
|
2398
|
+
import themeVitesseDark from "shiki/themes/vitesse-dark.mjs";
|
|
2399
|
+
import themeVitesseLight from "shiki/themes/vitesse-light.mjs";
|
|
2400
|
+
import { jsx as jsx18 } from "react/jsx-runtime";
|
|
2401
|
+
var SHIKI_THEME_DARK = "vitesse-dark";
|
|
2402
|
+
var SHIKI_THEME_LIGHT = "vitesse-light";
|
|
2403
|
+
var highlighterPromise = null;
|
|
2404
|
+
function getHighlighter() {
|
|
2405
|
+
if (!highlighterPromise) {
|
|
2406
|
+
highlighterPromise = createHighlighterCore({
|
|
2407
|
+
themes: [themeVitesseDark, themeVitesseLight],
|
|
2408
|
+
langs: [
|
|
2409
|
+
langJavascript,
|
|
2410
|
+
langTypescript,
|
|
2411
|
+
langPython,
|
|
2412
|
+
langHtml,
|
|
2413
|
+
langCss,
|
|
2414
|
+
langJson,
|
|
2415
|
+
langBash,
|
|
2416
|
+
langMarkdown,
|
|
2417
|
+
langJsx,
|
|
2418
|
+
langTsx,
|
|
2419
|
+
langSql,
|
|
2420
|
+
langYaml,
|
|
2421
|
+
langRust,
|
|
2422
|
+
langGo,
|
|
2423
|
+
langJava,
|
|
2424
|
+
langC,
|
|
2425
|
+
langCpp
|
|
2426
|
+
],
|
|
2427
|
+
engine: createJavaScriptRegexEngine()
|
|
2428
|
+
});
|
|
2429
|
+
}
|
|
2430
|
+
return highlighterPromise;
|
|
2431
|
+
}
|
|
2432
|
+
getHighlighter();
|
|
2433
|
+
var ShikiSyntaxHighlighter = ({
|
|
2434
|
+
components: { Pre, Code: Code2 },
|
|
2435
|
+
language,
|
|
2436
|
+
code
|
|
2437
|
+
}) => {
|
|
2438
|
+
const [html, setHtml] = useState4(null);
|
|
2439
|
+
useEffect3(() => {
|
|
2440
|
+
let cancelled = false;
|
|
853
2441
|
(async () => {
|
|
854
2442
|
try {
|
|
855
2443
|
const highlighter = await getHighlighter();
|
|
@@ -874,8 +2462,17 @@ var ShikiSyntaxHighlighter = ({
|
|
|
874
2462
|
cancelled = true;
|
|
875
2463
|
};
|
|
876
2464
|
}, [code, language]);
|
|
2465
|
+
if (isArtifactFenceLanguage(language)) {
|
|
2466
|
+
try {
|
|
2467
|
+
const parsed = JSON.parse(code);
|
|
2468
|
+
if (isArtifact(parsed)) {
|
|
2469
|
+
return /* @__PURE__ */ jsx18(ArtifactView, { artifact: parsed });
|
|
2470
|
+
}
|
|
2471
|
+
} catch {
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
877
2474
|
if (html) {
|
|
878
|
-
return /* @__PURE__ */
|
|
2475
|
+
return /* @__PURE__ */ jsx18(
|
|
879
2476
|
"div",
|
|
880
2477
|
{
|
|
881
2478
|
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",
|
|
@@ -883,14 +2480,14 @@ var ShikiSyntaxHighlighter = ({
|
|
|
883
2480
|
}
|
|
884
2481
|
);
|
|
885
2482
|
}
|
|
886
|
-
return /* @__PURE__ */
|
|
2483
|
+
return /* @__PURE__ */ jsx18(Pre, { children: /* @__PURE__ */ jsx18(Code2, { children: code }) });
|
|
887
2484
|
};
|
|
888
2485
|
var syntax_highlighter_default = ShikiSyntaxHighlighter;
|
|
889
2486
|
|
|
890
2487
|
// src/components/markdown-text.tsx
|
|
891
|
-
import { jsx as
|
|
2488
|
+
import { jsx as jsx19, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
892
2489
|
var MarkdownTextImpl = () => {
|
|
893
|
-
return /* @__PURE__ */
|
|
2490
|
+
return /* @__PURE__ */ jsx19(
|
|
894
2491
|
MarkdownTextPrimitive,
|
|
895
2492
|
{
|
|
896
2493
|
remarkPlugins: [remarkGfm, remarkMath],
|
|
@@ -906,24 +2503,25 @@ var MarkdownTextImpl = () => {
|
|
|
906
2503
|
var MarkdownText = memo(MarkdownTextImpl);
|
|
907
2504
|
var CodeHeader = ({ language, code }) => {
|
|
908
2505
|
const { isCopied, copyToClipboard } = useCopyToClipboard();
|
|
2506
|
+
if (isArtifactFenceLanguage(language)) return null;
|
|
909
2507
|
const onCopy = () => {
|
|
910
2508
|
if (!code || isCopied) return;
|
|
911
2509
|
copyToClipboard(code);
|
|
912
2510
|
};
|
|
913
|
-
return /* @__PURE__ */
|
|
914
|
-
/* @__PURE__ */
|
|
915
|
-
/* @__PURE__ */
|
|
2511
|
+
return /* @__PURE__ */ jsxs10("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: [
|
|
2512
|
+
/* @__PURE__ */ jsxs10("span", { className: "flex items-center gap-2 text-xs font-semibold tracking-wide text-muted-foreground/80 uppercase", children: [
|
|
2513
|
+
/* @__PURE__ */ jsx19("span", { className: "inline-block h-2 w-2 rounded-full bg-primary/40" }),
|
|
916
2514
|
language
|
|
917
2515
|
] }),
|
|
918
|
-
/* @__PURE__ */
|
|
2516
|
+
/* @__PURE__ */ jsxs10(
|
|
919
2517
|
TooltipIconButton,
|
|
920
2518
|
{
|
|
921
2519
|
tooltip: isCopied ? "Copied!" : "Copy",
|
|
922
2520
|
onClick: onCopy,
|
|
923
2521
|
className: "transition-colors hover:text-foreground",
|
|
924
2522
|
children: [
|
|
925
|
-
!isCopied && /* @__PURE__ */
|
|
926
|
-
isCopied && /* @__PURE__ */
|
|
2523
|
+
!isCopied && /* @__PURE__ */ jsx19(CopyIcon, { className: "h-3.5 w-3.5" }),
|
|
2524
|
+
isCopied && /* @__PURE__ */ jsx19(CheckIcon2, { className: "h-3.5 w-3.5 text-emerald-500" })
|
|
927
2525
|
]
|
|
928
2526
|
}
|
|
929
2527
|
)
|
|
@@ -932,7 +2530,7 @@ var CodeHeader = ({ language, code }) => {
|
|
|
932
2530
|
var useCopyToClipboard = ({
|
|
933
2531
|
copiedDuration = 3e3
|
|
934
2532
|
} = {}) => {
|
|
935
|
-
const [isCopied, setIsCopied] =
|
|
2533
|
+
const [isCopied, setIsCopied] = useState5(false);
|
|
936
2534
|
const copyToClipboard = (value) => {
|
|
937
2535
|
if (!value) return;
|
|
938
2536
|
navigator.clipboard.writeText(value).then(() => {
|
|
@@ -943,7 +2541,7 @@ var useCopyToClipboard = ({
|
|
|
943
2541
|
return { isCopied, copyToClipboard };
|
|
944
2542
|
};
|
|
945
2543
|
var defaultComponents = memoizeMarkdownComponents({
|
|
946
|
-
h1: ({ className, ...props }) => /* @__PURE__ */
|
|
2544
|
+
h1: ({ className, ...props }) => /* @__PURE__ */ jsx19(
|
|
947
2545
|
"h1",
|
|
948
2546
|
{
|
|
949
2547
|
className: cn(
|
|
@@ -953,7 +2551,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
953
2551
|
...props
|
|
954
2552
|
}
|
|
955
2553
|
),
|
|
956
|
-
h2: ({ className, ...props }) => /* @__PURE__ */
|
|
2554
|
+
h2: ({ className, ...props }) => /* @__PURE__ */ jsx19(
|
|
957
2555
|
"h2",
|
|
958
2556
|
{
|
|
959
2557
|
className: cn(
|
|
@@ -963,7 +2561,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
963
2561
|
...props
|
|
964
2562
|
}
|
|
965
2563
|
),
|
|
966
|
-
h3: ({ className, ...props }) => /* @__PURE__ */
|
|
2564
|
+
h3: ({ className, ...props }) => /* @__PURE__ */ jsx19(
|
|
967
2565
|
"h3",
|
|
968
2566
|
{
|
|
969
2567
|
className: cn(
|
|
@@ -973,7 +2571,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
973
2571
|
...props
|
|
974
2572
|
}
|
|
975
2573
|
),
|
|
976
|
-
h4: ({ className, ...props }) => /* @__PURE__ */
|
|
2574
|
+
h4: ({ className, ...props }) => /* @__PURE__ */ jsx19(
|
|
977
2575
|
"h4",
|
|
978
2576
|
{
|
|
979
2577
|
className: cn(
|
|
@@ -983,7 +2581,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
983
2581
|
...props
|
|
984
2582
|
}
|
|
985
2583
|
),
|
|
986
|
-
h5: ({ className, ...props }) => /* @__PURE__ */
|
|
2584
|
+
h5: ({ className, ...props }) => /* @__PURE__ */ jsx19(
|
|
987
2585
|
"h5",
|
|
988
2586
|
{
|
|
989
2587
|
className: cn(
|
|
@@ -993,7 +2591,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
993
2591
|
...props
|
|
994
2592
|
}
|
|
995
2593
|
),
|
|
996
|
-
h6: ({ className, ...props }) => /* @__PURE__ */
|
|
2594
|
+
h6: ({ className, ...props }) => /* @__PURE__ */ jsx19(
|
|
997
2595
|
"h6",
|
|
998
2596
|
{
|
|
999
2597
|
className: cn(
|
|
@@ -1003,7 +2601,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
1003
2601
|
...props
|
|
1004
2602
|
}
|
|
1005
2603
|
),
|
|
1006
|
-
p: ({ className, ...props }) => /* @__PURE__ */
|
|
2604
|
+
p: ({ className, ...props }) => /* @__PURE__ */ jsx19(
|
|
1007
2605
|
"p",
|
|
1008
2606
|
{
|
|
1009
2607
|
className: cn(
|
|
@@ -1013,7 +2611,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
1013
2611
|
...props
|
|
1014
2612
|
}
|
|
1015
2613
|
),
|
|
1016
|
-
a: ({ className, ...props }) => /* @__PURE__ */
|
|
2614
|
+
a: ({ className, ...props }) => /* @__PURE__ */ jsx19(
|
|
1017
2615
|
"a",
|
|
1018
2616
|
{
|
|
1019
2617
|
className: cn(
|
|
@@ -1025,7 +2623,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
1025
2623
|
...props
|
|
1026
2624
|
}
|
|
1027
2625
|
),
|
|
1028
|
-
blockquote: ({ className, ...props }) => /* @__PURE__ */
|
|
2626
|
+
blockquote: ({ className, ...props }) => /* @__PURE__ */ jsx19(
|
|
1029
2627
|
"blockquote",
|
|
1030
2628
|
{
|
|
1031
2629
|
className: cn(
|
|
@@ -1035,7 +2633,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
1035
2633
|
...props
|
|
1036
2634
|
}
|
|
1037
2635
|
),
|
|
1038
|
-
ul: ({ className, ...props }) => /* @__PURE__ */
|
|
2636
|
+
ul: ({ className, ...props }) => /* @__PURE__ */ jsx19(
|
|
1039
2637
|
"ul",
|
|
1040
2638
|
{
|
|
1041
2639
|
className: cn(
|
|
@@ -1045,7 +2643,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
1045
2643
|
...props
|
|
1046
2644
|
}
|
|
1047
2645
|
),
|
|
1048
|
-
ol: ({ className, ...props }) => /* @__PURE__ */
|
|
2646
|
+
ol: ({ className, ...props }) => /* @__PURE__ */ jsx19(
|
|
1049
2647
|
"ol",
|
|
1050
2648
|
{
|
|
1051
2649
|
className: cn(
|
|
@@ -1055,7 +2653,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
1055
2653
|
...props
|
|
1056
2654
|
}
|
|
1057
2655
|
),
|
|
1058
|
-
hr: ({ className, ...props }) => /* @__PURE__ */
|
|
2656
|
+
hr: ({ className, ...props }) => /* @__PURE__ */ jsx19(
|
|
1059
2657
|
"hr",
|
|
1060
2658
|
{
|
|
1061
2659
|
className: cn(
|
|
@@ -1065,14 +2663,14 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
1065
2663
|
...props
|
|
1066
2664
|
}
|
|
1067
2665
|
),
|
|
1068
|
-
table: ({ className, ...props }) => /* @__PURE__ */
|
|
2666
|
+
table: ({ className, ...props }) => /* @__PURE__ */ jsx19("div", { className: "my-4 w-full overflow-x-auto rounded-lg border border-border/50", children: /* @__PURE__ */ jsx19(
|
|
1069
2667
|
"table",
|
|
1070
2668
|
{
|
|
1071
2669
|
className: cn("aui-md-table w-full border-collapse text-sm", className),
|
|
1072
2670
|
...props
|
|
1073
2671
|
}
|
|
1074
2672
|
) }),
|
|
1075
|
-
th: ({ className, ...props }) => /* @__PURE__ */
|
|
2673
|
+
th: ({ className, ...props }) => /* @__PURE__ */ jsx19(
|
|
1076
2674
|
"th",
|
|
1077
2675
|
{
|
|
1078
2676
|
className: cn(
|
|
@@ -1082,7 +2680,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
1082
2680
|
...props
|
|
1083
2681
|
}
|
|
1084
2682
|
),
|
|
1085
|
-
td: ({ className, ...props }) => /* @__PURE__ */
|
|
2683
|
+
td: ({ className, ...props }) => /* @__PURE__ */ jsx19(
|
|
1086
2684
|
"td",
|
|
1087
2685
|
{
|
|
1088
2686
|
className: cn(
|
|
@@ -1092,7 +2690,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
1092
2690
|
...props
|
|
1093
2691
|
}
|
|
1094
2692
|
),
|
|
1095
|
-
tr: ({ className, ...props }) => /* @__PURE__ */
|
|
2693
|
+
tr: ({ className, ...props }) => /* @__PURE__ */ jsx19(
|
|
1096
2694
|
"tr",
|
|
1097
2695
|
{
|
|
1098
2696
|
className: cn(
|
|
@@ -1102,8 +2700,8 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
1102
2700
|
...props
|
|
1103
2701
|
}
|
|
1104
2702
|
),
|
|
1105
|
-
li: ({ className, ...props }) => /* @__PURE__ */
|
|
1106
|
-
sup: ({ className, ...props }) => /* @__PURE__ */
|
|
2703
|
+
li: ({ className, ...props }) => /* @__PURE__ */ jsx19("li", { className: cn("aui-md-li leading-[1.7]", className), ...props }),
|
|
2704
|
+
sup: ({ className, ...props }) => /* @__PURE__ */ jsx19(
|
|
1107
2705
|
"sup",
|
|
1108
2706
|
{
|
|
1109
2707
|
className: cn(
|
|
@@ -1113,7 +2711,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
1113
2711
|
...props
|
|
1114
2712
|
}
|
|
1115
2713
|
),
|
|
1116
|
-
pre: ({ className, ...props }) => /* @__PURE__ */
|
|
2714
|
+
pre: ({ className, ...props }) => /* @__PURE__ */ jsx19(
|
|
1117
2715
|
"pre",
|
|
1118
2716
|
{
|
|
1119
2717
|
className: cn(
|
|
@@ -1125,119 +2723,391 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
1125
2723
|
),
|
|
1126
2724
|
code: function Code({ className, ...props }) {
|
|
1127
2725
|
const isCodeBlock = useIsMarkdownCodeBlock();
|
|
1128
|
-
return /* @__PURE__ */
|
|
2726
|
+
return /* @__PURE__ */ jsx19(
|
|
1129
2727
|
"code",
|
|
1130
2728
|
{
|
|
1131
|
-
className: cn(
|
|
1132
|
-
!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",
|
|
1133
|
-
className
|
|
1134
|
-
),
|
|
1135
|
-
...props
|
|
2729
|
+
className: cn(
|
|
2730
|
+
!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",
|
|
2731
|
+
className
|
|
2732
|
+
),
|
|
2733
|
+
...props
|
|
2734
|
+
}
|
|
2735
|
+
);
|
|
2736
|
+
},
|
|
2737
|
+
strong: ({ className, ...props }) => /* @__PURE__ */ jsx19("strong", { className: cn("font-semibold text-foreground", className), ...props }),
|
|
2738
|
+
em: ({ className, ...props }) => /* @__PURE__ */ jsx19("em", { className: cn("italic", className), ...props }),
|
|
2739
|
+
CodeHeader
|
|
2740
|
+
});
|
|
2741
|
+
|
|
2742
|
+
// src/components/tool-fallback.tsx
|
|
2743
|
+
import { memo as memo3, useState as useState6 } from "react";
|
|
2744
|
+
import { ChevronDownIcon, WrenchIcon } from "lucide-react";
|
|
2745
|
+
|
|
2746
|
+
// src/ui/shimmer.tsx
|
|
2747
|
+
import { motion as motion2 } from "motion/react";
|
|
2748
|
+
import {
|
|
2749
|
+
memo as memo2,
|
|
2750
|
+
useMemo as useMemo4
|
|
2751
|
+
} from "react";
|
|
2752
|
+
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
2753
|
+
var ShimmerComponent = ({
|
|
2754
|
+
children,
|
|
2755
|
+
as: Component = "p",
|
|
2756
|
+
className,
|
|
2757
|
+
duration = 2,
|
|
2758
|
+
spread = 2
|
|
2759
|
+
}) => {
|
|
2760
|
+
const MotionComponent = motion2.create(
|
|
2761
|
+
Component
|
|
2762
|
+
);
|
|
2763
|
+
const dynamicSpread = useMemo4(
|
|
2764
|
+
() => (children?.length ?? 0) * spread,
|
|
2765
|
+
[children, spread]
|
|
2766
|
+
);
|
|
2767
|
+
return /* @__PURE__ */ jsx20(
|
|
2768
|
+
MotionComponent,
|
|
2769
|
+
{
|
|
2770
|
+
animate: { backgroundPosition: "0% center" },
|
|
2771
|
+
className: cn(
|
|
2772
|
+
"relative inline-block bg-[length:250%_100%,auto] bg-clip-text text-transparent",
|
|
2773
|
+
"[--bg:linear-gradient(90deg,#0000_calc(50%-var(--spread)),var(--color-background),#0000_calc(50%+var(--spread)))] [background-repeat:no-repeat,padding-box]",
|
|
2774
|
+
className
|
|
2775
|
+
),
|
|
2776
|
+
initial: { backgroundPosition: "100% center" },
|
|
2777
|
+
style: {
|
|
2778
|
+
"--spread": `${dynamicSpread}px`,
|
|
2779
|
+
backgroundImage: "var(--bg), linear-gradient(var(--color-muted-foreground), var(--color-muted-foreground))"
|
|
2780
|
+
},
|
|
2781
|
+
transition: {
|
|
2782
|
+
repeat: Number.POSITIVE_INFINITY,
|
|
2783
|
+
duration,
|
|
2784
|
+
ease: "linear"
|
|
2785
|
+
},
|
|
2786
|
+
children
|
|
2787
|
+
}
|
|
2788
|
+
);
|
|
2789
|
+
};
|
|
2790
|
+
var Shimmer = memo2(ShimmerComponent);
|
|
2791
|
+
|
|
2792
|
+
// src/components/tool-fallback.tsx
|
|
2793
|
+
import { jsx as jsx21, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2794
|
+
var ToolFallbackImpl = ({
|
|
2795
|
+
toolName,
|
|
2796
|
+
argsText,
|
|
2797
|
+
result,
|
|
2798
|
+
status
|
|
2799
|
+
}) => {
|
|
2800
|
+
const isRunning = status?.type === "running";
|
|
2801
|
+
const isError = status?.type === "incomplete" && status.reason !== "cancelled";
|
|
2802
|
+
if (isRunning) {
|
|
2803
|
+
return /* @__PURE__ */ jsxs11("div", { className: "aui-tool-fallback-running flex items-center gap-2 py-1 text-sm text-muted-foreground", children: [
|
|
2804
|
+
/* @__PURE__ */ jsx21(WrenchIcon, { className: "size-4" }),
|
|
2805
|
+
/* @__PURE__ */ jsx21(Shimmer, { as: "span", duration: 1.8, spread: 2.5, children: `Using tool: ${toolName}` })
|
|
2806
|
+
] });
|
|
2807
|
+
}
|
|
2808
|
+
return /* @__PURE__ */ jsx21(
|
|
2809
|
+
ToolPanel,
|
|
2810
|
+
{
|
|
2811
|
+
toolName,
|
|
2812
|
+
argsText,
|
|
2813
|
+
result,
|
|
2814
|
+
isError
|
|
2815
|
+
}
|
|
2816
|
+
);
|
|
2817
|
+
};
|
|
2818
|
+
var ToolPanel = ({ toolName, argsText, result, isError }) => {
|
|
2819
|
+
const [open, setOpen] = useState6(false);
|
|
2820
|
+
return /* @__PURE__ */ jsxs11(
|
|
2821
|
+
"div",
|
|
2822
|
+
{
|
|
2823
|
+
className: cn(
|
|
2824
|
+
"aui-tool-fallback-root my-2 overflow-hidden rounded-lg border border-border/60 bg-muted/30 text-sm",
|
|
2825
|
+
isError && "border-destructive/50 bg-destructive/5"
|
|
2826
|
+
),
|
|
2827
|
+
children: [
|
|
2828
|
+
/* @__PURE__ */ jsxs11(
|
|
2829
|
+
"button",
|
|
2830
|
+
{
|
|
2831
|
+
type: "button",
|
|
2832
|
+
onClick: () => setOpen((v) => !v),
|
|
2833
|
+
className: "aui-tool-fallback-header flex w-full items-center gap-2 px-3 py-2 text-left text-muted-foreground transition-colors hover:bg-muted/50",
|
|
2834
|
+
"aria-expanded": open,
|
|
2835
|
+
children: [
|
|
2836
|
+
/* @__PURE__ */ jsx21(WrenchIcon, { className: "size-3.5" }),
|
|
2837
|
+
/* @__PURE__ */ jsx21("span", { className: "aui-tool-fallback-name flex-1 truncate font-mono text-xs font-medium text-foreground/80", children: toolName }),
|
|
2838
|
+
isError && /* @__PURE__ */ jsx21("span", { className: "aui-tool-fallback-status text-xs font-medium text-destructive", children: "error" }),
|
|
2839
|
+
/* @__PURE__ */ jsx21(
|
|
2840
|
+
ChevronDownIcon,
|
|
2841
|
+
{
|
|
2842
|
+
className: cn(
|
|
2843
|
+
"size-3.5 shrink-0 transition-transform",
|
|
2844
|
+
open && "rotate-180"
|
|
2845
|
+
)
|
|
2846
|
+
}
|
|
2847
|
+
)
|
|
2848
|
+
]
|
|
2849
|
+
}
|
|
2850
|
+
),
|
|
2851
|
+
open && /* @__PURE__ */ jsxs11("div", { className: "aui-tool-fallback-body grid gap-2 border-t border-border/40 bg-background/50 px-3 py-2.5 text-xs", children: [
|
|
2852
|
+
argsText && argsText !== "{}" && /* @__PURE__ */ jsx21(Section, { label: "Input", value: argsText }),
|
|
2853
|
+
result !== void 0 && result !== null && /* @__PURE__ */ jsx21(Section, { label: "Output", value: formatResult(result) })
|
|
2854
|
+
] })
|
|
2855
|
+
]
|
|
2856
|
+
}
|
|
2857
|
+
);
|
|
2858
|
+
};
|
|
2859
|
+
var Section = ({ label, value }) => /* @__PURE__ */ jsxs11("div", { className: "aui-tool-fallback-section", children: [
|
|
2860
|
+
/* @__PURE__ */ jsx21("div", { className: "aui-tool-fallback-section-label mb-0.5 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: label }),
|
|
2861
|
+
/* @__PURE__ */ jsx21("pre", { className: "aui-tool-fallback-section-value overflow-x-auto whitespace-pre-wrap break-words font-mono text-[11px] leading-relaxed text-foreground/85", children: value })
|
|
2862
|
+
] });
|
|
2863
|
+
function formatResult(result) {
|
|
2864
|
+
if (typeof result === "string") return result;
|
|
2865
|
+
try {
|
|
2866
|
+
return JSON.stringify(result, null, 2);
|
|
2867
|
+
} catch {
|
|
2868
|
+
return String(result);
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
var ToolFallback = memo3(
|
|
2872
|
+
ToolFallbackImpl
|
|
2873
|
+
);
|
|
2874
|
+
ToolFallback.displayName = "ToolFallback";
|
|
2875
|
+
|
|
2876
|
+
// src/artifacts/tool-artifact.tsx
|
|
2877
|
+
import { jsx as jsx22 } from "react/jsx-runtime";
|
|
2878
|
+
var ToolArtifactFallback = (props) => {
|
|
2879
|
+
const registry = useArtifactRegistry();
|
|
2880
|
+
const isRunning = props.status?.type === "running";
|
|
2881
|
+
if (!isRunning) {
|
|
2882
|
+
const artifact = parseArtifactFromToolResult(props.result);
|
|
2883
|
+
if (artifact) {
|
|
2884
|
+
const Renderer = registry[artifact.type];
|
|
2885
|
+
if (Renderer) {
|
|
2886
|
+
return /* @__PURE__ */ jsx22(Renderer, { artifact });
|
|
2887
|
+
}
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
return /* @__PURE__ */ jsx22(ToolFallback, { ...props });
|
|
2891
|
+
};
|
|
2892
|
+
|
|
2893
|
+
// src/components/composer.tsx
|
|
2894
|
+
import {
|
|
2895
|
+
AuiIf,
|
|
2896
|
+
ComposerPrimitive as ComposerPrimitive2,
|
|
2897
|
+
useComposerRuntime
|
|
2898
|
+
} from "@assistant-ui/react";
|
|
2899
|
+
import { ArrowUpIcon, SquareIcon } from "lucide-react";
|
|
2900
|
+
import { Fragment as Fragment2, jsx as jsx23, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2901
|
+
var Composer = ({
|
|
2902
|
+
placeholder = "Send a message...",
|
|
2903
|
+
showAttachments = true,
|
|
2904
|
+
toolbar,
|
|
2905
|
+
sendTooltip = "Send message",
|
|
2906
|
+
noAutoFocus,
|
|
2907
|
+
className
|
|
2908
|
+
}) => {
|
|
2909
|
+
return /* @__PURE__ */ jsx23(
|
|
2910
|
+
ComposerPrimitive2.Root,
|
|
2911
|
+
{
|
|
2912
|
+
className: cn(
|
|
2913
|
+
"aui-composer-root relative mt-3 flex w-full flex-col",
|
|
2914
|
+
className
|
|
2915
|
+
),
|
|
2916
|
+
children: /* @__PURE__ */ jsxs12(
|
|
2917
|
+
ComposerPrimitive2.AttachmentDropzone,
|
|
2918
|
+
{
|
|
2919
|
+
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",
|
|
2920
|
+
children: [
|
|
2921
|
+
showAttachments && /* @__PURE__ */ jsx23(ComposerAttachments, {}),
|
|
2922
|
+
/* @__PURE__ */ jsx23(ComposerInput, { placeholder, autoFocus: !noAutoFocus }),
|
|
2923
|
+
/* @__PURE__ */ jsx23(
|
|
2924
|
+
ComposerToolbar,
|
|
2925
|
+
{
|
|
2926
|
+
showAttachments,
|
|
2927
|
+
toolbar,
|
|
2928
|
+
sendTooltip
|
|
2929
|
+
}
|
|
2930
|
+
)
|
|
2931
|
+
]
|
|
2932
|
+
}
|
|
2933
|
+
)
|
|
2934
|
+
}
|
|
2935
|
+
);
|
|
2936
|
+
};
|
|
2937
|
+
var ComposerInput = ({
|
|
2938
|
+
placeholder,
|
|
2939
|
+
autoFocus
|
|
2940
|
+
}) => {
|
|
2941
|
+
const composer = useComposerRuntime();
|
|
2942
|
+
const onKeyDown = (e) => {
|
|
2943
|
+
if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing) {
|
|
2944
|
+
e.preventDefault();
|
|
2945
|
+
composer.send();
|
|
2946
|
+
}
|
|
2947
|
+
};
|
|
2948
|
+
const onInput = (e) => {
|
|
2949
|
+
const el = e.currentTarget;
|
|
2950
|
+
el.style.height = "auto";
|
|
2951
|
+
el.style.height = `${Math.min(el.scrollHeight, 240)}px`;
|
|
2952
|
+
};
|
|
2953
|
+
return /* @__PURE__ */ jsx23(
|
|
2954
|
+
ComposerPrimitive2.Input,
|
|
2955
|
+
{
|
|
2956
|
+
placeholder,
|
|
2957
|
+
className: "aui-composer-input mb-1 max-h-60 min-h-12 w-full resize-none bg-transparent px-4 pt-2 pb-3 text-sm outline-none placeholder:text-muted-foreground focus-visible:ring-0",
|
|
2958
|
+
rows: 1,
|
|
2959
|
+
autoFocus,
|
|
2960
|
+
"aria-label": "Message input",
|
|
2961
|
+
onKeyDown,
|
|
2962
|
+
onInput
|
|
2963
|
+
}
|
|
2964
|
+
);
|
|
2965
|
+
};
|
|
2966
|
+
var ComposerToolbar = ({ showAttachments, toolbar, sendTooltip }) => {
|
|
2967
|
+
return /* @__PURE__ */ jsxs12("div", { className: "aui-composer-action-wrapper relative mx-2 mb-2 flex items-center gap-1", children: [
|
|
2968
|
+
showAttachments && /* @__PURE__ */ jsx23(ComposerAddAttachment, {}),
|
|
2969
|
+
toolbar,
|
|
2970
|
+
/* @__PURE__ */ jsx23("div", { className: "flex-1" }),
|
|
2971
|
+
/* @__PURE__ */ jsx23(ComposerSendOrCancel, { sendTooltip })
|
|
2972
|
+
] });
|
|
2973
|
+
};
|
|
2974
|
+
var ComposerSendOrCancel = ({ sendTooltip }) => {
|
|
2975
|
+
return /* @__PURE__ */ jsxs12(Fragment2, { children: [
|
|
2976
|
+
/* @__PURE__ */ jsx23(AuiIf, { condition: (s) => !s.thread.isRunning, children: /* @__PURE__ */ jsx23(ComposerPrimitive2.Send, { asChild: true, children: /* @__PURE__ */ jsx23(
|
|
2977
|
+
TooltipIconButton,
|
|
2978
|
+
{
|
|
2979
|
+
tooltip: sendTooltip,
|
|
2980
|
+
side: "bottom",
|
|
2981
|
+
type: "submit",
|
|
2982
|
+
variant: "default",
|
|
2983
|
+
size: "icon",
|
|
2984
|
+
className: "aui-composer-send size-8 rounded-full",
|
|
2985
|
+
"aria-label": "Send message",
|
|
2986
|
+
children: /* @__PURE__ */ jsx23(ArrowUpIcon, { className: "aui-composer-send-icon size-4" })
|
|
2987
|
+
}
|
|
2988
|
+
) }) }),
|
|
2989
|
+
/* @__PURE__ */ jsx23(AuiIf, { condition: (s) => s.thread.isRunning, children: /* @__PURE__ */ jsx23(ComposerPrimitive2.Cancel, { asChild: true, children: /* @__PURE__ */ jsx23(
|
|
2990
|
+
Button,
|
|
2991
|
+
{
|
|
2992
|
+
type: "button",
|
|
2993
|
+
variant: "default",
|
|
2994
|
+
size: "icon",
|
|
2995
|
+
className: "aui-composer-cancel size-8 rounded-full",
|
|
2996
|
+
"aria-label": "Stop generating",
|
|
2997
|
+
children: /* @__PURE__ */ jsx23(SquareIcon, { className: "aui-composer-cancel-icon size-3 fill-current" })
|
|
1136
2998
|
}
|
|
1137
|
-
)
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
|
-
em: ({ className, ...props }) => /* @__PURE__ */ jsx9("em", { className: cn("italic", className), ...props }),
|
|
1141
|
-
CodeHeader
|
|
1142
|
-
});
|
|
1143
|
-
|
|
1144
|
-
// src/components/tool-fallback.tsx
|
|
1145
|
-
import { memo as memo3 } from "react";
|
|
1146
|
-
import { WrenchIcon } from "lucide-react";
|
|
2999
|
+
) }) })
|
|
3000
|
+
] });
|
|
3001
|
+
};
|
|
1147
3002
|
|
|
1148
|
-
// src/
|
|
1149
|
-
import { motion } from "motion/react";
|
|
3003
|
+
// src/components/suggestions.tsx
|
|
1150
3004
|
import {
|
|
1151
|
-
|
|
1152
|
-
useMemo
|
|
3005
|
+
useEffect as useEffect4,
|
|
3006
|
+
useMemo as useMemo5,
|
|
3007
|
+
useState as useState7
|
|
1153
3008
|
} from "react";
|
|
1154
|
-
import {
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
spread = 2
|
|
3009
|
+
import { useThreadRuntime as useThreadRuntime3 } from "@assistant-ui/react";
|
|
3010
|
+
import { jsx as jsx24, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
3011
|
+
var Suggestions = ({
|
|
3012
|
+
suggestions,
|
|
3013
|
+
layout = "grid",
|
|
3014
|
+
className
|
|
1161
3015
|
}) => {
|
|
1162
|
-
const
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
() => (children?.length ?? 0) * spread,
|
|
1167
|
-
[children, spread]
|
|
1168
|
-
);
|
|
1169
|
-
return /* @__PURE__ */ jsx10(
|
|
1170
|
-
MotionComponent,
|
|
3016
|
+
const items = useResolvedSuggestions(suggestions);
|
|
3017
|
+
if (!items || items.length === 0) return null;
|
|
3018
|
+
return /* @__PURE__ */ jsx24(
|
|
3019
|
+
"div",
|
|
1171
3020
|
{
|
|
1172
|
-
animate: { backgroundPosition: "0% center" },
|
|
1173
3021
|
className: cn(
|
|
1174
|
-
"
|
|
1175
|
-
"
|
|
3022
|
+
"aui-thread-suggestions w-full pb-4",
|
|
3023
|
+
layout === "grid" ? "grid gap-2 @md:grid-cols-2" : "flex gap-2 overflow-x-auto pb-1 [&::-webkit-scrollbar]:hidden",
|
|
1176
3024
|
className
|
|
1177
3025
|
),
|
|
1178
|
-
|
|
1179
|
-
style: {
|
|
1180
|
-
"--spread": `${dynamicSpread}px`,
|
|
1181
|
-
backgroundImage: "var(--bg), linear-gradient(var(--color-muted-foreground), var(--color-muted-foreground))"
|
|
1182
|
-
},
|
|
1183
|
-
transition: {
|
|
1184
|
-
repeat: Number.POSITIVE_INFINITY,
|
|
1185
|
-
duration,
|
|
1186
|
-
ease: "linear"
|
|
1187
|
-
},
|
|
1188
|
-
children
|
|
3026
|
+
children: items.map((s, i) => /* @__PURE__ */ jsx24(SuggestionChip, { suggestion: s, compact: layout === "row" }, s.title + i))
|
|
1189
3027
|
}
|
|
1190
3028
|
);
|
|
1191
3029
|
};
|
|
1192
|
-
var
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1196
|
-
var ToolFallbackImpl = ({
|
|
1197
|
-
toolName,
|
|
1198
|
-
status
|
|
3030
|
+
var SuggestionChip = ({
|
|
3031
|
+
suggestion,
|
|
3032
|
+
compact
|
|
1199
3033
|
}) => {
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
3034
|
+
const runtime = useThreadRuntime3();
|
|
3035
|
+
const onClick = () => {
|
|
3036
|
+
const text = suggestion.prompt ?? suggestion.title;
|
|
3037
|
+
runtime.append({ role: "user", content: [{ type: "text", text }] });
|
|
3038
|
+
};
|
|
3039
|
+
return /* @__PURE__ */ jsx24("div", { className: "aui-thread-suggestion-display fade-in slide-in-from-bottom-2 animate-in fill-mode-both duration-200", children: /* @__PURE__ */ jsxs13(
|
|
3040
|
+
Button,
|
|
3041
|
+
{
|
|
3042
|
+
variant: "ghost",
|
|
3043
|
+
onClick,
|
|
3044
|
+
className: cn(
|
|
3045
|
+
"aui-thread-suggestion h-auto rounded-2xl border text-left text-sm transition-colors hover:bg-muted",
|
|
3046
|
+
compact ? "shrink-0 flex-row items-center gap-2 whitespace-nowrap px-3 py-2" : "w-full flex-wrap items-start justify-start gap-1 px-4 py-3 @md:flex-col"
|
|
3047
|
+
),
|
|
3048
|
+
children: [
|
|
3049
|
+
suggestion.icon && /* @__PURE__ */ jsx24("span", { className: "aui-thread-suggestion-icon shrink-0 text-muted-foreground", children: suggestion.icon }),
|
|
3050
|
+
/* @__PURE__ */ jsx24("span", { className: "aui-thread-suggestion-text-1 font-medium", children: suggestion.title }),
|
|
3051
|
+
suggestion.description && !compact && /* @__PURE__ */ jsx24("span", { className: "aui-thread-suggestion-text-2 text-muted-foreground", children: suggestion.description })
|
|
3052
|
+
]
|
|
3053
|
+
}
|
|
3054
|
+
) });
|
|
1205
3055
|
};
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
)
|
|
1209
|
-
|
|
3056
|
+
function useResolvedSuggestions(source) {
|
|
3057
|
+
const [resolved, setResolved] = useState7(
|
|
3058
|
+
() => Array.isArray(source) ? source : void 0
|
|
3059
|
+
);
|
|
3060
|
+
useEffect4(() => {
|
|
3061
|
+
if (!source) {
|
|
3062
|
+
setResolved(void 0);
|
|
3063
|
+
return;
|
|
3064
|
+
}
|
|
3065
|
+
if (Array.isArray(source)) {
|
|
3066
|
+
setResolved(source);
|
|
3067
|
+
return;
|
|
3068
|
+
}
|
|
3069
|
+
let cancelled = false;
|
|
3070
|
+
Promise.resolve().then(() => source()).then((value) => {
|
|
3071
|
+
if (!cancelled) setResolved(value);
|
|
3072
|
+
}).catch(() => {
|
|
3073
|
+
if (!cancelled) setResolved([]);
|
|
3074
|
+
});
|
|
3075
|
+
return () => {
|
|
3076
|
+
cancelled = true;
|
|
3077
|
+
};
|
|
3078
|
+
}, [source]);
|
|
3079
|
+
return useMemo5(() => resolved, [resolved]);
|
|
3080
|
+
}
|
|
1210
3081
|
|
|
1211
3082
|
// src/components/thread.tsx
|
|
1212
3083
|
import {
|
|
1213
3084
|
ActionBarMorePrimitive,
|
|
1214
3085
|
ActionBarPrimitive,
|
|
1215
|
-
AuiIf,
|
|
1216
|
-
ComposerPrimitive as
|
|
3086
|
+
AuiIf as AuiIf2,
|
|
3087
|
+
ComposerPrimitive as ComposerPrimitive3,
|
|
1217
3088
|
ErrorPrimitive,
|
|
1218
3089
|
MessagePrimitive as MessagePrimitive2,
|
|
1219
|
-
ThreadPrimitive
|
|
1220
|
-
useThreadRuntime
|
|
3090
|
+
ThreadPrimitive
|
|
1221
3091
|
} from "@assistant-ui/react";
|
|
1222
3092
|
import {
|
|
1223
3093
|
ArrowDownIcon,
|
|
1224
|
-
|
|
1225
|
-
CheckIcon as CheckIcon2,
|
|
3094
|
+
CheckIcon as CheckIcon3,
|
|
1226
3095
|
CopyIcon as CopyIcon2,
|
|
1227
3096
|
DownloadIcon,
|
|
1228
3097
|
MoreHorizontalIcon,
|
|
1229
3098
|
PencilIcon,
|
|
1230
|
-
RefreshCwIcon
|
|
1231
|
-
SquareIcon
|
|
3099
|
+
RefreshCwIcon
|
|
1232
3100
|
} from "lucide-react";
|
|
1233
|
-
import { jsx as
|
|
3101
|
+
import { jsx as jsx25, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1234
3102
|
var Thread = ({
|
|
1235
3103
|
className,
|
|
1236
3104
|
maxWidth = "44rem",
|
|
1237
3105
|
welcome,
|
|
1238
3106
|
suggestions,
|
|
1239
3107
|
composerPlaceholder = "Send a message...",
|
|
1240
|
-
components
|
|
3108
|
+
components,
|
|
3109
|
+
artifacts,
|
|
3110
|
+
onArtifactEvent
|
|
1241
3111
|
}) => {
|
|
1242
3112
|
const WelcomeSlot = components?.Welcome ?? ThreadWelcome;
|
|
1243
3113
|
const ComposerSlot = components?.Composer ?? Composer;
|
|
@@ -1245,59 +3115,79 @@ var Thread = ({
|
|
|
1245
3115
|
const AssistantMessageSlot = components?.AssistantMessage ?? AssistantMessage;
|
|
1246
3116
|
const EditComposerSlot = components?.EditComposer ?? EditComposer;
|
|
1247
3117
|
const ScrollToBottomSlot = components?.ScrollToBottom ?? ThreadScrollToBottom;
|
|
1248
|
-
|
|
1249
|
-
|
|
3118
|
+
const SuggestionsSlot = components?.Suggestions ?? Suggestions;
|
|
3119
|
+
return /* @__PURE__ */ jsx25(
|
|
3120
|
+
ArtifactRegistryProvider,
|
|
1250
3121
|
{
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
),
|
|
1255
|
-
|
|
1256
|
-
children: /* @__PURE__ */ jsxs7(
|
|
1257
|
-
ThreadPrimitive.Viewport,
|
|
3122
|
+
renderers: artifacts?.renderers,
|
|
3123
|
+
override: artifacts?.override,
|
|
3124
|
+
children: /* @__PURE__ */ jsx25(UiEventProvider, { onEvent: onArtifactEvent ?? (() => {
|
|
3125
|
+
}), children: /* @__PURE__ */ jsx25(
|
|
3126
|
+
ThreadPrimitive.Root,
|
|
1258
3127
|
{
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
3128
|
+
className: cn(
|
|
3129
|
+
"aui-root aui-thread-root @container flex h-full flex-col bg-background",
|
|
3130
|
+
className
|
|
3131
|
+
),
|
|
3132
|
+
style: { ["--thread-max-width"]: maxWidth },
|
|
3133
|
+
children: /* @__PURE__ */ jsxs14(
|
|
3134
|
+
ThreadPrimitive.Viewport,
|
|
3135
|
+
{
|
|
3136
|
+
turnAnchor: "bottom",
|
|
3137
|
+
className: "aui-thread-viewport relative flex flex-1 flex-col overflow-x-auto overflow-y-scroll px-4 pt-4",
|
|
3138
|
+
children: [
|
|
3139
|
+
/* @__PURE__ */ jsx25(
|
|
3140
|
+
WelcomeSlot,
|
|
3141
|
+
{
|
|
3142
|
+
config: welcome,
|
|
3143
|
+
suggestions,
|
|
3144
|
+
Suggestions: SuggestionsSlot
|
|
3145
|
+
}
|
|
3146
|
+
),
|
|
3147
|
+
/* @__PURE__ */ jsx25(
|
|
3148
|
+
ThreadPrimitive.Messages,
|
|
3149
|
+
{
|
|
3150
|
+
components: {
|
|
3151
|
+
UserMessage: UserMessageSlot,
|
|
3152
|
+
EditComposer: EditComposerSlot,
|
|
3153
|
+
AssistantMessage: AssistantMessageSlot
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
),
|
|
3157
|
+
/* @__PURE__ */ jsxs14(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: [
|
|
3158
|
+
/* @__PURE__ */ jsx25(ScrollToBottomSlot, {}),
|
|
3159
|
+
/* @__PURE__ */ jsx25(ComposerSlot, { placeholder: composerPlaceholder })
|
|
3160
|
+
] })
|
|
3161
|
+
]
|
|
3162
|
+
}
|
|
3163
|
+
)
|
|
1278
3164
|
}
|
|
1279
|
-
)
|
|
3165
|
+
) })
|
|
1280
3166
|
}
|
|
1281
3167
|
);
|
|
1282
3168
|
};
|
|
1283
3169
|
var ThreadScrollToBottom = () => {
|
|
1284
|
-
return /* @__PURE__ */
|
|
3170
|
+
return /* @__PURE__ */ jsx25(ThreadPrimitive.ScrollToBottom, { asChild: true, children: /* @__PURE__ */ jsx25(
|
|
1285
3171
|
TooltipIconButton,
|
|
1286
3172
|
{
|
|
1287
3173
|
tooltip: "Scroll to bottom",
|
|
1288
3174
|
variant: "outline",
|
|
1289
3175
|
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",
|
|
1290
|
-
children: /* @__PURE__ */
|
|
3176
|
+
children: /* @__PURE__ */ jsx25(ArrowDownIcon, {})
|
|
1291
3177
|
}
|
|
1292
3178
|
) });
|
|
1293
3179
|
};
|
|
1294
|
-
var ThreadWelcome = ({
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
3180
|
+
var ThreadWelcome = ({
|
|
3181
|
+
config,
|
|
3182
|
+
suggestions,
|
|
3183
|
+
Suggestions: SuggestionsSlot = Suggestions
|
|
3184
|
+
}) => {
|
|
3185
|
+
return /* @__PURE__ */ jsx25(AuiIf2, { condition: (s) => s.thread.isEmpty, children: /* @__PURE__ */ jsxs14("div", { className: "aui-thread-welcome-root mx-auto my-auto flex w-full max-w-(--thread-max-width) grow flex-col", children: [
|
|
3186
|
+
/* @__PURE__ */ jsx25("div", { className: "aui-thread-welcome-center flex w-full grow flex-col items-center justify-center", children: /* @__PURE__ */ jsxs14("div", { className: "aui-thread-welcome-message flex size-full flex-col items-center justify-center px-4 text-center", children: [
|
|
3187
|
+
/* @__PURE__ */ jsxs14("div", { className: "fade-in animate-in fill-mode-both relative mb-6 flex size-14 items-center justify-center duration-300", children: [
|
|
3188
|
+
/* @__PURE__ */ jsx25("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" }),
|
|
3189
|
+
/* @__PURE__ */ jsx25("div", { className: "animate-ai-pulse-ring absolute inset-0" }),
|
|
3190
|
+
/* @__PURE__ */ jsx25(
|
|
1301
3191
|
"svg",
|
|
1302
3192
|
{
|
|
1303
3193
|
xmlns: "http://www.w3.org/2000/svg",
|
|
@@ -1308,110 +3198,45 @@ var ThreadWelcome = ({ config, suggestions }) => {
|
|
|
1308
3198
|
strokeLinecap: "round",
|
|
1309
3199
|
strokeLinejoin: "round",
|
|
1310
3200
|
className: "animate-ai-breathe relative size-7 text-primary/75",
|
|
1311
|
-
children: /* @__PURE__ */
|
|
3201
|
+
children: /* @__PURE__ */ jsx25("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" })
|
|
1312
3202
|
}
|
|
1313
3203
|
)
|
|
1314
3204
|
] }),
|
|
1315
|
-
/* @__PURE__ */
|
|
1316
|
-
/* @__PURE__ */
|
|
3205
|
+
/* @__PURE__ */ jsx25("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: config?.heading ?? "How can I help you today?" }),
|
|
3206
|
+
/* @__PURE__ */ jsx25("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: config?.subheading ?? "Send a message to start a conversation." })
|
|
1317
3207
|
] }) }),
|
|
1318
|
-
suggestions &&
|
|
1319
|
-
] }) });
|
|
1320
|
-
};
|
|
1321
|
-
var ThreadSuggestions = ({ suggestions }) => {
|
|
1322
|
-
return /* @__PURE__ */ jsx12("div", { className: "aui-thread-welcome-suggestions grid w-full @md:grid-cols-2 gap-2 pb-4", children: suggestions.map((s, i) => /* @__PURE__ */ jsx12(ThreadSuggestionItem, { title: s.title, description: s.description }, i)) });
|
|
1323
|
-
};
|
|
1324
|
-
var ThreadSuggestionItem = ({ title, description }) => {
|
|
1325
|
-
const runtime = useThreadRuntime();
|
|
1326
|
-
return /* @__PURE__ */ jsx12("div", { className: "aui-thread-welcome-suggestion-display fade-in slide-in-from-bottom-2 animate-in fill-mode-both duration-200", children: /* @__PURE__ */ jsxs7(
|
|
1327
|
-
Button,
|
|
1328
|
-
{
|
|
1329
|
-
variant: "ghost",
|
|
1330
|
-
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",
|
|
1331
|
-
onClick: () => runtime.append({
|
|
1332
|
-
role: "user",
|
|
1333
|
-
content: [{ type: "text", text: title }]
|
|
1334
|
-
}),
|
|
1335
|
-
children: [
|
|
1336
|
-
/* @__PURE__ */ jsx12("span", { className: "aui-thread-welcome-suggestion-text-1 font-medium", children: title }),
|
|
1337
|
-
description && /* @__PURE__ */ jsx12("span", { className: "aui-thread-welcome-suggestion-text-2 text-muted-foreground", children: description })
|
|
1338
|
-
]
|
|
1339
|
-
}
|
|
1340
|
-
) });
|
|
1341
|
-
};
|
|
1342
|
-
var Composer = ({ placeholder }) => {
|
|
1343
|
-
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: [
|
|
1344
|
-
/* @__PURE__ */ jsx12(ComposerAttachments, {}),
|
|
1345
|
-
/* @__PURE__ */ jsx12(
|
|
1346
|
-
ComposerPrimitive2.Input,
|
|
1347
|
-
{
|
|
1348
|
-
placeholder: placeholder ?? "Send a message...",
|
|
1349
|
-
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",
|
|
1350
|
-
rows: 1,
|
|
1351
|
-
autoFocus: true,
|
|
1352
|
-
"aria-label": "Message input"
|
|
1353
|
-
}
|
|
1354
|
-
),
|
|
1355
|
-
/* @__PURE__ */ jsx12(ComposerAction, {})
|
|
3208
|
+
suggestions && /* @__PURE__ */ jsx25(SuggestionsSlot, { suggestions })
|
|
1356
3209
|
] }) });
|
|
1357
3210
|
};
|
|
1358
|
-
var ComposerAction = () => {
|
|
1359
|
-
return /* @__PURE__ */ jsxs7("div", { className: "aui-composer-action-wrapper relative mx-2 mb-2 flex items-center justify-end", children: [
|
|
1360
|
-
/* @__PURE__ */ jsx12(AuiIf, { condition: (s) => !s.thread.isRunning, children: /* @__PURE__ */ jsx12(ComposerPrimitive2.Send, { asChild: true, children: /* @__PURE__ */ jsx12(
|
|
1361
|
-
TooltipIconButton,
|
|
1362
|
-
{
|
|
1363
|
-
tooltip: "Send message",
|
|
1364
|
-
side: "bottom",
|
|
1365
|
-
type: "submit",
|
|
1366
|
-
variant: "default",
|
|
1367
|
-
size: "icon",
|
|
1368
|
-
className: "aui-composer-send size-8 rounded-full",
|
|
1369
|
-
"aria-label": "Send message",
|
|
1370
|
-
children: /* @__PURE__ */ jsx12(ArrowUpIcon, { className: "aui-composer-send-icon size-4" })
|
|
1371
|
-
}
|
|
1372
|
-
) }) }),
|
|
1373
|
-
/* @__PURE__ */ jsx12(AuiIf, { condition: (s) => s.thread.isRunning, children: /* @__PURE__ */ jsx12(ComposerPrimitive2.Cancel, { asChild: true, children: /* @__PURE__ */ jsx12(
|
|
1374
|
-
Button,
|
|
1375
|
-
{
|
|
1376
|
-
type: "button",
|
|
1377
|
-
variant: "default",
|
|
1378
|
-
size: "icon",
|
|
1379
|
-
className: "aui-composer-cancel size-8 rounded-full",
|
|
1380
|
-
"aria-label": "Stop generating",
|
|
1381
|
-
children: /* @__PURE__ */ jsx12(SquareIcon, { className: "aui-composer-cancel-icon size-3 fill-current" })
|
|
1382
|
-
}
|
|
1383
|
-
) }) })
|
|
1384
|
-
] });
|
|
1385
|
-
};
|
|
1386
3211
|
var MessageError = () => {
|
|
1387
|
-
return /* @__PURE__ */
|
|
3212
|
+
return /* @__PURE__ */ jsx25(MessagePrimitive2.Error, { children: /* @__PURE__ */ jsx25(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__ */ jsx25(ErrorPrimitive.Message, { className: "aui-message-error-message line-clamp-2" }) }) });
|
|
1388
3213
|
};
|
|
1389
3214
|
var AssistantMessage = () => {
|
|
1390
|
-
return /* @__PURE__ */
|
|
3215
|
+
return /* @__PURE__ */ jsxs14(
|
|
1391
3216
|
MessagePrimitive2.Root,
|
|
1392
3217
|
{
|
|
1393
3218
|
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",
|
|
1394
3219
|
"data-role": "assistant",
|
|
1395
3220
|
children: [
|
|
1396
|
-
/* @__PURE__ */
|
|
1397
|
-
/* @__PURE__ */
|
|
3221
|
+
/* @__PURE__ */ jsxs14("div", { className: "aui-assistant-message-content wrap-break-word px-2 text-foreground leading-relaxed", children: [
|
|
3222
|
+
/* @__PURE__ */ jsx25(
|
|
1398
3223
|
MessagePrimitive2.Parts,
|
|
1399
3224
|
{
|
|
1400
3225
|
components: {
|
|
1401
3226
|
Text: MarkdownText,
|
|
1402
|
-
tools: { Fallback:
|
|
3227
|
+
tools: { Fallback: ToolArtifactFallback }
|
|
1403
3228
|
}
|
|
1404
3229
|
}
|
|
1405
3230
|
),
|
|
1406
|
-
/* @__PURE__ */
|
|
3231
|
+
/* @__PURE__ */ jsx25(MessageError, {})
|
|
1407
3232
|
] }),
|
|
1408
|
-
/* @__PURE__ */
|
|
3233
|
+
/* @__PURE__ */ jsx25("div", { className: "aui-assistant-message-footer mt-1 ml-2 flex", children: /* @__PURE__ */ jsx25(AssistantActionBar, {}) })
|
|
1409
3234
|
]
|
|
1410
3235
|
}
|
|
1411
3236
|
);
|
|
1412
3237
|
};
|
|
1413
3238
|
var AssistantActionBar = () => {
|
|
1414
|
-
return /* @__PURE__ */
|
|
3239
|
+
return /* @__PURE__ */ jsxs14(
|
|
1415
3240
|
ActionBarPrimitive.Root,
|
|
1416
3241
|
{
|
|
1417
3242
|
hideWhenRunning: true,
|
|
@@ -1419,28 +3244,28 @@ var AssistantActionBar = () => {
|
|
|
1419
3244
|
autohideFloat: "single-branch",
|
|
1420
3245
|
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",
|
|
1421
3246
|
children: [
|
|
1422
|
-
/* @__PURE__ */
|
|
1423
|
-
/* @__PURE__ */
|
|
1424
|
-
/* @__PURE__ */
|
|
3247
|
+
/* @__PURE__ */ jsx25(ActionBarPrimitive.Copy, { asChild: true, children: /* @__PURE__ */ jsxs14(TooltipIconButton, { tooltip: "Copy", children: [
|
|
3248
|
+
/* @__PURE__ */ jsx25(AuiIf2, { condition: (s) => s.message.isCopied, children: /* @__PURE__ */ jsx25(CheckIcon3, {}) }),
|
|
3249
|
+
/* @__PURE__ */ jsx25(AuiIf2, { condition: (s) => !s.message.isCopied, children: /* @__PURE__ */ jsx25(CopyIcon2, {}) })
|
|
1425
3250
|
] }) }),
|
|
1426
|
-
/* @__PURE__ */
|
|
1427
|
-
/* @__PURE__ */
|
|
1428
|
-
/* @__PURE__ */
|
|
3251
|
+
/* @__PURE__ */ jsx25(ActionBarPrimitive.Reload, { asChild: true, children: /* @__PURE__ */ jsx25(TooltipIconButton, { tooltip: "Refresh", children: /* @__PURE__ */ jsx25(RefreshCwIcon, {}) }) }),
|
|
3252
|
+
/* @__PURE__ */ jsxs14(ActionBarMorePrimitive.Root, { children: [
|
|
3253
|
+
/* @__PURE__ */ jsx25(ActionBarMorePrimitive.Trigger, { asChild: true, children: /* @__PURE__ */ jsx25(
|
|
1429
3254
|
TooltipIconButton,
|
|
1430
3255
|
{
|
|
1431
3256
|
tooltip: "More",
|
|
1432
3257
|
className: "data-[state=open]:bg-accent",
|
|
1433
|
-
children: /* @__PURE__ */
|
|
3258
|
+
children: /* @__PURE__ */ jsx25(MoreHorizontalIcon, {})
|
|
1434
3259
|
}
|
|
1435
3260
|
) }),
|
|
1436
|
-
/* @__PURE__ */
|
|
3261
|
+
/* @__PURE__ */ jsx25(
|
|
1437
3262
|
ActionBarMorePrimitive.Content,
|
|
1438
3263
|
{
|
|
1439
3264
|
side: "bottom",
|
|
1440
3265
|
align: "start",
|
|
1441
3266
|
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",
|
|
1442
|
-
children: /* @__PURE__ */
|
|
1443
|
-
/* @__PURE__ */
|
|
3267
|
+
children: /* @__PURE__ */ jsx25(ActionBarPrimitive.ExportMarkdown, { asChild: true, children: /* @__PURE__ */ jsxs14(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: [
|
|
3268
|
+
/* @__PURE__ */ jsx25(DownloadIcon, { className: "size-4" }),
|
|
1444
3269
|
"Export as Markdown"
|
|
1445
3270
|
] }) })
|
|
1446
3271
|
}
|
|
@@ -1451,83 +3276,398 @@ var AssistantActionBar = () => {
|
|
|
1451
3276
|
);
|
|
1452
3277
|
};
|
|
1453
3278
|
var UserMessage = () => {
|
|
1454
|
-
return /* @__PURE__ */
|
|
3279
|
+
return /* @__PURE__ */ jsxs14(
|
|
1455
3280
|
MessagePrimitive2.Root,
|
|
1456
3281
|
{
|
|
1457
3282
|
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",
|
|
1458
3283
|
"data-role": "user",
|
|
1459
3284
|
children: [
|
|
1460
|
-
/* @__PURE__ */
|
|
1461
|
-
/* @__PURE__ */
|
|
1462
|
-
/* @__PURE__ */
|
|
1463
|
-
/* @__PURE__ */
|
|
3285
|
+
/* @__PURE__ */ jsx25(UserMessageAttachments, {}),
|
|
3286
|
+
/* @__PURE__ */ jsxs14("div", { className: "aui-user-message-content-wrapper relative col-start-2 min-w-0", children: [
|
|
3287
|
+
/* @__PURE__ */ jsx25("div", { className: "aui-user-message-content wrap-break-word rounded-2xl bg-muted px-4 py-2.5 text-foreground", children: /* @__PURE__ */ jsx25(MessagePrimitive2.Parts, {}) }),
|
|
3288
|
+
/* @__PURE__ */ jsx25("div", { className: "aui-user-action-bar-wrapper absolute top-1/2 left-0 -translate-x-full -translate-y-1/2 pr-2", children: /* @__PURE__ */ jsx25(UserActionBar, {}) })
|
|
1464
3289
|
] })
|
|
1465
3290
|
]
|
|
1466
3291
|
}
|
|
1467
3292
|
);
|
|
1468
3293
|
};
|
|
1469
3294
|
var UserActionBar = () => {
|
|
1470
|
-
return /* @__PURE__ */
|
|
3295
|
+
return /* @__PURE__ */ jsx25(
|
|
1471
3296
|
ActionBarPrimitive.Root,
|
|
1472
3297
|
{
|
|
1473
3298
|
hideWhenRunning: true,
|
|
1474
3299
|
autohide: "not-last",
|
|
1475
3300
|
className: "aui-user-action-bar-root flex flex-col items-end",
|
|
1476
|
-
children: /* @__PURE__ */
|
|
3301
|
+
children: /* @__PURE__ */ jsx25(ActionBarPrimitive.Edit, { asChild: true, children: /* @__PURE__ */ jsx25(TooltipIconButton, { tooltip: "Edit", className: "aui-user-action-edit p-4", children: /* @__PURE__ */ jsx25(PencilIcon, {}) }) })
|
|
1477
3302
|
}
|
|
1478
3303
|
);
|
|
1479
3304
|
};
|
|
1480
3305
|
var EditComposer = () => {
|
|
1481
|
-
return /* @__PURE__ */
|
|
1482
|
-
/* @__PURE__ */
|
|
1483
|
-
|
|
3306
|
+
return /* @__PURE__ */ jsx25(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__ */ jsxs14(ComposerPrimitive3.Root, { className: "aui-edit-composer-root ml-auto flex w-full max-w-[85%] flex-col rounded-2xl bg-muted", children: [
|
|
3307
|
+
/* @__PURE__ */ jsx25(
|
|
3308
|
+
ComposerPrimitive3.Input,
|
|
1484
3309
|
{
|
|
1485
3310
|
className: "aui-edit-composer-input min-h-14 w-full resize-none bg-transparent p-4 text-foreground text-sm outline-none",
|
|
1486
3311
|
autoFocus: true
|
|
1487
3312
|
}
|
|
1488
3313
|
),
|
|
1489
|
-
/* @__PURE__ */
|
|
1490
|
-
/* @__PURE__ */
|
|
1491
|
-
/* @__PURE__ */
|
|
3314
|
+
/* @__PURE__ */ jsxs14("div", { className: "aui-edit-composer-footer mx-3 mb-3 flex items-center gap-2 self-end", children: [
|
|
3315
|
+
/* @__PURE__ */ jsx25(ComposerPrimitive3.Cancel, { asChild: true, children: /* @__PURE__ */ jsx25(Button, { variant: "ghost", size: "sm", children: "Cancel" }) }),
|
|
3316
|
+
/* @__PURE__ */ jsx25(ComposerPrimitive3.Send, { asChild: true, children: /* @__PURE__ */ jsx25(Button, { size: "sm", children: "Update" }) })
|
|
1492
3317
|
] })
|
|
1493
3318
|
] }) });
|
|
1494
3319
|
};
|
|
1495
3320
|
|
|
1496
3321
|
// src/components/chat.tsx
|
|
1497
|
-
import { jsx as
|
|
3322
|
+
import { jsx as jsx26 } from "react/jsx-runtime";
|
|
1498
3323
|
function TimbalChat({
|
|
1499
3324
|
workforceId,
|
|
1500
3325
|
baseUrl,
|
|
1501
3326
|
fetch: fetch2,
|
|
3327
|
+
attachments,
|
|
3328
|
+
attachmentsUploadUrl,
|
|
3329
|
+
attachmentsAccept,
|
|
3330
|
+
debug,
|
|
1502
3331
|
...threadProps
|
|
1503
3332
|
}) {
|
|
1504
|
-
return /* @__PURE__ */
|
|
3333
|
+
return /* @__PURE__ */ jsx26(
|
|
3334
|
+
TimbalRuntimeProvider,
|
|
3335
|
+
{
|
|
3336
|
+
workforceId,
|
|
3337
|
+
baseUrl,
|
|
3338
|
+
fetch: fetch2,
|
|
3339
|
+
attachments,
|
|
3340
|
+
attachmentsUploadUrl,
|
|
3341
|
+
attachmentsAccept,
|
|
3342
|
+
debug,
|
|
3343
|
+
children: /* @__PURE__ */ jsx26(Thread, { ...threadProps })
|
|
3344
|
+
}
|
|
3345
|
+
);
|
|
3346
|
+
}
|
|
3347
|
+
|
|
3348
|
+
// src/artifacts/agent-instructions.ts
|
|
3349
|
+
var ARTIFACT_AGENT_INSTRUCTIONS = `
|
|
3350
|
+
## Rich artifacts (Timbal chat UI)
|
|
3351
|
+
|
|
3352
|
+
When you need charts, tables, choice widgets, or interactive controls, return a **JSON artifact object** instead of plain prose. The chat UI renders these automatically.
|
|
3353
|
+
|
|
3354
|
+
### Delivery channels (either works)
|
|
3355
|
+
|
|
3356
|
+
1. **Tool result (preferred)** \u2014 return a single JSON object (or a JSON string) from a tool. The object must include a string \`type\` field.
|
|
3357
|
+
2. **Inline markdown fence** \u2014 embed the same JSON inside a fenced block:
|
|
3358
|
+
|
|
3359
|
+
\`\`\`timbal-artifact
|
|
3360
|
+
{"type":"chart","data":[{"month":"Jan","sales":120}]}
|
|
3361
|
+
\`\`\`
|
|
3362
|
+
|
|
3363
|
+
The alias \`\`\`timbal\`\`\` is also accepted.
|
|
3364
|
+
|
|
3365
|
+
### Built-in artifact types
|
|
3366
|
+
|
|
3367
|
+
| \`type\` | Use for |
|
|
3368
|
+
|---|---|
|
|
3369
|
+
| \`chart\` | Bar, line, area, or pie charts. Fields: \`data\`, optional \`chartType\`, \`xKey\`, \`dataKey\`, \`title\`, \`unit\`. |
|
|
3370
|
+
| \`table\` | Tabular data. Fields: \`rows\`, optional \`columns\`, \`title\`. |
|
|
3371
|
+
| \`question\` | In-thread multiple choice. Fields: \`options: [{ id, label, description? }]\`, optional \`prompt\`, \`multi\`. User replies are sent back as a normal user message. |
|
|
3372
|
+
| \`html\` | Custom HTML/CSS/JS in an iframe. Fields: \`content\` (HTML document or fragment), optional \`title\`, \`height\`, \`sandboxed\` (default \`true\`; set \`false\` for unrestricted scripts/CDN). |
|
|
3373
|
+
| \`json\` | Fallback structured view. Fields: \`data\`, optional \`title\`. |
|
|
3374
|
+
| \`ui\` | **Interactive UI** composed from a fixed node palette (hover, click, drag). See below. |
|
|
3375
|
+
|
|
3376
|
+
### When to use \`type: "html"\`
|
|
3377
|
+
|
|
3378
|
+
Use \`html\` when the user wants a **rich visual or interactive page** that does not fit the \`ui\` palette \u2014 e.g. styled layouts, SVG graphics, CSS animations, canvas, small games, calculators, or multi-section mockups. Pass a full HTML document or a body fragment in \`content\`.
|
|
3379
|
+
|
|
3380
|
+
- Inline \`<style>\`, \`<script>\`, SVG, and canvas are supported.
|
|
3381
|
+
- Default \`sandboxed: true\` runs in an isolated iframe with scripts enabled.
|
|
3382
|
+
- Set \`sandboxed: false\` only for trusted content that needs external CDN scripts/styles or full DOM freedom.
|
|
3383
|
+
- Prefer \`ui\` when controls should send chat messages or host events; prefer \`html\` for self-contained mini-apps and visual demos.
|
|
3384
|
+
|
|
3385
|
+
### When to use \`type: "ui"\`
|
|
3386
|
+
|
|
3387
|
+
Use a \`ui\` artifact when the user should **hover, click, drag, or adjust controls** in-thread and those actions should integrate with the chat runtime (messages, \`onArtifactEvent\`). For standalone visual/interactive HTML, use \`html\` instead.
|
|
3388
|
+
|
|
3389
|
+
Each \`ui\` artifact has:
|
|
3390
|
+
|
|
3391
|
+
- \`initialState\` \u2014 optional object seeding local state (per widget instance).
|
|
3392
|
+
- \`root\` \u2014 a single node tree (see node kinds below).
|
|
3393
|
+
- optional \`title\` \u2014 card heading.
|
|
3394
|
+
|
|
3395
|
+
**Bindings:** anywhere a primitive is accepted, you may use \`{ "$bind": "dotted.path" }\` to read from \`initialState\` / local state (e.g. \`{ "$bind": "qty" }\`).
|
|
3396
|
+
|
|
3397
|
+
**Actions:** nodes may attach \`onClick\`, \`onChange\`, or \`onDragEnd\` with one action or an array:
|
|
3398
|
+
|
|
3399
|
+
| Action | Shape | Effect |
|
|
3400
|
+
|---|---|---|
|
|
3401
|
+
| User message | \`{ "kind": "message", "text": "..." }\` or \`{ "kind": "message", "text": { "$bind": "path" } }\` | Sends text as the next user message. |
|
|
3402
|
+
| Set state | \`{ "kind": "set", "path": "foo", "value": 1 }\` | Writes local widget state. |
|
|
3403
|
+
| Toggle boolean | \`{ "kind": "toggle", "path": "enabled" }\` | Flips a boolean at \`path\`. |
|
|
3404
|
+
| Host event | \`{ "kind": "emit", "name": "event-name", "payload": { ... } }\` | Bubbles to the host app (\`onArtifactEvent\` on \`<Thread>\`). |
|
|
3405
|
+
|
|
3406
|
+
### \`ui\` node palette (\`root.kind\`)
|
|
3407
|
+
|
|
3408
|
+
| \`kind\` | Purpose | Key fields |
|
|
3409
|
+
|---|---|---|
|
|
3410
|
+
| \`box\` | Layout container | \`children\`, \`direction\` (\`row\`/\`col\`), \`gap\`, \`padding\`, \`align\`, \`justify\`, \`wrap\` |
|
|
3411
|
+
| \`text\` | Body text | \`value\`, optional \`muted\`, \`size\`, \`weight\` |
|
|
3412
|
+
| \`heading\` | Heading | \`value\`, optional \`level\` (1\u20134) |
|
|
3413
|
+
| \`badge\` | Pill label | \`value\`, optional \`tone\` (\`default\`, \`primary\`, \`success\`, \`warn\`, \`danger\`) |
|
|
3414
|
+
| \`button\` | Clickable button | \`label\`, optional \`variant\`, \`size\`, \`disabled\`, \`onClick\` |
|
|
3415
|
+
| \`toggle\` | Boolean switch | \`binding\` (state path), optional \`label\`, \`onChange\` |
|
|
3416
|
+
| \`slider\` | Numeric range | \`binding\`, optional \`min\`, \`max\`, \`step\`, \`label\`, \`showValue\`, \`onChange\` |
|
|
3417
|
+
| \`tooltip\` | Hover tooltip | \`content\`, \`child\` (single node), optional \`side\` |
|
|
3418
|
+
| \`draggable\` | Drag gesture | \`child\`, optional \`axis\` (\`x\`/\`y\`/\`both\`), \`snapBack\`, \`onDragEnd\` |
|
|
3419
|
+
| \`custom\` | Host-registered widget | \`name\`, optional \`props\`, \`children\` \u2014 only if the app registered that name |
|
|
3420
|
+
|
|
3421
|
+
### Example \`ui\` artifact
|
|
3422
|
+
|
|
3423
|
+
\`\`\`json
|
|
3424
|
+
{
|
|
3425
|
+
"type": "ui",
|
|
3426
|
+
"title": "Configure plan",
|
|
3427
|
+
"initialState": { "qty": 1, "premium": false },
|
|
3428
|
+
"root": {
|
|
3429
|
+
"kind": "box",
|
|
3430
|
+
"direction": "col",
|
|
3431
|
+
"gap": 3,
|
|
3432
|
+
"children": [
|
|
3433
|
+
{ "kind": "heading", "value": "Choose quantity", "level": 3 },
|
|
3434
|
+
{
|
|
3435
|
+
"kind": "tooltip",
|
|
3436
|
+
"content": "Drag to adjust quantity",
|
|
3437
|
+
"child": {
|
|
3438
|
+
"kind": "slider",
|
|
3439
|
+
"binding": "qty",
|
|
3440
|
+
"min": 1,
|
|
3441
|
+
"max": 50,
|
|
3442
|
+
"label": "Quantity",
|
|
3443
|
+
"onChange": { "kind": "emit", "name": "qty-changed" }
|
|
3444
|
+
}
|
|
3445
|
+
},
|
|
3446
|
+
{ "kind": "toggle", "binding": "premium", "label": "Premium support" },
|
|
3447
|
+
{
|
|
3448
|
+
"kind": "button",
|
|
3449
|
+
"label": "Confirm",
|
|
3450
|
+
"onClick": { "kind": "message", "text": { "$bind": "qty" } }
|
|
3451
|
+
}
|
|
3452
|
+
]
|
|
3453
|
+
}
|
|
1505
3454
|
}
|
|
3455
|
+
\`\`\`
|
|
3456
|
+
|
|
3457
|
+
### Rules
|
|
3458
|
+
|
|
3459
|
+
- Always set \`type\` to a built-in value above unless the app documented a custom type.
|
|
3460
|
+
- Prefer \`ui\` over \`html\` when actions must bubble to the host chat (\`message\`, \`emit\`).
|
|
3461
|
+
- Prefer \`question\` for simple A/B/C choices; use \`ui\` when you need sliders, toggles, drag, or multi-control layouts.
|
|
3462
|
+
- Keep \`data\` arrays reasonably small (charts/tables).
|
|
3463
|
+
|
|
3464
|
+
### After calling an artifact tool (critical)
|
|
3465
|
+
|
|
3466
|
+
When you call a tool that returns an artifact (\`make_chart\`, \`ask_question\`, \`show_table\`, \`show_html\`, \`make_ui_demo\`, etc.):
|
|
3467
|
+
|
|
3468
|
+
1. **Do not** paste, quote, paraphrase as JSON, or fence the tool return value in your assistant message. The chat UI already renders it from the tool result.
|
|
3469
|
+
2. **Do not** emit a matching \`\`\`timbal-artifact\`\`\` block for the same payload \u2014 pick **one** channel (tool result only).
|
|
3470
|
+
3. Your follow-up text should be **empty**, or at most **one short sentence** (e.g. "Pick an option above." / "Try the controls."). Never include \`type\`, \`options\`, \`data\`, or dict/JSON syntax.
|
|
3471
|
+
4. Treat the widget as visible to the user; refer to it as "above" / "the chart" / "the choices" \u2014 never reproduce its contents.
|
|
3472
|
+
`.trim();
|
|
1506
3473
|
|
|
1507
3474
|
// src/index.ts
|
|
1508
3475
|
import {
|
|
1509
3476
|
ThreadPrimitive as ThreadPrimitive2,
|
|
1510
3477
|
MessagePrimitive as MessagePrimitive3,
|
|
1511
|
-
ComposerPrimitive as
|
|
3478
|
+
ComposerPrimitive as ComposerPrimitive4,
|
|
1512
3479
|
ActionBarPrimitive as ActionBarPrimitive2,
|
|
3480
|
+
AssistantRuntimeProvider as AssistantRuntimeProvider2,
|
|
1513
3481
|
useThread,
|
|
1514
|
-
useThreadRuntime as
|
|
3482
|
+
useThreadRuntime as useThreadRuntime4,
|
|
1515
3483
|
useMessageRuntime,
|
|
1516
|
-
useComposerRuntime
|
|
3484
|
+
useComposerRuntime as useComposerRuntime2
|
|
1517
3485
|
} from "@assistant-ui/react";
|
|
1518
3486
|
|
|
3487
|
+
// src/hooks/use-workforces.ts
|
|
3488
|
+
import { useEffect as useEffect5, useMemo as useMemo6, useRef as useRef2, useState as useState8 } from "react";
|
|
3489
|
+
function useWorkforces(options = {}) {
|
|
3490
|
+
const { baseUrl = "/api", fetch: fetchFn, pickInitial } = options;
|
|
3491
|
+
const [workforces, setWorkforces] = useState8([]);
|
|
3492
|
+
const [selectedId, setSelectedId] = useState8("");
|
|
3493
|
+
const [isLoading, setIsLoading] = useState8(true);
|
|
3494
|
+
const [error, setError] = useState8(null);
|
|
3495
|
+
const fetchFnRef = useRef2(fetchFn ?? authFetch);
|
|
3496
|
+
useEffect5(() => {
|
|
3497
|
+
fetchFnRef.current = fetchFn ?? authFetch;
|
|
3498
|
+
}, [fetchFn]);
|
|
3499
|
+
const pickInitialRef = useRef2(pickInitial);
|
|
3500
|
+
useEffect5(() => {
|
|
3501
|
+
pickInitialRef.current = pickInitial;
|
|
3502
|
+
}, [pickInitial]);
|
|
3503
|
+
const load = useMemo6(() => {
|
|
3504
|
+
return async () => {
|
|
3505
|
+
setIsLoading(true);
|
|
3506
|
+
setError(null);
|
|
3507
|
+
try {
|
|
3508
|
+
const res = await fetchFnRef.current(`${baseUrl}/workforce`);
|
|
3509
|
+
if (!res.ok) throw new Error(`Failed to load workforces (${res.status})`);
|
|
3510
|
+
const data = await res.json();
|
|
3511
|
+
setWorkforces(data);
|
|
3512
|
+
setSelectedId((current) => {
|
|
3513
|
+
if (current && data.some((w) => idOf(w) === current)) return current;
|
|
3514
|
+
const initial = pickInitialRef.current?.(data) ?? data.find((w) => w.type === "agent") ?? data[0];
|
|
3515
|
+
return initial ? idOf(initial) : "";
|
|
3516
|
+
});
|
|
3517
|
+
} catch (err) {
|
|
3518
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
3519
|
+
} finally {
|
|
3520
|
+
setIsLoading(false);
|
|
3521
|
+
}
|
|
3522
|
+
};
|
|
3523
|
+
}, [baseUrl]);
|
|
3524
|
+
useEffect5(() => {
|
|
3525
|
+
load();
|
|
3526
|
+
}, [load]);
|
|
3527
|
+
const selected = useMemo6(
|
|
3528
|
+
() => workforces.find((w) => idOf(w) === selectedId),
|
|
3529
|
+
[workforces, selectedId]
|
|
3530
|
+
);
|
|
3531
|
+
return {
|
|
3532
|
+
workforces,
|
|
3533
|
+
selectedId,
|
|
3534
|
+
setSelectedId,
|
|
3535
|
+
selected,
|
|
3536
|
+
isLoading,
|
|
3537
|
+
error,
|
|
3538
|
+
refresh: load
|
|
3539
|
+
};
|
|
3540
|
+
}
|
|
3541
|
+
function idOf(item) {
|
|
3542
|
+
return item.id ?? item.uid ?? item.name ?? "";
|
|
3543
|
+
}
|
|
3544
|
+
|
|
3545
|
+
// src/components/workforce-selector.tsx
|
|
3546
|
+
import { ChevronDownIcon as ChevronDownIcon2 } from "lucide-react";
|
|
3547
|
+
import { jsx as jsx27, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
3548
|
+
var WorkforceSelector = ({
|
|
3549
|
+
workforces,
|
|
3550
|
+
value,
|
|
3551
|
+
onChange,
|
|
3552
|
+
hideWhenSingle = true,
|
|
3553
|
+
className,
|
|
3554
|
+
placeholder = "Select agent"
|
|
3555
|
+
}) => {
|
|
3556
|
+
if (workforces.length === 0) return null;
|
|
3557
|
+
if (hideWhenSingle && workforces.length === 1) return null;
|
|
3558
|
+
return /* @__PURE__ */ jsxs15("div", { className: cn("aui-workforce-selector relative inline-flex items-center", className), children: [
|
|
3559
|
+
/* @__PURE__ */ jsxs15(
|
|
3560
|
+
"select",
|
|
3561
|
+
{
|
|
3562
|
+
className: "aui-workforce-selector-input h-7 cursor-pointer appearance-none rounded-md border-none bg-transparent pr-5 pl-1.5 text-xs font-medium text-muted-foreground shadow-none outline-none ring-0 transition-colors hover:text-foreground focus:ring-0",
|
|
3563
|
+
value,
|
|
3564
|
+
onChange: (e) => onChange(e.target.value),
|
|
3565
|
+
"aria-label": placeholder,
|
|
3566
|
+
children: [
|
|
3567
|
+
!value && /* @__PURE__ */ jsx27("option", { value: "", children: placeholder }),
|
|
3568
|
+
workforces.map((w) => {
|
|
3569
|
+
const id = idOf2(w);
|
|
3570
|
+
return /* @__PURE__ */ jsx27("option", { value: id, children: w.name ?? id }, id);
|
|
3571
|
+
})
|
|
3572
|
+
]
|
|
3573
|
+
}
|
|
3574
|
+
),
|
|
3575
|
+
/* @__PURE__ */ jsx27(ChevronDownIcon2, { className: "aui-workforce-selector-icon pointer-events-none absolute right-1 size-3 text-muted-foreground" })
|
|
3576
|
+
] });
|
|
3577
|
+
};
|
|
3578
|
+
function idOf2(item) {
|
|
3579
|
+
return item.id ?? item.uid ?? item.name ?? "";
|
|
3580
|
+
}
|
|
3581
|
+
|
|
3582
|
+
// src/components/chat-shell.tsx
|
|
3583
|
+
import { Fragment as Fragment3, jsx as jsx28, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
3584
|
+
var TimbalChatShell = ({
|
|
3585
|
+
workforceId,
|
|
3586
|
+
brand,
|
|
3587
|
+
headerActions,
|
|
3588
|
+
hideWorkforceSelector,
|
|
3589
|
+
className,
|
|
3590
|
+
headerClassName,
|
|
3591
|
+
baseUrl,
|
|
3592
|
+
fetch: fetch2,
|
|
3593
|
+
...chatProps
|
|
3594
|
+
}) => {
|
|
3595
|
+
const {
|
|
3596
|
+
workforces,
|
|
3597
|
+
selectedId,
|
|
3598
|
+
setSelectedId
|
|
3599
|
+
} = useWorkforces({ baseUrl, fetch: fetch2 });
|
|
3600
|
+
const effectiveId = workforceId ?? selectedId;
|
|
3601
|
+
const showSelector = !hideWorkforceSelector && !workforceId && workforces.length > 0;
|
|
3602
|
+
return /* @__PURE__ */ jsxs16(
|
|
3603
|
+
"div",
|
|
3604
|
+
{
|
|
3605
|
+
className: cn(
|
|
3606
|
+
"aui-chat-shell flex h-screen flex-col overflow-hidden",
|
|
3607
|
+
className
|
|
3608
|
+
),
|
|
3609
|
+
children: [
|
|
3610
|
+
/* @__PURE__ */ jsxs16(
|
|
3611
|
+
"header",
|
|
3612
|
+
{
|
|
3613
|
+
className: cn(
|
|
3614
|
+
"aui-chat-shell-header flex shrink-0 items-center justify-between border-b border-border/50 bg-background/90 px-5 py-2 backdrop-blur-md",
|
|
3615
|
+
headerClassName
|
|
3616
|
+
),
|
|
3617
|
+
children: [
|
|
3618
|
+
/* @__PURE__ */ jsxs16("div", { className: "flex items-center", children: [
|
|
3619
|
+
brand,
|
|
3620
|
+
showSelector && /* @__PURE__ */ jsxs16(Fragment3, { children: [
|
|
3621
|
+
/* @__PURE__ */ jsx28("div", { className: "mx-3.5 h-3.5 w-px bg-border" }),
|
|
3622
|
+
/* @__PURE__ */ jsx28(
|
|
3623
|
+
WorkforceSelector,
|
|
3624
|
+
{
|
|
3625
|
+
workforces,
|
|
3626
|
+
value: selectedId,
|
|
3627
|
+
onChange: setSelectedId
|
|
3628
|
+
}
|
|
3629
|
+
)
|
|
3630
|
+
] })
|
|
3631
|
+
] }),
|
|
3632
|
+
/* @__PURE__ */ jsx28("div", { className: "flex items-center gap-0.5", children: headerActions })
|
|
3633
|
+
]
|
|
3634
|
+
}
|
|
3635
|
+
),
|
|
3636
|
+
/* @__PURE__ */ jsx28(
|
|
3637
|
+
TimbalChat,
|
|
3638
|
+
{
|
|
3639
|
+
workforceId: effectiveId,
|
|
3640
|
+
baseUrl,
|
|
3641
|
+
fetch: fetch2,
|
|
3642
|
+
className: "min-h-0 flex-1",
|
|
3643
|
+
...chatProps
|
|
3644
|
+
},
|
|
3645
|
+
effectiveId
|
|
3646
|
+
)
|
|
3647
|
+
]
|
|
3648
|
+
}
|
|
3649
|
+
);
|
|
3650
|
+
};
|
|
3651
|
+
|
|
1519
3652
|
// src/auth/provider.tsx
|
|
1520
3653
|
import {
|
|
1521
|
-
createContext,
|
|
1522
|
-
useCallback as
|
|
1523
|
-
useContext,
|
|
1524
|
-
useEffect as
|
|
1525
|
-
useState as
|
|
3654
|
+
createContext as createContext4,
|
|
3655
|
+
useCallback as useCallback3,
|
|
3656
|
+
useContext as useContext4,
|
|
3657
|
+
useEffect as useEffect6,
|
|
3658
|
+
useState as useState9
|
|
1526
3659
|
} from "react";
|
|
1527
|
-
import { jsx as
|
|
1528
|
-
|
|
3660
|
+
import { jsx as jsx29 } from "react/jsx-runtime";
|
|
3661
|
+
function isInsideIframe() {
|
|
3662
|
+
try {
|
|
3663
|
+
return typeof window !== "undefined" && window.self !== window.top;
|
|
3664
|
+
} catch {
|
|
3665
|
+
return true;
|
|
3666
|
+
}
|
|
3667
|
+
}
|
|
3668
|
+
var SessionContext = createContext4(void 0);
|
|
1529
3669
|
var useSession = () => {
|
|
1530
|
-
const context =
|
|
3670
|
+
const context = useContext4(SessionContext);
|
|
1531
3671
|
if (context === void 0) {
|
|
1532
3672
|
throw new Error("useSession must be used within a SessionProvider");
|
|
1533
3673
|
}
|
|
@@ -1537,9 +3677,10 @@ var SessionProvider = ({
|
|
|
1537
3677
|
children,
|
|
1538
3678
|
enabled = true
|
|
1539
3679
|
}) => {
|
|
1540
|
-
const [user, setUser] =
|
|
1541
|
-
const [loading, setLoading] =
|
|
1542
|
-
|
|
3680
|
+
const [user, setUser] = useState9(null);
|
|
3681
|
+
const [loading, setLoading] = useState9(enabled);
|
|
3682
|
+
const [embedded] = useState9(isInsideIframe);
|
|
3683
|
+
useEffect6(() => {
|
|
1543
3684
|
if (!enabled) {
|
|
1544
3685
|
setLoading(false);
|
|
1545
3686
|
return;
|
|
@@ -1571,14 +3712,36 @@ var SessionProvider = ({
|
|
|
1571
3712
|
if (ignore) return;
|
|
1572
3713
|
clearTokens();
|
|
1573
3714
|
}
|
|
1574
|
-
|
|
3715
|
+
if (!ignore && !embedded) {
|
|
3716
|
+
setLoading(false);
|
|
3717
|
+
}
|
|
1575
3718
|
};
|
|
1576
3719
|
restoreSession();
|
|
3720
|
+
let messageCleanup;
|
|
3721
|
+
if (embedded) {
|
|
3722
|
+
const handleMessage = async (event) => {
|
|
3723
|
+
if (ignore) return;
|
|
3724
|
+
if (event.data?.type !== "timbal:auth" || !event.data.token) return;
|
|
3725
|
+
setAccessToken(event.data.token);
|
|
3726
|
+
if (event.data.refreshToken) {
|
|
3727
|
+
setRefreshToken(event.data.refreshToken);
|
|
3728
|
+
}
|
|
3729
|
+
const u = await fetchCurrentUser();
|
|
3730
|
+
if (!ignore) {
|
|
3731
|
+
setUser(u);
|
|
3732
|
+
setLoading(false);
|
|
3733
|
+
}
|
|
3734
|
+
};
|
|
3735
|
+
window.addEventListener("message", handleMessage);
|
|
3736
|
+
window.parent.postMessage({ type: "timbal:request-session" }, "*");
|
|
3737
|
+
messageCleanup = () => window.removeEventListener("message", handleMessage);
|
|
3738
|
+
}
|
|
1577
3739
|
return () => {
|
|
1578
3740
|
ignore = true;
|
|
3741
|
+
messageCleanup?.();
|
|
1579
3742
|
};
|
|
1580
|
-
}, [enabled]);
|
|
1581
|
-
const logout =
|
|
3743
|
+
}, [enabled, embedded]);
|
|
3744
|
+
const logout = useCallback3(() => {
|
|
1582
3745
|
clearTokens();
|
|
1583
3746
|
setUser(null);
|
|
1584
3747
|
const returnTo = encodeURIComponent(
|
|
@@ -1588,13 +3751,14 @@ var SessionProvider = ({
|
|
|
1588
3751
|
() => window.location.href = `/api/auth/login?return_to=${returnTo}`
|
|
1589
3752
|
);
|
|
1590
3753
|
}, []);
|
|
1591
|
-
return /* @__PURE__ */
|
|
3754
|
+
return /* @__PURE__ */ jsx29(
|
|
1592
3755
|
SessionContext.Provider,
|
|
1593
3756
|
{
|
|
1594
3757
|
value: {
|
|
1595
3758
|
user,
|
|
1596
3759
|
loading,
|
|
1597
3760
|
isAuthenticated: !!user,
|
|
3761
|
+
isEmbedded: embedded,
|
|
1598
3762
|
logout
|
|
1599
3763
|
},
|
|
1600
3764
|
children
|
|
@@ -1604,20 +3768,20 @@ var SessionProvider = ({
|
|
|
1604
3768
|
|
|
1605
3769
|
// src/auth/guard.tsx
|
|
1606
3770
|
import { Loader2 } from "lucide-react";
|
|
1607
|
-
import { jsx as
|
|
3771
|
+
import { jsx as jsx30 } from "react/jsx-runtime";
|
|
1608
3772
|
var AuthGuard = ({
|
|
1609
3773
|
children,
|
|
1610
3774
|
requireAuth = false,
|
|
1611
3775
|
enabled = true
|
|
1612
3776
|
}) => {
|
|
1613
|
-
const { isAuthenticated, loading } = useSession();
|
|
3777
|
+
const { isAuthenticated, loading, isEmbedded } = useSession();
|
|
1614
3778
|
if (!enabled) {
|
|
1615
3779
|
return children;
|
|
1616
3780
|
}
|
|
1617
3781
|
if (loading) {
|
|
1618
|
-
return /* @__PURE__ */
|
|
3782
|
+
return /* @__PURE__ */ jsx30("div", { className: "flex items-center justify-center h-screen", children: /* @__PURE__ */ jsx30(Loader2, { className: "w-8 h-8 animate-spin" }) });
|
|
1619
3783
|
}
|
|
1620
|
-
if (requireAuth && !isAuthenticated) {
|
|
3784
|
+
if (requireAuth && !isAuthenticated && !isEmbedded) {
|
|
1621
3785
|
const returnTo = encodeURIComponent(
|
|
1622
3786
|
window.location.pathname + window.location.search
|
|
1623
3787
|
);
|
|
@@ -1627,15 +3791,24 @@ var AuthGuard = ({
|
|
|
1627
3791
|
return children;
|
|
1628
3792
|
};
|
|
1629
3793
|
export {
|
|
3794
|
+
ARTIFACT_AGENT_INSTRUCTIONS,
|
|
3795
|
+
ARTIFACT_FENCE_LANGUAGES,
|
|
1630
3796
|
ActionBarPrimitive2 as ActionBarPrimitive,
|
|
3797
|
+
ArtifactCard,
|
|
3798
|
+
ArtifactRegistryProvider,
|
|
3799
|
+
ArtifactView,
|
|
3800
|
+
AssistantRuntimeProvider2 as AssistantRuntimeProvider,
|
|
1631
3801
|
AuthGuard,
|
|
1632
3802
|
Avatar,
|
|
1633
3803
|
AvatarFallback,
|
|
1634
3804
|
AvatarImage,
|
|
1635
3805
|
Button,
|
|
3806
|
+
ChartArtifactView,
|
|
3807
|
+
Composer,
|
|
1636
3808
|
ComposerAddAttachment,
|
|
1637
3809
|
ComposerAttachments,
|
|
1638
|
-
|
|
3810
|
+
ComposerPrimitive4 as ComposerPrimitive,
|
|
3811
|
+
DEFAULT_UPLOAD_ACCEPT,
|
|
1639
3812
|
Dialog,
|
|
1640
3813
|
DialogClose,
|
|
1641
3814
|
DialogContent,
|
|
@@ -1643,33 +3816,70 @@ export {
|
|
|
1643
3816
|
DialogPortal,
|
|
1644
3817
|
DialogTitle,
|
|
1645
3818
|
DialogTrigger,
|
|
3819
|
+
HtmlArtifactView,
|
|
3820
|
+
JsonArtifactView,
|
|
1646
3821
|
MarkdownText,
|
|
1647
3822
|
MessagePrimitive3 as MessagePrimitive,
|
|
3823
|
+
QuestionArtifactView,
|
|
1648
3824
|
SessionProvider,
|
|
1649
3825
|
Shimmer,
|
|
3826
|
+
Suggestions,
|
|
1650
3827
|
syntax_highlighter_default as SyntaxHighlighter,
|
|
3828
|
+
TableArtifactView,
|
|
1651
3829
|
Thread,
|
|
1652
3830
|
ThreadPrimitive2 as ThreadPrimitive,
|
|
1653
3831
|
TimbalChat,
|
|
3832
|
+
TimbalChatShell,
|
|
1654
3833
|
TimbalRuntimeProvider,
|
|
3834
|
+
ToolArtifactFallback,
|
|
1655
3835
|
ToolFallback,
|
|
1656
3836
|
Tooltip,
|
|
1657
3837
|
TooltipContent,
|
|
1658
3838
|
TooltipIconButton,
|
|
1659
3839
|
TooltipProvider,
|
|
1660
3840
|
TooltipTrigger,
|
|
3841
|
+
UiArtifactView,
|
|
3842
|
+
UiCustomNodeRegistryProvider,
|
|
3843
|
+
UiEventProvider,
|
|
3844
|
+
UiNodeView,
|
|
1661
3845
|
UserMessageAttachments,
|
|
3846
|
+
WorkforceSelector,
|
|
1662
3847
|
authFetch,
|
|
1663
3848
|
buttonVariants,
|
|
1664
3849
|
clearTokens,
|
|
1665
3850
|
cn,
|
|
3851
|
+
createDefaultAttachmentAdapter,
|
|
3852
|
+
createUploadAttachmentAdapter,
|
|
3853
|
+
defaultArtifactRenderers,
|
|
1666
3854
|
fetchCurrentUser,
|
|
3855
|
+
findMarkdownArtifacts,
|
|
1667
3856
|
getAccessToken,
|
|
3857
|
+
getPath,
|
|
1668
3858
|
getRefreshToken,
|
|
3859
|
+
isArtifact,
|
|
3860
|
+
isArtifactFenceLanguage,
|
|
3861
|
+
isUiBinding,
|
|
3862
|
+
parseArtifactFromToolResult,
|
|
3863
|
+
parseSSELine2 as parseSSELine,
|
|
1669
3864
|
refreshAccessToken,
|
|
1670
|
-
|
|
3865
|
+
resolveAttachmentAdapter,
|
|
3866
|
+
resolveBindable,
|
|
3867
|
+
setAccessToken,
|
|
3868
|
+
setPath,
|
|
3869
|
+
setRefreshToken,
|
|
3870
|
+
splitMarkdownByArtifacts,
|
|
3871
|
+
useArtifactRegistry,
|
|
3872
|
+
useComposerRuntime2 as useComposerRuntime,
|
|
1671
3873
|
useMessageRuntime,
|
|
3874
|
+
useResolvedSuggestions,
|
|
1672
3875
|
useSession,
|
|
1673
3876
|
useThread,
|
|
1674
|
-
|
|
3877
|
+
useThreadRuntime4 as useThreadRuntime,
|
|
3878
|
+
useTimbalRuntime,
|
|
3879
|
+
useTimbalStream,
|
|
3880
|
+
useUiCustomNodeRegistry,
|
|
3881
|
+
useUiDispatch,
|
|
3882
|
+
useUiEventEmitter,
|
|
3883
|
+
useUiState,
|
|
3884
|
+
useWorkforces
|
|
1675
3885
|
};
|