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