@timbal-ai/timbal-react 0.2.2 → 0.4.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 +3323 -511
- package/dist/index.d.cts +1000 -31
- package/dist/index.d.ts +1000 -31
- package/dist/index.esm.js +3252 -509
- package/dist/styles.css +180 -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
|
|
@@ -87,13 +95,506 @@ var fetchCurrentUser = async () => {
|
|
|
87
95
|
}
|
|
88
96
|
};
|
|
89
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
|
+
|
|
90
552
|
// src/runtime/provider.tsx
|
|
91
553
|
import { jsx } from "react/jsx-runtime";
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
+
};
|
|
97
598
|
function findParentId(messages, beforeIndex) {
|
|
98
599
|
const slice = beforeIndex !== void 0 ? messages.slice(0, beforeIndex) : messages;
|
|
99
600
|
for (let i = slice.length - 1; i >= 0; i--) {
|
|
@@ -101,18 +602,18 @@ function findParentId(messages, beforeIndex) {
|
|
|
101
602
|
}
|
|
102
603
|
return null;
|
|
103
604
|
}
|
|
104
|
-
function isTopLevelStart(event) {
|
|
105
|
-
return event.type === "START" && typeof event.run_id === "string" && typeof event.path === "string" && !event.path.includes(".");
|
|
106
|
-
}
|
|
107
605
|
function getTextFromMessage(message) {
|
|
108
606
|
const part = message.content.find((c) => c.type === "text");
|
|
109
|
-
return part?.type === "text" ? part.text :
|
|
607
|
+
return part?.type === "text" ? part.text : "";
|
|
110
608
|
}
|
|
111
|
-
function
|
|
609
|
+
function getAttachmentsFromMessage(message) {
|
|
610
|
+
return message.attachments?.length ? message.attachments : void 0;
|
|
611
|
+
}
|
|
612
|
+
function useTimbalStream({
|
|
112
613
|
workforceId,
|
|
113
|
-
children,
|
|
114
614
|
baseUrl = "/api",
|
|
115
|
-
fetch: fetchFn
|
|
615
|
+
fetch: fetchFn,
|
|
616
|
+
debug = false
|
|
116
617
|
}) {
|
|
117
618
|
const [messages, setMessages] = useState([]);
|
|
118
619
|
const [isRunning, setIsRunning] = useState(false);
|
|
@@ -122,40 +623,41 @@ function TimbalRuntimeProvider({
|
|
|
122
623
|
useEffect(() => {
|
|
123
624
|
fetchFnRef.current = fetchFn ?? authFetch;
|
|
124
625
|
}, [fetchFn]);
|
|
626
|
+
const debugRef = useRef(debug);
|
|
627
|
+
useEffect(() => {
|
|
628
|
+
debugRef.current = debug;
|
|
629
|
+
}, [debug]);
|
|
125
630
|
useEffect(() => {
|
|
126
631
|
messagesRef.current = messages;
|
|
127
632
|
}, [messages]);
|
|
128
633
|
const streamAssistantResponse = useCallback(
|
|
129
|
-
async (input, userId, assistantId, parentId, signal) => {
|
|
130
|
-
const
|
|
131
|
-
const toolIndexById = /* @__PURE__ */ new Map();
|
|
132
|
-
const lastTextPart = () => {
|
|
133
|
-
const last = parts[parts.length - 1];
|
|
134
|
-
if (last?.type === "text") return last;
|
|
135
|
-
const next = { type: "text", text: "" };
|
|
136
|
-
parts.push(next);
|
|
137
|
-
return next;
|
|
138
|
-
};
|
|
634
|
+
async (input, attachments, userId, assistantId, parentId, signal) => {
|
|
635
|
+
const state = createReducerState();
|
|
139
636
|
const flush = () => {
|
|
140
637
|
setMessages(
|
|
141
|
-
(prev) => prev.map(
|
|
638
|
+
(prev) => prev.map(
|
|
639
|
+
(m) => m.id === assistantId ? { ...m, content: [...state.parts] } : m
|
|
640
|
+
)
|
|
142
641
|
);
|
|
143
642
|
};
|
|
144
643
|
const stampRunId = (runId) => {
|
|
145
644
|
setMessages(
|
|
146
|
-
(prev) => prev.map(
|
|
645
|
+
(prev) => prev.map(
|
|
646
|
+
(m) => m.id === userId || m.id === assistantId ? { ...m, runId } : m
|
|
647
|
+
)
|
|
147
648
|
);
|
|
148
649
|
};
|
|
149
650
|
try {
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
+
);
|
|
159
661
|
if (!res.ok || !res.body) throw new Error(`Request failed: ${res.status}`);
|
|
160
662
|
const reader = res.body.getReader();
|
|
161
663
|
const decoder = new TextDecoder();
|
|
@@ -170,84 +672,34 @@ function TimbalRuntimeProvider({
|
|
|
170
672
|
for (const line of lines) {
|
|
171
673
|
const event = parseSSELine(line);
|
|
172
674
|
if (!event) continue;
|
|
173
|
-
if (
|
|
174
|
-
|
|
175
|
-
stampRunId(capturedRunId);
|
|
675
|
+
if (debugRef.current) {
|
|
676
|
+
console.debug("[timbal]", event.type, event);
|
|
176
677
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
lastTextPart().text += item.text_delta;
|
|
183
|
-
flush();
|
|
184
|
-
} else if (item.type === "tool_use") {
|
|
185
|
-
const toolCallId = item.id || `tool-${crypto.randomUUID()}`;
|
|
186
|
-
const inputStr = typeof item.input === "string" ? item.input : JSON.stringify(item.input ?? {});
|
|
187
|
-
parts.push({
|
|
188
|
-
type: "tool-call",
|
|
189
|
-
toolCallId,
|
|
190
|
-
toolName: item.name || "unknown",
|
|
191
|
-
argsText: inputStr
|
|
192
|
-
});
|
|
193
|
-
toolIndexById.set(toolCallId, parts.length - 1);
|
|
194
|
-
flush();
|
|
195
|
-
} else if (item.type === "tool_use_delta") {
|
|
196
|
-
const idx = toolIndexById.get(item.id);
|
|
197
|
-
if (idx !== void 0 && typeof item.input_delta === "string") {
|
|
198
|
-
parts[idx].argsText += item.input_delta;
|
|
199
|
-
flush();
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
break;
|
|
203
|
-
}
|
|
204
|
-
case "OUTPUT": {
|
|
205
|
-
const output = event.output;
|
|
206
|
-
if (!output) break;
|
|
207
|
-
if (typeof output === "object" && Array.isArray(output.content)) {
|
|
208
|
-
for (const block of output.content) {
|
|
209
|
-
if (block.type === "tool_use") {
|
|
210
|
-
const id = block.id || `tool-${crypto.randomUUID()}`;
|
|
211
|
-
const idx = toolIndexById.get(id);
|
|
212
|
-
if (idx !== void 0) {
|
|
213
|
-
parts[idx].result = "Tool executed";
|
|
214
|
-
} else {
|
|
215
|
-
const inputStr = typeof block.input === "string" ? block.input : JSON.stringify(block.input ?? {});
|
|
216
|
-
parts.push({
|
|
217
|
-
type: "tool-call",
|
|
218
|
-
toolCallId: id,
|
|
219
|
-
toolName: block.name || "unknown",
|
|
220
|
-
argsText: inputStr,
|
|
221
|
-
result: "Tool executed"
|
|
222
|
-
});
|
|
223
|
-
toolIndexById.set(id, parts.length - 1);
|
|
224
|
-
}
|
|
225
|
-
} else if (block.type === "text" && typeof block.text === "string" && !lastTextPart().text) {
|
|
226
|
-
lastTextPart().text = block.text;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
flush();
|
|
230
|
-
} else if (parts.length === 0) {
|
|
231
|
-
const text = typeof output === "string" ? output : JSON.stringify(output);
|
|
232
|
-
parts.push({ type: "text", text });
|
|
233
|
-
flush();
|
|
234
|
-
}
|
|
235
|
-
break;
|
|
678
|
+
if (!capturedRunId) {
|
|
679
|
+
const runId = readTopLevelStartRunId(event);
|
|
680
|
+
if (runId) {
|
|
681
|
+
capturedRunId = runId;
|
|
682
|
+
stampRunId(runId);
|
|
236
683
|
}
|
|
237
684
|
}
|
|
685
|
+
const changed = reduceSseEvent(state, event);
|
|
686
|
+
if (changed) flush();
|
|
238
687
|
}
|
|
239
688
|
}
|
|
240
689
|
if (buffer.trim()) {
|
|
241
690
|
const event = parseSSELine(buffer);
|
|
242
|
-
if (event
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
691
|
+
if (event) {
|
|
692
|
+
if (debugRef.current) {
|
|
693
|
+
console.debug("[timbal]", event.type, event);
|
|
694
|
+
}
|
|
695
|
+
if (reduceSseEvent(state, event)) flush();
|
|
246
696
|
}
|
|
247
697
|
}
|
|
248
698
|
} catch (err) {
|
|
249
699
|
if (err.name !== "AbortError") {
|
|
250
|
-
if (parts.length === 0)
|
|
700
|
+
if (state.parts.length === 0) {
|
|
701
|
+
state.parts.push({ type: "text", text: "Something went wrong." });
|
|
702
|
+
}
|
|
251
703
|
flush();
|
|
252
704
|
}
|
|
253
705
|
} finally {
|
|
@@ -257,44 +709,46 @@ function TimbalRuntimeProvider({
|
|
|
257
709
|
},
|
|
258
710
|
[workforceId, baseUrl]
|
|
259
711
|
);
|
|
260
|
-
const
|
|
261
|
-
async (
|
|
262
|
-
const textPart = message.content.find((c) => c.type === "text");
|
|
263
|
-
if (!textPart || textPart.type !== "text") return;
|
|
264
|
-
const input = textPart.text;
|
|
712
|
+
const send = useCallback(
|
|
713
|
+
async (input, options) => {
|
|
265
714
|
const userId = crypto.randomUUID();
|
|
266
715
|
const assistantId = crypto.randomUUID();
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
|
|
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
|
+
};
|
|
275
724
|
setMessages([
|
|
276
725
|
...base,
|
|
277
|
-
|
|
278
|
-
]);
|
|
279
|
-
setIsRunning(true);
|
|
280
|
-
setMessages((prev) => [
|
|
281
|
-
...prev,
|
|
726
|
+
userMessage,
|
|
282
727
|
{ id: assistantId, role: "assistant", content: [] }
|
|
283
728
|
]);
|
|
729
|
+
setIsRunning(true);
|
|
284
730
|
const controller = new AbortController();
|
|
285
731
|
abortRef.current = controller;
|
|
286
|
-
await streamAssistantResponse(
|
|
732
|
+
await streamAssistantResponse(
|
|
733
|
+
input,
|
|
734
|
+
options?.attachments,
|
|
735
|
+
userId,
|
|
736
|
+
assistantId,
|
|
737
|
+
parentId,
|
|
738
|
+
controller.signal
|
|
739
|
+
);
|
|
287
740
|
},
|
|
288
741
|
[streamAssistantResponse]
|
|
289
742
|
);
|
|
290
|
-
const
|
|
743
|
+
const reload = useCallback(
|
|
291
744
|
async (messageId) => {
|
|
292
745
|
const current = messagesRef.current;
|
|
293
746
|
const idx = messageId ? current.findIndex((m) => m.id === messageId) : current.length - 2;
|
|
294
747
|
const userMessage = idx >= 0 ? current[idx] : null;
|
|
295
748
|
if (!userMessage || userMessage.role !== "user") return;
|
|
296
749
|
const input = getTextFromMessage(userMessage);
|
|
297
|
-
|
|
750
|
+
const messageAttachments = getAttachmentsFromMessage(userMessage);
|
|
751
|
+
if (!input && !messageAttachments?.length) return;
|
|
298
752
|
const assistantId = crypto.randomUUID();
|
|
299
753
|
const parentId = findParentId(current, idx);
|
|
300
754
|
setMessages((prev) => [
|
|
@@ -304,25 +758,138 @@ function TimbalRuntimeProvider({
|
|
|
304
758
|
setIsRunning(true);
|
|
305
759
|
const controller = new AbortController();
|
|
306
760
|
abortRef.current = controller;
|
|
307
|
-
await streamAssistantResponse(
|
|
761
|
+
await streamAssistantResponse(
|
|
762
|
+
input,
|
|
763
|
+
messageAttachments,
|
|
764
|
+
userMessage.id,
|
|
765
|
+
assistantId,
|
|
766
|
+
parentId,
|
|
767
|
+
controller.signal
|
|
768
|
+
);
|
|
308
769
|
},
|
|
309
770
|
[streamAssistantResponse]
|
|
310
771
|
);
|
|
311
|
-
const
|
|
772
|
+
const cancel = useCallback(() => {
|
|
773
|
+
abortRef.current?.abort();
|
|
774
|
+
}, []);
|
|
775
|
+
const clear = useCallback(() => {
|
|
312
776
|
abortRef.current?.abort();
|
|
777
|
+
setMessages([]);
|
|
313
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]);
|
|
314
849
|
const runtime = useExternalStoreRuntime({
|
|
315
|
-
isRunning,
|
|
316
|
-
messages,
|
|
850
|
+
isRunning: stream.isRunning,
|
|
851
|
+
messages: stream.messages,
|
|
317
852
|
convertMessage,
|
|
318
853
|
onNew,
|
|
319
854
|
onEdit: onNew,
|
|
320
855
|
onReload,
|
|
321
|
-
onCancel
|
|
856
|
+
onCancel,
|
|
857
|
+
...attachmentAdapter ? { adapters: { attachments: attachmentAdapter } } : {}
|
|
322
858
|
});
|
|
323
|
-
return /* @__PURE__ */ jsx(AssistantRuntimeProvider, { runtime, children });
|
|
859
|
+
return /* @__PURE__ */ jsx(TimbalStreamContext.Provider, { value: stream, children: /* @__PURE__ */ jsx(AssistantRuntimeProvider, { runtime, children }) });
|
|
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));
|
|
324
865
|
}
|
|
325
866
|
|
|
867
|
+
// src/index.ts
|
|
868
|
+
import { parseSSELine as parseSSELine2 } from "@timbal-ai/timbal-sdk";
|
|
869
|
+
|
|
870
|
+
// src/components/thread.tsx
|
|
871
|
+
import {
|
|
872
|
+
ActionBarMorePrimitive,
|
|
873
|
+
ActionBarPrimitive,
|
|
874
|
+
AuiIf as AuiIf2,
|
|
875
|
+
ComposerPrimitive as ComposerPrimitive3,
|
|
876
|
+
ErrorPrimitive,
|
|
877
|
+
MessagePartPrimitive,
|
|
878
|
+
MessagePrimitive as MessagePrimitive2,
|
|
879
|
+
ThreadPrimitive,
|
|
880
|
+
useThread
|
|
881
|
+
} from "@assistant-ui/react";
|
|
882
|
+
import {
|
|
883
|
+
ArrowDownIcon,
|
|
884
|
+
CheckIcon as CheckIcon3,
|
|
885
|
+
CopyIcon as CopyIcon2,
|
|
886
|
+
DownloadIcon,
|
|
887
|
+
MoreHorizontalIcon,
|
|
888
|
+
PencilIcon,
|
|
889
|
+
RefreshCwIcon
|
|
890
|
+
} from "lucide-react";
|
|
891
|
+
import { motion as motion4 } from "motion/react";
|
|
892
|
+
|
|
326
893
|
// src/components/attachment.tsx
|
|
327
894
|
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
328
895
|
import { XIcon as XIcon2, PlusIcon, FileText } from "lucide-react";
|
|
@@ -535,87 +1102,147 @@ function AvatarFallback({
|
|
|
535
1102
|
}
|
|
536
1103
|
|
|
537
1104
|
// src/components/tooltip-icon-button.tsx
|
|
538
|
-
import { forwardRef } from "react";
|
|
539
|
-
import { Slottable } from "@radix-ui/react-slot";
|
|
1105
|
+
import { forwardRef as forwardRef2 } from "react";
|
|
540
1106
|
|
|
541
|
-
// src/ui/button.tsx
|
|
542
|
-
import
|
|
1107
|
+
// src/ui/timbal-v2-button.tsx
|
|
1108
|
+
import * as React from "react";
|
|
543
1109
|
import { Slot } from "radix-ui";
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
1110
|
+
|
|
1111
|
+
// src/ui/timbal-v2-button-tokens.ts
|
|
1112
|
+
var TIMBAL_V2_SIZE_HEIGHT = {
|
|
1113
|
+
xs: "min-h-8 h-8",
|
|
1114
|
+
sm: "min-h-9 h-9",
|
|
1115
|
+
md: "min-h-10 h-10",
|
|
1116
|
+
lg: "min-h-11 h-11"
|
|
1117
|
+
};
|
|
1118
|
+
var TIMBAL_V2_SIZE_ICON = {
|
|
1119
|
+
xs: "min-h-8 min-w-8 size-8",
|
|
1120
|
+
sm: "min-h-8 min-w-8 size-8",
|
|
1121
|
+
md: "min-h-10 min-w-10 size-10",
|
|
1122
|
+
lg: "min-h-11 min-w-11 size-11"
|
|
1123
|
+
};
|
|
1124
|
+
var TIMBAL_V2_SIZE_LABEL_PX = {
|
|
1125
|
+
xs: "px-3",
|
|
1126
|
+
sm: "px-4",
|
|
1127
|
+
md: "px-5",
|
|
1128
|
+
lg: "px-6"
|
|
1129
|
+
};
|
|
1130
|
+
var TIMBAL_V2_FILL = {
|
|
1131
|
+
primary: "bg-gradient-to-b from-neutral-800 to-black group-hover/tbv2:from-neutral-700 group-hover/tbv2:to-neutral-900 group-active/tbv2:from-black group-active/tbv2:to-black dark:from-white dark:to-neutral-200 dark:group-hover/tbv2:from-white dark:group-hover/tbv2:to-neutral-100 dark:group-active/tbv2:from-neutral-200 dark:group-active/tbv2:to-neutral-400",
|
|
1132
|
+
informative: "bg-blue-600 group-active/tbv2:[background-image:linear-gradient(to_top,rgba(0,0,0,0.08),transparent_55%)]",
|
|
1133
|
+
destructive: "bg-gradient-to-b from-white to-neutral-50/75 group-hover/tbv2:from-red-50/90 group-hover/tbv2:to-red-100/70 group-active/tbv2:from-red-100/90 group-active/tbv2:to-red-200/65 dark:from-white/[0.05] dark:to-white/[0.025] dark:group-hover/tbv2:from-red-500/12 dark:group-hover/tbv2:to-red-500/8 dark:group-active/tbv2:from-red-500/20 dark:group-active/tbv2:to-red-500/12",
|
|
1134
|
+
secondary: "bg-gradient-to-b from-white to-neutral-50/70 group-hover/tbv2:from-neutral-50/50 group-hover/tbv2:to-neutral-100/65 group-active/tbv2:from-neutral-100/70 group-active/tbv2:to-neutral-200/65 dark:from-white/[0.05] dark:to-white/[0.025] dark:group-hover/tbv2:from-white/[0.07] dark:group-hover/tbv2:to-white/[0.045] dark:group-active/tbv2:from-white/[0.10] dark:group-active/tbv2:to-white/[0.07]",
|
|
1135
|
+
ghost: "bg-transparent group-hover/tbv2:bg-neutral-100/70 group-active/tbv2:bg-neutral-200/70 dark:group-hover/tbv2:bg-white/10 dark:group-active/tbv2:bg-white/15",
|
|
1136
|
+
link: "bg-transparent"
|
|
1137
|
+
};
|
|
1138
|
+
var TIMBAL_V2_LABEL = {
|
|
1139
|
+
primary: "text-white dark:text-neutral-900",
|
|
1140
|
+
informative: "text-white",
|
|
1141
|
+
destructive: "text-destructive dark:text-red-400",
|
|
1142
|
+
secondary: "text-foreground",
|
|
1143
|
+
ghost: "text-foreground",
|
|
1144
|
+
link: "text-foreground underline decoration-black/25 underline-offset-2 group-hover/tbv2:decoration-black/45 dark:decoration-white/25 dark:group-hover/tbv2:decoration-white/45"
|
|
1145
|
+
};
|
|
1146
|
+
var TIMBAL_V2_BORDER = {
|
|
1147
|
+
primary: "",
|
|
1148
|
+
informative: "border border-white/15 dark:border-white/10",
|
|
1149
|
+
destructive: "border border-destructive/45 dark:border-red-500/55",
|
|
1150
|
+
secondary: "border border-neutral-200/80 dark:border-white/[0.08]",
|
|
1151
|
+
ghost: "",
|
|
1152
|
+
link: ""
|
|
1153
|
+
};
|
|
1154
|
+
var TIMBAL_V2_SHADOW = {
|
|
1155
|
+
primary: "shadow-sm shadow-black/15 dark:shadow-black/40",
|
|
1156
|
+
informative: "shadow-sm shadow-blue-900/20 dark:shadow-black/40",
|
|
1157
|
+
destructive: "shadow-[0_1px_2px_-0.5px_rgba(0,0,0,0.05)] dark:shadow-[0_1px_3px_rgba(0,0,0,0.22)]",
|
|
1158
|
+
secondary: "shadow-[0_1px_2px_-0.5px_rgba(0,0,0,0.05)] dark:shadow-[0_1px_3px_rgba(0,0,0,0.22)]",
|
|
1159
|
+
ghost: "",
|
|
1160
|
+
link: ""
|
|
1161
|
+
};
|
|
1162
|
+
var TIMBAL_V2_PILL_SURFACE = "bg-gradient-to-b from-white to-neutral-50/70 border border-neutral-200/80 shadow-[0_1px_2px_-0.5px_rgba(0,0,0,0.05)] dark:from-white/[0.05] dark:to-white/[0.025] dark:border-white/[0.08] dark:shadow-[0_1px_3px_rgba(0,0,0,0.22)]";
|
|
1163
|
+
var TIMBAL_V2_SECONDARY_CHROME = "bg-gradient-to-b from-white to-neutral-50/70 border border-neutral-200/80 shadow-[0_1px_2px_-0.5px_rgba(0,0,0,0.05)] transition-[background-color,box-shadow,border-color] duration-200 ease-in-out hover:from-neutral-50/40 hover:to-neutral-100/60 active:from-neutral-100/65 active:to-neutral-200/65 dark:from-white/[0.05] dark:to-white/[0.025] dark:border-white/[0.08] dark:shadow-[0_1px_3px_rgba(0,0,0,0.22)] dark:hover:from-white/[0.07] dark:hover:to-white/[0.045] dark:active:from-white/[0.10] dark:active:to-white/[0.07]";
|
|
1164
|
+
|
|
1165
|
+
// src/ui/timbal-v2-button.tsx
|
|
1166
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1167
|
+
var TimbalV2Button = React.forwardRef(function TimbalV2Button2({
|
|
1168
|
+
variant = "secondary",
|
|
1169
|
+
size = "sm",
|
|
1170
|
+
isIconOnly = false,
|
|
1171
|
+
isLoading = false,
|
|
1172
|
+
fullWidth = false,
|
|
578
1173
|
asChild = false,
|
|
1174
|
+
className,
|
|
1175
|
+
disabled,
|
|
1176
|
+
type = "button",
|
|
1177
|
+
children,
|
|
579
1178
|
...props
|
|
580
|
-
}) {
|
|
1179
|
+
}, ref) {
|
|
1180
|
+
const isDisabled = disabled || isLoading;
|
|
581
1181
|
const Comp = asChild ? Slot.Root : "button";
|
|
582
|
-
|
|
1182
|
+
const sizeClass = isIconOnly ? TIMBAL_V2_SIZE_ICON[size] : TIMBAL_V2_SIZE_HEIGHT[size];
|
|
1183
|
+
const radiusClass = variant === "link" || variant === "ghost" ? "rounded-md" : "rounded-full";
|
|
1184
|
+
return /* @__PURE__ */ jsxs3(
|
|
583
1185
|
Comp,
|
|
584
1186
|
{
|
|
585
|
-
|
|
1187
|
+
ref,
|
|
1188
|
+
type: asChild ? void 0 : type,
|
|
1189
|
+
disabled: asChild ? void 0 : isDisabled,
|
|
1190
|
+
"aria-disabled": asChild && isDisabled ? true : void 0,
|
|
1191
|
+
"data-slot": "timbal-v2-button",
|
|
586
1192
|
"data-variant": variant,
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
1193
|
+
className: cn(
|
|
1194
|
+
"group/tbv2 relative box-border inline-flex flex-col items-stretch overflow-hidden border-0 bg-transparent p-0 text-sm font-normal shadow-none transition duration-200 ease-in-out",
|
|
1195
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-400/60 focus-visible:ring-offset-1 focus-visible:ring-offset-background",
|
|
1196
|
+
sizeClass,
|
|
1197
|
+
radiusClass,
|
|
1198
|
+
TIMBAL_V2_BORDER[variant],
|
|
1199
|
+
TIMBAL_V2_SHADOW[variant],
|
|
1200
|
+
fullWidth && "w-full",
|
|
1201
|
+
isDisabled && "pointer-events-none opacity-50",
|
|
1202
|
+
className
|
|
1203
|
+
),
|
|
1204
|
+
...props,
|
|
1205
|
+
children: [
|
|
1206
|
+
/* @__PURE__ */ jsx5(
|
|
1207
|
+
"span",
|
|
1208
|
+
{
|
|
1209
|
+
"aria-hidden": true,
|
|
1210
|
+
className: cn(
|
|
1211
|
+
"pointer-events-none absolute inset-0 transition duration-200 ease-in-out",
|
|
1212
|
+
TIMBAL_V2_FILL[variant]
|
|
1213
|
+
)
|
|
1214
|
+
}
|
|
1215
|
+
),
|
|
1216
|
+
/* @__PURE__ */ jsx5(
|
|
1217
|
+
"span",
|
|
1218
|
+
{
|
|
1219
|
+
className: cn(
|
|
1220
|
+
"relative z-10 flex min-h-0 flex-1 items-center justify-center gap-1 leading-tight",
|
|
1221
|
+
!isIconOnly && TIMBAL_V2_SIZE_LABEL_PX[size],
|
|
1222
|
+
TIMBAL_V2_LABEL[variant]
|
|
1223
|
+
),
|
|
1224
|
+
children: isLoading ? /* @__PURE__ */ jsx5("span", { className: "size-4 animate-spin rounded-full border-2 border-current border-t-transparent" }) : children
|
|
1225
|
+
}
|
|
1226
|
+
)
|
|
1227
|
+
]
|
|
590
1228
|
}
|
|
591
1229
|
);
|
|
592
|
-
}
|
|
1230
|
+
});
|
|
593
1231
|
|
|
594
1232
|
// src/components/tooltip-icon-button.tsx
|
|
595
|
-
import { jsx as jsx6, jsxs as
|
|
596
|
-
var TooltipIconButton =
|
|
597
|
-
return /* @__PURE__ */
|
|
598
|
-
/* @__PURE__ */ jsx6(TooltipTrigger, { asChild: true, children: /* @__PURE__ */
|
|
599
|
-
|
|
600
|
-
{
|
|
601
|
-
|
|
602
|
-
size: "icon",
|
|
603
|
-
...rest,
|
|
604
|
-
className: cn("aui-button-icon size-6 p-1", className),
|
|
605
|
-
ref,
|
|
606
|
-
children: [
|
|
607
|
-
/* @__PURE__ */ jsx6(Slottable, { children }),
|
|
608
|
-
/* @__PURE__ */ jsx6("span", { className: "aui-sr-only sr-only", children: tooltip })
|
|
609
|
-
]
|
|
610
|
-
}
|
|
611
|
-
) }),
|
|
1233
|
+
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1234
|
+
var TooltipIconButton = forwardRef2(function TooltipIconButton2({ tooltip, side = "bottom", variant = "secondary", children, ...props }, ref) {
|
|
1235
|
+
return /* @__PURE__ */ jsxs4(Tooltip, { children: [
|
|
1236
|
+
/* @__PURE__ */ jsx6(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs4(TimbalV2Button, { ref, variant, size: "sm", isIconOnly: true, ...props, children: [
|
|
1237
|
+
children,
|
|
1238
|
+
/* @__PURE__ */ jsx6("span", { className: "sr-only", children: tooltip })
|
|
1239
|
+
] }) }),
|
|
612
1240
|
/* @__PURE__ */ jsx6(TooltipContent, { side, children: tooltip })
|
|
613
1241
|
] });
|
|
614
1242
|
});
|
|
615
|
-
TooltipIconButton.displayName = "TooltipIconButton";
|
|
616
1243
|
|
|
617
1244
|
// src/components/attachment.tsx
|
|
618
|
-
import { jsx as jsx7, jsxs as
|
|
1245
|
+
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
619
1246
|
var useFileSrc = (file) => {
|
|
620
1247
|
const [src, setSrc] = useState2(void 0);
|
|
621
1248
|
useEffect2(() => {
|
|
@@ -661,7 +1288,7 @@ var AttachmentPreview = ({ src }) => {
|
|
|
661
1288
|
var AttachmentPreviewDialog = ({ children }) => {
|
|
662
1289
|
const src = useAttachmentSrc();
|
|
663
1290
|
if (!src) return children;
|
|
664
|
-
return /* @__PURE__ */
|
|
1291
|
+
return /* @__PURE__ */ jsxs5(Dialog, { children: [
|
|
665
1292
|
/* @__PURE__ */ jsx7(
|
|
666
1293
|
DialogTrigger,
|
|
667
1294
|
{
|
|
@@ -670,7 +1297,7 @@ var AttachmentPreviewDialog = ({ children }) => {
|
|
|
670
1297
|
children
|
|
671
1298
|
}
|
|
672
1299
|
),
|
|
673
|
-
/* @__PURE__ */
|
|
1300
|
+
/* @__PURE__ */ jsxs5(DialogContent, { className: "aui-attachment-preview-dialog-content p-2 sm:max-w-3xl [&>button]:rounded-full [&>button]:bg-foreground/60 [&>button]:p-1 [&>button]:opacity-100 [&>button]:ring-0! [&_svg]:text-background [&>button]:hover:[&_svg]:text-destructive", children: [
|
|
674
1301
|
/* @__PURE__ */ jsx7(DialogTitle, { className: "aui-sr-only sr-only", children: "Image Attachment Preview" }),
|
|
675
1302
|
/* @__PURE__ */ jsx7("div", { className: "aui-attachment-preview relative mx-auto flex max-h-[80dvh] w-full items-center justify-center overflow-hidden bg-background", children: /* @__PURE__ */ jsx7(AttachmentPreview, { src }) })
|
|
676
1303
|
] })
|
|
@@ -679,7 +1306,7 @@ var AttachmentPreviewDialog = ({ children }) => {
|
|
|
679
1306
|
var AttachmentThumb = () => {
|
|
680
1307
|
const isImage = useAuiState((s) => s.attachment.type === "image");
|
|
681
1308
|
const src = useAttachmentSrc();
|
|
682
|
-
return /* @__PURE__ */
|
|
1309
|
+
return /* @__PURE__ */ jsxs5(Avatar, { className: "aui-attachment-tile-avatar h-full w-full rounded-none", children: [
|
|
683
1310
|
/* @__PURE__ */ jsx7(
|
|
684
1311
|
AvatarImage,
|
|
685
1312
|
{
|
|
@@ -708,8 +1335,8 @@ var AttachmentUI = () => {
|
|
|
708
1335
|
throw new Error(`Unknown attachment type: ${type}`);
|
|
709
1336
|
}
|
|
710
1337
|
});
|
|
711
|
-
return /* @__PURE__ */
|
|
712
|
-
/* @__PURE__ */
|
|
1338
|
+
return /* @__PURE__ */ jsxs5(Tooltip, { children: [
|
|
1339
|
+
/* @__PURE__ */ jsxs5(
|
|
713
1340
|
AttachmentPrimitive.Root,
|
|
714
1341
|
{
|
|
715
1342
|
className: cn(
|
|
@@ -765,33 +1392,1222 @@ var ComposerAddAttachment = () => {
|
|
|
765
1392
|
{
|
|
766
1393
|
tooltip: "Add Attachment",
|
|
767
1394
|
side: "bottom",
|
|
768
|
-
variant: "
|
|
769
|
-
|
|
770
|
-
className: "aui-composer-add-attachment size-8.5 rounded-full p-1 font-semibold text-xs hover:bg-muted-foreground/15 dark:border-muted-foreground/15 dark:hover:bg-muted-foreground/30",
|
|
1395
|
+
variant: "secondary",
|
|
1396
|
+
className: "aui-composer-add-attachment shrink-0 text-neutral-500 dark:text-muted-foreground",
|
|
771
1397
|
"aria-label": "Add Attachment",
|
|
772
|
-
children: /* @__PURE__ */ jsx7(PlusIcon, { className: "aui-attachment-add-icon size-
|
|
1398
|
+
children: /* @__PURE__ */ jsx7(PlusIcon, { className: "aui-attachment-add-icon size-4 stroke-[1.5]" })
|
|
1399
|
+
}
|
|
1400
|
+
) });
|
|
1401
|
+
};
|
|
1402
|
+
|
|
1403
|
+
// src/components/markdown-text.tsx
|
|
1404
|
+
import "@assistant-ui/react-markdown/styles/dot.css";
|
|
1405
|
+
import "katex/dist/katex.min.css";
|
|
1406
|
+
import {
|
|
1407
|
+
MarkdownTextPrimitive,
|
|
1408
|
+
unstable_memoizeMarkdownComponents as memoizeMarkdownComponents,
|
|
1409
|
+
useIsMarkdownCodeBlock
|
|
1410
|
+
} from "@assistant-ui/react-markdown";
|
|
1411
|
+
import remarkGfm from "remark-gfm";
|
|
1412
|
+
import remarkMath from "remark-math";
|
|
1413
|
+
import rehypeKatex from "rehype-katex";
|
|
1414
|
+
import { memo, useState as useState5 } from "react";
|
|
1415
|
+
import { CheckIcon as CheckIcon2, CopyIcon } from "lucide-react";
|
|
1416
|
+
|
|
1417
|
+
// src/components/syntax-highlighter.tsx
|
|
1418
|
+
import { useEffect as useEffect3, useState as useState4 } from "react";
|
|
1419
|
+
import { createHighlighterCore } from "shiki/core";
|
|
1420
|
+
import { createJavaScriptRegexEngine } from "shiki/engine/javascript";
|
|
1421
|
+
|
|
1422
|
+
// src/artifacts/registry.tsx
|
|
1423
|
+
import { createContext as createContext3, useContext as useContext3, useMemo as useMemo3 } from "react";
|
|
1424
|
+
|
|
1425
|
+
// src/artifacts/chart-artifact.tsx
|
|
1426
|
+
import { useMemo as useMemo2 } from "react";
|
|
1427
|
+
|
|
1428
|
+
// src/artifacts/artifact-card.tsx
|
|
1429
|
+
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1430
|
+
var ArtifactCard = ({ title, kind, className, bodyClassName, toolbar, children }) => {
|
|
1431
|
+
const hasHeader = Boolean(title || toolbar);
|
|
1432
|
+
return /* @__PURE__ */ jsxs6(
|
|
1433
|
+
"div",
|
|
1434
|
+
{
|
|
1435
|
+
className: cn(
|
|
1436
|
+
"aui-artifact-root my-3 overflow-hidden rounded-xl border border-border/60 bg-background shadow-sm",
|
|
1437
|
+
className
|
|
1438
|
+
),
|
|
1439
|
+
"data-artifact-kind": kind,
|
|
1440
|
+
children: [
|
|
1441
|
+
hasHeader && /* @__PURE__ */ jsxs6("div", { className: "aui-artifact-header flex items-center gap-2 border-b border-border/40 bg-muted/30 px-3 py-1.5", children: [
|
|
1442
|
+
title && /* @__PURE__ */ jsx8("span", { className: "aui-artifact-title flex-1 truncate text-xs font-semibold text-foreground/80", children: title }),
|
|
1443
|
+
!title && /* @__PURE__ */ jsx8("span", { className: "flex-1" }),
|
|
1444
|
+
toolbar
|
|
1445
|
+
] }),
|
|
1446
|
+
/* @__PURE__ */ jsx8("div", { className: cn("aui-artifact-body", bodyClassName), children })
|
|
1447
|
+
]
|
|
1448
|
+
}
|
|
1449
|
+
);
|
|
1450
|
+
};
|
|
1451
|
+
|
|
1452
|
+
// src/artifacts/chart-artifact.tsx
|
|
1453
|
+
import { Fragment, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1454
|
+
var ChartArtifactView = ({
|
|
1455
|
+
artifact
|
|
1456
|
+
}) => {
|
|
1457
|
+
const { type: _t, chartType = "bar", data = [] } = artifact;
|
|
1458
|
+
const xKey = artifact.xKey ?? inferXKey(data);
|
|
1459
|
+
const dataKeys = useMemo2(() => {
|
|
1460
|
+
if (Array.isArray(artifact.dataKey)) return artifact.dataKey;
|
|
1461
|
+
if (typeof artifact.dataKey === "string") return [artifact.dataKey];
|
|
1462
|
+
return inferDataKeys(data, xKey);
|
|
1463
|
+
}, [artifact.dataKey, data, xKey]);
|
|
1464
|
+
return /* @__PURE__ */ jsx9(ArtifactCard, { title: artifact.title, kind: "chart", children: /* @__PURE__ */ jsxs7("div", { className: "aui-artifact-chart p-3", children: [
|
|
1465
|
+
/* @__PURE__ */ jsx9(
|
|
1466
|
+
ChartSvg,
|
|
1467
|
+
{
|
|
1468
|
+
chartType,
|
|
1469
|
+
data,
|
|
1470
|
+
xKey,
|
|
1471
|
+
dataKeys,
|
|
1472
|
+
unit: artifact.unit
|
|
1473
|
+
}
|
|
1474
|
+
),
|
|
1475
|
+
dataKeys.length > 1 && /* @__PURE__ */ jsx9(Legend, { dataKeys })
|
|
1476
|
+
] }) });
|
|
1477
|
+
};
|
|
1478
|
+
var COLORS = [
|
|
1479
|
+
"var(--primary, #6366f1)",
|
|
1480
|
+
"#22c55e",
|
|
1481
|
+
"#f59e0b",
|
|
1482
|
+
"#ef4444",
|
|
1483
|
+
"#06b6d4",
|
|
1484
|
+
"#a855f7"
|
|
1485
|
+
];
|
|
1486
|
+
var W = 600;
|
|
1487
|
+
var H = 240;
|
|
1488
|
+
var PAD = { top: 12, right: 16, bottom: 28, left: 36 };
|
|
1489
|
+
var ChartSvg = ({ chartType, data, xKey, dataKeys, unit }) => {
|
|
1490
|
+
if (data.length === 0 || dataKeys.length === 0) {
|
|
1491
|
+
return /* @__PURE__ */ jsx9(EmptyState, {});
|
|
1492
|
+
}
|
|
1493
|
+
if (chartType === "pie") {
|
|
1494
|
+
return /* @__PURE__ */ jsx9(PieChart, { data, xKey, dataKey: dataKeys[0] });
|
|
1495
|
+
}
|
|
1496
|
+
const innerW = W - PAD.left - PAD.right;
|
|
1497
|
+
const innerH = H - PAD.top - PAD.bottom;
|
|
1498
|
+
const all = dataKeys.flatMap((k) => data.map((d) => toNum(d[k])));
|
|
1499
|
+
const maxV = Math.max(0, ...all);
|
|
1500
|
+
const minV = Math.min(0, ...all);
|
|
1501
|
+
const range = maxV - minV || 1;
|
|
1502
|
+
const yScale = (v) => PAD.top + innerH - (v - minV) / range * innerH;
|
|
1503
|
+
const xCount = data.length;
|
|
1504
|
+
const xStep = xCount > 1 ? innerW / (xCount - 1) : innerW;
|
|
1505
|
+
const xPos = (i) => chartType === "bar" ? PAD.left + innerW * (i + 0.5) / xCount : PAD.left + i * xStep;
|
|
1506
|
+
const ticks = niceTicks(minV, maxV);
|
|
1507
|
+
return /* @__PURE__ */ jsxs7(
|
|
1508
|
+
"svg",
|
|
1509
|
+
{
|
|
1510
|
+
viewBox: `0 0 ${W} ${H}`,
|
|
1511
|
+
className: "aui-artifact-chart-svg w-full",
|
|
1512
|
+
role: "img",
|
|
1513
|
+
"aria-label": "Chart",
|
|
1514
|
+
children: [
|
|
1515
|
+
ticks.map((t, i) => /* @__PURE__ */ jsxs7("g", { children: [
|
|
1516
|
+
/* @__PURE__ */ jsx9(
|
|
1517
|
+
"line",
|
|
1518
|
+
{
|
|
1519
|
+
x1: PAD.left,
|
|
1520
|
+
x2: W - PAD.right,
|
|
1521
|
+
y1: yScale(t),
|
|
1522
|
+
y2: yScale(t),
|
|
1523
|
+
stroke: "currentColor",
|
|
1524
|
+
strokeOpacity: 0.08
|
|
1525
|
+
}
|
|
1526
|
+
),
|
|
1527
|
+
/* @__PURE__ */ jsx9(
|
|
1528
|
+
"text",
|
|
1529
|
+
{
|
|
1530
|
+
x: PAD.left - 6,
|
|
1531
|
+
y: yScale(t),
|
|
1532
|
+
textAnchor: "end",
|
|
1533
|
+
dominantBaseline: "middle",
|
|
1534
|
+
className: "fill-muted-foreground text-[10px]",
|
|
1535
|
+
children: formatTick(t, unit)
|
|
1536
|
+
}
|
|
1537
|
+
)
|
|
1538
|
+
] }, i)),
|
|
1539
|
+
data.map((d, i) => /* @__PURE__ */ jsx9(
|
|
1540
|
+
"text",
|
|
1541
|
+
{
|
|
1542
|
+
x: xPos(i),
|
|
1543
|
+
y: H - PAD.bottom + 14,
|
|
1544
|
+
textAnchor: "middle",
|
|
1545
|
+
className: "fill-muted-foreground text-[10px]",
|
|
1546
|
+
children: String(d[xKey] ?? i)
|
|
1547
|
+
},
|
|
1548
|
+
i
|
|
1549
|
+
)),
|
|
1550
|
+
chartType === "bar" && renderBars({ data, dataKeys, xCount, xPos, yScale, minV, innerW }),
|
|
1551
|
+
chartType === "line" && dataKeys.map((k, ki) => /* @__PURE__ */ jsx9(
|
|
1552
|
+
Polyline,
|
|
1553
|
+
{
|
|
1554
|
+
data,
|
|
1555
|
+
dataKey: k,
|
|
1556
|
+
xPos,
|
|
1557
|
+
yScale,
|
|
1558
|
+
color: COLORS[ki % COLORS.length]
|
|
1559
|
+
},
|
|
1560
|
+
k
|
|
1561
|
+
)),
|
|
1562
|
+
chartType === "area" && dataKeys.map((k, ki) => /* @__PURE__ */ jsx9(
|
|
1563
|
+
Area,
|
|
1564
|
+
{
|
|
1565
|
+
data,
|
|
1566
|
+
dataKey: k,
|
|
1567
|
+
xPos,
|
|
1568
|
+
yScale,
|
|
1569
|
+
baseY: yScale(Math.max(0, minV)),
|
|
1570
|
+
color: COLORS[ki % COLORS.length]
|
|
1571
|
+
},
|
|
1572
|
+
k
|
|
1573
|
+
))
|
|
1574
|
+
]
|
|
1575
|
+
}
|
|
1576
|
+
);
|
|
1577
|
+
};
|
|
1578
|
+
function renderBars(args) {
|
|
1579
|
+
const { data, dataKeys, xCount, xPos, yScale, minV, innerW } = args;
|
|
1580
|
+
const groupWidth = innerW / xCount * 0.7;
|
|
1581
|
+
const barWidth = groupWidth / dataKeys.length;
|
|
1582
|
+
const baseY = yScale(Math.max(0, minV));
|
|
1583
|
+
return dataKeys.flatMap(
|
|
1584
|
+
(k, ki) => data.map((d, i) => {
|
|
1585
|
+
const v = toNum(d[k]);
|
|
1586
|
+
const y = yScale(v);
|
|
1587
|
+
const x = xPos(i) - groupWidth / 2 + ki * barWidth;
|
|
1588
|
+
const top = Math.min(y, baseY);
|
|
1589
|
+
const height = Math.abs(y - baseY);
|
|
1590
|
+
return /* @__PURE__ */ jsx9(
|
|
1591
|
+
"rect",
|
|
1592
|
+
{
|
|
1593
|
+
x,
|
|
1594
|
+
y: top,
|
|
1595
|
+
width: Math.max(1, barWidth - 2),
|
|
1596
|
+
height: Math.max(1, height),
|
|
1597
|
+
rx: 2,
|
|
1598
|
+
fill: COLORS[ki % COLORS.length]
|
|
1599
|
+
},
|
|
1600
|
+
`${k}-${i}`
|
|
1601
|
+
);
|
|
1602
|
+
})
|
|
1603
|
+
);
|
|
1604
|
+
}
|
|
1605
|
+
var Polyline = ({ data, dataKey, xPos, yScale, color }) => {
|
|
1606
|
+
const points = data.map((d, i) => `${xPos(i)},${yScale(toNum(d[dataKey]))}`).join(" ");
|
|
1607
|
+
return /* @__PURE__ */ jsx9(
|
|
1608
|
+
"polyline",
|
|
1609
|
+
{
|
|
1610
|
+
points,
|
|
1611
|
+
fill: "none",
|
|
1612
|
+
stroke: color,
|
|
1613
|
+
strokeWidth: 2,
|
|
1614
|
+
strokeLinejoin: "round",
|
|
1615
|
+
strokeLinecap: "round"
|
|
1616
|
+
}
|
|
1617
|
+
);
|
|
1618
|
+
};
|
|
1619
|
+
var Area = ({ data, dataKey, xPos, yScale, baseY, color }) => {
|
|
1620
|
+
if (data.length === 0) return null;
|
|
1621
|
+
const top = data.map((d, i) => `${xPos(i)},${yScale(toNum(d[dataKey]))}`).join(" ");
|
|
1622
|
+
const path = `M ${xPos(0)},${baseY} L ${top} L ${xPos(data.length - 1)},${baseY} Z`;
|
|
1623
|
+
return /* @__PURE__ */ jsxs7(Fragment, { children: [
|
|
1624
|
+
/* @__PURE__ */ jsx9("path", { d: path, fill: color, fillOpacity: 0.18 }),
|
|
1625
|
+
/* @__PURE__ */ jsx9(Polyline, { data, dataKey, xPos, yScale, color })
|
|
1626
|
+
] });
|
|
1627
|
+
};
|
|
1628
|
+
var PieChart = ({ data, xKey, dataKey }) => {
|
|
1629
|
+
const cx = W / 2;
|
|
1630
|
+
const cy = H / 2;
|
|
1631
|
+
const r = Math.min(W, H) / 2 - 16;
|
|
1632
|
+
const total = data.reduce((sum, d) => sum + toNum(d[dataKey]), 0) || 1;
|
|
1633
|
+
let acc = 0;
|
|
1634
|
+
return /* @__PURE__ */ jsx9(
|
|
1635
|
+
"svg",
|
|
1636
|
+
{
|
|
1637
|
+
viewBox: `0 0 ${W} ${H}`,
|
|
1638
|
+
className: "aui-artifact-chart-svg w-full",
|
|
1639
|
+
role: "img",
|
|
1640
|
+
"aria-label": "Pie chart",
|
|
1641
|
+
children: data.map((d, i) => {
|
|
1642
|
+
const value = toNum(d[dataKey]);
|
|
1643
|
+
const start = acc / total * Math.PI * 2;
|
|
1644
|
+
acc += value;
|
|
1645
|
+
const end = acc / total * Math.PI * 2;
|
|
1646
|
+
return /* @__PURE__ */ jsx9(
|
|
1647
|
+
PieSlice,
|
|
1648
|
+
{
|
|
1649
|
+
cx,
|
|
1650
|
+
cy,
|
|
1651
|
+
r,
|
|
1652
|
+
start,
|
|
1653
|
+
end,
|
|
1654
|
+
color: COLORS[i % COLORS.length],
|
|
1655
|
+
label: String(d[xKey] ?? i)
|
|
1656
|
+
},
|
|
1657
|
+
i
|
|
1658
|
+
);
|
|
1659
|
+
})
|
|
1660
|
+
}
|
|
1661
|
+
);
|
|
1662
|
+
};
|
|
1663
|
+
var PieSlice = ({ cx, cy, r, start, end, color, label }) => {
|
|
1664
|
+
const x1 = cx + Math.sin(start) * r;
|
|
1665
|
+
const y1 = cy - Math.cos(start) * r;
|
|
1666
|
+
const x2 = cx + Math.sin(end) * r;
|
|
1667
|
+
const y2 = cy - Math.cos(end) * r;
|
|
1668
|
+
const large = end - start > Math.PI ? 1 : 0;
|
|
1669
|
+
const path = `M ${cx} ${cy} L ${x1} ${y1} A ${r} ${r} 0 ${large} 1 ${x2} ${y2} Z`;
|
|
1670
|
+
const mid = (start + end) / 2;
|
|
1671
|
+
const lx = cx + Math.sin(mid) * (r * 0.65);
|
|
1672
|
+
const ly = cy - Math.cos(mid) * (r * 0.65);
|
|
1673
|
+
return /* @__PURE__ */ jsxs7("g", { children: [
|
|
1674
|
+
/* @__PURE__ */ jsx9("path", { d: path, fill: color, stroke: "var(--background, #fff)", strokeWidth: 1 }),
|
|
1675
|
+
/* @__PURE__ */ jsx9(
|
|
1676
|
+
"text",
|
|
1677
|
+
{
|
|
1678
|
+
x: lx,
|
|
1679
|
+
y: ly,
|
|
1680
|
+
textAnchor: "middle",
|
|
1681
|
+
dominantBaseline: "middle",
|
|
1682
|
+
className: "fill-white text-[10px] font-semibold",
|
|
1683
|
+
children: label
|
|
1684
|
+
}
|
|
1685
|
+
)
|
|
1686
|
+
] });
|
|
1687
|
+
};
|
|
1688
|
+
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__ */ jsxs7("span", { className: "inline-flex items-center gap-1.5", children: [
|
|
1689
|
+
/* @__PURE__ */ jsx9(
|
|
1690
|
+
"span",
|
|
1691
|
+
{
|
|
1692
|
+
className: "inline-block size-2 rounded-sm",
|
|
1693
|
+
style: { background: COLORS[i % COLORS.length] },
|
|
1694
|
+
"aria-hidden": true
|
|
1695
|
+
}
|
|
1696
|
+
),
|
|
1697
|
+
k
|
|
1698
|
+
] }, k)) });
|
|
1699
|
+
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" });
|
|
1700
|
+
function inferXKey(data) {
|
|
1701
|
+
if (data.length === 0) return "x";
|
|
1702
|
+
for (const k of Object.keys(data[0])) {
|
|
1703
|
+
if (typeof data[0][k] !== "number") return k;
|
|
1704
|
+
}
|
|
1705
|
+
return Object.keys(data[0])[0] ?? "x";
|
|
1706
|
+
}
|
|
1707
|
+
function inferDataKeys(data, xKey) {
|
|
1708
|
+
if (data.length === 0) return [];
|
|
1709
|
+
return Object.keys(data[0]).filter(
|
|
1710
|
+
(k) => k !== xKey && typeof data[0][k] === "number"
|
|
1711
|
+
);
|
|
1712
|
+
}
|
|
1713
|
+
function toNum(value) {
|
|
1714
|
+
const n = typeof value === "number" ? value : Number(value);
|
|
1715
|
+
return Number.isFinite(n) ? n : 0;
|
|
1716
|
+
}
|
|
1717
|
+
function niceTicks(min, max, count = 4) {
|
|
1718
|
+
if (max === min) return [min];
|
|
1719
|
+
const range = max - min;
|
|
1720
|
+
const step = niceStep(range / count);
|
|
1721
|
+
const start = Math.floor(min / step) * step;
|
|
1722
|
+
const out = [];
|
|
1723
|
+
for (let v = start; v <= max + step / 2; v += step) {
|
|
1724
|
+
out.push(round(v));
|
|
1725
|
+
}
|
|
1726
|
+
return out;
|
|
1727
|
+
}
|
|
1728
|
+
function niceStep(raw) {
|
|
1729
|
+
const exp = Math.floor(Math.log10(Math.abs(raw))) || 0;
|
|
1730
|
+
const base = Math.pow(10, exp);
|
|
1731
|
+
const norm = raw / base;
|
|
1732
|
+
let nice = 1;
|
|
1733
|
+
if (norm >= 5) nice = 5;
|
|
1734
|
+
else if (norm >= 2) nice = 2;
|
|
1735
|
+
return nice * base;
|
|
1736
|
+
}
|
|
1737
|
+
function round(v) {
|
|
1738
|
+
return Math.round(v * 1e6) / 1e6;
|
|
1739
|
+
}
|
|
1740
|
+
function formatTick(v, unit) {
|
|
1741
|
+
const s = Math.abs(v) >= 1e3 ? `${(v / 1e3).toFixed(1)}k` : String(round(v));
|
|
1742
|
+
return unit ? `${s}${unit}` : s;
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
// src/artifacts/question-artifact.tsx
|
|
1746
|
+
import { useCallback as useCallback2, useState as useState3 } from "react";
|
|
1747
|
+
import { useThreadRuntime } from "@assistant-ui/react";
|
|
1748
|
+
import { CheckIcon } from "lucide-react";
|
|
1749
|
+
|
|
1750
|
+
// src/ui/chrome.ts
|
|
1751
|
+
var STUDIO_TOPBAR_GAP = "0.5rem";
|
|
1752
|
+
var STUDIO_TOPBAR_HEIGHT = "3rem";
|
|
1753
|
+
var STUDIO_PILL_HEIGHT = "2.5rem";
|
|
1754
|
+
var STUDIO_SIDEBAR_GAP = "0.5rem";
|
|
1755
|
+
var STUDIO_SIDEBAR_WIDTH = "3rem";
|
|
1756
|
+
var STUDIO_INSET_LEFT = `calc(${STUDIO_SIDEBAR_GAP} + ${STUDIO_SIDEBAR_WIDTH})`;
|
|
1757
|
+
var studioChromeShellStyle = {
|
|
1758
|
+
"--studio-topbar-gap": STUDIO_TOPBAR_GAP,
|
|
1759
|
+
"--studio-topbar-height": STUDIO_TOPBAR_HEIGHT,
|
|
1760
|
+
"--studio-chrome-pill-height": STUDIO_PILL_HEIGHT,
|
|
1761
|
+
"--studio-inset-top": `calc(${STUDIO_TOPBAR_GAP} + ${STUDIO_TOPBAR_HEIGHT})`,
|
|
1762
|
+
"--studio-sidebar-gap": STUDIO_SIDEBAR_GAP,
|
|
1763
|
+
"--studio-sidebar-width": STUDIO_SIDEBAR_WIDTH,
|
|
1764
|
+
"--studio-inset-left": STUDIO_INSET_LEFT
|
|
1765
|
+
};
|
|
1766
|
+
var studioTopbarPillHeightClass = "h-[var(--studio-chrome-pill-height)] min-h-[var(--studio-chrome-pill-height)]";
|
|
1767
|
+
var studioTopbarIconPillClass = "shrink-0 flex-none size-[var(--studio-chrome-pill-height)] min-h-[var(--studio-chrome-pill-height)] min-w-[var(--studio-chrome-pill-height)]";
|
|
1768
|
+
var studioPlaygroundGradientClass = "bg-gradient-to-b from-neutral-200/60 via-neutral-100/30 to-background dark:from-zinc-800 dark:via-zinc-900 dark:to-zinc-950";
|
|
1769
|
+
var studioComposeInputShellClass = "flex w-full flex-col rounded-2xl border border-neutral-200/60 bg-background shadow-lg shadow-black/5 outline-none transition-[box-shadow,border-color] focus-within:border-neutral-400/80 focus-within:ring-2 focus-within:ring-foreground/5 focus-within:shadow-xl focus-within:shadow-black/10 dark:border-white/12 dark:bg-zinc-900 dark:shadow-black/20 dark:focus-within:border-white/22 dark:focus-within:ring-0";
|
|
1770
|
+
var studioPillSurfaceClass = TIMBAL_V2_PILL_SURFACE;
|
|
1771
|
+
var studioSecondaryChromeClass = TIMBAL_V2_SECONDARY_CHROME;
|
|
1772
|
+
var studioIntegrationSurfaceSolid = "bg-white bg-gradient-to-b from-white to-neutral-50/70 shadow-[0_1px_2px_-0.5px_rgba(0,0,0,0.05)] dark:bg-zinc-900 dark:from-white/[0.05] dark:to-white/[0.025] dark:shadow-[0_1px_3px_rgba(0,0,0,0.22)]";
|
|
1773
|
+
var studioIntegrationBorder = "border border-neutral-200/80 dark:border-white/[0.08]";
|
|
1774
|
+
var studioIntegrationCardClass = cn(
|
|
1775
|
+
"rounded-xl",
|
|
1776
|
+
studioIntegrationSurfaceSolid,
|
|
1777
|
+
studioIntegrationBorder
|
|
1778
|
+
);
|
|
1779
|
+
var studioIntegrationIconTileClass = cn(
|
|
1780
|
+
"flex size-9 shrink-0 items-center justify-center rounded-lg",
|
|
1781
|
+
studioIntegrationSurfaceSolid,
|
|
1782
|
+
studioIntegrationBorder
|
|
1783
|
+
);
|
|
1784
|
+
var studioListRowButtonClass = cn(
|
|
1785
|
+
"flex w-full cursor-pointer items-center gap-3 rounded-xl px-3 py-2.5 text-left",
|
|
1786
|
+
studioIntegrationCardClass,
|
|
1787
|
+
"transition-[background-color,box-shadow,border-color] duration-200 ease-in-out",
|
|
1788
|
+
"hover:border-neutral-300 dark:hover:border-white/15",
|
|
1789
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2 focus-visible:ring-offset-background dark:focus-visible:ring-white/20"
|
|
1790
|
+
);
|
|
1791
|
+
var studioComposerIoWellClass = cn(
|
|
1792
|
+
"rounded-lg",
|
|
1793
|
+
studioIntegrationSurfaceSolid,
|
|
1794
|
+
studioIntegrationBorder
|
|
1795
|
+
);
|
|
1796
|
+
var studioToolCardShellClass = cn(
|
|
1797
|
+
studioIntegrationCardClass,
|
|
1798
|
+
"my-2 min-h-0 overflow-hidden"
|
|
1799
|
+
);
|
|
1800
|
+
var studioTimelineRowButtonClass = "group flex w-full min-w-0 cursor-pointer items-center justify-start rounded-md border-0 bg-transparent py-1 text-left shadow-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2";
|
|
1801
|
+
var studioTimelineTextClass = "text-xs font-normal leading-snug";
|
|
1802
|
+
var studioTimelineActionClass = cn(
|
|
1803
|
+
studioTimelineTextClass,
|
|
1804
|
+
"shrink-0 text-foreground/70 transition-colors duration-150 group-hover:text-foreground/80"
|
|
1805
|
+
);
|
|
1806
|
+
var studioTimelineShimmerActionClass = cn(
|
|
1807
|
+
studioTimelineTextClass,
|
|
1808
|
+
"shrink-0"
|
|
1809
|
+
);
|
|
1810
|
+
var studioTimelineDetailClass = cn(
|
|
1811
|
+
studioTimelineTextClass,
|
|
1812
|
+
"min-w-0 truncate text-muted-foreground transition-colors duration-150"
|
|
1813
|
+
);
|
|
1814
|
+
function studioTimelineChevronClass(expanded) {
|
|
1815
|
+
return cn(
|
|
1816
|
+
"ml-0.5 size-3 min-h-3 min-w-3 shrink-0 transition-all duration-150",
|
|
1817
|
+
expanded ? "rotate-90 text-foreground opacity-60" : "text-muted-foreground opacity-0 group-hover:opacity-70"
|
|
1818
|
+
);
|
|
1819
|
+
}
|
|
1820
|
+
var studioTimelineBodyPadClass = "flex flex-col gap-2 pt-0.5 pb-0.5";
|
|
1821
|
+
var studioArtifactShellClass = cn(
|
|
1822
|
+
studioIntegrationCardClass,
|
|
1823
|
+
"my-2 w-full min-w-0 overflow-hidden"
|
|
1824
|
+
);
|
|
1825
|
+
var studioQuestionOptionClass = "flex w-full items-center gap-2 rounded-lg border border-transparent px-2 py-1.5 text-left text-sm transition-[background-color,border-color,box-shadow] duration-200 hover:bg-neutral-100/80 dark:hover:bg-white/[0.05] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2";
|
|
1826
|
+
var studioQuestionOptionSelectedClass = cn(
|
|
1827
|
+
studioQuestionOptionClass,
|
|
1828
|
+
"border-neutral-200/80 bg-neutral-50/90 ring-1 ring-foreground/10 dark:border-white/[0.12] dark:bg-white/[0.06] dark:ring-white/10"
|
|
1829
|
+
);
|
|
1830
|
+
|
|
1831
|
+
// src/artifacts/question-artifact.tsx
|
|
1832
|
+
import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1833
|
+
function optionKey(option, index) {
|
|
1834
|
+
const id = option.id?.trim();
|
|
1835
|
+
return id ? id : `__option-${index}`;
|
|
1836
|
+
}
|
|
1837
|
+
var OptionRadio = ({ selected }) => /* @__PURE__ */ jsx10(
|
|
1838
|
+
"span",
|
|
1839
|
+
{
|
|
1840
|
+
className: cn(
|
|
1841
|
+
"flex size-4 shrink-0 items-center justify-center rounded-full border-2 transition-colors",
|
|
1842
|
+
selected ? "border-foreground bg-foreground text-background" : "border-neutral-300 bg-background dark:border-white/20"
|
|
1843
|
+
),
|
|
1844
|
+
"aria-hidden": true,
|
|
1845
|
+
children: selected ? /* @__PURE__ */ jsx10(CheckIcon, { className: "size-2.5 stroke-[3]" }) : null
|
|
1846
|
+
}
|
|
1847
|
+
);
|
|
1848
|
+
var QuestionArtifactView = ({
|
|
1849
|
+
artifact
|
|
1850
|
+
}) => {
|
|
1851
|
+
const runtime = useThreadRuntime();
|
|
1852
|
+
const [selected, setSelected] = useState3([]);
|
|
1853
|
+
const [submittedIds, setSubmittedIds] = useState3(null);
|
|
1854
|
+
const isMulti = artifact.multi === true;
|
|
1855
|
+
const isDisabled = submittedIds !== null;
|
|
1856
|
+
const send = useCallback2(
|
|
1857
|
+
(keys) => {
|
|
1858
|
+
if (keys.length === 0) return;
|
|
1859
|
+
const labels = artifact.options.map((option, index) => ({ option, key: optionKey(option, index) })).filter(({ key }) => keys.includes(key)).map(({ option }) => option.label);
|
|
1860
|
+
setSubmittedIds(keys);
|
|
1861
|
+
runtime.append({
|
|
1862
|
+
role: "user",
|
|
1863
|
+
content: [{ type: "text", text: labels.join(", ") }]
|
|
1864
|
+
});
|
|
1865
|
+
},
|
|
1866
|
+
[artifact.options, runtime]
|
|
1867
|
+
);
|
|
1868
|
+
const onPick = useCallback2(
|
|
1869
|
+
(key) => {
|
|
1870
|
+
if (isDisabled) return;
|
|
1871
|
+
if (!isMulti) {
|
|
1872
|
+
send([key]);
|
|
1873
|
+
return;
|
|
1874
|
+
}
|
|
1875
|
+
setSelected(
|
|
1876
|
+
(prev) => prev.includes(key) ? prev.filter((id) => id !== key) : [...prev, key]
|
|
1877
|
+
);
|
|
1878
|
+
},
|
|
1879
|
+
[isDisabled, isMulti, send]
|
|
1880
|
+
);
|
|
1881
|
+
const onConfirm = useCallback2(() => {
|
|
1882
|
+
send(selected);
|
|
1883
|
+
}, [selected, send]);
|
|
1884
|
+
return /* @__PURE__ */ jsx10("div", { className: studioArtifactShellClass, "data-artifact-kind": "question", children: /* @__PURE__ */ jsxs8("div", { className: "px-2.5 py-2", children: [
|
|
1885
|
+
artifact.prompt ? /* @__PURE__ */ jsx10("p", { className: "mb-2 text-sm font-normal leading-snug text-foreground", children: artifact.prompt }) : null,
|
|
1886
|
+
/* @__PURE__ */ jsx10("div", { className: "flex flex-col gap-0.5", role: "list", children: artifact.options.map((option, index) => {
|
|
1887
|
+
const key = optionKey(option, index);
|
|
1888
|
+
const isSelected = submittedIds ? submittedIds.includes(key) : isMulti && selected.includes(key);
|
|
1889
|
+
return /* @__PURE__ */ jsxs8(
|
|
1890
|
+
"button",
|
|
1891
|
+
{
|
|
1892
|
+
type: "button",
|
|
1893
|
+
role: "listitem",
|
|
1894
|
+
disabled: isDisabled,
|
|
1895
|
+
onClick: () => onPick(key),
|
|
1896
|
+
className: cn(
|
|
1897
|
+
isSelected ? studioQuestionOptionSelectedClass : studioQuestionOptionClass,
|
|
1898
|
+
isDisabled && (isSelected ? "cursor-default" : "cursor-not-allowed opacity-50")
|
|
1899
|
+
),
|
|
1900
|
+
children: [
|
|
1901
|
+
/* @__PURE__ */ jsx10(OptionRadio, { selected: isSelected }),
|
|
1902
|
+
/* @__PURE__ */ jsxs8("span", { className: "min-w-0 flex-1 text-left", children: [
|
|
1903
|
+
/* @__PURE__ */ jsx10("span", { className: "block font-normal text-foreground", children: option.label }),
|
|
1904
|
+
option.description ? /* @__PURE__ */ jsx10("span", { className: "mt-0.5 block text-xs text-muted-foreground", children: option.description }) : null
|
|
1905
|
+
] })
|
|
1906
|
+
]
|
|
1907
|
+
},
|
|
1908
|
+
key
|
|
1909
|
+
);
|
|
1910
|
+
}) }),
|
|
1911
|
+
isMulti && !submittedIds ? /* @__PURE__ */ jsx10("div", { className: "mt-2 flex justify-end", children: /* @__PURE__ */ jsx10(
|
|
1912
|
+
TimbalV2Button,
|
|
1913
|
+
{
|
|
1914
|
+
type: "button",
|
|
1915
|
+
variant: "primary",
|
|
1916
|
+
size: "sm",
|
|
1917
|
+
disabled: selected.length === 0,
|
|
1918
|
+
onClick: onConfirm,
|
|
1919
|
+
children: "Confirm"
|
|
1920
|
+
}
|
|
1921
|
+
) }) : null
|
|
1922
|
+
] }) });
|
|
1923
|
+
};
|
|
1924
|
+
|
|
1925
|
+
// src/artifacts/html-artifact.tsx
|
|
1926
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
1927
|
+
var HtmlArtifactView = ({ artifact }) => {
|
|
1928
|
+
const sandboxed = artifact.sandboxed !== false;
|
|
1929
|
+
const sandbox = sandboxed ? "allow-scripts allow-same-origin allow-forms allow-modals allow-popups allow-pointer-lock" : void 0;
|
|
1930
|
+
const height = artifact.height ?? "320px";
|
|
1931
|
+
return /* @__PURE__ */ jsx11(ArtifactCard, { title: artifact.title, kind: "html", children: /* @__PURE__ */ jsx11(
|
|
1932
|
+
"iframe",
|
|
1933
|
+
{
|
|
1934
|
+
title: artifact.title ?? "HTML artifact",
|
|
1935
|
+
srcDoc: artifact.content,
|
|
1936
|
+
sandbox,
|
|
1937
|
+
className: "aui-artifact-html w-full border-0 bg-background",
|
|
1938
|
+
style: { height }
|
|
1939
|
+
}
|
|
1940
|
+
) });
|
|
1941
|
+
};
|
|
1942
|
+
|
|
1943
|
+
// src/artifacts/json-artifact.tsx
|
|
1944
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
1945
|
+
var JsonArtifactView = ({
|
|
1946
|
+
artifact
|
|
1947
|
+
}) => {
|
|
1948
|
+
const data = "data" in artifact ? artifact.data : artifact;
|
|
1949
|
+
const title = artifact.title;
|
|
1950
|
+
let body;
|
|
1951
|
+
try {
|
|
1952
|
+
body = JSON.stringify(data, null, 2);
|
|
1953
|
+
} catch {
|
|
1954
|
+
body = String(data);
|
|
1955
|
+
}
|
|
1956
|
+
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 }) });
|
|
1957
|
+
};
|
|
1958
|
+
|
|
1959
|
+
// src/artifacts/table-artifact.tsx
|
|
1960
|
+
import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1961
|
+
var TableArtifactView = ({ artifact }) => {
|
|
1962
|
+
const rows = artifact.rows ?? [];
|
|
1963
|
+
const columns = artifact.columns ?? deriveColumns(rows);
|
|
1964
|
+
return /* @__PURE__ */ jsx13(ArtifactCard, { title: artifact.title, kind: "table", children: /* @__PURE__ */ jsx13("div", { className: "aui-artifact-table-wrap overflow-x-auto", children: /* @__PURE__ */ jsxs9("table", { className: "aui-artifact-table w-full border-collapse text-sm", children: [
|
|
1965
|
+
/* @__PURE__ */ jsx13("thead", { children: /* @__PURE__ */ jsx13("tr", { className: "border-b border-border/40 bg-muted/20", children: columns.map((col) => /* @__PURE__ */ jsx13(
|
|
1966
|
+
"th",
|
|
1967
|
+
{
|
|
1968
|
+
className: "px-3 py-2 text-left text-xs font-semibold uppercase tracking-wider text-muted-foreground",
|
|
1969
|
+
children: col.label ?? col.key
|
|
1970
|
+
},
|
|
1971
|
+
col.key
|
|
1972
|
+
)) }) }),
|
|
1973
|
+
/* @__PURE__ */ jsx13("tbody", { children: rows.map((row, i) => /* @__PURE__ */ jsx13(
|
|
1974
|
+
"tr",
|
|
1975
|
+
{
|
|
1976
|
+
className: "border-b border-border/30 transition-colors last:border-b-0 hover:bg-muted/20",
|
|
1977
|
+
children: columns.map((col) => /* @__PURE__ */ jsx13(
|
|
1978
|
+
"td",
|
|
1979
|
+
{
|
|
1980
|
+
className: "px-3 py-2 align-top text-foreground/85",
|
|
1981
|
+
children: formatCell(row[col.key])
|
|
1982
|
+
},
|
|
1983
|
+
col.key
|
|
1984
|
+
))
|
|
1985
|
+
},
|
|
1986
|
+
i
|
|
1987
|
+
)) })
|
|
1988
|
+
] }) }) });
|
|
1989
|
+
};
|
|
1990
|
+
function deriveColumns(rows) {
|
|
1991
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1992
|
+
const cols = [];
|
|
1993
|
+
for (const row of rows) {
|
|
1994
|
+
for (const key of Object.keys(row)) {
|
|
1995
|
+
if (!seen.has(key)) {
|
|
1996
|
+
seen.add(key);
|
|
1997
|
+
cols.push({ key });
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
return cols;
|
|
2002
|
+
}
|
|
2003
|
+
function formatCell(value) {
|
|
2004
|
+
if (value === null || value === void 0) return "";
|
|
2005
|
+
if (typeof value === "object") return JSON.stringify(value);
|
|
2006
|
+
return String(value);
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
// src/artifacts/ui/ui-artifact.tsx
|
|
2010
|
+
import { useReducer } from "react";
|
|
2011
|
+
|
|
2012
|
+
// src/artifacts/ui/types.ts
|
|
2013
|
+
function isUiBinding(value) {
|
|
2014
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.$bind === "string";
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
// src/artifacts/ui/state.ts
|
|
2018
|
+
function uiStateReducer(state, action) {
|
|
2019
|
+
switch (action.type) {
|
|
2020
|
+
case "set":
|
|
2021
|
+
return setPath(state, action.path, action.value);
|
|
2022
|
+
case "toggle": {
|
|
2023
|
+
const current = getPath(state, action.path);
|
|
2024
|
+
return setPath(state, action.path, !current);
|
|
2025
|
+
}
|
|
2026
|
+
case "replace":
|
|
2027
|
+
return action.state;
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
function getPath(state, path) {
|
|
2031
|
+
if (!path) return void 0;
|
|
2032
|
+
const parts = path.split(".");
|
|
2033
|
+
let cursor = state;
|
|
2034
|
+
for (const part of parts) {
|
|
2035
|
+
if (typeof cursor !== "object" || cursor === null) return void 0;
|
|
2036
|
+
cursor = cursor[part];
|
|
2037
|
+
}
|
|
2038
|
+
return cursor;
|
|
2039
|
+
}
|
|
2040
|
+
function setPath(state, path, value) {
|
|
2041
|
+
if (!path) return state;
|
|
2042
|
+
const parts = path.split(".");
|
|
2043
|
+
const next = { ...state };
|
|
2044
|
+
let cursor = next;
|
|
2045
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
2046
|
+
const key = parts[i];
|
|
2047
|
+
const child = cursor[key];
|
|
2048
|
+
const cloned = typeof child === "object" && child !== null && !Array.isArray(child) ? { ...child } : {};
|
|
2049
|
+
cursor[key] = cloned;
|
|
2050
|
+
cursor = cloned;
|
|
2051
|
+
}
|
|
2052
|
+
cursor[parts[parts.length - 1]] = value;
|
|
2053
|
+
return next;
|
|
2054
|
+
}
|
|
2055
|
+
function resolveBindable(value, state) {
|
|
2056
|
+
if (isUiBinding(value)) {
|
|
2057
|
+
return getPath(state, value.$bind);
|
|
2058
|
+
}
|
|
2059
|
+
return value;
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
// src/artifacts/ui/registry.tsx
|
|
2063
|
+
import {
|
|
2064
|
+
createContext as createContext2,
|
|
2065
|
+
useContext as useContext2
|
|
2066
|
+
} from "react";
|
|
2067
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
2068
|
+
var UiStateContext = createContext2({});
|
|
2069
|
+
var UiDispatchContext = createContext2(() => {
|
|
2070
|
+
});
|
|
2071
|
+
var UiStateProvider = ({ state, dispatch, children }) => /* @__PURE__ */ jsx14(UiStateContext.Provider, { value: state, children: /* @__PURE__ */ jsx14(UiDispatchContext.Provider, { value: dispatch, children }) });
|
|
2072
|
+
function useUiState() {
|
|
2073
|
+
return useContext2(UiStateContext);
|
|
2074
|
+
}
|
|
2075
|
+
function useUiDispatch() {
|
|
2076
|
+
return useContext2(UiDispatchContext);
|
|
2077
|
+
}
|
|
2078
|
+
var UiEventContext = createContext2(
|
|
2079
|
+
null
|
|
2080
|
+
);
|
|
2081
|
+
var UiEventProvider = ({ onEvent, children }) => /* @__PURE__ */ jsx14(UiEventContext.Provider, { value: onEvent, children });
|
|
2082
|
+
function useUiEventEmitter() {
|
|
2083
|
+
return useContext2(UiEventContext);
|
|
2084
|
+
}
|
|
2085
|
+
var UiCustomNodeRegistryContext = createContext2({});
|
|
2086
|
+
var UiCustomNodeRegistryProvider = ({ renderers, children }) => /* @__PURE__ */ jsx14(UiCustomNodeRegistryContext.Provider, { value: renderers, children });
|
|
2087
|
+
function useUiCustomNodeRegistry() {
|
|
2088
|
+
return useContext2(UiCustomNodeRegistryContext);
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
// src/artifacts/ui/nodes.tsx
|
|
2092
|
+
import { useCallback as useCallback3 } from "react";
|
|
2093
|
+
import { motion } from "motion/react";
|
|
2094
|
+
import { useThreadRuntime as useThreadRuntime2 } from "@assistant-ui/react";
|
|
2095
|
+
|
|
2096
|
+
// src/ui/button.tsx
|
|
2097
|
+
import { cva } from "class-variance-authority";
|
|
2098
|
+
import { Slot as Slot2 } from "radix-ui";
|
|
2099
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
2100
|
+
var buttonVariants = cva(
|
|
2101
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
2102
|
+
{
|
|
2103
|
+
variants: {
|
|
2104
|
+
variant: {
|
|
2105
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
2106
|
+
destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
|
2107
|
+
outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
|
2108
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
2109
|
+
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
2110
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
2111
|
+
},
|
|
2112
|
+
size: {
|
|
2113
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
2114
|
+
xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
2115
|
+
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
|
2116
|
+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
2117
|
+
icon: "size-9",
|
|
2118
|
+
"icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
|
|
2119
|
+
"icon-sm": "size-8",
|
|
2120
|
+
"icon-lg": "size-10"
|
|
2121
|
+
}
|
|
2122
|
+
},
|
|
2123
|
+
defaultVariants: {
|
|
2124
|
+
variant: "default",
|
|
2125
|
+
size: "default"
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
);
|
|
2129
|
+
function Button({
|
|
2130
|
+
className,
|
|
2131
|
+
variant = "default",
|
|
2132
|
+
size = "default",
|
|
2133
|
+
asChild = false,
|
|
2134
|
+
...props
|
|
2135
|
+
}) {
|
|
2136
|
+
const Comp = asChild ? Slot2.Root : "button";
|
|
2137
|
+
return /* @__PURE__ */ jsx15(
|
|
2138
|
+
Comp,
|
|
2139
|
+
{
|
|
2140
|
+
"data-slot": "button",
|
|
2141
|
+
"data-variant": variant,
|
|
2142
|
+
"data-size": size,
|
|
2143
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
2144
|
+
...props
|
|
2145
|
+
}
|
|
2146
|
+
);
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
// src/artifacts/ui/nodes.tsx
|
|
2150
|
+
import { jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2151
|
+
var UiNodeView = ({ node }) => {
|
|
2152
|
+
switch (node.kind) {
|
|
2153
|
+
case "box":
|
|
2154
|
+
return /* @__PURE__ */ jsx16(BoxNode, { node });
|
|
2155
|
+
case "text":
|
|
2156
|
+
return /* @__PURE__ */ jsx16(TextNode, { node });
|
|
2157
|
+
case "heading":
|
|
2158
|
+
return /* @__PURE__ */ jsx16(HeadingNode, { node });
|
|
2159
|
+
case "badge":
|
|
2160
|
+
return /* @__PURE__ */ jsx16(BadgeNode, { node });
|
|
2161
|
+
case "button":
|
|
2162
|
+
return /* @__PURE__ */ jsx16(ButtonNode, { node });
|
|
2163
|
+
case "toggle":
|
|
2164
|
+
return /* @__PURE__ */ jsx16(ToggleNode, { node });
|
|
2165
|
+
case "slider":
|
|
2166
|
+
return /* @__PURE__ */ jsx16(SliderNode, { node });
|
|
2167
|
+
case "tooltip":
|
|
2168
|
+
return /* @__PURE__ */ jsx16(TooltipNode, { node });
|
|
2169
|
+
case "draggable":
|
|
2170
|
+
return /* @__PURE__ */ jsx16(DraggableNode, { node });
|
|
2171
|
+
case "custom":
|
|
2172
|
+
return /* @__PURE__ */ jsx16(CustomNode, { node });
|
|
2173
|
+
default:
|
|
2174
|
+
return null;
|
|
2175
|
+
}
|
|
2176
|
+
};
|
|
2177
|
+
function useActionRunner() {
|
|
2178
|
+
const state = useUiState();
|
|
2179
|
+
const dispatch = useUiDispatch();
|
|
2180
|
+
const runtime = useThreadRuntime2();
|
|
2181
|
+
const emit = useUiEventEmitter();
|
|
2182
|
+
return useCallback3(
|
|
2183
|
+
(actions) => {
|
|
2184
|
+
if (!actions) return;
|
|
2185
|
+
const list = Array.isArray(actions) ? actions : [actions];
|
|
2186
|
+
for (const action of list) {
|
|
2187
|
+
switch (action.kind) {
|
|
2188
|
+
case "message": {
|
|
2189
|
+
const text = resolveBindable(action.text, state);
|
|
2190
|
+
if (typeof text === "string" && text.length > 0) {
|
|
2191
|
+
runtime?.append({
|
|
2192
|
+
role: "user",
|
|
2193
|
+
content: [{ type: "text", text }]
|
|
2194
|
+
});
|
|
2195
|
+
}
|
|
2196
|
+
break;
|
|
2197
|
+
}
|
|
2198
|
+
case "set": {
|
|
2199
|
+
const value = resolveBindable(action.value, state);
|
|
2200
|
+
dispatch({ type: "set", path: action.path, value });
|
|
2201
|
+
break;
|
|
2202
|
+
}
|
|
2203
|
+
case "toggle": {
|
|
2204
|
+
dispatch({ type: "toggle", path: action.path });
|
|
2205
|
+
break;
|
|
2206
|
+
}
|
|
2207
|
+
case "emit": {
|
|
2208
|
+
emit?.({ name: action.name, payload: action.payload });
|
|
2209
|
+
break;
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
},
|
|
2214
|
+
[state, dispatch, runtime, emit]
|
|
2215
|
+
);
|
|
2216
|
+
}
|
|
2217
|
+
var ALIGN_CLS = {
|
|
2218
|
+
start: "items-start",
|
|
2219
|
+
center: "items-center",
|
|
2220
|
+
end: "items-end",
|
|
2221
|
+
stretch: "items-stretch"
|
|
2222
|
+
};
|
|
2223
|
+
var JUSTIFY_CLS = {
|
|
2224
|
+
start: "justify-start",
|
|
2225
|
+
center: "justify-center",
|
|
2226
|
+
end: "justify-end",
|
|
2227
|
+
between: "justify-between",
|
|
2228
|
+
around: "justify-around"
|
|
2229
|
+
};
|
|
2230
|
+
var BoxNode = ({ node }) => {
|
|
2231
|
+
const dir = node.direction ?? "col";
|
|
2232
|
+
return /* @__PURE__ */ jsx16(
|
|
2233
|
+
"div",
|
|
2234
|
+
{
|
|
2235
|
+
className: cn(
|
|
2236
|
+
"aui-ui-box flex",
|
|
2237
|
+
dir === "col" ? "flex-col" : "flex-row",
|
|
2238
|
+
node.wrap && "flex-wrap",
|
|
2239
|
+
node.align && ALIGN_CLS[node.align],
|
|
2240
|
+
node.justify && JUSTIFY_CLS[node.justify],
|
|
2241
|
+
node.className
|
|
2242
|
+
),
|
|
2243
|
+
style: {
|
|
2244
|
+
gap: node.gap !== void 0 ? `${node.gap * 0.25}rem` : void 0,
|
|
2245
|
+
padding: node.padding !== void 0 ? `${node.padding * 0.25}rem` : void 0
|
|
2246
|
+
},
|
|
2247
|
+
children: node.children?.map((child, i) => /* @__PURE__ */ jsx16(UiNodeView, { node: child }, child.id ?? i))
|
|
2248
|
+
}
|
|
2249
|
+
);
|
|
2250
|
+
};
|
|
2251
|
+
var TEXT_SIZE = {
|
|
2252
|
+
xs: "text-xs",
|
|
2253
|
+
sm: "text-sm",
|
|
2254
|
+
base: "text-base",
|
|
2255
|
+
lg: "text-lg"
|
|
2256
|
+
};
|
|
2257
|
+
var TEXT_WEIGHT = {
|
|
2258
|
+
normal: "font-normal",
|
|
2259
|
+
medium: "font-medium",
|
|
2260
|
+
semibold: "font-semibold",
|
|
2261
|
+
bold: "font-bold"
|
|
2262
|
+
};
|
|
2263
|
+
var TextNode = ({ node }) => {
|
|
2264
|
+
const state = useUiState();
|
|
2265
|
+
const value = resolveBindable(node.value, state);
|
|
2266
|
+
return /* @__PURE__ */ jsx16(
|
|
2267
|
+
"span",
|
|
2268
|
+
{
|
|
2269
|
+
className: cn(
|
|
2270
|
+
"aui-ui-text",
|
|
2271
|
+
node.muted && "text-muted-foreground",
|
|
2272
|
+
node.size && TEXT_SIZE[node.size],
|
|
2273
|
+
node.weight && TEXT_WEIGHT[node.weight],
|
|
2274
|
+
node.className
|
|
2275
|
+
),
|
|
2276
|
+
children: value === void 0 || value === null ? "" : String(value)
|
|
2277
|
+
}
|
|
2278
|
+
);
|
|
2279
|
+
};
|
|
2280
|
+
var HEADING_CLS = {
|
|
2281
|
+
1: "text-2xl",
|
|
2282
|
+
2: "text-xl",
|
|
2283
|
+
3: "text-lg",
|
|
2284
|
+
4: "text-base"
|
|
2285
|
+
};
|
|
2286
|
+
var HeadingNode = ({ node }) => {
|
|
2287
|
+
const state = useUiState();
|
|
2288
|
+
const value = String(resolveBindable(node.value, state) ?? "");
|
|
2289
|
+
const level = node.level ?? 2;
|
|
2290
|
+
const cls = cn(
|
|
2291
|
+
"aui-ui-heading font-semibold text-foreground",
|
|
2292
|
+
HEADING_CLS[level],
|
|
2293
|
+
node.className
|
|
2294
|
+
);
|
|
2295
|
+
switch (level) {
|
|
2296
|
+
case 1:
|
|
2297
|
+
return /* @__PURE__ */ jsx16("h1", { className: cls, children: value });
|
|
2298
|
+
case 2:
|
|
2299
|
+
return /* @__PURE__ */ jsx16("h2", { className: cls, children: value });
|
|
2300
|
+
case 3:
|
|
2301
|
+
return /* @__PURE__ */ jsx16("h3", { className: cls, children: value });
|
|
2302
|
+
case 4:
|
|
2303
|
+
return /* @__PURE__ */ jsx16("h4", { className: cls, children: value });
|
|
2304
|
+
}
|
|
2305
|
+
};
|
|
2306
|
+
var BADGE_TONE = {
|
|
2307
|
+
default: "bg-muted text-foreground",
|
|
2308
|
+
primary: "bg-primary/10 text-primary",
|
|
2309
|
+
success: "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400",
|
|
2310
|
+
warn: "bg-amber-500/10 text-amber-600 dark:text-amber-400",
|
|
2311
|
+
danger: "bg-destructive/10 text-destructive"
|
|
2312
|
+
};
|
|
2313
|
+
var BadgeNode = ({ node }) => {
|
|
2314
|
+
const state = useUiState();
|
|
2315
|
+
const value = String(resolveBindable(node.value, state) ?? "");
|
|
2316
|
+
return /* @__PURE__ */ jsx16(
|
|
2317
|
+
"span",
|
|
2318
|
+
{
|
|
2319
|
+
className: cn(
|
|
2320
|
+
"aui-ui-badge inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium",
|
|
2321
|
+
BADGE_TONE[node.tone ?? "default"],
|
|
2322
|
+
node.className
|
|
2323
|
+
),
|
|
2324
|
+
children: value
|
|
2325
|
+
}
|
|
2326
|
+
);
|
|
2327
|
+
};
|
|
2328
|
+
var ButtonNode = ({ node }) => {
|
|
2329
|
+
const state = useUiState();
|
|
2330
|
+
const run = useActionRunner();
|
|
2331
|
+
const label = String(resolveBindable(node.label, state) ?? "");
|
|
2332
|
+
const disabled = node.disabled !== void 0 ? Boolean(resolveBindable(node.disabled, state)) : false;
|
|
2333
|
+
return /* @__PURE__ */ jsx16(
|
|
2334
|
+
Button,
|
|
2335
|
+
{
|
|
2336
|
+
variant: node.variant ?? "default",
|
|
2337
|
+
size: node.size ?? "default",
|
|
2338
|
+
disabled,
|
|
2339
|
+
className: node.className,
|
|
2340
|
+
onClick: () => run(node.onClick),
|
|
2341
|
+
children: label
|
|
2342
|
+
}
|
|
2343
|
+
);
|
|
2344
|
+
};
|
|
2345
|
+
var ToggleNode = ({ node }) => {
|
|
2346
|
+
const state = useUiState();
|
|
2347
|
+
const dispatch = useUiDispatch();
|
|
2348
|
+
const run = useActionRunner();
|
|
2349
|
+
const value = Boolean(getPath(state, node.binding));
|
|
2350
|
+
const label = node.label ? String(resolveBindable(node.label, state) ?? "") : null;
|
|
2351
|
+
const onToggle = () => {
|
|
2352
|
+
dispatch({ type: "toggle", path: node.binding });
|
|
2353
|
+
run(node.onChange);
|
|
2354
|
+
};
|
|
2355
|
+
return /* @__PURE__ */ jsxs10(
|
|
2356
|
+
"label",
|
|
2357
|
+
{
|
|
2358
|
+
className: cn(
|
|
2359
|
+
"aui-ui-toggle inline-flex cursor-pointer items-center gap-2 text-sm select-none",
|
|
2360
|
+
node.className
|
|
2361
|
+
),
|
|
2362
|
+
children: [
|
|
2363
|
+
/* @__PURE__ */ jsx16(
|
|
2364
|
+
"button",
|
|
2365
|
+
{
|
|
2366
|
+
type: "button",
|
|
2367
|
+
role: "switch",
|
|
2368
|
+
"aria-checked": value,
|
|
2369
|
+
onClick: onToggle,
|
|
2370
|
+
className: cn(
|
|
2371
|
+
"relative inline-flex h-5 w-9 shrink-0 items-center rounded-full border transition-colors",
|
|
2372
|
+
value ? "border-primary bg-primary" : "border-border bg-muted hover:bg-muted/80"
|
|
2373
|
+
),
|
|
2374
|
+
children: /* @__PURE__ */ jsx16(
|
|
2375
|
+
"span",
|
|
2376
|
+
{
|
|
2377
|
+
className: cn(
|
|
2378
|
+
"inline-block size-4 transform rounded-full bg-background shadow transition-transform",
|
|
2379
|
+
value ? "translate-x-4" : "translate-x-0.5"
|
|
2380
|
+
),
|
|
2381
|
+
"aria-hidden": true
|
|
2382
|
+
}
|
|
2383
|
+
)
|
|
2384
|
+
}
|
|
2385
|
+
),
|
|
2386
|
+
label && /* @__PURE__ */ jsx16("span", { className: "text-foreground/85", children: label })
|
|
2387
|
+
]
|
|
2388
|
+
}
|
|
2389
|
+
);
|
|
2390
|
+
};
|
|
2391
|
+
var SliderNode = ({ node }) => {
|
|
2392
|
+
const state = useUiState();
|
|
2393
|
+
const dispatch = useUiDispatch();
|
|
2394
|
+
const run = useActionRunner();
|
|
2395
|
+
const min = node.min ?? 0;
|
|
2396
|
+
const max = node.max ?? 100;
|
|
2397
|
+
const step = node.step ?? 1;
|
|
2398
|
+
const raw = getPath(state, node.binding);
|
|
2399
|
+
const value = typeof raw === "number" ? raw : min;
|
|
2400
|
+
const showValue = node.showValue ?? true;
|
|
2401
|
+
const label = node.label ? String(resolveBindable(node.label, state) ?? "") : null;
|
|
2402
|
+
const onChange = (e) => {
|
|
2403
|
+
const next = Number(e.target.value);
|
|
2404
|
+
dispatch({ type: "set", path: node.binding, value: next });
|
|
2405
|
+
};
|
|
2406
|
+
return /* @__PURE__ */ jsxs10("div", { className: cn("aui-ui-slider flex flex-col gap-1", node.className), children: [
|
|
2407
|
+
(label || showValue) && /* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-between text-xs text-muted-foreground", children: [
|
|
2408
|
+
label && /* @__PURE__ */ jsx16("span", { children: label }),
|
|
2409
|
+
showValue && /* @__PURE__ */ jsx16("span", { className: "font-mono", children: value })
|
|
2410
|
+
] }),
|
|
2411
|
+
/* @__PURE__ */ jsx16(
|
|
2412
|
+
"input",
|
|
2413
|
+
{
|
|
2414
|
+
type: "range",
|
|
2415
|
+
min,
|
|
2416
|
+
max,
|
|
2417
|
+
step,
|
|
2418
|
+
value,
|
|
2419
|
+
onChange,
|
|
2420
|
+
onMouseUp: () => run(node.onChange),
|
|
2421
|
+
onKeyUp: (e) => {
|
|
2422
|
+
if (e.key === "Enter" || e.key === " ") run(node.onChange);
|
|
2423
|
+
},
|
|
2424
|
+
onTouchEnd: () => run(node.onChange),
|
|
2425
|
+
className: "aui-ui-slider-input h-1.5 w-full cursor-pointer appearance-none rounded-full bg-muted accent-primary"
|
|
2426
|
+
}
|
|
2427
|
+
)
|
|
2428
|
+
] });
|
|
2429
|
+
};
|
|
2430
|
+
var TooltipNode = ({ node }) => {
|
|
2431
|
+
const state = useUiState();
|
|
2432
|
+
const content = String(resolveBindable(node.content, state) ?? "");
|
|
2433
|
+
return /* @__PURE__ */ jsx16(TooltipProvider, { children: /* @__PURE__ */ jsxs10(Tooltip, { children: [
|
|
2434
|
+
/* @__PURE__ */ jsx16(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx16("span", { className: cn("aui-ui-tooltip-trigger inline-flex", node.className), children: /* @__PURE__ */ jsx16(UiNodeView, { node: node.child }) }) }),
|
|
2435
|
+
/* @__PURE__ */ jsx16(TooltipContent, { side: node.side ?? "top", children: content })
|
|
2436
|
+
] }) });
|
|
2437
|
+
};
|
|
2438
|
+
var DraggableNode = ({ node }) => {
|
|
2439
|
+
const run = useActionRunner();
|
|
2440
|
+
const snapBack = node.snapBack ?? true;
|
|
2441
|
+
const axis = node.axis ?? "both";
|
|
2442
|
+
const dragProp = axis === "both" ? true : axis;
|
|
2443
|
+
return /* @__PURE__ */ jsx16(
|
|
2444
|
+
motion.div,
|
|
2445
|
+
{
|
|
2446
|
+
drag: dragProp,
|
|
2447
|
+
dragMomentum: false,
|
|
2448
|
+
dragSnapToOrigin: snapBack,
|
|
2449
|
+
whileDrag: { scale: 1.02, cursor: "grabbing" },
|
|
2450
|
+
onDragEnd: () => run(node.onDragEnd),
|
|
2451
|
+
className: cn(
|
|
2452
|
+
"aui-ui-draggable inline-block cursor-grab touch-none",
|
|
2453
|
+
node.className
|
|
2454
|
+
),
|
|
2455
|
+
children: /* @__PURE__ */ jsx16(UiNodeView, { node: node.child })
|
|
773
2456
|
}
|
|
774
|
-
)
|
|
2457
|
+
);
|
|
775
2458
|
};
|
|
2459
|
+
var CustomNode = ({ node }) => {
|
|
2460
|
+
const state = useUiState();
|
|
2461
|
+
const registry = useUiCustomNodeRegistry();
|
|
2462
|
+
const Renderer = registry[node.name];
|
|
2463
|
+
if (!Renderer) return null;
|
|
2464
|
+
const resolvedProps = resolveProps(node.props ?? {}, state);
|
|
2465
|
+
const children = node.children?.map((child, i) => /* @__PURE__ */ jsx16(UiNodeView, { node: child }, child.id ?? i));
|
|
2466
|
+
return /* @__PURE__ */ jsx16(Renderer, { props: resolvedProps, children });
|
|
2467
|
+
};
|
|
2468
|
+
function resolveProps(props, state) {
|
|
2469
|
+
const out = {};
|
|
2470
|
+
for (const [k, v] of Object.entries(props)) {
|
|
2471
|
+
out[k] = resolveBindable(v, state);
|
|
2472
|
+
}
|
|
2473
|
+
return out;
|
|
2474
|
+
}
|
|
776
2475
|
|
|
777
|
-
// src/
|
|
778
|
-
import "
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
import {
|
|
789
|
-
|
|
2476
|
+
// src/artifacts/ui/ui-artifact.tsx
|
|
2477
|
+
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
2478
|
+
var UiArtifactView = ({ artifact }) => {
|
|
2479
|
+
const [state, dispatch] = useReducer(
|
|
2480
|
+
uiStateReducer,
|
|
2481
|
+
artifact.initialState ?? {}
|
|
2482
|
+
);
|
|
2483
|
+
return /* @__PURE__ */ jsx17(ArtifactCard, { title: artifact.title, kind: "ui", children: /* @__PURE__ */ jsx17(UiStateProvider, { state, dispatch, children: /* @__PURE__ */ jsx17("div", { className: "aui-ui-root p-3", children: /* @__PURE__ */ jsx17(UiNodeView, { node: artifact.root }) }) }) });
|
|
2484
|
+
};
|
|
2485
|
+
|
|
2486
|
+
// src/artifacts/registry.tsx
|
|
2487
|
+
import { jsx as jsx18 } from "react/jsx-runtime";
|
|
2488
|
+
var defaultArtifactRenderers = {
|
|
2489
|
+
chart: ChartArtifactView,
|
|
2490
|
+
question: QuestionArtifactView,
|
|
2491
|
+
html: HtmlArtifactView,
|
|
2492
|
+
json: JsonArtifactView,
|
|
2493
|
+
table: TableArtifactView,
|
|
2494
|
+
ui: UiArtifactView
|
|
2495
|
+
};
|
|
2496
|
+
var ArtifactRegistryContext = createContext3(
|
|
2497
|
+
defaultArtifactRenderers
|
|
2498
|
+
);
|
|
2499
|
+
var ArtifactRegistryProvider = ({ renderers, override, children }) => {
|
|
2500
|
+
const merged = useMemo3(() => {
|
|
2501
|
+
if (!renderers) return defaultArtifactRenderers;
|
|
2502
|
+
if (override) return renderers;
|
|
2503
|
+
return { ...defaultArtifactRenderers, ...renderers };
|
|
2504
|
+
}, [renderers, override]);
|
|
2505
|
+
return /* @__PURE__ */ jsx18(ArtifactRegistryContext.Provider, { value: merged, children });
|
|
2506
|
+
};
|
|
2507
|
+
function useArtifactRegistry() {
|
|
2508
|
+
return useContext3(ArtifactRegistryContext);
|
|
2509
|
+
}
|
|
2510
|
+
var ArtifactView = ({ artifact }) => {
|
|
2511
|
+
const registry = useArtifactRegistry();
|
|
2512
|
+
const Renderer = registry[artifact.type] ?? registry.json;
|
|
2513
|
+
if (!Renderer) return null;
|
|
2514
|
+
return /* @__PURE__ */ jsx18(Renderer, { artifact });
|
|
2515
|
+
};
|
|
2516
|
+
|
|
2517
|
+
// src/artifacts/parse.ts
|
|
2518
|
+
var ARTIFACT_FENCE_LANGUAGES = /* @__PURE__ */ new Set([
|
|
2519
|
+
"timbal-artifact",
|
|
2520
|
+
"timbal"
|
|
2521
|
+
]);
|
|
2522
|
+
function isArtifactFenceLanguage(language) {
|
|
2523
|
+
return typeof language === "string" && ARTIFACT_FENCE_LANGUAGES.has(language);
|
|
2524
|
+
}
|
|
2525
|
+
function tryParseStructuredText(text) {
|
|
2526
|
+
const trimmed = text.trim();
|
|
2527
|
+
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return null;
|
|
2528
|
+
try {
|
|
2529
|
+
return JSON.parse(trimmed);
|
|
2530
|
+
} catch {
|
|
2531
|
+
}
|
|
2532
|
+
try {
|
|
2533
|
+
const asJson = trimmed.replace(/\bTrue\b/g, "true").replace(/\bFalse\b/g, "false").replace(/\bNone\b/g, "null").replace(/'/g, '"');
|
|
2534
|
+
return JSON.parse(asJson);
|
|
2535
|
+
} catch {
|
|
2536
|
+
return null;
|
|
2537
|
+
}
|
|
2538
|
+
}
|
|
2539
|
+
function parseArtifactFromToolResult(result) {
|
|
2540
|
+
if (result === void 0 || result === null) return null;
|
|
2541
|
+
if (typeof result === "string") {
|
|
2542
|
+
const parsed = tryParseStructuredText(result);
|
|
2543
|
+
if (parsed === null) return null;
|
|
2544
|
+
return parseArtifactFromToolResult(parsed);
|
|
2545
|
+
}
|
|
2546
|
+
if (Array.isArray(result)) {
|
|
2547
|
+
for (const item of result) {
|
|
2548
|
+
if (typeof item === "object" && item !== null && "text" in item) {
|
|
2549
|
+
const text = item.text;
|
|
2550
|
+
if (typeof text === "string") {
|
|
2551
|
+
const fromText = parseArtifactFromToolResult(text);
|
|
2552
|
+
if (fromText) return fromText;
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
return null;
|
|
2557
|
+
}
|
|
2558
|
+
if (typeof result === "object") {
|
|
2559
|
+
const obj = result;
|
|
2560
|
+
if (obj.type === "text" && typeof obj.text === "string") {
|
|
2561
|
+
return parseArtifactFromToolResult(obj.text);
|
|
2562
|
+
}
|
|
2563
|
+
if (obj.type === "thinking" && typeof obj.thinking === "string") {
|
|
2564
|
+
return parseArtifactFromToolResult(obj.thinking);
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
return isArtifact(result) ? result : null;
|
|
2568
|
+
}
|
|
2569
|
+
var FENCE_RE = /```(?:timbal-artifact|timbal)\s*\n([\s\S]*?)\n```/g;
|
|
2570
|
+
function findMarkdownArtifacts(markdown) {
|
|
2571
|
+
const matches = [];
|
|
2572
|
+
FENCE_RE.lastIndex = 0;
|
|
2573
|
+
let m;
|
|
2574
|
+
while ((m = FENCE_RE.exec(markdown)) !== null) {
|
|
2575
|
+
const raw = m[0];
|
|
2576
|
+
const body = m[1];
|
|
2577
|
+
try {
|
|
2578
|
+
const parsed = JSON.parse(body);
|
|
2579
|
+
if (isArtifact(parsed)) {
|
|
2580
|
+
matches.push({
|
|
2581
|
+
artifact: parsed,
|
|
2582
|
+
raw,
|
|
2583
|
+
start: m.index,
|
|
2584
|
+
end: m.index + raw.length
|
|
2585
|
+
});
|
|
2586
|
+
}
|
|
2587
|
+
} catch {
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
return matches;
|
|
2591
|
+
}
|
|
2592
|
+
function splitMarkdownByArtifacts(markdown) {
|
|
2593
|
+
const matches = findMarkdownArtifacts(markdown);
|
|
2594
|
+
if (matches.length === 0) return [{ kind: "text", text: markdown }];
|
|
2595
|
+
const segments = [];
|
|
2596
|
+
let cursor = 0;
|
|
2597
|
+
for (const match of matches) {
|
|
2598
|
+
if (match.start > cursor) {
|
|
2599
|
+
segments.push({ kind: "text", text: markdown.slice(cursor, match.start) });
|
|
2600
|
+
}
|
|
2601
|
+
segments.push({ kind: "artifact", artifact: match.artifact });
|
|
2602
|
+
cursor = match.end;
|
|
2603
|
+
}
|
|
2604
|
+
if (cursor < markdown.length) {
|
|
2605
|
+
segments.push({ kind: "text", text: markdown.slice(cursor) });
|
|
2606
|
+
}
|
|
2607
|
+
return segments;
|
|
2608
|
+
}
|
|
790
2609
|
|
|
791
2610
|
// src/components/syntax-highlighter.tsx
|
|
792
|
-
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
793
|
-
import { createHighlighterCore } from "shiki/core";
|
|
794
|
-
import { createJavaScriptRegexEngine } from "shiki/engine/javascript";
|
|
795
2611
|
import langJavascript from "shiki/langs/javascript.mjs";
|
|
796
2612
|
import langTypescript from "shiki/langs/typescript.mjs";
|
|
797
2613
|
import langPython from "shiki/langs/python.mjs";
|
|
@@ -811,7 +2627,7 @@ import langC from "shiki/langs/c.mjs";
|
|
|
811
2627
|
import langCpp from "shiki/langs/cpp.mjs";
|
|
812
2628
|
import themeVitesseDark from "shiki/themes/vitesse-dark.mjs";
|
|
813
2629
|
import themeVitesseLight from "shiki/themes/vitesse-light.mjs";
|
|
814
|
-
import { jsx as
|
|
2630
|
+
import { jsx as jsx19 } from "react/jsx-runtime";
|
|
815
2631
|
var SHIKI_THEME_DARK = "vitesse-dark";
|
|
816
2632
|
var SHIKI_THEME_LIGHT = "vitesse-light";
|
|
817
2633
|
var highlighterPromise = null;
|
|
@@ -849,7 +2665,7 @@ var ShikiSyntaxHighlighter = ({
|
|
|
849
2665
|
language,
|
|
850
2666
|
code
|
|
851
2667
|
}) => {
|
|
852
|
-
const [html, setHtml] =
|
|
2668
|
+
const [html, setHtml] = useState4(null);
|
|
853
2669
|
useEffect3(() => {
|
|
854
2670
|
let cancelled = false;
|
|
855
2671
|
(async () => {
|
|
@@ -876,8 +2692,17 @@ var ShikiSyntaxHighlighter = ({
|
|
|
876
2692
|
cancelled = true;
|
|
877
2693
|
};
|
|
878
2694
|
}, [code, language]);
|
|
2695
|
+
if (isArtifactFenceLanguage(language)) {
|
|
2696
|
+
try {
|
|
2697
|
+
const parsed = JSON.parse(code);
|
|
2698
|
+
if (isArtifact(parsed)) {
|
|
2699
|
+
return /* @__PURE__ */ jsx19(ArtifactView, { artifact: parsed });
|
|
2700
|
+
}
|
|
2701
|
+
} catch {
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
879
2704
|
if (html) {
|
|
880
|
-
return /* @__PURE__ */
|
|
2705
|
+
return /* @__PURE__ */ jsx19(
|
|
881
2706
|
"div",
|
|
882
2707
|
{
|
|
883
2708
|
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",
|
|
@@ -885,14 +2710,14 @@ var ShikiSyntaxHighlighter = ({
|
|
|
885
2710
|
}
|
|
886
2711
|
);
|
|
887
2712
|
}
|
|
888
|
-
return /* @__PURE__ */
|
|
2713
|
+
return /* @__PURE__ */ jsx19(Pre, { children: /* @__PURE__ */ jsx19(Code2, { children: code }) });
|
|
889
2714
|
};
|
|
890
2715
|
var syntax_highlighter_default = ShikiSyntaxHighlighter;
|
|
891
2716
|
|
|
892
2717
|
// src/components/markdown-text.tsx
|
|
893
|
-
import { jsx as
|
|
2718
|
+
import { jsx as jsx20, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
894
2719
|
var MarkdownTextImpl = () => {
|
|
895
|
-
return /* @__PURE__ */
|
|
2720
|
+
return /* @__PURE__ */ jsx20(
|
|
896
2721
|
MarkdownTextPrimitive,
|
|
897
2722
|
{
|
|
898
2723
|
remarkPlugins: [remarkGfm, remarkMath],
|
|
@@ -908,24 +2733,25 @@ var MarkdownTextImpl = () => {
|
|
|
908
2733
|
var MarkdownText = memo(MarkdownTextImpl);
|
|
909
2734
|
var CodeHeader = ({ language, code }) => {
|
|
910
2735
|
const { isCopied, copyToClipboard } = useCopyToClipboard();
|
|
2736
|
+
if (isArtifactFenceLanguage(language)) return null;
|
|
911
2737
|
const onCopy = () => {
|
|
912
2738
|
if (!code || isCopied) return;
|
|
913
2739
|
copyToClipboard(code);
|
|
914
2740
|
};
|
|
915
|
-
return /* @__PURE__ */
|
|
916
|
-
/* @__PURE__ */
|
|
917
|
-
/* @__PURE__ */
|
|
2741
|
+
return /* @__PURE__ */ jsxs11("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: [
|
|
2742
|
+
/* @__PURE__ */ jsxs11("span", { className: "flex items-center gap-2 text-xs font-semibold tracking-wide text-muted-foreground/80 uppercase", children: [
|
|
2743
|
+
/* @__PURE__ */ jsx20("span", { className: "inline-block h-2 w-2 rounded-full bg-primary/40" }),
|
|
918
2744
|
language
|
|
919
2745
|
] }),
|
|
920
|
-
/* @__PURE__ */
|
|
2746
|
+
/* @__PURE__ */ jsxs11(
|
|
921
2747
|
TooltipIconButton,
|
|
922
2748
|
{
|
|
923
2749
|
tooltip: isCopied ? "Copied!" : "Copy",
|
|
924
2750
|
onClick: onCopy,
|
|
925
2751
|
className: "transition-colors hover:text-foreground",
|
|
926
2752
|
children: [
|
|
927
|
-
!isCopied && /* @__PURE__ */
|
|
928
|
-
isCopied && /* @__PURE__ */
|
|
2753
|
+
!isCopied && /* @__PURE__ */ jsx20(CopyIcon, { className: "h-3.5 w-3.5" }),
|
|
2754
|
+
isCopied && /* @__PURE__ */ jsx20(CheckIcon2, { className: "h-3.5 w-3.5 text-emerald-500" })
|
|
929
2755
|
]
|
|
930
2756
|
}
|
|
931
2757
|
)
|
|
@@ -934,7 +2760,7 @@ var CodeHeader = ({ language, code }) => {
|
|
|
934
2760
|
var useCopyToClipboard = ({
|
|
935
2761
|
copiedDuration = 3e3
|
|
936
2762
|
} = {}) => {
|
|
937
|
-
const [isCopied, setIsCopied] =
|
|
2763
|
+
const [isCopied, setIsCopied] = useState5(false);
|
|
938
2764
|
const copyToClipboard = (value) => {
|
|
939
2765
|
if (!value) return;
|
|
940
2766
|
navigator.clipboard.writeText(value).then(() => {
|
|
@@ -945,7 +2771,7 @@ var useCopyToClipboard = ({
|
|
|
945
2771
|
return { isCopied, copyToClipboard };
|
|
946
2772
|
};
|
|
947
2773
|
var defaultComponents = memoizeMarkdownComponents({
|
|
948
|
-
h1: ({ className, ...props }) => /* @__PURE__ */
|
|
2774
|
+
h1: ({ className, ...props }) => /* @__PURE__ */ jsx20(
|
|
949
2775
|
"h1",
|
|
950
2776
|
{
|
|
951
2777
|
className: cn(
|
|
@@ -955,7 +2781,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
955
2781
|
...props
|
|
956
2782
|
}
|
|
957
2783
|
),
|
|
958
|
-
h2: ({ className, ...props }) => /* @__PURE__ */
|
|
2784
|
+
h2: ({ className, ...props }) => /* @__PURE__ */ jsx20(
|
|
959
2785
|
"h2",
|
|
960
2786
|
{
|
|
961
2787
|
className: cn(
|
|
@@ -965,7 +2791,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
965
2791
|
...props
|
|
966
2792
|
}
|
|
967
2793
|
),
|
|
968
|
-
h3: ({ className, ...props }) => /* @__PURE__ */
|
|
2794
|
+
h3: ({ className, ...props }) => /* @__PURE__ */ jsx20(
|
|
969
2795
|
"h3",
|
|
970
2796
|
{
|
|
971
2797
|
className: cn(
|
|
@@ -975,7 +2801,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
975
2801
|
...props
|
|
976
2802
|
}
|
|
977
2803
|
),
|
|
978
|
-
h4: ({ className, ...props }) => /* @__PURE__ */
|
|
2804
|
+
h4: ({ className, ...props }) => /* @__PURE__ */ jsx20(
|
|
979
2805
|
"h4",
|
|
980
2806
|
{
|
|
981
2807
|
className: cn(
|
|
@@ -985,7 +2811,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
985
2811
|
...props
|
|
986
2812
|
}
|
|
987
2813
|
),
|
|
988
|
-
h5: ({ className, ...props }) => /* @__PURE__ */
|
|
2814
|
+
h5: ({ className, ...props }) => /* @__PURE__ */ jsx20(
|
|
989
2815
|
"h5",
|
|
990
2816
|
{
|
|
991
2817
|
className: cn(
|
|
@@ -995,7 +2821,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
995
2821
|
...props
|
|
996
2822
|
}
|
|
997
2823
|
),
|
|
998
|
-
h6: ({ className, ...props }) => /* @__PURE__ */
|
|
2824
|
+
h6: ({ className, ...props }) => /* @__PURE__ */ jsx20(
|
|
999
2825
|
"h6",
|
|
1000
2826
|
{
|
|
1001
2827
|
className: cn(
|
|
@@ -1005,7 +2831,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
1005
2831
|
...props
|
|
1006
2832
|
}
|
|
1007
2833
|
),
|
|
1008
|
-
p: ({ className, ...props }) => /* @__PURE__ */
|
|
2834
|
+
p: ({ className, ...props }) => /* @__PURE__ */ jsx20(
|
|
1009
2835
|
"p",
|
|
1010
2836
|
{
|
|
1011
2837
|
className: cn(
|
|
@@ -1015,7 +2841,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
1015
2841
|
...props
|
|
1016
2842
|
}
|
|
1017
2843
|
),
|
|
1018
|
-
a: ({ className, ...props }) => /* @__PURE__ */
|
|
2844
|
+
a: ({ className, ...props }) => /* @__PURE__ */ jsx20(
|
|
1019
2845
|
"a",
|
|
1020
2846
|
{
|
|
1021
2847
|
className: cn(
|
|
@@ -1027,7 +2853,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
1027
2853
|
...props
|
|
1028
2854
|
}
|
|
1029
2855
|
),
|
|
1030
|
-
blockquote: ({ className, ...props }) => /* @__PURE__ */
|
|
2856
|
+
blockquote: ({ className, ...props }) => /* @__PURE__ */ jsx20(
|
|
1031
2857
|
"blockquote",
|
|
1032
2858
|
{
|
|
1033
2859
|
className: cn(
|
|
@@ -1037,7 +2863,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
1037
2863
|
...props
|
|
1038
2864
|
}
|
|
1039
2865
|
),
|
|
1040
|
-
ul: ({ className, ...props }) => /* @__PURE__ */
|
|
2866
|
+
ul: ({ className, ...props }) => /* @__PURE__ */ jsx20(
|
|
1041
2867
|
"ul",
|
|
1042
2868
|
{
|
|
1043
2869
|
className: cn(
|
|
@@ -1047,7 +2873,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
1047
2873
|
...props
|
|
1048
2874
|
}
|
|
1049
2875
|
),
|
|
1050
|
-
ol: ({ className, ...props }) => /* @__PURE__ */
|
|
2876
|
+
ol: ({ className, ...props }) => /* @__PURE__ */ jsx20(
|
|
1051
2877
|
"ol",
|
|
1052
2878
|
{
|
|
1053
2879
|
className: cn(
|
|
@@ -1057,7 +2883,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
1057
2883
|
...props
|
|
1058
2884
|
}
|
|
1059
2885
|
),
|
|
1060
|
-
hr: ({ className, ...props }) => /* @__PURE__ */
|
|
2886
|
+
hr: ({ className, ...props }) => /* @__PURE__ */ jsx20(
|
|
1061
2887
|
"hr",
|
|
1062
2888
|
{
|
|
1063
2889
|
className: cn(
|
|
@@ -1067,14 +2893,14 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
1067
2893
|
...props
|
|
1068
2894
|
}
|
|
1069
2895
|
),
|
|
1070
|
-
table: ({ className, ...props }) => /* @__PURE__ */
|
|
2896
|
+
table: ({ className, ...props }) => /* @__PURE__ */ jsx20("div", { className: "my-4 w-full overflow-x-auto rounded-lg border border-border/50", children: /* @__PURE__ */ jsx20(
|
|
1071
2897
|
"table",
|
|
1072
2898
|
{
|
|
1073
2899
|
className: cn("aui-md-table w-full border-collapse text-sm", className),
|
|
1074
2900
|
...props
|
|
1075
2901
|
}
|
|
1076
2902
|
) }),
|
|
1077
|
-
th: ({ className, ...props }) => /* @__PURE__ */
|
|
2903
|
+
th: ({ className, ...props }) => /* @__PURE__ */ jsx20(
|
|
1078
2904
|
"th",
|
|
1079
2905
|
{
|
|
1080
2906
|
className: cn(
|
|
@@ -1084,7 +2910,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
1084
2910
|
...props
|
|
1085
2911
|
}
|
|
1086
2912
|
),
|
|
1087
|
-
td: ({ className, ...props }) => /* @__PURE__ */
|
|
2913
|
+
td: ({ className, ...props }) => /* @__PURE__ */ jsx20(
|
|
1088
2914
|
"td",
|
|
1089
2915
|
{
|
|
1090
2916
|
className: cn(
|
|
@@ -1094,7 +2920,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
1094
2920
|
...props
|
|
1095
2921
|
}
|
|
1096
2922
|
),
|
|
1097
|
-
tr: ({ className, ...props }) => /* @__PURE__ */
|
|
2923
|
+
tr: ({ className, ...props }) => /* @__PURE__ */ jsx20(
|
|
1098
2924
|
"tr",
|
|
1099
2925
|
{
|
|
1100
2926
|
className: cn(
|
|
@@ -1104,8 +2930,8 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
1104
2930
|
...props
|
|
1105
2931
|
}
|
|
1106
2932
|
),
|
|
1107
|
-
li: ({ className, ...props }) => /* @__PURE__ */
|
|
1108
|
-
sup: ({ className, ...props }) => /* @__PURE__ */
|
|
2933
|
+
li: ({ className, ...props }) => /* @__PURE__ */ jsx20("li", { className: cn("aui-md-li leading-[1.7]", className), ...props }),
|
|
2934
|
+
sup: ({ className, ...props }) => /* @__PURE__ */ jsx20(
|
|
1109
2935
|
"sup",
|
|
1110
2936
|
{
|
|
1111
2937
|
className: cn(
|
|
@@ -1115,7 +2941,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
1115
2941
|
...props
|
|
1116
2942
|
}
|
|
1117
2943
|
),
|
|
1118
|
-
pre: ({ className, ...props }) => /* @__PURE__ */
|
|
2944
|
+
pre: ({ className, ...props }) => /* @__PURE__ */ jsx20(
|
|
1119
2945
|
"pre",
|
|
1120
2946
|
{
|
|
1121
2947
|
className: cn(
|
|
@@ -1127,7 +2953,7 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
1127
2953
|
),
|
|
1128
2954
|
code: function Code({ className, ...props }) {
|
|
1129
2955
|
const isCodeBlock = useIsMarkdownCodeBlock();
|
|
1130
|
-
return /* @__PURE__ */
|
|
2956
|
+
return /* @__PURE__ */ jsx20(
|
|
1131
2957
|
"code",
|
|
1132
2958
|
{
|
|
1133
2959
|
className: cn(
|
|
@@ -1138,108 +2964,556 @@ var defaultComponents = memoizeMarkdownComponents({
|
|
|
1138
2964
|
}
|
|
1139
2965
|
);
|
|
1140
2966
|
},
|
|
1141
|
-
strong: ({ className, ...props }) => /* @__PURE__ */
|
|
1142
|
-
em: ({ className, ...props }) => /* @__PURE__ */
|
|
2967
|
+
strong: ({ className, ...props }) => /* @__PURE__ */ jsx20("strong", { className: cn("font-semibold text-foreground", className), ...props }),
|
|
2968
|
+
em: ({ className, ...props }) => /* @__PURE__ */ jsx20("em", { className: cn("italic", className), ...props }),
|
|
1143
2969
|
CodeHeader
|
|
1144
2970
|
});
|
|
1145
2971
|
|
|
1146
2972
|
// src/components/tool-fallback.tsx
|
|
1147
|
-
import { memo as memo3 } from "react";
|
|
1148
|
-
import {
|
|
2973
|
+
import { memo as memo3, useMemo as useMemo5, useState as useState6 } from "react";
|
|
2974
|
+
import { ChevronRightIcon } from "lucide-react";
|
|
2975
|
+
import {
|
|
2976
|
+
useAuiState as useAuiState2
|
|
2977
|
+
} from "@assistant-ui/react";
|
|
1149
2978
|
|
|
1150
2979
|
// src/ui/shimmer.tsx
|
|
1151
|
-
import { motion } from "motion/react";
|
|
2980
|
+
import { motion as motion2 } from "motion/react";
|
|
1152
2981
|
import {
|
|
1153
2982
|
memo as memo2,
|
|
1154
|
-
useMemo
|
|
2983
|
+
useMemo as useMemo4
|
|
2984
|
+
} from "react";
|
|
2985
|
+
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
2986
|
+
var ShimmerComponent = ({
|
|
2987
|
+
children,
|
|
2988
|
+
as: Component = "p",
|
|
2989
|
+
className,
|
|
2990
|
+
duration = 2,
|
|
2991
|
+
spread = 2
|
|
2992
|
+
}) => {
|
|
2993
|
+
const MotionComponent = motion2.create(
|
|
2994
|
+
Component
|
|
2995
|
+
);
|
|
2996
|
+
const dynamicSpread = useMemo4(
|
|
2997
|
+
() => (children?.length ?? 0) * spread,
|
|
2998
|
+
[children, spread]
|
|
2999
|
+
);
|
|
3000
|
+
return /* @__PURE__ */ jsx21(
|
|
3001
|
+
MotionComponent,
|
|
3002
|
+
{
|
|
3003
|
+
animate: { backgroundPosition: "0% center" },
|
|
3004
|
+
className: cn(
|
|
3005
|
+
"relative inline-block bg-[length:250%_100%,auto] bg-clip-text text-transparent",
|
|
3006
|
+
"[--bg:linear-gradient(90deg,#0000_calc(50%-var(--spread)),var(--color-background),#0000_calc(50%+var(--spread)))] [background-repeat:no-repeat,padding-box]",
|
|
3007
|
+
className
|
|
3008
|
+
),
|
|
3009
|
+
initial: { backgroundPosition: "100% center" },
|
|
3010
|
+
style: {
|
|
3011
|
+
"--spread": `${dynamicSpread}px`,
|
|
3012
|
+
backgroundImage: "var(--bg), linear-gradient(var(--color-muted-foreground), var(--color-muted-foreground))"
|
|
3013
|
+
},
|
|
3014
|
+
transition: {
|
|
3015
|
+
repeat: Number.POSITIVE_INFINITY,
|
|
3016
|
+
duration,
|
|
3017
|
+
ease: "linear"
|
|
3018
|
+
},
|
|
3019
|
+
children
|
|
3020
|
+
}
|
|
3021
|
+
);
|
|
3022
|
+
};
|
|
3023
|
+
var Shimmer = memo2(ShimmerComponent);
|
|
3024
|
+
|
|
3025
|
+
// src/components/motion.tsx
|
|
3026
|
+
import { AnimatePresence, motion as motion3, useReducedMotion } from "motion/react";
|
|
3027
|
+
import { jsx as jsx22 } from "react/jsx-runtime";
|
|
3028
|
+
var luxuryEase = [0.16, 1, 0.3, 1];
|
|
3029
|
+
var TOOL_ENTER_MS = 0.78;
|
|
3030
|
+
var TOOL_EXIT_MS = 0.28;
|
|
3031
|
+
function toolPresenceTransition(reduced) {
|
|
3032
|
+
return {
|
|
3033
|
+
enter: {
|
|
3034
|
+
duration: reduced ? 0.35 : TOOL_ENTER_MS,
|
|
3035
|
+
ease: luxuryEase
|
|
3036
|
+
},
|
|
3037
|
+
exit: {
|
|
3038
|
+
duration: reduced ? 0.2 : TOOL_EXIT_MS,
|
|
3039
|
+
ease: [0.4, 0, 1, 1]
|
|
3040
|
+
}
|
|
3041
|
+
};
|
|
3042
|
+
}
|
|
3043
|
+
function toolMotionState(reduced, entering, variant) {
|
|
3044
|
+
if (reduced) {
|
|
3045
|
+
return entering ? { opacity: 0, y: variant === "executing" ? 8 : 10 } : { opacity: 1, y: 0 };
|
|
3046
|
+
}
|
|
3047
|
+
if (variant === "executing") {
|
|
3048
|
+
return entering ? { opacity: 0, y: 12 } : { opacity: 1, y: 0 };
|
|
3049
|
+
}
|
|
3050
|
+
return entering ? { opacity: 0, y: 14, filter: "blur(10px)" } : { opacity: 1, y: 0, filter: "blur(0px)" };
|
|
3051
|
+
}
|
|
3052
|
+
function ToolMotion({ children, className, motionKey }) {
|
|
3053
|
+
const reduced = useReducedMotion() ?? false;
|
|
3054
|
+
const { enter, exit } = toolPresenceTransition(reduced);
|
|
3055
|
+
return /* @__PURE__ */ jsx22(
|
|
3056
|
+
motion3.div,
|
|
3057
|
+
{
|
|
3058
|
+
className: cn("aui-tool-motion w-full min-w-0", className),
|
|
3059
|
+
initial: toolMotionState(reduced, true, "settled"),
|
|
3060
|
+
animate: toolMotionState(reduced, false, "settled"),
|
|
3061
|
+
exit: reduced ? { opacity: 0, y: 6, transition: exit } : { opacity: 0, y: 8, filter: "blur(6px)", transition: exit },
|
|
3062
|
+
transition: enter,
|
|
3063
|
+
style: { willChange: "opacity, transform, filter" },
|
|
3064
|
+
children
|
|
3065
|
+
},
|
|
3066
|
+
motionKey
|
|
3067
|
+
);
|
|
3068
|
+
}
|
|
3069
|
+
function ToolPresence({
|
|
3070
|
+
presenceKey,
|
|
3071
|
+
children,
|
|
3072
|
+
className,
|
|
3073
|
+
variant = "settled"
|
|
3074
|
+
}) {
|
|
3075
|
+
const reduced = useReducedMotion() ?? false;
|
|
3076
|
+
const { enter, exit } = toolPresenceTransition(reduced);
|
|
3077
|
+
const enterTransition = variant === "executing" ? { duration: reduced ? 0.3 : 0.52, ease: luxuryEase } : enter;
|
|
3078
|
+
return /* @__PURE__ */ jsx22(AnimatePresence, { mode: "wait", initial: true, children: /* @__PURE__ */ jsx22(
|
|
3079
|
+
motion3.div,
|
|
3080
|
+
{
|
|
3081
|
+
className: cn("aui-tool-presence w-full min-w-0", className),
|
|
3082
|
+
initial: toolMotionState(reduced, true, variant),
|
|
3083
|
+
animate: toolMotionState(reduced, false, variant),
|
|
3084
|
+
exit: reduced ? { opacity: 0, y: 6, transition: exit } : { opacity: 0, y: 8, filter: "blur(6px)", transition: exit },
|
|
3085
|
+
transition: enterTransition,
|
|
3086
|
+
style: {
|
|
3087
|
+
willChange: variant === "executing" ? "opacity, transform" : "opacity, transform, filter"
|
|
3088
|
+
},
|
|
3089
|
+
children
|
|
3090
|
+
},
|
|
3091
|
+
presenceKey
|
|
3092
|
+
) });
|
|
3093
|
+
}
|
|
3094
|
+
function ToolBodyPresence({
|
|
3095
|
+
open,
|
|
3096
|
+
children,
|
|
3097
|
+
className
|
|
3098
|
+
}) {
|
|
3099
|
+
const reduced = useReducedMotion() ?? false;
|
|
3100
|
+
return /* @__PURE__ */ jsx22(
|
|
3101
|
+
"div",
|
|
3102
|
+
{
|
|
3103
|
+
className: cn(
|
|
3104
|
+
"aui-tool-body grid min-h-0 transition-[grid-template-rows]",
|
|
3105
|
+
open ? reduced ? "duration-200 ease-out" : "duration-[340ms] ease-[cubic-bezier(0.16,1,0.3,1)]" : reduced ? "duration-150 ease-[cubic-bezier(0.4,0,0.2,1)]" : "duration-200 ease-[cubic-bezier(0.4,0,0.2,1)]"
|
|
3106
|
+
),
|
|
3107
|
+
style: { gridTemplateRows: open ? "1fr" : "0fr" },
|
|
3108
|
+
children: /* @__PURE__ */ jsx22("div", { className: "min-h-0 overflow-hidden", children: /* @__PURE__ */ jsx22(
|
|
3109
|
+
"div",
|
|
3110
|
+
{
|
|
3111
|
+
className: cn(
|
|
3112
|
+
className,
|
|
3113
|
+
"transition-opacity",
|
|
3114
|
+
open ? reduced ? "opacity-100 duration-200 ease-out" : "opacity-100 duration-300 ease-[cubic-bezier(0.16,1,0.3,1)] delay-75" : reduced ? "opacity-0 duration-100 ease-in" : "opacity-0 duration-150 ease-[cubic-bezier(0.4,0,0.2,1)]"
|
|
3115
|
+
),
|
|
3116
|
+
children
|
|
3117
|
+
}
|
|
3118
|
+
) })
|
|
3119
|
+
}
|
|
3120
|
+
);
|
|
3121
|
+
}
|
|
3122
|
+
|
|
3123
|
+
// src/components/tool-fallback.tsx
|
|
3124
|
+
import { jsx as jsx23, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3125
|
+
function detectRunning({
|
|
3126
|
+
status,
|
|
3127
|
+
result,
|
|
3128
|
+
streamRunning
|
|
3129
|
+
}) {
|
|
3130
|
+
const isError = status?.type === "incomplete" && status.reason !== "cancelled";
|
|
3131
|
+
if (isError) return false;
|
|
3132
|
+
if (status?.type === "running") return true;
|
|
3133
|
+
if (status?.type === "complete") return false;
|
|
3134
|
+
return streamRunning && result === void 0;
|
|
3135
|
+
}
|
|
3136
|
+
function useToolRunning(props) {
|
|
3137
|
+
const { isRunning: streamRunning } = useTimbalRuntime();
|
|
3138
|
+
const partStatus = useAuiState2((s) => s.part.status);
|
|
3139
|
+
return detectRunning({
|
|
3140
|
+
status: partStatus ?? props.status,
|
|
3141
|
+
result: props.result,
|
|
3142
|
+
streamRunning
|
|
3143
|
+
});
|
|
3144
|
+
}
|
|
3145
|
+
function formatToolLabel(toolName) {
|
|
3146
|
+
return toolName.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").toLowerCase();
|
|
3147
|
+
}
|
|
3148
|
+
function formatToolResult(result) {
|
|
3149
|
+
if (typeof result === "string") return result;
|
|
3150
|
+
try {
|
|
3151
|
+
return JSON.stringify(result, null, 2);
|
|
3152
|
+
} catch {
|
|
3153
|
+
return String(result);
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
var TimelineActionLabel = ({ action, detail, shimmer = false }) => /* @__PURE__ */ jsxs12("span", { className: "inline-flex min-w-0 max-w-full items-baseline gap-1", children: [
|
|
3157
|
+
action ? shimmer ? /* @__PURE__ */ jsx23(
|
|
3158
|
+
Shimmer,
|
|
3159
|
+
{
|
|
3160
|
+
as: "span",
|
|
3161
|
+
className: cn(studioTimelineShimmerActionClass, "aui-tool-shimmer"),
|
|
3162
|
+
duration: 1.8,
|
|
3163
|
+
spread: 2.5,
|
|
3164
|
+
children: action
|
|
3165
|
+
}
|
|
3166
|
+
) : /* @__PURE__ */ jsx23("span", { className: studioTimelineActionClass, children: action }) : null,
|
|
3167
|
+
detail ? /* @__PURE__ */ jsx23("span", { className: studioTimelineDetailClass, children: detail }) : null
|
|
3168
|
+
] });
|
|
3169
|
+
var TimelineHoverChevron = ({ expanded }) => /* @__PURE__ */ jsx23(
|
|
3170
|
+
ChevronRightIcon,
|
|
3171
|
+
{
|
|
3172
|
+
className: studioTimelineChevronClass(expanded),
|
|
3173
|
+
"aria-hidden": true
|
|
3174
|
+
}
|
|
3175
|
+
);
|
|
3176
|
+
var ToolPanel = ({ toolName, argsText, result, isError }) => {
|
|
3177
|
+
const [open, setOpen] = useState6(false);
|
|
3178
|
+
const detail = formatToolLabel(toolName);
|
|
3179
|
+
const formattedArgs = useMemo5(() => {
|
|
3180
|
+
if (!argsText || argsText === "{}") return null;
|
|
3181
|
+
try {
|
|
3182
|
+
return JSON.stringify(JSON.parse(argsText), null, 2);
|
|
3183
|
+
} catch {
|
|
3184
|
+
return argsText;
|
|
3185
|
+
}
|
|
3186
|
+
}, [argsText]);
|
|
3187
|
+
const formattedResult = useMemo5(() => {
|
|
3188
|
+
if (result === void 0 || result === null) return null;
|
|
3189
|
+
return formatToolResult(result);
|
|
3190
|
+
}, [result]);
|
|
3191
|
+
const hasBody = Boolean(formattedArgs || formattedResult);
|
|
3192
|
+
const action = isError ? "Failed" : "Used";
|
|
3193
|
+
if (!hasBody) {
|
|
3194
|
+
return /* @__PURE__ */ jsx23("div", { className: "aui-tool-row w-full min-w-0", children: /* @__PURE__ */ jsx23(TimelineActionLabel, { action, detail }) });
|
|
3195
|
+
}
|
|
3196
|
+
return /* @__PURE__ */ jsxs12("div", { className: "aui-tool-row w-full min-w-0", children: [
|
|
3197
|
+
/* @__PURE__ */ jsx23(
|
|
3198
|
+
"button",
|
|
3199
|
+
{
|
|
3200
|
+
type: "button",
|
|
3201
|
+
onClick: () => setOpen((v) => !v),
|
|
3202
|
+
"aria-expanded": open,
|
|
3203
|
+
"aria-label": `${action} ${detail}`,
|
|
3204
|
+
className: studioTimelineRowButtonClass,
|
|
3205
|
+
children: /* @__PURE__ */ jsxs12(
|
|
3206
|
+
"span",
|
|
3207
|
+
{
|
|
3208
|
+
className: cn(
|
|
3209
|
+
"inline-flex min-w-0 max-w-full items-center gap-0.5",
|
|
3210
|
+
studioTimelineTextClass,
|
|
3211
|
+
"text-foreground"
|
|
3212
|
+
),
|
|
3213
|
+
children: [
|
|
3214
|
+
/* @__PURE__ */ jsx23(TimelineActionLabel, { action, detail }),
|
|
3215
|
+
/* @__PURE__ */ jsx23(TimelineHoverChevron, { expanded: open })
|
|
3216
|
+
]
|
|
3217
|
+
}
|
|
3218
|
+
)
|
|
3219
|
+
}
|
|
3220
|
+
),
|
|
3221
|
+
/* @__PURE__ */ jsxs12(
|
|
3222
|
+
ToolBodyPresence,
|
|
3223
|
+
{
|
|
3224
|
+
open,
|
|
3225
|
+
className: cn(studioTimelineBodyPadClass, "gap-2"),
|
|
3226
|
+
children: [
|
|
3227
|
+
formattedArgs ? /* @__PURE__ */ jsx23(
|
|
3228
|
+
"div",
|
|
3229
|
+
{
|
|
3230
|
+
className: cn(
|
|
3231
|
+
studioComposerIoWellClass,
|
|
3232
|
+
"max-h-48 overflow-auto px-2.5 py-2"
|
|
3233
|
+
),
|
|
3234
|
+
children: /* @__PURE__ */ jsx23("pre", { className: "whitespace-pre-wrap break-words font-mono text-[11px] font-normal leading-relaxed text-foreground", children: formattedArgs })
|
|
3235
|
+
}
|
|
3236
|
+
) : null,
|
|
3237
|
+
formattedResult ? /* @__PURE__ */ jsx23(
|
|
3238
|
+
"div",
|
|
3239
|
+
{
|
|
3240
|
+
className: cn(
|
|
3241
|
+
studioComposerIoWellClass,
|
|
3242
|
+
"max-h-48 overflow-auto px-2.5 py-2"
|
|
3243
|
+
),
|
|
3244
|
+
children: /* @__PURE__ */ jsx23("pre", { className: "whitespace-pre-wrap break-words font-mono text-[11px] font-normal leading-relaxed text-foreground", children: formattedResult })
|
|
3245
|
+
}
|
|
3246
|
+
) : null
|
|
3247
|
+
]
|
|
3248
|
+
}
|
|
3249
|
+
)
|
|
3250
|
+
] });
|
|
3251
|
+
};
|
|
3252
|
+
var ToolFallbackImpl = ({
|
|
3253
|
+
toolName,
|
|
3254
|
+
argsText,
|
|
3255
|
+
result,
|
|
3256
|
+
status
|
|
3257
|
+
}) => {
|
|
3258
|
+
const isRunning = useToolRunning({ status, result });
|
|
3259
|
+
const isError = status?.type === "incomplete" && status.reason !== "cancelled";
|
|
3260
|
+
const presenceKey = isRunning ? "running" : isError ? "error" : "complete";
|
|
3261
|
+
return /* @__PURE__ */ jsx23(
|
|
3262
|
+
ToolPresence,
|
|
3263
|
+
{
|
|
3264
|
+
presenceKey,
|
|
3265
|
+
variant: isRunning ? "executing" : "settled",
|
|
3266
|
+
className: "py-0.5",
|
|
3267
|
+
children: isRunning ? /* @__PURE__ */ jsx23("div", { className: "aui-tool-running", children: /* @__PURE__ */ jsx23(
|
|
3268
|
+
TimelineActionLabel,
|
|
3269
|
+
{
|
|
3270
|
+
action: "Using",
|
|
3271
|
+
detail: formatToolLabel(toolName),
|
|
3272
|
+
shimmer: true
|
|
3273
|
+
}
|
|
3274
|
+
) }) : /* @__PURE__ */ jsx23(
|
|
3275
|
+
ToolPanel,
|
|
3276
|
+
{
|
|
3277
|
+
toolName,
|
|
3278
|
+
argsText,
|
|
3279
|
+
result,
|
|
3280
|
+
isError
|
|
3281
|
+
}
|
|
3282
|
+
)
|
|
3283
|
+
}
|
|
3284
|
+
);
|
|
3285
|
+
};
|
|
3286
|
+
var ToolFallback = memo3(
|
|
3287
|
+
ToolFallbackImpl
|
|
3288
|
+
);
|
|
3289
|
+
ToolFallback.displayName = "ToolFallback";
|
|
3290
|
+
|
|
3291
|
+
// src/artifacts/tool-artifact.tsx
|
|
3292
|
+
import { jsx as jsx24 } from "react/jsx-runtime";
|
|
3293
|
+
var ToolArtifactFallback = (props) => {
|
|
3294
|
+
const registry = useArtifactRegistry();
|
|
3295
|
+
const isRunning = useToolRunning({
|
|
3296
|
+
status: props.status,
|
|
3297
|
+
result: props.result
|
|
3298
|
+
});
|
|
3299
|
+
if (!isRunning) {
|
|
3300
|
+
const artifact = parseArtifactFromToolResult(props.result);
|
|
3301
|
+
if (artifact) {
|
|
3302
|
+
const Renderer = registry[artifact.type];
|
|
3303
|
+
if (Renderer) {
|
|
3304
|
+
return /* @__PURE__ */ jsx24(
|
|
3305
|
+
ToolMotion,
|
|
3306
|
+
{
|
|
3307
|
+
motionKey: `artifact-${artifact.type}`,
|
|
3308
|
+
className: "aui-tool-artifact",
|
|
3309
|
+
children: /* @__PURE__ */ jsx24(Renderer, { artifact })
|
|
3310
|
+
}
|
|
3311
|
+
);
|
|
3312
|
+
}
|
|
3313
|
+
}
|
|
3314
|
+
}
|
|
3315
|
+
return /* @__PURE__ */ jsx24(ToolFallback, { ...props });
|
|
3316
|
+
};
|
|
3317
|
+
|
|
3318
|
+
// src/components/composer.tsx
|
|
3319
|
+
import {
|
|
3320
|
+
AuiIf,
|
|
3321
|
+
ComposerPrimitive as ComposerPrimitive2,
|
|
3322
|
+
useComposerRuntime
|
|
3323
|
+
} from "@assistant-ui/react";
|
|
3324
|
+
import { ArrowUpIcon, SquareIcon } from "lucide-react";
|
|
3325
|
+
import { Fragment as Fragment2, jsx as jsx25, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
3326
|
+
var Composer = ({
|
|
3327
|
+
placeholder = "Send a message...",
|
|
3328
|
+
showAttachments = true,
|
|
3329
|
+
toolbar,
|
|
3330
|
+
sendTooltip = "Send message",
|
|
3331
|
+
noAutoFocus,
|
|
3332
|
+
className
|
|
3333
|
+
}) => {
|
|
3334
|
+
return /* @__PURE__ */ jsx25(
|
|
3335
|
+
ComposerPrimitive2.Root,
|
|
3336
|
+
{
|
|
3337
|
+
className: cn(
|
|
3338
|
+
"aui-composer-root relative flex w-full flex-col",
|
|
3339
|
+
className
|
|
3340
|
+
),
|
|
3341
|
+
children: /* @__PURE__ */ jsxs13(
|
|
3342
|
+
ComposerPrimitive2.AttachmentDropzone,
|
|
3343
|
+
{
|
|
3344
|
+
className: cn(
|
|
3345
|
+
studioComposeInputShellClass,
|
|
3346
|
+
"data-[dragging=true]:border-2 data-[dragging=true]:border-dashed data-[dragging=true]:border-primary data-[dragging=true]:bg-accent/50"
|
|
3347
|
+
),
|
|
3348
|
+
children: [
|
|
3349
|
+
showAttachments && /* @__PURE__ */ jsx25(ComposerAttachments, {}),
|
|
3350
|
+
/* @__PURE__ */ jsx25(ComposerInput, { placeholder, autoFocus: !noAutoFocus }),
|
|
3351
|
+
/* @__PURE__ */ jsx25(
|
|
3352
|
+
ComposerToolbar,
|
|
3353
|
+
{
|
|
3354
|
+
showAttachments,
|
|
3355
|
+
toolbar,
|
|
3356
|
+
sendTooltip
|
|
3357
|
+
}
|
|
3358
|
+
)
|
|
3359
|
+
]
|
|
3360
|
+
}
|
|
3361
|
+
)
|
|
3362
|
+
}
|
|
3363
|
+
);
|
|
3364
|
+
};
|
|
3365
|
+
var ComposerInput = ({
|
|
3366
|
+
placeholder,
|
|
3367
|
+
autoFocus
|
|
3368
|
+
}) => {
|
|
3369
|
+
const composer = useComposerRuntime();
|
|
3370
|
+
const onKeyDown = (e) => {
|
|
3371
|
+
if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing) {
|
|
3372
|
+
e.preventDefault();
|
|
3373
|
+
composer.send();
|
|
3374
|
+
}
|
|
3375
|
+
};
|
|
3376
|
+
const onInput = (e) => {
|
|
3377
|
+
const el = e.currentTarget;
|
|
3378
|
+
el.style.height = "auto";
|
|
3379
|
+
el.style.height = `${Math.min(el.scrollHeight, 240)}px`;
|
|
3380
|
+
};
|
|
3381
|
+
return /* @__PURE__ */ jsx25(
|
|
3382
|
+
ComposerPrimitive2.Input,
|
|
3383
|
+
{
|
|
3384
|
+
placeholder,
|
|
3385
|
+
className: "aui-composer-input max-h-60 min-h-14 w-full resize-none bg-transparent px-3 pt-3 pb-1 text-sm outline-none placeholder:text-neutral-400 focus-visible:ring-0 dark:placeholder:text-neutral-500",
|
|
3386
|
+
rows: 1,
|
|
3387
|
+
autoFocus,
|
|
3388
|
+
"aria-label": "Message input",
|
|
3389
|
+
onKeyDown,
|
|
3390
|
+
onInput
|
|
3391
|
+
}
|
|
3392
|
+
);
|
|
3393
|
+
};
|
|
3394
|
+
var ComposerToolbar = ({ showAttachments, toolbar, sendTooltip }) => {
|
|
3395
|
+
return /* @__PURE__ */ jsxs13("div", { className: "aui-composer-action-wrapper flex items-center justify-between gap-1 px-2.5 pb-2.5", children: [
|
|
3396
|
+
/* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-1", children: [
|
|
3397
|
+
showAttachments && /* @__PURE__ */ jsx25(ComposerAddAttachment, {}),
|
|
3398
|
+
toolbar
|
|
3399
|
+
] }),
|
|
3400
|
+
/* @__PURE__ */ jsx25(ComposerSendOrCancel, { sendTooltip })
|
|
3401
|
+
] });
|
|
3402
|
+
};
|
|
3403
|
+
var ComposerSendOrCancel = ({ sendTooltip }) => {
|
|
3404
|
+
return /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
3405
|
+
/* @__PURE__ */ jsx25(AuiIf, { condition: (s) => !s.thread.isRunning, children: /* @__PURE__ */ jsx25(ComposerPrimitive2.Send, { asChild: true, children: /* @__PURE__ */ jsx25(
|
|
3406
|
+
TooltipIconButton,
|
|
3407
|
+
{
|
|
3408
|
+
tooltip: sendTooltip,
|
|
3409
|
+
variant: "primary",
|
|
3410
|
+
type: "submit",
|
|
3411
|
+
className: "aui-composer-send shrink-0 disabled:opacity-30",
|
|
3412
|
+
"aria-label": "Send message",
|
|
3413
|
+
children: /* @__PURE__ */ jsx25(ArrowUpIcon, { className: "aui-composer-send-icon size-4" })
|
|
3414
|
+
}
|
|
3415
|
+
) }) }),
|
|
3416
|
+
/* @__PURE__ */ jsx25(AuiIf, { condition: (s) => s.thread.isRunning, children: /* @__PURE__ */ jsx25(ComposerPrimitive2.Cancel, { asChild: true, children: /* @__PURE__ */ jsx25(
|
|
3417
|
+
TooltipIconButton,
|
|
3418
|
+
{
|
|
3419
|
+
tooltip: "Stop generating",
|
|
3420
|
+
variant: "primary",
|
|
3421
|
+
className: "aui-composer-cancel shrink-0",
|
|
3422
|
+
"aria-label": "Stop generating",
|
|
3423
|
+
children: /* @__PURE__ */ jsx25(SquareIcon, { className: "aui-composer-cancel-icon size-3 fill-current" })
|
|
3424
|
+
}
|
|
3425
|
+
) }) })
|
|
3426
|
+
] });
|
|
3427
|
+
};
|
|
3428
|
+
|
|
3429
|
+
// src/components/suggestions.tsx
|
|
3430
|
+
import {
|
|
3431
|
+
useEffect as useEffect4,
|
|
3432
|
+
useMemo as useMemo6,
|
|
3433
|
+
useState as useState7
|
|
1155
3434
|
} from "react";
|
|
1156
|
-
import {
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
spread = 2
|
|
3435
|
+
import { useThreadRuntime as useThreadRuntime3 } from "@assistant-ui/react";
|
|
3436
|
+
import { ArrowUpIcon as ArrowUpIcon2 } from "lucide-react";
|
|
3437
|
+
import { jsx as jsx26, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
3438
|
+
var Suggestions = ({
|
|
3439
|
+
suggestions,
|
|
3440
|
+
className
|
|
1163
3441
|
}) => {
|
|
1164
|
-
const
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
() => (children?.length ?? 0) * spread,
|
|
1169
|
-
[children, spread]
|
|
1170
|
-
);
|
|
1171
|
-
return /* @__PURE__ */ jsx10(
|
|
1172
|
-
MotionComponent,
|
|
3442
|
+
const items = useResolvedSuggestions(suggestions);
|
|
3443
|
+
if (!items || items.length === 0) return null;
|
|
3444
|
+
return /* @__PURE__ */ jsx26(
|
|
3445
|
+
"div",
|
|
1173
3446
|
{
|
|
1174
|
-
animate: { backgroundPosition: "0% center" },
|
|
1175
3447
|
className: cn(
|
|
1176
|
-
"
|
|
1177
|
-
"[--bg:linear-gradient(90deg,#0000_calc(50%-var(--spread)),var(--color-background),#0000_calc(50%+var(--spread)))] [background-repeat:no-repeat,padding-box]",
|
|
3448
|
+
"aui-thread-suggestions flex w-full flex-col gap-2 pb-2.5",
|
|
1178
3449
|
className
|
|
1179
3450
|
),
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
backgroundImage: "var(--bg), linear-gradient(var(--color-muted-foreground), var(--color-muted-foreground))"
|
|
1184
|
-
},
|
|
1185
|
-
transition: {
|
|
1186
|
-
repeat: Number.POSITIVE_INFINITY,
|
|
1187
|
-
duration,
|
|
1188
|
-
ease: "linear"
|
|
1189
|
-
},
|
|
1190
|
-
children
|
|
3451
|
+
role: "list",
|
|
3452
|
+
"aria-label": "Suggested prompts",
|
|
3453
|
+
children: items.map((suggestion, i) => /* @__PURE__ */ jsx26(SuggestionRow, { suggestion }, (suggestion.prompt ?? suggestion.title) + i))
|
|
1191
3454
|
}
|
|
1192
3455
|
);
|
|
1193
3456
|
};
|
|
1194
|
-
var
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
3457
|
+
var SuggestionRow = ({ suggestion }) => {
|
|
3458
|
+
const runtime = useThreadRuntime3();
|
|
3459
|
+
const onClick = () => {
|
|
3460
|
+
const text = suggestion.prompt ?? suggestion.title;
|
|
3461
|
+
runtime.append({ role: "user", content: [{ type: "text", text }] });
|
|
3462
|
+
};
|
|
3463
|
+
return /* @__PURE__ */ jsxs14(
|
|
3464
|
+
"button",
|
|
3465
|
+
{
|
|
3466
|
+
type: "button",
|
|
3467
|
+
role: "listitem",
|
|
3468
|
+
onClick,
|
|
3469
|
+
className: cn("aui-thread-suggestion", studioListRowButtonClass),
|
|
3470
|
+
children: [
|
|
3471
|
+
/* @__PURE__ */ jsx26("span", { className: "aui-thread-suggestion-icon shrink-0 text-neutral-500 dark:text-muted-foreground", children: suggestion.icon ?? /* @__PURE__ */ jsx26(ArrowUpIcon2, { className: "size-4", strokeWidth: 1.75, "aria-hidden": true }) }),
|
|
3472
|
+
/* @__PURE__ */ jsxs14("span", { className: "aui-thread-suggestion-text min-w-0 flex-1 text-left", children: [
|
|
3473
|
+
/* @__PURE__ */ jsx26("span", { className: "aui-thread-suggestion-text-1 block truncate text-sm font-normal text-foreground dark:text-foreground/95", children: suggestion.title }),
|
|
3474
|
+
suggestion.description && /* @__PURE__ */ jsx26("span", { className: "aui-thread-suggestion-text-2 mt-0.5 block truncate text-xs text-muted-foreground", children: suggestion.description })
|
|
3475
|
+
] })
|
|
3476
|
+
]
|
|
3477
|
+
}
|
|
3478
|
+
);
|
|
1207
3479
|
};
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
)
|
|
1211
|
-
|
|
3480
|
+
function useResolvedSuggestions(source) {
|
|
3481
|
+
const [resolved, setResolved] = useState7(
|
|
3482
|
+
() => Array.isArray(source) ? source : void 0
|
|
3483
|
+
);
|
|
3484
|
+
useEffect4(() => {
|
|
3485
|
+
if (!source) {
|
|
3486
|
+
setResolved(void 0);
|
|
3487
|
+
return;
|
|
3488
|
+
}
|
|
3489
|
+
if (Array.isArray(source)) {
|
|
3490
|
+
setResolved(source);
|
|
3491
|
+
return;
|
|
3492
|
+
}
|
|
3493
|
+
let cancelled = false;
|
|
3494
|
+
Promise.resolve().then(() => source()).then((value) => {
|
|
3495
|
+
if (!cancelled) setResolved(value);
|
|
3496
|
+
}).catch(() => {
|
|
3497
|
+
if (!cancelled) setResolved([]);
|
|
3498
|
+
});
|
|
3499
|
+
return () => {
|
|
3500
|
+
cancelled = true;
|
|
3501
|
+
};
|
|
3502
|
+
}, [source]);
|
|
3503
|
+
return useMemo6(() => resolved, [resolved]);
|
|
3504
|
+
}
|
|
1212
3505
|
|
|
1213
3506
|
// src/components/thread.tsx
|
|
1214
|
-
import {
|
|
1215
|
-
ActionBarMorePrimitive,
|
|
1216
|
-
ActionBarPrimitive,
|
|
1217
|
-
AuiIf,
|
|
1218
|
-
ComposerPrimitive as ComposerPrimitive2,
|
|
1219
|
-
ErrorPrimitive,
|
|
1220
|
-
MessagePrimitive as MessagePrimitive2,
|
|
1221
|
-
ThreadPrimitive,
|
|
1222
|
-
useThreadRuntime
|
|
1223
|
-
} from "@assistant-ui/react";
|
|
1224
|
-
import {
|
|
1225
|
-
ArrowDownIcon,
|
|
1226
|
-
ArrowUpIcon,
|
|
1227
|
-
CheckIcon as CheckIcon2,
|
|
1228
|
-
CopyIcon as CopyIcon2,
|
|
1229
|
-
DownloadIcon,
|
|
1230
|
-
MoreHorizontalIcon,
|
|
1231
|
-
PencilIcon,
|
|
1232
|
-
RefreshCwIcon,
|
|
1233
|
-
SquareIcon
|
|
1234
|
-
} from "lucide-react";
|
|
1235
|
-
import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
3507
|
+
import { jsx as jsx27, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1236
3508
|
var Thread = ({
|
|
1237
3509
|
className,
|
|
1238
3510
|
maxWidth = "44rem",
|
|
1239
3511
|
welcome,
|
|
1240
3512
|
suggestions,
|
|
1241
3513
|
composerPlaceholder = "Send a message...",
|
|
1242
|
-
components
|
|
3514
|
+
components,
|
|
3515
|
+
artifacts,
|
|
3516
|
+
onArtifactEvent
|
|
1243
3517
|
}) => {
|
|
1244
3518
|
const WelcomeSlot = components?.Welcome ?? ThreadWelcome;
|
|
1245
3519
|
const ComposerSlot = components?.Composer ?? Composer;
|
|
@@ -1247,202 +3521,217 @@ var Thread = ({
|
|
|
1247
3521
|
const AssistantMessageSlot = components?.AssistantMessage ?? AssistantMessage;
|
|
1248
3522
|
const EditComposerSlot = components?.EditComposer ?? EditComposer;
|
|
1249
3523
|
const ScrollToBottomSlot = components?.ScrollToBottom ?? ThreadScrollToBottom;
|
|
1250
|
-
|
|
1251
|
-
|
|
3524
|
+
const SuggestionsSlot = components?.Suggestions ?? Suggestions;
|
|
3525
|
+
return /* @__PURE__ */ jsx27(
|
|
3526
|
+
ArtifactRegistryProvider,
|
|
1252
3527
|
{
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
),
|
|
1257
|
-
|
|
1258
|
-
children: /* @__PURE__ */ jsxs7(
|
|
1259
|
-
ThreadPrimitive.Viewport,
|
|
3528
|
+
renderers: artifacts?.renderers,
|
|
3529
|
+
override: artifacts?.override,
|
|
3530
|
+
children: /* @__PURE__ */ jsx27(UiEventProvider, { onEvent: onArtifactEvent ?? (() => {
|
|
3531
|
+
}), children: /* @__PURE__ */ jsx27(
|
|
3532
|
+
ThreadPrimitive.Root,
|
|
1260
3533
|
{
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
3534
|
+
className: cn(
|
|
3535
|
+
"aui-root aui-thread-root @container flex h-full flex-col bg-background",
|
|
3536
|
+
className
|
|
3537
|
+
),
|
|
3538
|
+
style: { ["--thread-max-width"]: maxWidth },
|
|
3539
|
+
children: /* @__PURE__ */ jsxs15(
|
|
3540
|
+
ThreadPrimitive.Viewport,
|
|
3541
|
+
{
|
|
3542
|
+
turnAnchor: "bottom",
|
|
3543
|
+
className: "aui-thread-viewport relative flex flex-1 flex-col overflow-x-auto overflow-y-scroll px-4 pt-4",
|
|
3544
|
+
children: [
|
|
3545
|
+
/* @__PURE__ */ jsx27(
|
|
3546
|
+
WelcomeSlot,
|
|
3547
|
+
{
|
|
3548
|
+
config: welcome,
|
|
3549
|
+
suggestions,
|
|
3550
|
+
Suggestions: SuggestionsSlot
|
|
3551
|
+
}
|
|
3552
|
+
),
|
|
3553
|
+
/* @__PURE__ */ jsx27(
|
|
3554
|
+
ThreadPrimitive.Messages,
|
|
3555
|
+
{
|
|
3556
|
+
components: {
|
|
3557
|
+
UserMessage: UserMessageSlot,
|
|
3558
|
+
EditComposer: EditComposerSlot,
|
|
3559
|
+
AssistantMessage: AssistantMessageSlot
|
|
3560
|
+
}
|
|
3561
|
+
}
|
|
3562
|
+
),
|
|
3563
|
+
/* @__PURE__ */ jsxs15(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: [
|
|
3564
|
+
/* @__PURE__ */ jsx27(ScrollToBottomSlot, {}),
|
|
3565
|
+
/* @__PURE__ */ jsx27(ComposerSlot, { placeholder: composerPlaceholder })
|
|
3566
|
+
] })
|
|
3567
|
+
]
|
|
3568
|
+
}
|
|
3569
|
+
)
|
|
1280
3570
|
}
|
|
1281
|
-
)
|
|
3571
|
+
) })
|
|
1282
3572
|
}
|
|
1283
3573
|
);
|
|
1284
3574
|
};
|
|
1285
3575
|
var ThreadScrollToBottom = () => {
|
|
1286
|
-
return /* @__PURE__ */
|
|
3576
|
+
return /* @__PURE__ */ jsx27(ThreadPrimitive.ScrollToBottom, { asChild: true, children: /* @__PURE__ */ jsx27(
|
|
1287
3577
|
TooltipIconButton,
|
|
1288
3578
|
{
|
|
1289
3579
|
tooltip: "Scroll to bottom",
|
|
1290
|
-
variant: "
|
|
1291
|
-
className: "aui-thread-scroll-to-bottom absolute -top-12 z-10 self-center
|
|
1292
|
-
children: /* @__PURE__ */
|
|
3580
|
+
variant: "secondary",
|
|
3581
|
+
className: "aui-thread-scroll-to-bottom absolute -top-12 z-10 self-center disabled:invisible",
|
|
3582
|
+
children: /* @__PURE__ */ jsx27(ArrowDownIcon, { className: "size-4" })
|
|
1293
3583
|
}
|
|
1294
3584
|
) });
|
|
1295
3585
|
};
|
|
1296
|
-
var
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
/* @__PURE__ */ jsx12("div", { className: "animate-ai-pulse-ring absolute inset-0" }),
|
|
1302
|
-
/* @__PURE__ */ jsx12(
|
|
1303
|
-
"svg",
|
|
1304
|
-
{
|
|
1305
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
1306
|
-
viewBox: "0 0 24 24",
|
|
1307
|
-
fill: "none",
|
|
1308
|
-
stroke: "currentColor",
|
|
1309
|
-
strokeWidth: "1.5",
|
|
1310
|
-
strokeLinecap: "round",
|
|
1311
|
-
strokeLinejoin: "round",
|
|
1312
|
-
className: "animate-ai-breathe relative size-7 text-primary/75",
|
|
1313
|
-
children: /* @__PURE__ */ jsx12("path", { d: "M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z" })
|
|
1314
|
-
}
|
|
1315
|
-
)
|
|
1316
|
-
] }),
|
|
1317
|
-
/* @__PURE__ */ jsx12("h1", { className: "aui-thread-welcome-message-inner fade-in slide-in-from-bottom-1 animate-in fill-mode-both font-semibold text-2xl duration-200", children: config?.heading ?? "How can I help you today?" }),
|
|
1318
|
-
/* @__PURE__ */ jsx12("p", { className: "aui-thread-welcome-message-inner fade-in slide-in-from-bottom-1 animate-in fill-mode-both text-muted-foreground mt-2 delay-75 duration-200", children: config?.subheading ?? "Send a message to start a conversation." })
|
|
1319
|
-
] }) }),
|
|
1320
|
-
suggestions && suggestions.length > 0 && /* @__PURE__ */ jsx12(ThreadSuggestions, { suggestions })
|
|
1321
|
-
] }) });
|
|
1322
|
-
};
|
|
1323
|
-
var ThreadSuggestions = ({ suggestions }) => {
|
|
1324
|
-
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)) });
|
|
3586
|
+
var welcomeStagger = {
|
|
3587
|
+
initial: {},
|
|
3588
|
+
animate: {
|
|
3589
|
+
transition: { staggerChildren: 0.16, delayChildren: 0.12 }
|
|
3590
|
+
}
|
|
1325
3591
|
};
|
|
1326
|
-
var
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
onClick: () => runtime.append({
|
|
1334
|
-
role: "user",
|
|
1335
|
-
content: [{ type: "text", text: title }]
|
|
1336
|
-
}),
|
|
1337
|
-
children: [
|
|
1338
|
-
/* @__PURE__ */ jsx12("span", { className: "aui-thread-welcome-suggestion-text-1 font-medium", children: title }),
|
|
1339
|
-
description && /* @__PURE__ */ jsx12("span", { className: "aui-thread-welcome-suggestion-text-2 text-muted-foreground", children: description })
|
|
1340
|
-
]
|
|
1341
|
-
}
|
|
1342
|
-
) });
|
|
3592
|
+
var welcomeItem = {
|
|
3593
|
+
initial: { opacity: 0, y: 14 },
|
|
3594
|
+
animate: {
|
|
3595
|
+
opacity: 1,
|
|
3596
|
+
y: 0,
|
|
3597
|
+
transition: { duration: 0.9, ease: luxuryEase }
|
|
3598
|
+
}
|
|
1343
3599
|
};
|
|
1344
|
-
var
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
rows: 1,
|
|
1353
|
-
autoFocus: true,
|
|
1354
|
-
"aria-label": "Message input"
|
|
1355
|
-
}
|
|
1356
|
-
),
|
|
1357
|
-
/* @__PURE__ */ jsx12(ComposerAction, {})
|
|
1358
|
-
] }) });
|
|
3600
|
+
var welcomeIcon = {
|
|
3601
|
+
initial: { opacity: 0, y: 10, scale: 0.96 },
|
|
3602
|
+
animate: {
|
|
3603
|
+
opacity: 1,
|
|
3604
|
+
y: 0,
|
|
3605
|
+
scale: 1,
|
|
3606
|
+
transition: { duration: 1.1, ease: luxuryEase }
|
|
3607
|
+
}
|
|
1359
3608
|
};
|
|
1360
|
-
var
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
className: "aui-composer-send size-8 rounded-full",
|
|
1371
|
-
"aria-label": "Send message",
|
|
1372
|
-
children: /* @__PURE__ */ jsx12(ArrowUpIcon, { className: "aui-composer-send-icon size-4" })
|
|
1373
|
-
}
|
|
1374
|
-
) }) }),
|
|
1375
|
-
/* @__PURE__ */ jsx12(AuiIf, { condition: (s) => s.thread.isRunning, children: /* @__PURE__ */ jsx12(ComposerPrimitive2.Cancel, { asChild: true, children: /* @__PURE__ */ jsx12(
|
|
1376
|
-
Button,
|
|
3609
|
+
var ThreadWelcome = ({
|
|
3610
|
+
config,
|
|
3611
|
+
suggestions,
|
|
3612
|
+
Suggestions: SuggestionsSlot = Suggestions
|
|
3613
|
+
}) => {
|
|
3614
|
+
const isEmpty = useThread((s) => s.messages.length === 0);
|
|
3615
|
+
if (!isEmpty) return null;
|
|
3616
|
+
return /* @__PURE__ */ jsxs15("div", { className: "aui-thread-welcome-root mx-auto my-auto flex w-full max-w-(--thread-max-width) grow flex-col", children: [
|
|
3617
|
+
/* @__PURE__ */ jsx27("div", { className: "aui-thread-welcome-center flex w-full grow flex-col items-center justify-center", children: /* @__PURE__ */ jsxs15(
|
|
3618
|
+
motion4.div,
|
|
1377
3619
|
{
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
3620
|
+
className: "aui-thread-welcome-message flex flex-col items-center justify-center px-4 text-center",
|
|
3621
|
+
variants: welcomeStagger,
|
|
3622
|
+
initial: "initial",
|
|
3623
|
+
animate: "animate",
|
|
3624
|
+
children: [
|
|
3625
|
+
config?.icon && /* @__PURE__ */ jsx27(motion4.div, { variants: welcomeIcon, className: "mb-5", children: config.icon }),
|
|
3626
|
+
/* @__PURE__ */ jsx27(
|
|
3627
|
+
motion4.h1,
|
|
3628
|
+
{
|
|
3629
|
+
variants: welcomeItem,
|
|
3630
|
+
className: "aui-thread-welcome-message-inner font-semibold text-2xl",
|
|
3631
|
+
children: config?.heading ?? "How can I help you today?"
|
|
3632
|
+
}
|
|
3633
|
+
),
|
|
3634
|
+
/* @__PURE__ */ jsx27(
|
|
3635
|
+
motion4.p,
|
|
3636
|
+
{
|
|
3637
|
+
variants: welcomeItem,
|
|
3638
|
+
className: "aui-thread-welcome-message-inner mt-2 text-muted-foreground",
|
|
3639
|
+
children: config?.subheading ?? "Send a message to start a conversation."
|
|
3640
|
+
}
|
|
3641
|
+
)
|
|
3642
|
+
]
|
|
1384
3643
|
}
|
|
1385
|
-
) })
|
|
3644
|
+
) }),
|
|
3645
|
+
suggestions && /* @__PURE__ */ jsx27("div", { className: "aui-thread-welcome-suggestions mx-auto w-full max-w-(--thread-max-width) px-2", children: /* @__PURE__ */ jsx27(SuggestionsSlot, { suggestions }) })
|
|
1386
3646
|
] });
|
|
1387
3647
|
};
|
|
1388
3648
|
var MessageError = () => {
|
|
1389
|
-
return /* @__PURE__ */
|
|
3649
|
+
return /* @__PURE__ */ jsx27(MessagePrimitive2.Error, { children: /* @__PURE__ */ jsx27(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__ */ jsx27(ErrorPrimitive.Message, { className: "aui-message-error-message line-clamp-2" }) }) });
|
|
1390
3650
|
};
|
|
1391
3651
|
var AssistantMessage = () => {
|
|
1392
|
-
return /* @__PURE__ */
|
|
3652
|
+
return /* @__PURE__ */ jsxs15(
|
|
1393
3653
|
MessagePrimitive2.Root,
|
|
1394
3654
|
{
|
|
1395
3655
|
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",
|
|
1396
3656
|
"data-role": "assistant",
|
|
1397
3657
|
children: [
|
|
1398
|
-
/* @__PURE__ */
|
|
1399
|
-
/* @__PURE__ */
|
|
3658
|
+
/* @__PURE__ */ jsxs15("div", { className: "aui-assistant-message-content wrap-break-word px-2 text-foreground leading-relaxed", children: [
|
|
3659
|
+
/* @__PURE__ */ jsx27(
|
|
1400
3660
|
MessagePrimitive2.Parts,
|
|
1401
3661
|
{
|
|
1402
3662
|
components: {
|
|
1403
3663
|
Text: MarkdownText,
|
|
1404
|
-
|
|
3664
|
+
// `Override` (not `Fallback`) replaces the default tool renderer
|
|
3665
|
+
// entirely so we never fall back to the assistant-ui boilerplate.
|
|
3666
|
+
tools: { Override: ToolArtifactFallback }
|
|
1405
3667
|
}
|
|
1406
3668
|
}
|
|
1407
3669
|
),
|
|
1408
|
-
/* @__PURE__ */
|
|
3670
|
+
/* @__PURE__ */ jsx27(MessageError, {})
|
|
1409
3671
|
] }),
|
|
1410
|
-
/* @__PURE__ */
|
|
3672
|
+
/* @__PURE__ */ jsx27("div", { className: "aui-assistant-message-footer mt-0 ml-1 flex", children: /* @__PURE__ */ jsx27(AssistantActionBar, {}) })
|
|
1411
3673
|
]
|
|
1412
3674
|
}
|
|
1413
3675
|
);
|
|
1414
3676
|
};
|
|
3677
|
+
var ASSISTANT_ACTION_ICON_CLASS = cn(
|
|
3678
|
+
"size-6 min-h-6 min-w-6 text-muted-foreground/45 hover:text-muted-foreground/80",
|
|
3679
|
+
// The v2 fill span sits inside `group/tbv2 > span:first-child`. We mute it
|
|
3680
|
+
// here so action-bar buttons read as subtle icons rather than full pills.
|
|
3681
|
+
"[&>span:first-child]:bg-transparent",
|
|
3682
|
+
"[&>span:first-child]:group-hover/tbv2:bg-neutral-100/50",
|
|
3683
|
+
"dark:[&>span:first-child]:group-hover/tbv2:bg-white/8"
|
|
3684
|
+
);
|
|
1415
3685
|
var AssistantActionBar = () => {
|
|
1416
|
-
return /* @__PURE__ */
|
|
3686
|
+
return /* @__PURE__ */ jsxs15(
|
|
1417
3687
|
ActionBarPrimitive.Root,
|
|
1418
3688
|
{
|
|
1419
3689
|
hideWhenRunning: true,
|
|
1420
3690
|
autohide: "not-last",
|
|
1421
|
-
|
|
1422
|
-
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",
|
|
3691
|
+
className: "aui-assistant-action-bar-root flex items-center gap-0 bg-transparent px-0 py-0.5 text-muted-foreground/60",
|
|
1423
3692
|
children: [
|
|
1424
|
-
/* @__PURE__ */
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
3693
|
+
/* @__PURE__ */ jsx27(ActionBarPrimitive.Copy, { asChild: true, children: /* @__PURE__ */ jsxs15(
|
|
3694
|
+
TooltipIconButton,
|
|
3695
|
+
{
|
|
3696
|
+
tooltip: "Copy",
|
|
3697
|
+
variant: "ghost",
|
|
3698
|
+
className: ASSISTANT_ACTION_ICON_CLASS,
|
|
3699
|
+
children: [
|
|
3700
|
+
/* @__PURE__ */ jsx27(AuiIf2, { condition: (s) => s.message.isCopied, children: /* @__PURE__ */ jsx27(CheckIcon3, { className: "size-3" }) }),
|
|
3701
|
+
/* @__PURE__ */ jsx27(AuiIf2, { condition: (s) => !s.message.isCopied, children: /* @__PURE__ */ jsx27(CopyIcon2, { className: "size-3" }) })
|
|
3702
|
+
]
|
|
3703
|
+
}
|
|
3704
|
+
) }),
|
|
3705
|
+
/* @__PURE__ */ jsx27(ActionBarPrimitive.Reload, { asChild: true, children: /* @__PURE__ */ jsx27(
|
|
3706
|
+
TooltipIconButton,
|
|
3707
|
+
{
|
|
3708
|
+
tooltip: "Regenerate",
|
|
3709
|
+
variant: "ghost",
|
|
3710
|
+
className: ASSISTANT_ACTION_ICON_CLASS,
|
|
3711
|
+
children: /* @__PURE__ */ jsx27(RefreshCwIcon, { className: "size-3" })
|
|
3712
|
+
}
|
|
3713
|
+
) }),
|
|
3714
|
+
/* @__PURE__ */ jsxs15(ActionBarMorePrimitive.Root, { children: [
|
|
3715
|
+
/* @__PURE__ */ jsx27(ActionBarMorePrimitive.Trigger, { asChild: true, children: /* @__PURE__ */ jsx27(
|
|
1431
3716
|
TooltipIconButton,
|
|
1432
3717
|
{
|
|
1433
3718
|
tooltip: "More",
|
|
1434
|
-
|
|
1435
|
-
|
|
3719
|
+
variant: "ghost",
|
|
3720
|
+
className: cn(
|
|
3721
|
+
ASSISTANT_ACTION_ICON_CLASS,
|
|
3722
|
+
"data-[state=open]:text-muted-foreground/80"
|
|
3723
|
+
),
|
|
3724
|
+
children: /* @__PURE__ */ jsx27(MoreHorizontalIcon, { className: "size-3" })
|
|
1436
3725
|
}
|
|
1437
3726
|
) }),
|
|
1438
|
-
/* @__PURE__ */
|
|
3727
|
+
/* @__PURE__ */ jsx27(
|
|
1439
3728
|
ActionBarMorePrimitive.Content,
|
|
1440
3729
|
{
|
|
1441
3730
|
side: "bottom",
|
|
1442
3731
|
align: "start",
|
|
1443
|
-
className: "aui-action-bar-more-content z-50 min-w-
|
|
1444
|
-
children: /* @__PURE__ */
|
|
1445
|
-
/* @__PURE__ */
|
|
3732
|
+
className: "aui-action-bar-more-content z-50 min-w-36 overflow-hidden rounded-lg border border-neutral-200 bg-white p-1 text-foreground shadow-md dark:border-white/10 dark:bg-zinc-900",
|
|
3733
|
+
children: /* @__PURE__ */ jsx27(ActionBarPrimitive.ExportMarkdown, { asChild: true, children: /* @__PURE__ */ jsxs15(ActionBarMorePrimitive.Item, { className: "aui-action-bar-more-item flex cursor-pointer select-none items-center gap-2 rounded-md px-2 py-1.5 text-sm outline-none hover:bg-neutral-100 focus:bg-neutral-100 dark:hover:bg-zinc-800 dark:focus:bg-zinc-800", children: [
|
|
3734
|
+
/* @__PURE__ */ jsx27(DownloadIcon, { className: "size-4 shrink-0" }),
|
|
1446
3735
|
"Export as Markdown"
|
|
1447
3736
|
] }) })
|
|
1448
3737
|
}
|
|
@@ -1452,81 +3741,440 @@ var AssistantActionBar = () => {
|
|
|
1452
3741
|
}
|
|
1453
3742
|
);
|
|
1454
3743
|
};
|
|
3744
|
+
var UserMessageText = () => {
|
|
3745
|
+
return /* @__PURE__ */ jsx27("span", { className: "whitespace-pre-wrap", children: /* @__PURE__ */ jsx27(MessagePartPrimitive.Text, { smooth: false }) });
|
|
3746
|
+
};
|
|
1455
3747
|
var UserMessage = () => {
|
|
1456
|
-
return /* @__PURE__ */
|
|
3748
|
+
return /* @__PURE__ */ jsxs15(
|
|
1457
3749
|
MessagePrimitive2.Root,
|
|
1458
3750
|
{
|
|
1459
|
-
className: "aui-user-message-root
|
|
3751
|
+
className: "aui-user-message-root mx-auto flex w-full max-w-(--thread-max-width) flex-col items-end gap-2 px-2 py-3",
|
|
1460
3752
|
"data-role": "user",
|
|
1461
3753
|
children: [
|
|
1462
|
-
/* @__PURE__ */
|
|
1463
|
-
/* @__PURE__ */
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
3754
|
+
/* @__PURE__ */ jsx27(UserMessageAttachments, {}),
|
|
3755
|
+
/* @__PURE__ */ jsxs15(
|
|
3756
|
+
motion4.div,
|
|
3757
|
+
{
|
|
3758
|
+
className: "aui-user-message-content relative inline-block max-w-[80%] rounded-2xl bg-neutral-200 px-4 py-2.5 text-foreground dark:bg-neutral-700",
|
|
3759
|
+
initial: { opacity: 0, y: 8, scale: 0.99 },
|
|
3760
|
+
animate: { opacity: 1, y: 0, scale: 1 },
|
|
3761
|
+
transition: { duration: 0.65, ease: luxuryEase },
|
|
3762
|
+
children: [
|
|
3763
|
+
/* @__PURE__ */ jsx27(MessagePrimitive2.Parts, { components: { Text: UserMessageText } }),
|
|
3764
|
+
/* @__PURE__ */ jsx27("div", { className: "aui-user-action-bar-wrapper absolute top-1/2 left-0 -translate-x-full -translate-y-1/2 pr-2", children: /* @__PURE__ */ jsx27(UserActionBar, {}) })
|
|
3765
|
+
]
|
|
3766
|
+
}
|
|
3767
|
+
)
|
|
1467
3768
|
]
|
|
1468
3769
|
}
|
|
1469
3770
|
);
|
|
1470
3771
|
};
|
|
1471
3772
|
var UserActionBar = () => {
|
|
1472
|
-
return /* @__PURE__ */
|
|
3773
|
+
return /* @__PURE__ */ jsx27(
|
|
1473
3774
|
ActionBarPrimitive.Root,
|
|
1474
3775
|
{
|
|
1475
3776
|
hideWhenRunning: true,
|
|
1476
3777
|
autohide: "not-last",
|
|
1477
3778
|
className: "aui-user-action-bar-root flex flex-col items-end",
|
|
1478
|
-
children: /* @__PURE__ */
|
|
3779
|
+
children: /* @__PURE__ */ jsx27(ActionBarPrimitive.Edit, { asChild: true, children: /* @__PURE__ */ jsx27(
|
|
3780
|
+
TooltipIconButton,
|
|
3781
|
+
{
|
|
3782
|
+
tooltip: "Edit",
|
|
3783
|
+
variant: "ghost",
|
|
3784
|
+
className: ASSISTANT_ACTION_ICON_CLASS,
|
|
3785
|
+
children: /* @__PURE__ */ jsx27(PencilIcon, { className: "size-3" })
|
|
3786
|
+
}
|
|
3787
|
+
) })
|
|
1479
3788
|
}
|
|
1480
3789
|
);
|
|
1481
3790
|
};
|
|
1482
3791
|
var EditComposer = () => {
|
|
1483
|
-
return /* @__PURE__ */
|
|
1484
|
-
/* @__PURE__ */
|
|
1485
|
-
|
|
3792
|
+
return /* @__PURE__ */ jsx27(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__ */ jsxs15(ComposerPrimitive3.Root, { className: "aui-edit-composer-root ml-auto flex w-full max-w-[85%] flex-col rounded-2xl bg-muted", children: [
|
|
3793
|
+
/* @__PURE__ */ jsx27(
|
|
3794
|
+
ComposerPrimitive3.Input,
|
|
1486
3795
|
{
|
|
1487
3796
|
className: "aui-edit-composer-input min-h-14 w-full resize-none bg-transparent p-4 text-foreground text-sm outline-none",
|
|
1488
3797
|
autoFocus: true
|
|
1489
3798
|
}
|
|
1490
3799
|
),
|
|
1491
|
-
/* @__PURE__ */
|
|
1492
|
-
/* @__PURE__ */
|
|
1493
|
-
/* @__PURE__ */
|
|
3800
|
+
/* @__PURE__ */ jsxs15("div", { className: "aui-edit-composer-footer mx-3 mb-3 flex items-center gap-2 self-end", children: [
|
|
3801
|
+
/* @__PURE__ */ jsx27(ComposerPrimitive3.Cancel, { asChild: true, children: /* @__PURE__ */ jsx27(TimbalV2Button, { variant: "ghost", size: "sm", children: "Cancel" }) }),
|
|
3802
|
+
/* @__PURE__ */ jsx27(ComposerPrimitive3.Send, { asChild: true, children: /* @__PURE__ */ jsx27(TimbalV2Button, { variant: "primary", size: "sm", children: "Update" }) })
|
|
1494
3803
|
] })
|
|
1495
3804
|
] }) });
|
|
1496
3805
|
};
|
|
1497
3806
|
|
|
1498
3807
|
// src/components/chat.tsx
|
|
1499
|
-
import { jsx as
|
|
3808
|
+
import { jsx as jsx28 } from "react/jsx-runtime";
|
|
1500
3809
|
function TimbalChat({
|
|
1501
3810
|
workforceId,
|
|
1502
3811
|
baseUrl,
|
|
1503
3812
|
fetch: fetch2,
|
|
3813
|
+
attachments,
|
|
3814
|
+
attachmentsUploadUrl,
|
|
3815
|
+
attachmentsAccept,
|
|
3816
|
+
debug,
|
|
1504
3817
|
...threadProps
|
|
1505
3818
|
}) {
|
|
1506
|
-
return /* @__PURE__ */
|
|
3819
|
+
return /* @__PURE__ */ jsx28(
|
|
3820
|
+
TimbalRuntimeProvider,
|
|
3821
|
+
{
|
|
3822
|
+
workforceId,
|
|
3823
|
+
baseUrl,
|
|
3824
|
+
fetch: fetch2,
|
|
3825
|
+
attachments,
|
|
3826
|
+
attachmentsUploadUrl,
|
|
3827
|
+
attachmentsAccept,
|
|
3828
|
+
debug,
|
|
3829
|
+
children: /* @__PURE__ */ jsx28(Thread, { ...threadProps })
|
|
3830
|
+
}
|
|
3831
|
+
);
|
|
3832
|
+
}
|
|
3833
|
+
|
|
3834
|
+
// src/artifacts/agent-instructions.ts
|
|
3835
|
+
var ARTIFACT_AGENT_INSTRUCTIONS = `
|
|
3836
|
+
## Rich artifacts (Timbal chat UI)
|
|
3837
|
+
|
|
3838
|
+
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.
|
|
3839
|
+
|
|
3840
|
+
### Delivery channels (either works)
|
|
3841
|
+
|
|
3842
|
+
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.
|
|
3843
|
+
2. **Inline markdown fence** \u2014 embed the same JSON inside a fenced block:
|
|
3844
|
+
|
|
3845
|
+
\`\`\`timbal-artifact
|
|
3846
|
+
{"type":"chart","data":[{"month":"Jan","sales":120}]}
|
|
3847
|
+
\`\`\`
|
|
3848
|
+
|
|
3849
|
+
The alias \`\`\`timbal\`\`\` is also accepted.
|
|
3850
|
+
|
|
3851
|
+
### Built-in artifact types
|
|
3852
|
+
|
|
3853
|
+
| \`type\` | Use for |
|
|
3854
|
+
|---|---|
|
|
3855
|
+
| \`chart\` | Bar, line, area, or pie charts. Fields: \`data\`, optional \`chartType\`, \`xKey\`, \`dataKey\`, \`title\`, \`unit\`. |
|
|
3856
|
+
| \`table\` | Tabular data. Fields: \`rows\`, optional \`columns\`, \`title\`. |
|
|
3857
|
+
| \`question\` | In-thread multiple choice. Fields: \`options: [{ id, label, description? }]\`, optional \`prompt\`, \`multi\`. User replies are sent back as a normal user message. |
|
|
3858
|
+
| \`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). |
|
|
3859
|
+
| \`json\` | Fallback structured view. Fields: \`data\`, optional \`title\`. |
|
|
3860
|
+
| \`ui\` | **Interactive UI** composed from a fixed node palette (hover, click, drag). See below. |
|
|
3861
|
+
|
|
3862
|
+
### When to use \`type: "html"\`
|
|
3863
|
+
|
|
3864
|
+
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\`.
|
|
3865
|
+
|
|
3866
|
+
- Inline \`<style>\`, \`<script>\`, SVG, and canvas are supported.
|
|
3867
|
+
- Default \`sandboxed: true\` runs in an isolated iframe with scripts enabled.
|
|
3868
|
+
- Set \`sandboxed: false\` only for trusted content that needs external CDN scripts/styles or full DOM freedom.
|
|
3869
|
+
- Prefer \`ui\` when controls should send chat messages or host events; prefer \`html\` for self-contained mini-apps and visual demos.
|
|
3870
|
+
|
|
3871
|
+
### When to use \`type: "ui"\`
|
|
3872
|
+
|
|
3873
|
+
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.
|
|
3874
|
+
|
|
3875
|
+
Each \`ui\` artifact has:
|
|
3876
|
+
|
|
3877
|
+
- \`initialState\` \u2014 optional object seeding local state (per widget instance).
|
|
3878
|
+
- \`root\` \u2014 a single node tree (see node kinds below).
|
|
3879
|
+
- optional \`title\` \u2014 card heading.
|
|
3880
|
+
|
|
3881
|
+
**Bindings:** anywhere a primitive is accepted, you may use \`{ "$bind": "dotted.path" }\` to read from \`initialState\` / local state (e.g. \`{ "$bind": "qty" }\`).
|
|
3882
|
+
|
|
3883
|
+
**Actions:** nodes may attach \`onClick\`, \`onChange\`, or \`onDragEnd\` with one action or an array:
|
|
3884
|
+
|
|
3885
|
+
| Action | Shape | Effect |
|
|
3886
|
+
|---|---|---|
|
|
3887
|
+
| User message | \`{ "kind": "message", "text": "..." }\` or \`{ "kind": "message", "text": { "$bind": "path" } }\` | Sends text as the next user message. |
|
|
3888
|
+
| Set state | \`{ "kind": "set", "path": "foo", "value": 1 }\` | Writes local widget state. |
|
|
3889
|
+
| Toggle boolean | \`{ "kind": "toggle", "path": "enabled" }\` | Flips a boolean at \`path\`. |
|
|
3890
|
+
| Host event | \`{ "kind": "emit", "name": "event-name", "payload": { ... } }\` | Bubbles to the host app (\`onArtifactEvent\` on \`<Thread>\`). |
|
|
3891
|
+
|
|
3892
|
+
### \`ui\` node palette (\`root.kind\`)
|
|
3893
|
+
|
|
3894
|
+
| \`kind\` | Purpose | Key fields |
|
|
3895
|
+
|---|---|---|
|
|
3896
|
+
| \`box\` | Layout container | \`children\`, \`direction\` (\`row\`/\`col\`), \`gap\`, \`padding\`, \`align\`, \`justify\`, \`wrap\` |
|
|
3897
|
+
| \`text\` | Body text | \`value\`, optional \`muted\`, \`size\`, \`weight\` |
|
|
3898
|
+
| \`heading\` | Heading | \`value\`, optional \`level\` (1\u20134) |
|
|
3899
|
+
| \`badge\` | Pill label | \`value\`, optional \`tone\` (\`default\`, \`primary\`, \`success\`, \`warn\`, \`danger\`) |
|
|
3900
|
+
| \`button\` | Clickable button | \`label\`, optional \`variant\`, \`size\`, \`disabled\`, \`onClick\` |
|
|
3901
|
+
| \`toggle\` | Boolean switch | \`binding\` (state path), optional \`label\`, \`onChange\` |
|
|
3902
|
+
| \`slider\` | Numeric range | \`binding\`, optional \`min\`, \`max\`, \`step\`, \`label\`, \`showValue\`, \`onChange\` |
|
|
3903
|
+
| \`tooltip\` | Hover tooltip | \`content\`, \`child\` (single node), optional \`side\` |
|
|
3904
|
+
| \`draggable\` | Drag gesture | \`child\`, optional \`axis\` (\`x\`/\`y\`/\`both\`), \`snapBack\`, \`onDragEnd\` |
|
|
3905
|
+
| \`custom\` | Host-registered widget | \`name\`, optional \`props\`, \`children\` \u2014 only if the app registered that name |
|
|
3906
|
+
|
|
3907
|
+
### Example \`ui\` artifact
|
|
3908
|
+
|
|
3909
|
+
\`\`\`json
|
|
3910
|
+
{
|
|
3911
|
+
"type": "ui",
|
|
3912
|
+
"title": "Configure plan",
|
|
3913
|
+
"initialState": { "qty": 1, "premium": false },
|
|
3914
|
+
"root": {
|
|
3915
|
+
"kind": "box",
|
|
3916
|
+
"direction": "col",
|
|
3917
|
+
"gap": 3,
|
|
3918
|
+
"children": [
|
|
3919
|
+
{ "kind": "heading", "value": "Choose quantity", "level": 3 },
|
|
3920
|
+
{
|
|
3921
|
+
"kind": "tooltip",
|
|
3922
|
+
"content": "Drag to adjust quantity",
|
|
3923
|
+
"child": {
|
|
3924
|
+
"kind": "slider",
|
|
3925
|
+
"binding": "qty",
|
|
3926
|
+
"min": 1,
|
|
3927
|
+
"max": 50,
|
|
3928
|
+
"label": "Quantity",
|
|
3929
|
+
"onChange": { "kind": "emit", "name": "qty-changed" }
|
|
3930
|
+
}
|
|
3931
|
+
},
|
|
3932
|
+
{ "kind": "toggle", "binding": "premium", "label": "Premium support" },
|
|
3933
|
+
{
|
|
3934
|
+
"kind": "button",
|
|
3935
|
+
"label": "Confirm",
|
|
3936
|
+
"onClick": { "kind": "message", "text": { "$bind": "qty" } }
|
|
3937
|
+
}
|
|
3938
|
+
]
|
|
3939
|
+
}
|
|
1507
3940
|
}
|
|
3941
|
+
\`\`\`
|
|
3942
|
+
|
|
3943
|
+
### Rules
|
|
3944
|
+
|
|
3945
|
+
- Always set \`type\` to a built-in value above unless the app documented a custom type.
|
|
3946
|
+
- Prefer \`ui\` over \`html\` when actions must bubble to the host chat (\`message\`, \`emit\`).
|
|
3947
|
+
- Prefer \`question\` for simple A/B/C choices; use \`ui\` when you need sliders, toggles, drag, or multi-control layouts.
|
|
3948
|
+
- Keep \`data\` arrays reasonably small (charts/tables).
|
|
3949
|
+
|
|
3950
|
+
### After calling an artifact tool (critical)
|
|
3951
|
+
|
|
3952
|
+
When you call a tool that returns an artifact (\`make_chart\`, \`ask_question\`, \`show_table\`, \`show_html\`, \`make_ui_demo\`, etc.):
|
|
3953
|
+
|
|
3954
|
+
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.
|
|
3955
|
+
2. **Do not** emit a matching \`\`\`timbal-artifact\`\`\` block for the same payload \u2014 pick **one** channel (tool result only).
|
|
3956
|
+
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.
|
|
3957
|
+
4. Treat the widget as visible to the user; refer to it as "above" / "the chart" / "the choices" \u2014 never reproduce its contents.
|
|
3958
|
+
`.trim();
|
|
1508
3959
|
|
|
1509
3960
|
// src/index.ts
|
|
1510
3961
|
import {
|
|
1511
3962
|
ThreadPrimitive as ThreadPrimitive2,
|
|
1512
3963
|
MessagePrimitive as MessagePrimitive3,
|
|
1513
|
-
|
|
3964
|
+
MessagePartPrimitive as MessagePartPrimitive2,
|
|
3965
|
+
ComposerPrimitive as ComposerPrimitive4,
|
|
1514
3966
|
ActionBarPrimitive as ActionBarPrimitive2,
|
|
1515
|
-
|
|
1516
|
-
|
|
3967
|
+
ActionBarMorePrimitive as ActionBarMorePrimitive2,
|
|
3968
|
+
ErrorPrimitive as ErrorPrimitive2,
|
|
3969
|
+
AuiIf as AuiIf3,
|
|
3970
|
+
AssistantRuntimeProvider as AssistantRuntimeProvider2,
|
|
3971
|
+
useThread as useThread2,
|
|
3972
|
+
useThreadRuntime as useThreadRuntime4,
|
|
1517
3973
|
useMessageRuntime,
|
|
1518
|
-
useComposerRuntime
|
|
3974
|
+
useComposerRuntime as useComposerRuntime2,
|
|
3975
|
+
useAuiState as useAuiState3
|
|
1519
3976
|
} from "@assistant-ui/react";
|
|
1520
3977
|
|
|
3978
|
+
// src/hooks/use-workforces.ts
|
|
3979
|
+
import { useEffect as useEffect5, useMemo as useMemo7, useRef as useRef2, useState as useState8 } from "react";
|
|
3980
|
+
function useWorkforces(options = {}) {
|
|
3981
|
+
const { baseUrl = "/api", fetch: fetchFn, pickInitial } = options;
|
|
3982
|
+
const [workforces, setWorkforces] = useState8([]);
|
|
3983
|
+
const [selectedId, setSelectedId] = useState8("");
|
|
3984
|
+
const [isLoading, setIsLoading] = useState8(true);
|
|
3985
|
+
const [error, setError] = useState8(null);
|
|
3986
|
+
const fetchFnRef = useRef2(fetchFn ?? authFetch);
|
|
3987
|
+
useEffect5(() => {
|
|
3988
|
+
fetchFnRef.current = fetchFn ?? authFetch;
|
|
3989
|
+
}, [fetchFn]);
|
|
3990
|
+
const pickInitialRef = useRef2(pickInitial);
|
|
3991
|
+
useEffect5(() => {
|
|
3992
|
+
pickInitialRef.current = pickInitial;
|
|
3993
|
+
}, [pickInitial]);
|
|
3994
|
+
const load = useMemo7(() => {
|
|
3995
|
+
return async () => {
|
|
3996
|
+
setIsLoading(true);
|
|
3997
|
+
setError(null);
|
|
3998
|
+
try {
|
|
3999
|
+
const res = await fetchFnRef.current(`${baseUrl}/workforce`);
|
|
4000
|
+
if (!res.ok) throw new Error(`Failed to load workforces (${res.status})`);
|
|
4001
|
+
const data = await res.json();
|
|
4002
|
+
setWorkforces(data);
|
|
4003
|
+
setSelectedId((current) => {
|
|
4004
|
+
if (current && data.some((w) => idOf(w) === current)) return current;
|
|
4005
|
+
const initial = pickInitialRef.current?.(data) ?? data.find((w) => w.type === "agent") ?? data[0];
|
|
4006
|
+
return initial ? idOf(initial) : "";
|
|
4007
|
+
});
|
|
4008
|
+
} catch (err) {
|
|
4009
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
4010
|
+
} finally {
|
|
4011
|
+
setIsLoading(false);
|
|
4012
|
+
}
|
|
4013
|
+
};
|
|
4014
|
+
}, [baseUrl]);
|
|
4015
|
+
useEffect5(() => {
|
|
4016
|
+
load();
|
|
4017
|
+
}, [load]);
|
|
4018
|
+
const selected = useMemo7(
|
|
4019
|
+
() => workforces.find((w) => idOf(w) === selectedId),
|
|
4020
|
+
[workforces, selectedId]
|
|
4021
|
+
);
|
|
4022
|
+
return {
|
|
4023
|
+
workforces,
|
|
4024
|
+
selectedId,
|
|
4025
|
+
setSelectedId,
|
|
4026
|
+
selected,
|
|
4027
|
+
isLoading,
|
|
4028
|
+
error,
|
|
4029
|
+
refresh: load
|
|
4030
|
+
};
|
|
4031
|
+
}
|
|
4032
|
+
function idOf(item) {
|
|
4033
|
+
return item.id ?? item.uid ?? item.name ?? "";
|
|
4034
|
+
}
|
|
4035
|
+
|
|
4036
|
+
// src/components/workforce-selector.tsx
|
|
4037
|
+
import { ChevronDownIcon } from "lucide-react";
|
|
4038
|
+
import { jsx as jsx29, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
4039
|
+
var WorkforceSelector = ({
|
|
4040
|
+
workforces,
|
|
4041
|
+
value,
|
|
4042
|
+
onChange,
|
|
4043
|
+
hideWhenSingle = true,
|
|
4044
|
+
className,
|
|
4045
|
+
placeholder = "Select agent"
|
|
4046
|
+
}) => {
|
|
4047
|
+
if (workforces.length === 0) return null;
|
|
4048
|
+
if (hideWhenSingle && workforces.length === 1) return null;
|
|
4049
|
+
return /* @__PURE__ */ jsxs16(
|
|
4050
|
+
"div",
|
|
4051
|
+
{
|
|
4052
|
+
className: cn(
|
|
4053
|
+
"aui-workforce-selector relative inline-flex items-center",
|
|
4054
|
+
studioTopbarPillHeightClass,
|
|
4055
|
+
studioSecondaryChromeClass,
|
|
4056
|
+
"rounded-full",
|
|
4057
|
+
className
|
|
4058
|
+
),
|
|
4059
|
+
children: [
|
|
4060
|
+
/* @__PURE__ */ jsxs16(
|
|
4061
|
+
"select",
|
|
4062
|
+
{
|
|
4063
|
+
className: "aui-workforce-selector-input h-full cursor-pointer appearance-none rounded-full border-none bg-transparent pr-8 pl-3.5 text-sm font-medium text-foreground outline-none focus:outline-none",
|
|
4064
|
+
value,
|
|
4065
|
+
onChange: (e) => onChange(e.target.value),
|
|
4066
|
+
"aria-label": placeholder,
|
|
4067
|
+
children: [
|
|
4068
|
+
!value && /* @__PURE__ */ jsx29("option", { value: "", children: placeholder }),
|
|
4069
|
+
workforces.map((w) => {
|
|
4070
|
+
const id = idOf2(w);
|
|
4071
|
+
return /* @__PURE__ */ jsx29("option", { value: id, children: w.name ?? id }, id);
|
|
4072
|
+
})
|
|
4073
|
+
]
|
|
4074
|
+
}
|
|
4075
|
+
),
|
|
4076
|
+
/* @__PURE__ */ jsx29(
|
|
4077
|
+
ChevronDownIcon,
|
|
4078
|
+
{
|
|
4079
|
+
className: "aui-workforce-selector-icon pointer-events-none absolute right-3 size-3.5 text-muted-foreground/70",
|
|
4080
|
+
"aria-hidden": true
|
|
4081
|
+
}
|
|
4082
|
+
)
|
|
4083
|
+
]
|
|
4084
|
+
}
|
|
4085
|
+
);
|
|
4086
|
+
};
|
|
4087
|
+
function idOf2(item) {
|
|
4088
|
+
return item.id ?? item.uid ?? item.name ?? "";
|
|
4089
|
+
}
|
|
4090
|
+
|
|
4091
|
+
// src/components/chat-shell.tsx
|
|
4092
|
+
import { jsx as jsx30, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
4093
|
+
var TimbalChatShell = ({
|
|
4094
|
+
workforceId,
|
|
4095
|
+
brand,
|
|
4096
|
+
headerActions,
|
|
4097
|
+
hideWorkforceSelector,
|
|
4098
|
+
className,
|
|
4099
|
+
headerClassName,
|
|
4100
|
+
baseUrl,
|
|
4101
|
+
fetch: fetch2,
|
|
4102
|
+
...chatProps
|
|
4103
|
+
}) => {
|
|
4104
|
+
const { workforces, selectedId, setSelectedId } = useWorkforces({
|
|
4105
|
+
baseUrl,
|
|
4106
|
+
fetch: fetch2
|
|
4107
|
+
});
|
|
4108
|
+
const effectiveId = workforceId ?? selectedId;
|
|
4109
|
+
const showSelector = !hideWorkforceSelector && !workforceId && workforces.length > 0;
|
|
4110
|
+
return /* @__PURE__ */ jsxs17(
|
|
4111
|
+
"div",
|
|
4112
|
+
{
|
|
4113
|
+
className: cn(
|
|
4114
|
+
"aui-chat-shell relative flex h-dvh flex-col overflow-hidden bg-background",
|
|
4115
|
+
className
|
|
4116
|
+
),
|
|
4117
|
+
style: studioChromeShellStyle,
|
|
4118
|
+
children: [
|
|
4119
|
+
/* @__PURE__ */ jsx30(
|
|
4120
|
+
"div",
|
|
4121
|
+
{
|
|
4122
|
+
className: cn(
|
|
4123
|
+
"pointer-events-none absolute inset-0 z-0",
|
|
4124
|
+
studioPlaygroundGradientClass
|
|
4125
|
+
),
|
|
4126
|
+
"aria-hidden": true
|
|
4127
|
+
}
|
|
4128
|
+
),
|
|
4129
|
+
/* @__PURE__ */ jsxs17(
|
|
4130
|
+
"header",
|
|
4131
|
+
{
|
|
4132
|
+
className: cn(
|
|
4133
|
+
"aui-chat-shell-header relative z-10 flex shrink-0 items-center justify-between px-4 pt-[var(--studio-topbar-gap)] pb-2",
|
|
4134
|
+
headerClassName
|
|
4135
|
+
),
|
|
4136
|
+
style: { minHeight: "var(--studio-topbar-height)" },
|
|
4137
|
+
children: [
|
|
4138
|
+
/* @__PURE__ */ jsxs17("div", { className: "flex min-w-0 items-center gap-2", children: [
|
|
4139
|
+
brand,
|
|
4140
|
+
showSelector && /* @__PURE__ */ jsx30(
|
|
4141
|
+
WorkforceSelector,
|
|
4142
|
+
{
|
|
4143
|
+
workforces,
|
|
4144
|
+
value: selectedId,
|
|
4145
|
+
onChange: setSelectedId
|
|
4146
|
+
}
|
|
4147
|
+
)
|
|
4148
|
+
] }),
|
|
4149
|
+
/* @__PURE__ */ jsx30("div", { className: "flex shrink-0 items-center gap-1", children: headerActions })
|
|
4150
|
+
]
|
|
4151
|
+
}
|
|
4152
|
+
),
|
|
4153
|
+
/* @__PURE__ */ jsx30(
|
|
4154
|
+
TimbalChat,
|
|
4155
|
+
{
|
|
4156
|
+
workforceId: effectiveId,
|
|
4157
|
+
baseUrl,
|
|
4158
|
+
fetch: fetch2,
|
|
4159
|
+
className: "relative z-10 min-h-0 flex-1 bg-transparent",
|
|
4160
|
+
...chatProps
|
|
4161
|
+
},
|
|
4162
|
+
effectiveId
|
|
4163
|
+
)
|
|
4164
|
+
]
|
|
4165
|
+
}
|
|
4166
|
+
);
|
|
4167
|
+
};
|
|
4168
|
+
|
|
1521
4169
|
// src/auth/provider.tsx
|
|
1522
4170
|
import {
|
|
1523
|
-
createContext,
|
|
1524
|
-
useCallback as
|
|
1525
|
-
useContext,
|
|
1526
|
-
useEffect as
|
|
1527
|
-
useState as
|
|
4171
|
+
createContext as createContext4,
|
|
4172
|
+
useCallback as useCallback4,
|
|
4173
|
+
useContext as useContext4,
|
|
4174
|
+
useEffect as useEffect6,
|
|
4175
|
+
useState as useState9
|
|
1528
4176
|
} from "react";
|
|
1529
|
-
import { jsx as
|
|
4177
|
+
import { jsx as jsx31 } from "react/jsx-runtime";
|
|
1530
4178
|
function isInsideIframe() {
|
|
1531
4179
|
try {
|
|
1532
4180
|
return typeof window !== "undefined" && window.self !== window.top;
|
|
@@ -1534,9 +4182,9 @@ function isInsideIframe() {
|
|
|
1534
4182
|
return true;
|
|
1535
4183
|
}
|
|
1536
4184
|
}
|
|
1537
|
-
var SessionContext =
|
|
4185
|
+
var SessionContext = createContext4(void 0);
|
|
1538
4186
|
var useSession = () => {
|
|
1539
|
-
const context =
|
|
4187
|
+
const context = useContext4(SessionContext);
|
|
1540
4188
|
if (context === void 0) {
|
|
1541
4189
|
throw new Error("useSession must be used within a SessionProvider");
|
|
1542
4190
|
}
|
|
@@ -1546,10 +4194,10 @@ var SessionProvider = ({
|
|
|
1546
4194
|
children,
|
|
1547
4195
|
enabled = true
|
|
1548
4196
|
}) => {
|
|
1549
|
-
const [user, setUser] =
|
|
1550
|
-
const [loading, setLoading] =
|
|
1551
|
-
const [embedded] =
|
|
1552
|
-
|
|
4197
|
+
const [user, setUser] = useState9(null);
|
|
4198
|
+
const [loading, setLoading] = useState9(enabled);
|
|
4199
|
+
const [embedded] = useState9(isInsideIframe);
|
|
4200
|
+
useEffect6(() => {
|
|
1553
4201
|
if (!enabled) {
|
|
1554
4202
|
setLoading(false);
|
|
1555
4203
|
return;
|
|
@@ -1610,7 +4258,7 @@ var SessionProvider = ({
|
|
|
1610
4258
|
messageCleanup?.();
|
|
1611
4259
|
};
|
|
1612
4260
|
}, [enabled, embedded]);
|
|
1613
|
-
const logout =
|
|
4261
|
+
const logout = useCallback4(() => {
|
|
1614
4262
|
clearTokens();
|
|
1615
4263
|
setUser(null);
|
|
1616
4264
|
const returnTo = encodeURIComponent(
|
|
@@ -1620,7 +4268,7 @@ var SessionProvider = ({
|
|
|
1620
4268
|
() => window.location.href = `/api/auth/login?return_to=${returnTo}`
|
|
1621
4269
|
);
|
|
1622
4270
|
}, []);
|
|
1623
|
-
return /* @__PURE__ */
|
|
4271
|
+
return /* @__PURE__ */ jsx31(
|
|
1624
4272
|
SessionContext.Provider,
|
|
1625
4273
|
{
|
|
1626
4274
|
value: {
|
|
@@ -1637,7 +4285,7 @@ var SessionProvider = ({
|
|
|
1637
4285
|
|
|
1638
4286
|
// src/auth/guard.tsx
|
|
1639
4287
|
import { Loader2 } from "lucide-react";
|
|
1640
|
-
import { jsx as
|
|
4288
|
+
import { jsx as jsx32 } from "react/jsx-runtime";
|
|
1641
4289
|
var AuthGuard = ({
|
|
1642
4290
|
children,
|
|
1643
4291
|
requireAuth = false,
|
|
@@ -1648,7 +4296,7 @@ var AuthGuard = ({
|
|
|
1648
4296
|
return children;
|
|
1649
4297
|
}
|
|
1650
4298
|
if (loading) {
|
|
1651
|
-
return /* @__PURE__ */
|
|
4299
|
+
return /* @__PURE__ */ jsx32("div", { className: "flex items-center justify-center h-screen", children: /* @__PURE__ */ jsx32(Loader2, { className: "w-8 h-8 animate-spin" }) });
|
|
1652
4300
|
}
|
|
1653
4301
|
if (requireAuth && !isAuthenticated && !isEmbedded) {
|
|
1654
4302
|
const returnTo = encodeURIComponent(
|
|
@@ -1660,15 +4308,26 @@ var AuthGuard = ({
|
|
|
1660
4308
|
return children;
|
|
1661
4309
|
};
|
|
1662
4310
|
export {
|
|
4311
|
+
ARTIFACT_AGENT_INSTRUCTIONS,
|
|
4312
|
+
ARTIFACT_FENCE_LANGUAGES,
|
|
4313
|
+
ActionBarMorePrimitive2 as ActionBarMorePrimitive,
|
|
1663
4314
|
ActionBarPrimitive2 as ActionBarPrimitive,
|
|
4315
|
+
ArtifactCard,
|
|
4316
|
+
ArtifactRegistryProvider,
|
|
4317
|
+
ArtifactView,
|
|
4318
|
+
AssistantRuntimeProvider2 as AssistantRuntimeProvider,
|
|
4319
|
+
AuiIf3 as AuiIf,
|
|
1664
4320
|
AuthGuard,
|
|
1665
4321
|
Avatar,
|
|
1666
4322
|
AvatarFallback,
|
|
1667
4323
|
AvatarImage,
|
|
1668
4324
|
Button,
|
|
4325
|
+
ChartArtifactView,
|
|
4326
|
+
Composer,
|
|
1669
4327
|
ComposerAddAttachment,
|
|
1670
4328
|
ComposerAttachments,
|
|
1671
|
-
|
|
4329
|
+
ComposerPrimitive4 as ComposerPrimitive,
|
|
4330
|
+
DEFAULT_UPLOAD_ACCEPT,
|
|
1672
4331
|
Dialog,
|
|
1673
4332
|
DialogClose,
|
|
1674
4333
|
DialogContent,
|
|
@@ -1676,35 +4335,119 @@ export {
|
|
|
1676
4335
|
DialogPortal,
|
|
1677
4336
|
DialogTitle,
|
|
1678
4337
|
DialogTrigger,
|
|
4338
|
+
ErrorPrimitive2 as ErrorPrimitive,
|
|
4339
|
+
HtmlArtifactView,
|
|
4340
|
+
JsonArtifactView,
|
|
1679
4341
|
MarkdownText,
|
|
4342
|
+
MessagePartPrimitive2 as MessagePartPrimitive,
|
|
1680
4343
|
MessagePrimitive3 as MessagePrimitive,
|
|
4344
|
+
QuestionArtifactView,
|
|
4345
|
+
STUDIO_INSET_LEFT,
|
|
4346
|
+
STUDIO_PILL_HEIGHT,
|
|
4347
|
+
STUDIO_SIDEBAR_GAP,
|
|
4348
|
+
STUDIO_SIDEBAR_WIDTH,
|
|
4349
|
+
STUDIO_TOPBAR_GAP,
|
|
4350
|
+
STUDIO_TOPBAR_HEIGHT,
|
|
1681
4351
|
SessionProvider,
|
|
1682
4352
|
Shimmer,
|
|
4353
|
+
Suggestions,
|
|
1683
4354
|
syntax_highlighter_default as SyntaxHighlighter,
|
|
4355
|
+
TIMBAL_V2_BORDER,
|
|
4356
|
+
TIMBAL_V2_FILL,
|
|
4357
|
+
TIMBAL_V2_LABEL,
|
|
4358
|
+
TIMBAL_V2_PILL_SURFACE,
|
|
4359
|
+
TIMBAL_V2_SECONDARY_CHROME,
|
|
4360
|
+
TIMBAL_V2_SHADOW,
|
|
4361
|
+
TIMBAL_V2_SIZE_HEIGHT,
|
|
4362
|
+
TIMBAL_V2_SIZE_ICON,
|
|
4363
|
+
TIMBAL_V2_SIZE_LABEL_PX,
|
|
4364
|
+
TableArtifactView,
|
|
1684
4365
|
Thread,
|
|
1685
4366
|
ThreadPrimitive2 as ThreadPrimitive,
|
|
1686
4367
|
TimbalChat,
|
|
4368
|
+
TimbalChatShell,
|
|
1687
4369
|
TimbalRuntimeProvider,
|
|
4370
|
+
TimbalV2Button,
|
|
4371
|
+
ToolArtifactFallback,
|
|
4372
|
+
ToolBodyPresence,
|
|
1688
4373
|
ToolFallback,
|
|
4374
|
+
ToolMotion,
|
|
4375
|
+
ToolPresence,
|
|
1689
4376
|
Tooltip,
|
|
1690
4377
|
TooltipContent,
|
|
1691
4378
|
TooltipIconButton,
|
|
1692
4379
|
TooltipProvider,
|
|
1693
4380
|
TooltipTrigger,
|
|
4381
|
+
UiArtifactView,
|
|
4382
|
+
UiCustomNodeRegistryProvider,
|
|
4383
|
+
UiEventProvider,
|
|
4384
|
+
UiNodeView,
|
|
1694
4385
|
UserMessageAttachments,
|
|
4386
|
+
WorkforceSelector,
|
|
1695
4387
|
authFetch,
|
|
1696
4388
|
buttonVariants,
|
|
1697
4389
|
clearTokens,
|
|
1698
4390
|
cn,
|
|
4391
|
+
createDefaultAttachmentAdapter,
|
|
4392
|
+
createUploadAttachmentAdapter,
|
|
4393
|
+
defaultArtifactRenderers,
|
|
1699
4394
|
fetchCurrentUser,
|
|
4395
|
+
findMarkdownArtifacts,
|
|
1700
4396
|
getAccessToken,
|
|
4397
|
+
getPath,
|
|
1701
4398
|
getRefreshToken,
|
|
4399
|
+
isArtifact,
|
|
4400
|
+
isArtifactFenceLanguage,
|
|
4401
|
+
isUiBinding,
|
|
4402
|
+
luxuryEase,
|
|
4403
|
+
parseArtifactFromToolResult,
|
|
4404
|
+
parseSSELine2 as parseSSELine,
|
|
1702
4405
|
refreshAccessToken,
|
|
4406
|
+
resolveAttachmentAdapter,
|
|
4407
|
+
resolveBindable,
|
|
1703
4408
|
setAccessToken,
|
|
4409
|
+
setPath,
|
|
1704
4410
|
setRefreshToken,
|
|
1705
|
-
|
|
4411
|
+
splitMarkdownByArtifacts,
|
|
4412
|
+
studioArtifactShellClass,
|
|
4413
|
+
studioChromeShellStyle,
|
|
4414
|
+
studioComposeInputShellClass,
|
|
4415
|
+
studioComposerIoWellClass,
|
|
4416
|
+
studioIntegrationBorder,
|
|
4417
|
+
studioIntegrationCardClass,
|
|
4418
|
+
studioIntegrationIconTileClass,
|
|
4419
|
+
studioIntegrationSurfaceSolid,
|
|
4420
|
+
studioListRowButtonClass,
|
|
4421
|
+
studioPillSurfaceClass,
|
|
4422
|
+
studioPlaygroundGradientClass,
|
|
4423
|
+
studioQuestionOptionClass,
|
|
4424
|
+
studioQuestionOptionSelectedClass,
|
|
4425
|
+
studioSecondaryChromeClass,
|
|
4426
|
+
studioTimelineActionClass,
|
|
4427
|
+
studioTimelineBodyPadClass,
|
|
4428
|
+
studioTimelineChevronClass,
|
|
4429
|
+
studioTimelineDetailClass,
|
|
4430
|
+
studioTimelineRowButtonClass,
|
|
4431
|
+
studioTimelineShimmerActionClass,
|
|
4432
|
+
studioTimelineTextClass,
|
|
4433
|
+
studioToolCardShellClass,
|
|
4434
|
+
studioTopbarIconPillClass,
|
|
4435
|
+
studioTopbarPillHeightClass,
|
|
4436
|
+
toolPresenceTransition,
|
|
4437
|
+
useArtifactRegistry,
|
|
4438
|
+
useAuiState3 as useAuiState,
|
|
4439
|
+
useComposerRuntime2 as useComposerRuntime,
|
|
1706
4440
|
useMessageRuntime,
|
|
4441
|
+
useResolvedSuggestions,
|
|
1707
4442
|
useSession,
|
|
1708
|
-
useThread,
|
|
1709
|
-
|
|
4443
|
+
useThread2 as useThread,
|
|
4444
|
+
useThreadRuntime4 as useThreadRuntime,
|
|
4445
|
+
useTimbalRuntime,
|
|
4446
|
+
useTimbalStream,
|
|
4447
|
+
useToolRunning,
|
|
4448
|
+
useUiCustomNodeRegistry,
|
|
4449
|
+
useUiDispatch,
|
|
4450
|
+
useUiEventEmitter,
|
|
4451
|
+
useUiState,
|
|
4452
|
+
useWorkforces
|
|
1710
4453
|
};
|