@moldable-ai/ui 0.1.1
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/LICENSE +101 -0
- package/dist/components/chat/chat-input.d.ts +14 -0
- package/dist/components/chat/chat-input.d.ts.map +1 -0
- package/dist/components/chat/chat-input.js +36 -0
- package/dist/components/chat/chat-message.d.ts +30 -0
- package/dist/components/chat/chat-message.d.ts.map +1 -0
- package/dist/components/chat/chat-message.js +250 -0
- package/dist/components/chat/chat-messages.d.ts +10 -0
- package/dist/components/chat/chat-messages.d.ts.map +1 -0
- package/dist/components/chat/chat-messages.js +30 -0
- package/dist/components/chat/chat-panel.d.ts +60 -0
- package/dist/components/chat/chat-panel.d.ts.map +1 -0
- package/dist/components/chat/chat-panel.js +115 -0
- package/dist/components/chat/conversation-history.d.ts +18 -0
- package/dist/components/chat/conversation-history.d.ts.map +1 -0
- package/dist/components/chat/conversation-history.js +32 -0
- package/dist/components/chat/index.d.ts +10 -0
- package/dist/components/chat/index.d.ts.map +1 -0
- package/dist/components/chat/index.js +9 -0
- package/dist/components/chat/model-selector.d.ts +16 -0
- package/dist/components/chat/model-selector.d.ts.map +1 -0
- package/dist/components/chat/model-selector.js +9 -0
- package/dist/components/chat/reasoning-effort-selector.d.ts +14 -0
- package/dist/components/chat/reasoning-effort-selector.d.ts.map +1 -0
- package/dist/components/chat/reasoning-effort-selector.js +10 -0
- package/dist/components/chat/thinking-timeline.d.ts +21 -0
- package/dist/components/chat/thinking-timeline.d.ts.map +1 -0
- package/dist/components/chat/thinking-timeline.js +46 -0
- package/dist/components/chat/tool-handlers.d.ts +21 -0
- package/dist/components/chat/tool-handlers.d.ts.map +1 -0
- package/dist/components/chat/tool-handlers.js +434 -0
- package/dist/components/code-block.d.ts +9 -0
- package/dist/components/code-block.d.ts.map +1 -0
- package/dist/components/code-block.js +112 -0
- package/dist/components/markdown.d.ts +10 -0
- package/dist/components/markdown.d.ts.map +1 -0
- package/dist/components/markdown.js +189 -0
- package/dist/components/ui/alert-dialog.d.ts +15 -0
- package/dist/components/ui/alert-dialog.d.ts.map +1 -0
- package/dist/components/ui/alert-dialog.js +38 -0
- package/dist/components/ui/badge.d.ts +10 -0
- package/dist/components/ui/badge.d.ts.map +1 -0
- package/dist/components/ui/badge.js +22 -0
- package/dist/components/ui/button.d.ts +12 -0
- package/dist/components/ui/button.d.ts.map +1 -0
- package/dist/components/ui/button.js +35 -0
- package/dist/components/ui/card.d.ts +9 -0
- package/dist/components/ui/card.d.ts.map +1 -0
- package/dist/components/ui/card.js +16 -0
- package/dist/components/ui/checkbox.d.ts +5 -0
- package/dist/components/ui/checkbox.d.ts.map +1 -0
- package/dist/components/ui/checkbox.js +9 -0
- package/dist/components/ui/collapsible.d.ts +6 -0
- package/dist/components/ui/collapsible.d.ts.map +1 -0
- package/dist/components/ui/collapsible.js +5 -0
- package/dist/components/ui/command.d.ts +19 -0
- package/dist/components/ui/command.d.ts.map +1 -0
- package/dist/components/ui/command.js +29 -0
- package/dist/components/ui/dialog.d.ts +20 -0
- package/dist/components/ui/dialog.d.ts.map +1 -0
- package/dist/components/ui/dialog.js +23 -0
- package/dist/components/ui/dropdown-menu.d.ts +26 -0
- package/dist/components/ui/dropdown-menu.d.ts.map +1 -0
- package/dist/components/ui/dropdown-menu.js +51 -0
- package/dist/components/ui/index.d.ts +19 -0
- package/dist/components/ui/index.d.ts.map +1 -0
- package/dist/components/ui/index.js +18 -0
- package/dist/components/ui/input.d.ts +5 -0
- package/dist/components/ui/input.d.ts.map +1 -0
- package/dist/components/ui/input.js +8 -0
- package/dist/components/ui/label.d.ts +5 -0
- package/dist/components/ui/label.d.ts.map +1 -0
- package/dist/components/ui/label.js +8 -0
- package/dist/components/ui/popover.d.ts +8 -0
- package/dist/components/ui/popover.d.ts.map +1 -0
- package/dist/components/ui/popover.js +16 -0
- package/dist/components/ui/scroll-area.d.ts +6 -0
- package/dist/components/ui/scroll-area.d.ts.map +1 -0
- package/dist/components/ui/scroll-area.js +11 -0
- package/dist/components/ui/select.d.ts +14 -0
- package/dist/components/ui/select.d.ts.map +1 -0
- package/dist/components/ui/select.js +26 -0
- package/dist/components/ui/switch.d.ts +5 -0
- package/dist/components/ui/switch.d.ts.map +1 -0
- package/dist/components/ui/switch.js +8 -0
- package/dist/components/ui/tabs.d.ts +8 -0
- package/dist/components/ui/tabs.d.ts.map +1 -0
- package/dist/components/ui/tabs.js +16 -0
- package/dist/components/ui/textarea.d.ts +5 -0
- package/dist/components/ui/textarea.d.ts.map +1 -0
- package/dist/components/ui/textarea.js +8 -0
- package/dist/components/ui/tooltip.d.ts +8 -0
- package/dist/components/ui/tooltip.d.ts.map +1 -0
- package/dist/components/ui/tooltip.js +17 -0
- package/dist/components/widget-layout.d.ts +10 -0
- package/dist/components/widget-layout.d.ts.map +1 -0
- package/dist/components/widget-layout.js +30 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/lib/commands.d.ts +74 -0
- package/dist/lib/commands.d.ts.map +1 -0
- package/dist/lib/commands.js +73 -0
- package/dist/lib/commands.test.d.ts +2 -0
- package/dist/lib/commands.test.d.ts.map +1 -0
- package/dist/lib/commands.test.js +111 -0
- package/dist/lib/theme.d.ts +25 -0
- package/dist/lib/theme.d.ts.map +1 -0
- package/dist/lib/theme.js +103 -0
- package/dist/lib/utils.d.ts +3 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +5 -0
- package/dist/lib/workspace.d.ts +41 -0
- package/dist/lib/workspace.d.ts.map +1 -0
- package/dist/lib/workspace.js +82 -0
- package/package.json +87 -0
- package/src/styles/index.css +199 -0
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { BookOpen, Check, CheckCheck, ChevronDown, Copy, Download, ExternalLink, FileCode, FileText, FolderOpen, Globe, Plus, Search, Sparkles, Terminal, Trash2, X, } from 'lucide-react';
|
|
4
|
+
import { useState } from 'react';
|
|
5
|
+
import { cn } from '../../lib/utils';
|
|
6
|
+
import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from '../ui/collapsible';
|
|
7
|
+
import { ThinkingTimelineMarker } from './thinking-timeline';
|
|
8
|
+
/**
|
|
9
|
+
* Code block component for displaying command output or file contents
|
|
10
|
+
*/
|
|
11
|
+
function CodeBlock({ children, maxHeight = 200, className, }) {
|
|
12
|
+
return (_jsx("pre", { className: cn('bg-terminal text-terminal-foreground min-w-0 overflow-auto whitespace-pre-wrap break-all rounded-lg p-3 font-mono text-xs', className), style: { maxHeight }, children: children }));
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Copy button component
|
|
16
|
+
*/
|
|
17
|
+
function CopyButton({ text }) {
|
|
18
|
+
const [copied, setCopied] = useState(false);
|
|
19
|
+
const handleCopy = async () => {
|
|
20
|
+
await navigator.clipboard.writeText(text);
|
|
21
|
+
setCopied(true);
|
|
22
|
+
setTimeout(() => setCopied(false), 2000);
|
|
23
|
+
};
|
|
24
|
+
return (_jsx("button", { onClick: handleCopy, className: "text-terminal-muted hover:bg-terminal-border hover:text-terminal-foreground cursor-pointer rounded p-1 transition-colors", title: "Copy command", children: copied ? (_jsx(CheckCheck, { className: "text-success size-3.5" })) : (_jsx(Copy, { className: "size-3.5" })) }));
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Summarize a command for the header (truncate long commands)
|
|
28
|
+
*/
|
|
29
|
+
function summarizeCommand(command) {
|
|
30
|
+
// Get first line only
|
|
31
|
+
const firstLine = command.split('\n')[0];
|
|
32
|
+
// Truncate if too long
|
|
33
|
+
if (firstLine.length > 60) {
|
|
34
|
+
return firstLine.slice(0, 57) + '...';
|
|
35
|
+
}
|
|
36
|
+
return firstLine;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Terminal output component
|
|
40
|
+
*/
|
|
41
|
+
function TerminalOutput({ command, stdout, stderr, exitCode, error, }) {
|
|
42
|
+
const success = !error && (exitCode === 0 || exitCode === undefined);
|
|
43
|
+
const hasOutput = stdout || stderr || error;
|
|
44
|
+
return (_jsxs("div", { className: "border-terminal-border bg-terminal min-w-0 overflow-hidden rounded-lg border", children: [_jsxs("div", { className: "border-terminal-border bg-terminal-header flex min-w-0 items-center gap-2 border-b px-3 py-1.5", children: [_jsx(Terminal, { className: "text-terminal-muted size-3.5 shrink-0" }), _jsx("code", { className: "text-terminal-foreground min-w-0 flex-1 truncate font-mono text-xs", children: summarizeCommand(command) }), _jsx(CopyButton, { text: command }), success ? (_jsx(Check, { className: "text-success size-3.5 shrink-0" })) : (_jsx(X, { className: "text-terminal-error size-3.5 shrink-0" }))] }), _jsxs("div", { className: "max-h-[300px] overflow-auto p-3", children: [_jsxs("div", { className: "text-terminal-foreground mb-2 break-all font-mono text-xs", children: [_jsx("span", { className: "text-terminal-muted", children: "$" }), " ", command] }), hasOutput && (_jsxs("div", { className: "border-terminal-border/50 border-t pt-2", children: [stdout && (_jsx("pre", { className: "text-terminal-stdout whitespace-pre-wrap break-all font-mono text-xs", children: stdout })), stderr && (_jsx("pre", { className: "text-terminal-stderr whitespace-pre-wrap break-all font-mono text-xs", children: stderr })), error && (_jsx("pre", { className: "text-terminal-error whitespace-pre-wrap break-all font-mono text-xs", children: error }))] }))] })] }));
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* File operation indicator (inline, minimal)
|
|
48
|
+
*/
|
|
49
|
+
function FileOperation({ operation, path, success = true, children, }) {
|
|
50
|
+
const icons = {
|
|
51
|
+
read: FileText,
|
|
52
|
+
write: FileText,
|
|
53
|
+
list: FolderOpen,
|
|
54
|
+
check: FileText,
|
|
55
|
+
delete: Trash2,
|
|
56
|
+
edit: FileCode,
|
|
57
|
+
};
|
|
58
|
+
const labels = {
|
|
59
|
+
read: 'Read',
|
|
60
|
+
write: 'Wrote',
|
|
61
|
+
list: 'Listed',
|
|
62
|
+
check: 'Checked',
|
|
63
|
+
delete: 'Deleted',
|
|
64
|
+
edit: 'Edited',
|
|
65
|
+
};
|
|
66
|
+
const Icon = icons[operation];
|
|
67
|
+
return (_jsxs("div", { className: "my-1 min-w-0", children: [_jsxs("div", { className: cn('inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs', success
|
|
68
|
+
? 'bg-muted text-muted-foreground'
|
|
69
|
+
: 'bg-destructive/10 text-destructive'), children: [_jsx(Icon, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "shrink-0 font-medium", children: labels[operation] }), _jsx("code", { className: "bg-background/50 min-w-0 truncate rounded px-1 font-mono", children: path }), success ? (_jsx(Check, { className: "size-3 shrink-0 text-green-600" })) : (_jsx(X, { className: "size-3 shrink-0" }))] }), children] }));
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Truncate a file path for display, keeping the filename visible
|
|
73
|
+
*/
|
|
74
|
+
function truncatePath(path, maxLength = 50) {
|
|
75
|
+
if (path.length <= maxLength)
|
|
76
|
+
return path;
|
|
77
|
+
const parts = path.split('/');
|
|
78
|
+
const filename = parts.pop() || '';
|
|
79
|
+
if (filename.length >= maxLength - 3) {
|
|
80
|
+
return '...' + filename.slice(-(maxLength - 3));
|
|
81
|
+
}
|
|
82
|
+
let result = filename;
|
|
83
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
84
|
+
const next = parts[i] + '/' + result;
|
|
85
|
+
if (next.length > maxLength - 3) {
|
|
86
|
+
return '.../' + result;
|
|
87
|
+
}
|
|
88
|
+
result = next;
|
|
89
|
+
}
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Default tool handlers for Moldable tools
|
|
94
|
+
*/
|
|
95
|
+
export const DEFAULT_TOOL_HANDLERS = {
|
|
96
|
+
readFile: {
|
|
97
|
+
loadingLabel: 'Reading file...',
|
|
98
|
+
marker: ThinkingTimelineMarker.File,
|
|
99
|
+
inline: true,
|
|
100
|
+
renderLoading: () => (_jsxs("div", { className: "bg-muted text-muted-foreground inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(FileText, { className: "size-3.5 shrink-0 animate-pulse" }), _jsx("span", { className: "truncate", children: "Reading file..." })] })),
|
|
101
|
+
renderOutput: (output, toolCallId) => {
|
|
102
|
+
const result = (output ?? {});
|
|
103
|
+
if (!result.success) {
|
|
104
|
+
return (_jsx(FileOperation, { operation: "read", path: result.path || 'file', success: false }, toolCallId));
|
|
105
|
+
}
|
|
106
|
+
// For successful reads, show collapsible with content preview
|
|
107
|
+
return (_jsxs(Collapsible, { className: "my-1 min-w-0", children: [_jsxs(CollapsibleTrigger, { className: "bg-muted text-muted-foreground hover:bg-accent group inline-flex max-w-full cursor-pointer items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(FileText, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "shrink-0 font-medium", children: "Read" }), _jsx("code", { className: "bg-background/50 min-w-0 truncate rounded px-1 font-mono", children: result.path || 'file' }), _jsx(Check, { className: "size-3 shrink-0 text-green-600" }), _jsx(ChevronDown, { className: "size-3 shrink-0 transition-transform group-data-[state=open]:rotate-180" })] }), _jsx(CollapsibleContent, { className: "mt-2", children: _jsxs(CodeBlock, { maxHeight: 200, children: [result.content?.slice(0, 2000), (result.content?.length || 0) > 2000 && '\n... (truncated)'] }) })] }, toolCallId));
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
writeFile: {
|
|
111
|
+
loadingLabel: 'Writing file...',
|
|
112
|
+
marker: ThinkingTimelineMarker.File,
|
|
113
|
+
inline: true,
|
|
114
|
+
renderLoading: (args) => {
|
|
115
|
+
const { path, content } = (args ?? {});
|
|
116
|
+
// Show streaming preview of file content
|
|
117
|
+
if (content) {
|
|
118
|
+
const lines = content.split('\n');
|
|
119
|
+
const previewLines = lines.slice(0, 20);
|
|
120
|
+
const preview = previewLines.join('\n').slice(0, 1000);
|
|
121
|
+
const lineCount = lines.length;
|
|
122
|
+
return (_jsxs("div", { className: "my-1 min-w-0", children: [_jsxs("div", { className: "bg-muted text-muted-foreground inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(FileText, { className: "size-3.5 shrink-0 animate-pulse" }), _jsx("span", { className: "shrink-0 font-medium", children: "Writing" }), _jsx("code", { className: "bg-background/50 min-w-0 truncate rounded px-1 font-mono", children: path ? truncatePath(path) : 'file' }), _jsxs("span", { className: "text-muted-foreground/70 shrink-0", children: ["(", lineCount, " line", lineCount !== 1 ? 's' : '', ")"] })] }), _jsx("div", { className: "mt-2", children: _jsx(CodeBlock, { maxHeight: 200, children: preview }) })] }));
|
|
123
|
+
}
|
|
124
|
+
// Fallback when content hasn't started streaming yet
|
|
125
|
+
return (_jsxs("div", { className: "bg-muted text-muted-foreground inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(FileText, { className: "size-3.5 shrink-0 animate-pulse" }), _jsxs("span", { className: "truncate", children: ["Writing ", path ? truncatePath(path) : 'file', "..."] })] }));
|
|
126
|
+
},
|
|
127
|
+
renderOutput: (output, toolCallId) => {
|
|
128
|
+
const result = (output ?? {});
|
|
129
|
+
if (!result.success) {
|
|
130
|
+
return (_jsx(FileOperation, { operation: "write", path: result.path || 'file', success: false }, toolCallId));
|
|
131
|
+
}
|
|
132
|
+
// Show collapsible with content preview
|
|
133
|
+
return (_jsxs(Collapsible, { className: "my-1 min-w-0", children: [_jsxs(CollapsibleTrigger, { className: "bg-muted text-muted-foreground hover:bg-accent group inline-flex max-w-full cursor-pointer items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(FileText, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "shrink-0 font-medium", children: "Wrote" }), _jsx("code", { className: "bg-background/50 min-w-0 truncate rounded px-1 font-mono", children: truncatePath(result.path || 'file') }), result.lineCount && (_jsxs("span", { className: "text-muted-foreground/70 shrink-0", children: ["(", result.lineCount, " line", result.lineCount !== 1 ? 's' : '', ")"] })), _jsx(Check, { className: "size-3 shrink-0 text-green-600" }), _jsx(ChevronDown, { className: "size-3 shrink-0 transition-transform group-data-[state=open]:rotate-180" })] }), _jsx(CollapsibleContent, { className: "mt-2", children: _jsxs(CodeBlock, { maxHeight: 200, children: [result.preview, result.truncated && '\n... (truncated)'] }) })] }, toolCallId));
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
listDirectory: {
|
|
137
|
+
loadingLabel: 'Listing directory...',
|
|
138
|
+
marker: ThinkingTimelineMarker.Folder,
|
|
139
|
+
inline: true,
|
|
140
|
+
renderLoading: () => (_jsxs("div", { className: "bg-muted text-muted-foreground inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(FolderOpen, { className: "size-3.5 shrink-0 animate-pulse" }), _jsx("span", { className: "truncate", children: "Listing directory..." })] })),
|
|
141
|
+
renderOutput: (output, toolCallId) => {
|
|
142
|
+
const result = (output ?? {});
|
|
143
|
+
if (result.success === false) {
|
|
144
|
+
return (_jsx(FileOperation, { operation: "list", path: result.path || 'directory', success: false }, toolCallId));
|
|
145
|
+
}
|
|
146
|
+
// Format entries for display
|
|
147
|
+
const entries = result.items
|
|
148
|
+
? result.items.map((i) => `${i.type === 'directory' ? '📁' : '📄'} ${i.name}`)
|
|
149
|
+
: result.entries || [];
|
|
150
|
+
return (_jsxs(Collapsible, { className: "my-1 min-w-0", children: [_jsxs(CollapsibleTrigger, { className: "bg-muted text-muted-foreground hover:bg-accent group inline-flex max-w-full cursor-pointer items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(FolderOpen, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "shrink-0 font-medium", children: "Listed" }), _jsx("code", { className: "bg-background/50 min-w-0 truncate rounded px-1 font-mono", children: result.path || 'directory' }), _jsxs("span", { className: "text-muted-foreground/70 shrink-0", children: ["(", entries.length, " items)"] }), _jsx(ChevronDown, { className: "size-3 shrink-0 transition-transform group-data-[state=open]:rotate-180" })] }), _jsx(CollapsibleContent, { className: "mt-2", children: _jsx(CodeBlock, { maxHeight: 200, children: entries.join('\n') }) })] }, toolCallId));
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
fileExists: {
|
|
154
|
+
loadingLabel: 'Checking file...',
|
|
155
|
+
marker: ThinkingTimelineMarker.File,
|
|
156
|
+
inline: true,
|
|
157
|
+
renderLoading: () => (_jsxs("div", { className: "bg-muted text-muted-foreground inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(FileText, { className: "size-3.5 shrink-0 animate-pulse" }), _jsx("span", { className: "truncate", children: "Checking file..." })] })),
|
|
158
|
+
renderOutput: (output, toolCallId) => {
|
|
159
|
+
const result = (output ?? {});
|
|
160
|
+
return (_jsxs("div", { className: "bg-muted text-muted-foreground my-1 inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(FileText, { className: "size-3.5 shrink-0" }), _jsx("code", { className: "bg-background/50 min-w-0 truncate rounded px-1 font-mono", children: result.path || 'file' }), _jsx("span", { className: "shrink-0", children: result.exists ? 'exists' : 'not found' }), result.exists ? (_jsx(Check, { className: "size-3 shrink-0 text-green-600" })) : (_jsx(X, { className: "size-3 shrink-0 text-amber-500" }))] }, toolCallId));
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
executeBashCommand: {
|
|
164
|
+
loadingLabel: 'Running command...',
|
|
165
|
+
marker: ThinkingTimelineMarker.Terminal,
|
|
166
|
+
inline: true,
|
|
167
|
+
renderLoading: (args) => {
|
|
168
|
+
const { command } = (args ?? {});
|
|
169
|
+
// Show streaming command as it's being written
|
|
170
|
+
if (command && command.trim()) {
|
|
171
|
+
return (_jsxs("div", { className: "border-terminal-border bg-terminal my-2 min-w-0 overflow-hidden rounded-lg border", children: [_jsxs("div", { className: "bg-terminal-header flex min-w-0 items-center gap-2 px-3 py-1.5", children: [_jsx(Terminal, { className: "text-terminal-muted size-3.5 shrink-0 animate-pulse" }), _jsx("code", { className: "text-terminal-foreground min-w-0 flex-1 truncate font-mono text-xs", children: summarizeCommand(command) })] }), _jsx("div", { className: "max-h-[150px] overflow-auto p-3", children: _jsxs("div", { className: "text-terminal-foreground break-all font-mono text-xs", children: [_jsx("span", { className: "text-terminal-muted", children: "$" }), " ", command] }) })] }));
|
|
172
|
+
}
|
|
173
|
+
// Fallback when command hasn't started streaming
|
|
174
|
+
return (_jsx("div", { className: "border-terminal-border bg-terminal my-2 min-w-0 overflow-hidden rounded-lg border", children: _jsxs("div", { className: "bg-terminal-header flex min-w-0 items-center gap-2 px-3 py-1.5", children: [_jsx(Terminal, { className: "text-terminal-muted size-3.5 shrink-0 animate-pulse" }), _jsx("code", { className: "text-terminal-foreground/60 min-w-0 flex-1 truncate font-mono text-xs italic", children: "Generating command..." })] }) }));
|
|
175
|
+
},
|
|
176
|
+
renderOutput: (output, toolCallId) => {
|
|
177
|
+
const result = (output ?? {});
|
|
178
|
+
return (_jsx("div", { className: "my-2 min-w-0", children: _jsx(TerminalOutput, { command: result.command || 'command', stdout: result.stdout, stderr: result.stderr, exitCode: result.exitCode, error: result.error }) }, toolCallId));
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
runCommand: {
|
|
182
|
+
loadingLabel: 'Running command...',
|
|
183
|
+
marker: ThinkingTimelineMarker.Terminal,
|
|
184
|
+
inline: true,
|
|
185
|
+
renderLoading: (args) => {
|
|
186
|
+
const { command } = (args ?? {});
|
|
187
|
+
// Show streaming command as it's being written
|
|
188
|
+
if (command && command.trim()) {
|
|
189
|
+
return (_jsxs("div", { className: "border-terminal-border bg-terminal my-2 min-w-0 overflow-hidden rounded-lg border", children: [_jsxs("div", { className: "bg-terminal-header flex min-w-0 items-center gap-2 px-3 py-1.5", children: [_jsx(Terminal, { className: "text-terminal-muted size-3.5 shrink-0 animate-pulse" }), _jsx("code", { className: "text-terminal-foreground min-w-0 flex-1 truncate font-mono text-xs", children: summarizeCommand(command) })] }), _jsx("div", { className: "max-h-[150px] overflow-auto p-3", children: _jsxs("div", { className: "text-terminal-foreground break-all font-mono text-xs", children: [_jsx("span", { className: "text-terminal-muted", children: "$" }), " ", command] }) })] }));
|
|
190
|
+
}
|
|
191
|
+
// Fallback when command hasn't started streaming
|
|
192
|
+
return (_jsx("div", { className: "border-terminal-border bg-terminal my-2 min-w-0 overflow-hidden rounded-lg border", children: _jsxs("div", { className: "bg-terminal-header flex min-w-0 items-center gap-2 px-3 py-1.5", children: [_jsx(Terminal, { className: "text-terminal-muted size-3.5 shrink-0 animate-pulse" }), _jsx("code", { className: "text-terminal-foreground/60 min-w-0 flex-1 truncate font-mono text-xs italic", children: "Preparing command..." })] }) }));
|
|
193
|
+
},
|
|
194
|
+
renderOutput: (output, toolCallId) => {
|
|
195
|
+
const result = (output ?? {});
|
|
196
|
+
return (_jsx("div", { className: "my-2 min-w-0", children: _jsx(TerminalOutput, { command: result.command || 'command', stdout: result.stdout, stderr: result.stderr, exitCode: result.exitCode, error: result.error }) }, toolCallId));
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
200
|
+
// Additional File Operations
|
|
201
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
202
|
+
deleteFile: {
|
|
203
|
+
loadingLabel: 'Deleting file...',
|
|
204
|
+
marker: ThinkingTimelineMarker.File,
|
|
205
|
+
inline: true,
|
|
206
|
+
renderLoading: (args) => {
|
|
207
|
+
const { path } = (args ?? {});
|
|
208
|
+
return (_jsxs("div", { className: "bg-muted text-muted-foreground inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(Trash2, { className: "size-3.5 shrink-0 animate-pulse" }), _jsxs("span", { className: "truncate", children: ["Deleting ", path ? truncatePath(path) : 'file', "..."] })] }));
|
|
209
|
+
},
|
|
210
|
+
renderOutput: (output, toolCallId) => {
|
|
211
|
+
const result = (output ?? {});
|
|
212
|
+
return (_jsx(FileOperation, { operation: "delete", path: result.path || 'file', success: result.success !== false }, toolCallId));
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
editFile: {
|
|
216
|
+
loadingLabel: 'Editing file...',
|
|
217
|
+
marker: ThinkingTimelineMarker.File,
|
|
218
|
+
inline: true,
|
|
219
|
+
renderLoading: (args) => {
|
|
220
|
+
const { path } = (args ?? {});
|
|
221
|
+
return (_jsxs("div", { className: "bg-muted text-muted-foreground inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(FileCode, { className: "size-3.5 shrink-0 animate-pulse" }), _jsxs("span", { className: "truncate", children: ["Editing ", path ? truncatePath(path) : 'file', "..."] })] }));
|
|
222
|
+
},
|
|
223
|
+
renderOutput: (output, toolCallId) => {
|
|
224
|
+
const result = (output ?? {});
|
|
225
|
+
return (_jsx(FileOperation, { operation: "edit", path: result.path || 'file', success: result.success !== false }, toolCallId));
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
229
|
+
// Search Operations
|
|
230
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
231
|
+
grep: {
|
|
232
|
+
loadingLabel: 'Searching...',
|
|
233
|
+
marker: ThinkingTimelineMarker.Search,
|
|
234
|
+
inline: true,
|
|
235
|
+
renderLoading: (args) => {
|
|
236
|
+
const { pattern } = (args ?? {});
|
|
237
|
+
return (_jsxs("div", { className: "bg-muted text-muted-foreground inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(Search, { className: "size-3.5 shrink-0 animate-pulse" }), _jsxs("span", { className: "truncate", children: ["Searching", pattern
|
|
238
|
+
? ` for "${pattern.slice(0, 20)}${pattern.length > 20 ? '...' : ''}"`
|
|
239
|
+
: '', "..."] })] }));
|
|
240
|
+
},
|
|
241
|
+
renderOutput: (output, toolCallId) => {
|
|
242
|
+
const result = (output ?? {});
|
|
243
|
+
// Handle raw content output (ripgrep format)
|
|
244
|
+
if (result.content && !result.matches) {
|
|
245
|
+
const lines = result.content.split('\n').filter(Boolean);
|
|
246
|
+
return (_jsxs(Collapsible, { className: "my-1 min-w-0", children: [_jsxs(CollapsibleTrigger, { className: "bg-muted text-muted-foreground hover:bg-accent group inline-flex max-w-full cursor-pointer items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(Search, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "shrink-0 font-medium", children: "Search results" }), _jsxs("span", { className: "text-muted-foreground/70 shrink-0", children: ["(", lines.length, " lines)"] }), _jsx(ChevronDown, { className: "size-3 shrink-0 transition-transform group-data-[state=open]:rotate-180" })] }), _jsx(CollapsibleContent, { className: "mt-2", children: _jsx(CodeBlock, { maxHeight: 300, children: result.content }) })] }, toolCallId));
|
|
247
|
+
}
|
|
248
|
+
if (result.success === false || !result.matches?.length) {
|
|
249
|
+
return (_jsxs("div", { className: "bg-muted text-muted-foreground my-1 inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(Search, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "truncate", children: "No matches found" })] }, toolCallId));
|
|
250
|
+
}
|
|
251
|
+
// Group matches by file
|
|
252
|
+
const byFile = result.matches.reduce((acc, match) => {
|
|
253
|
+
if (!acc[match.file])
|
|
254
|
+
acc[match.file] = [];
|
|
255
|
+
acc[match.file].push(match);
|
|
256
|
+
return acc;
|
|
257
|
+
}, {});
|
|
258
|
+
const fileCount = Object.keys(byFile).length;
|
|
259
|
+
const matchCount = result.totalMatches || result.matches.length;
|
|
260
|
+
return (_jsxs(Collapsible, { className: "my-1 min-w-0", children: [_jsxs(CollapsibleTrigger, { className: "bg-muted text-muted-foreground hover:bg-accent group inline-flex max-w-full cursor-pointer items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(Search, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "shrink-0 font-medium", children: "Found" }), _jsxs("span", { className: "shrink-0", children: [matchCount, " match", matchCount !== 1 ? 'es' : '', " in ", fileCount, ' ', "file", fileCount !== 1 ? 's' : ''] }), result.truncated && (_jsx("span", { className: "shrink-0 text-amber-500", children: "(truncated)" })), _jsx(ChevronDown, { className: "size-3 shrink-0 transition-transform group-data-[state=open]:rotate-180" })] }), _jsx(CollapsibleContent, { className: "mt-2", children: _jsxs("div", { className: "space-y-2", children: [Object.entries(byFile)
|
|
261
|
+
.slice(0, 10)
|
|
262
|
+
.map(([file, matches]) => (_jsxs("div", { children: [_jsx("div", { className: "text-muted-foreground mb-1 font-mono text-xs", children: truncatePath(file, 60) }), _jsxs(CodeBlock, { maxHeight: 150, children: [matches
|
|
263
|
+
.slice(0, 5)
|
|
264
|
+
.map((m) => `${m.line}: ${m.content}`)
|
|
265
|
+
.join('\n'), matches.length > 5 &&
|
|
266
|
+
`\n... and ${matches.length - 5} more`] })] }, file))), Object.keys(byFile).length > 10 && (_jsxs("div", { className: "text-muted-foreground text-xs", children: ["... and ", Object.keys(byFile).length - 10, " more files"] }))] }) })] }, toolCallId));
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
globFileSearch: {
|
|
270
|
+
loadingLabel: 'Finding files...',
|
|
271
|
+
marker: ThinkingTimelineMarker.Search,
|
|
272
|
+
inline: true,
|
|
273
|
+
renderLoading: (args) => {
|
|
274
|
+
const { pattern } = (args ?? {});
|
|
275
|
+
return (_jsxs("div", { className: "bg-muted text-muted-foreground inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(Search, { className: "size-3.5 shrink-0 animate-pulse" }), _jsxs("span", { className: "truncate", children: ["Finding files", pattern ? ` matching "${pattern}"` : '', "..."] })] }));
|
|
276
|
+
},
|
|
277
|
+
renderOutput: (output, toolCallId) => {
|
|
278
|
+
const result = (output ?? {});
|
|
279
|
+
if (result.success === false || !result.files?.length) {
|
|
280
|
+
return (_jsxs("div", { className: "bg-muted text-muted-foreground my-1 inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(Search, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "truncate", children: "No files found" })] }, toolCallId));
|
|
281
|
+
}
|
|
282
|
+
return (_jsxs(Collapsible, { className: "my-1 min-w-0", children: [_jsxs(CollapsibleTrigger, { className: "bg-muted text-muted-foreground hover:bg-accent group inline-flex max-w-full cursor-pointer items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(Search, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "shrink-0 font-medium", children: "Found" }), _jsxs("span", { className: "shrink-0", children: [result.files.length, " file", result.files.length !== 1 ? 's' : ''] }), _jsx(ChevronDown, { className: "size-3 shrink-0 transition-transform group-data-[state=open]:rotate-180" })] }), _jsx(CollapsibleContent, { className: "mt-2", children: _jsxs(CodeBlock, { maxHeight: 200, children: [result.files
|
|
283
|
+
.slice(0, 50)
|
|
284
|
+
.map((f) => `📄 ${f}`)
|
|
285
|
+
.join('\n'), result.files.length > 50 &&
|
|
286
|
+
`\n... and ${result.files.length - 50} more files`] }) })] }, toolCallId));
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
290
|
+
// Web Search
|
|
291
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
292
|
+
webSearch: {
|
|
293
|
+
loadingLabel: 'Searching the web...',
|
|
294
|
+
marker: ThinkingTimelineMarker.Search,
|
|
295
|
+
inline: true,
|
|
296
|
+
renderLoading: (args) => {
|
|
297
|
+
const { query } = (args ?? {});
|
|
298
|
+
return (_jsxs("div", { className: "bg-muted text-muted-foreground inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(Globe, { className: "size-3.5 shrink-0 animate-pulse" }), _jsxs("span", { className: "truncate", children: ["Searching", query
|
|
299
|
+
? ` "${query.slice(0, 30)}${query.length > 30 ? '...' : ''}"`
|
|
300
|
+
: ' the web', "..."] })] }));
|
|
301
|
+
},
|
|
302
|
+
renderOutput: (output, toolCallId) => {
|
|
303
|
+
const result = (output ?? {});
|
|
304
|
+
if (result.success === false || !result.results?.length) {
|
|
305
|
+
return (_jsxs("div", { className: "bg-muted text-muted-foreground my-1 inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(Globe, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "truncate", children: "No results found" })] }, toolCallId));
|
|
306
|
+
}
|
|
307
|
+
return (_jsxs(Collapsible, { className: "my-1 min-w-0", children: [_jsxs(CollapsibleTrigger, { className: "bg-muted text-muted-foreground hover:bg-accent group inline-flex max-w-full cursor-pointer items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(Globe, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "shrink-0 font-medium", children: "Web search" }), _jsxs("span", { className: "shrink-0", children: [result.results.length, " result", result.results.length !== 1 ? 's' : ''] }), _jsx(ChevronDown, { className: "size-3 shrink-0 transition-transform group-data-[state=open]:rotate-180" })] }), _jsx(CollapsibleContent, { className: "mt-2", children: _jsxs("div", { className: "space-y-3", children: [result.results.slice(0, 5).map((r, idx) => (_jsxs("div", { className: "bg-muted/50 rounded-md p-2 text-xs", children: [_jsxs("a", { href: r.link, target: "_blank", rel: "noopener noreferrer", className: "text-primary inline-flex items-center gap-1 font-medium hover:underline", children: [r.title, _jsx(ExternalLink, { className: "size-3" })] }), _jsx("div", { className: "text-muted-foreground mt-1 line-clamp-2", children: r.snippet }), _jsx("div", { className: "text-muted-foreground/60 mt-1 truncate font-mono text-[10px]", children: r.link })] }, idx))), result.results.length > 5 && (_jsxs("div", { className: "text-muted-foreground text-xs", children: ["... and ", result.results.length - 5, " more results"] }))] }) })] }, toolCallId));
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
311
|
+
// Agent Skills Management
|
|
312
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
313
|
+
listSkillRepos: {
|
|
314
|
+
loadingLabel: 'Listing skill repositories...',
|
|
315
|
+
marker: ThinkingTimelineMarker.Default,
|
|
316
|
+
inline: true,
|
|
317
|
+
renderLoading: () => (_jsxs("div", { className: "bg-muted text-muted-foreground inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(BookOpen, { className: "size-3.5 shrink-0 animate-pulse" }), _jsx("span", { className: "truncate", children: "Loading skill repositories..." })] })),
|
|
318
|
+
renderOutput: (output, toolCallId) => {
|
|
319
|
+
const result = (output ?? {});
|
|
320
|
+
if (result.success === false || !result.repositories?.length) {
|
|
321
|
+
return (_jsxs("div", { className: "bg-muted text-muted-foreground my-1 inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(BookOpen, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "truncate", children: result.error || 'No skill repositories configured' })] }, toolCallId));
|
|
322
|
+
}
|
|
323
|
+
return (_jsxs(Collapsible, { defaultOpen: true, className: "my-1 min-w-0", children: [_jsxs(CollapsibleTrigger, { className: "bg-muted text-muted-foreground hover:bg-accent group inline-flex max-w-full cursor-pointer items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(BookOpen, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "shrink-0 font-medium", children: "Skill Repositories" }), _jsxs("span", { className: "shrink-0", children: ["(", result.repositories.length, " repo", result.repositories.length !== 1 ? 's' : '', ")"] }), _jsx(ChevronDown, { className: "size-3 shrink-0 transition-transform group-data-[state=open]:rotate-180" })] }), _jsx(CollapsibleContent, { className: "mt-2", children: _jsx("div", { className: "space-y-2", children: result.repositories.map((repo, idx) => (_jsxs("div", { className: "bg-muted/50 rounded-md p-2.5 text-xs", children: [_jsxs("div", { className: "flex items-center gap-2", children: [repo.enabled ? (_jsx(Check, { className: "size-3.5 shrink-0 text-green-600" })) : (_jsx(X, { className: "text-muted-foreground size-3.5 shrink-0" })), _jsx("span", { className: "font-medium", children: repo.name }), _jsx("code", { className: "bg-background/50 text-muted-foreground rounded px-1 font-mono text-[10px]", children: repo.url })] }), _jsxs("div", { className: "text-muted-foreground mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-[10px]", children: [_jsxs("span", { children: ["Mode:", ' ', _jsx("span", { className: "text-foreground font-medium", children: repo.mode })] }), repo.mode !== 'all' && repo.skills.length > 0 && (_jsxs("span", { children: ["Skills:", ' ', _jsxs("span", { className: "text-foreground font-medium", children: [repo.skills.slice(0, 5).join(', '), repo.skills.length > 5 &&
|
|
324
|
+
` +${repo.skills.length - 5} more`] })] })), repo.lastSync && (_jsxs("span", { children: ["Last sync:", ' ', new Date(repo.lastSync).toLocaleDateString()] }))] })] }, idx))) }) })] }, toolCallId));
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
listAvailableSkills: {
|
|
328
|
+
loadingLabel: 'Fetching available skills...',
|
|
329
|
+
marker: ThinkingTimelineMarker.Default,
|
|
330
|
+
inline: true,
|
|
331
|
+
renderLoading: (args) => {
|
|
332
|
+
const { repoName } = (args ?? {});
|
|
333
|
+
return (_jsxs("div", { className: "bg-muted text-muted-foreground inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(Sparkles, { className: "size-3.5 shrink-0 animate-pulse" }), _jsxs("span", { className: "truncate", children: ["Fetching skills", repoName ? ` from ${repoName}` : '', "..."] })] }));
|
|
334
|
+
},
|
|
335
|
+
renderOutput: (output, toolCallId) => {
|
|
336
|
+
const result = (output ?? {});
|
|
337
|
+
if (result.success === false) {
|
|
338
|
+
return (_jsxs("div", { className: "bg-destructive/10 text-destructive my-1 inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(Sparkles, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "truncate", children: result.error || 'Failed to fetch skills' })] }, toolCallId));
|
|
339
|
+
}
|
|
340
|
+
const available = result.available || [];
|
|
341
|
+
const selected = result.selected || [];
|
|
342
|
+
return (_jsxs(Collapsible, { defaultOpen: true, className: "my-1 min-w-0", children: [_jsxs(CollapsibleTrigger, { className: "bg-muted text-muted-foreground hover:bg-accent group inline-flex max-w-full cursor-pointer items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(Sparkles, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "shrink-0 font-medium", children: result.repoName || 'Available Skills' }), _jsxs("span", { className: "shrink-0", children: ["(", selected.length, "/", available.length, " selected)"] }), _jsx(ChevronDown, { className: "size-3 shrink-0 transition-transform group-data-[state=open]:rotate-180" })] }), _jsx(CollapsibleContent, { className: "mt-2", children: _jsxs("div", { className: "bg-muted/50 rounded-md p-2", children: [_jsx("div", { className: "mb-2 flex flex-wrap gap-1.5", children: available.map((skill) => {
|
|
343
|
+
const isSelected = selected.includes(skill);
|
|
344
|
+
return (_jsxs("span", { className: cn('inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] font-medium', isSelected
|
|
345
|
+
? 'bg-primary/10 text-primary'
|
|
346
|
+
: 'bg-muted text-muted-foreground'), children: [isSelected && _jsx(Check, { className: "size-2.5" }), skill] }, skill));
|
|
347
|
+
}) }), result.mode && (_jsxs("div", { className: "text-muted-foreground text-[10px]", children: ["Selection mode:", ' ', _jsx("span", { className: "font-medium", children: result.mode })] }))] }) })] }, toolCallId));
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
syncSkills: {
|
|
351
|
+
loadingLabel: 'Syncing skills...',
|
|
352
|
+
marker: ThinkingTimelineMarker.Default,
|
|
353
|
+
inline: true,
|
|
354
|
+
renderLoading: (args) => {
|
|
355
|
+
const { repoName } = (args ?? {});
|
|
356
|
+
return (_jsxs("div", { className: "bg-muted text-muted-foreground inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(Download, { className: "size-3.5 shrink-0 animate-pulse" }), _jsxs("span", { className: "truncate", children: ["Syncing skills", repoName ? ` from ${repoName}` : '', "..."] })] }));
|
|
357
|
+
},
|
|
358
|
+
renderOutput: (output, toolCallId) => {
|
|
359
|
+
const result = (output ?? {});
|
|
360
|
+
if (result.success === false) {
|
|
361
|
+
return (_jsxs("div", { className: "bg-destructive/10 text-destructive my-1 inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(Download, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "truncate", children: result.error || 'Sync failed' })] }, toolCallId));
|
|
362
|
+
}
|
|
363
|
+
const synced = result.synced || 0;
|
|
364
|
+
const failed = result.failed || 0;
|
|
365
|
+
return (_jsxs("div", { className: "bg-muted text-muted-foreground my-1 min-w-0", children: [_jsxs("div", { className: "inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(Download, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "font-medium", children: "Skills synced" }), _jsxs("span", { className: "text-green-600", children: [synced, " synced"] }), failed > 0 && (_jsxs("span", { className: "text-amber-500", children: [failed, " failed"] })), _jsx(Check, { className: "size-3 shrink-0 text-green-600" })] }), result.skills && result.skills.length > 0 && (_jsx("div", { className: "mt-1.5 flex flex-wrap gap-1 px-2 pb-1", children: result.skills.map((skill) => (_jsxs("span", { className: "bg-primary/10 text-primary inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] font-medium", children: [_jsx(Check, { className: "size-2.5" }), skill] }, skill))) }))] }, toolCallId));
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
addSkillRepo: {
|
|
369
|
+
loadingLabel: 'Adding skill repository...',
|
|
370
|
+
marker: ThinkingTimelineMarker.Default,
|
|
371
|
+
inline: true,
|
|
372
|
+
renderLoading: (args) => {
|
|
373
|
+
const { url } = (args ?? {});
|
|
374
|
+
return (_jsxs("div", { className: "bg-muted text-muted-foreground inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(Plus, { className: "size-3.5 shrink-0 animate-pulse" }), _jsxs("span", { className: "truncate", children: ["Adding repository", url ? `: ${url}` : '', "..."] })] }));
|
|
375
|
+
},
|
|
376
|
+
renderOutput: (output, toolCallId) => {
|
|
377
|
+
const result = (output ?? {});
|
|
378
|
+
if (result.success === false) {
|
|
379
|
+
return (_jsxs("div", { className: "bg-destructive/10 text-destructive my-1 inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(Plus, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "truncate", children: result.error || 'Failed to add repository' })] }, toolCallId));
|
|
380
|
+
}
|
|
381
|
+
return (_jsxs("div", { className: "bg-muted text-muted-foreground my-1 min-w-0", children: [_jsxs("div", { className: "inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(Plus, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "font-medium", children: "Repository added" }), _jsx("code", { className: "bg-background/50 truncate rounded px-1 font-mono text-[10px]", children: result.url }), _jsx(Check, { className: "size-3 shrink-0 text-green-600" })] }), result.availableSkills && result.availableSkills.length > 0 && (_jsxs("div", { className: "text-muted-foreground px-2 pb-1 text-[10px]", children: [result.availableSkills.length, " skill", result.availableSkills.length !== 1 ? 's' : '', " available:", ' ', result.availableSkills.slice(0, 8).join(', '), result.availableSkills.length > 8 &&
|
|
382
|
+
` +${result.availableSkills.length - 8} more`] }))] }, toolCallId));
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
updateSkillSelection: {
|
|
386
|
+
loadingLabel: 'Updating skill selection...',
|
|
387
|
+
marker: ThinkingTimelineMarker.Default,
|
|
388
|
+
inline: true,
|
|
389
|
+
renderLoading: () => (_jsxs("div", { className: "bg-muted text-muted-foreground inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(Sparkles, { className: "size-3.5 shrink-0 animate-pulse" }), _jsx("span", { className: "truncate", children: "Updating skill selection..." })] })),
|
|
390
|
+
renderOutput: (output, toolCallId) => {
|
|
391
|
+
const result = (output ?? {});
|
|
392
|
+
if (result.success === false) {
|
|
393
|
+
return (_jsxs("div", { className: "bg-destructive/10 text-destructive my-1 inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(Sparkles, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "truncate", children: result.error || 'Update failed' })] }, toolCallId));
|
|
394
|
+
}
|
|
395
|
+
return (_jsxs("div", { className: "bg-muted text-muted-foreground my-1 inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(Sparkles, { className: "size-3.5 shrink-0" }), _jsxs("span", { className: "font-medium", children: [result.repoName || 'Selection', " updated"] }), _jsxs("span", { children: ["Mode: ", _jsx("span", { className: "font-medium", children: result.mode })] }), result.skills && result.skills.length > 0 && (_jsxs("span", { className: "truncate", children: ["(", result.skills.length, " skill", result.skills.length !== 1 ? 's' : '', ")"] })), _jsx(Check, { className: "size-3 shrink-0 text-green-600" })] }, toolCallId));
|
|
396
|
+
},
|
|
397
|
+
},
|
|
398
|
+
initSkillsConfig: {
|
|
399
|
+
loadingLabel: 'Initializing skills config...',
|
|
400
|
+
marker: ThinkingTimelineMarker.Default,
|
|
401
|
+
inline: true,
|
|
402
|
+
renderLoading: () => (_jsxs("div", { className: "bg-muted text-muted-foreground inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(BookOpen, { className: "size-3.5 shrink-0 animate-pulse" }), _jsx("span", { className: "truncate", children: "Initializing skills configuration..." })] })),
|
|
403
|
+
renderOutput: (output, toolCallId) => {
|
|
404
|
+
const result = (output ?? {});
|
|
405
|
+
if (result.success === false) {
|
|
406
|
+
return (_jsxs("div", { className: "bg-destructive/10 text-destructive my-1 inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(BookOpen, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "truncate", children: result.error || 'Initialization failed' })] }, toolCallId));
|
|
407
|
+
}
|
|
408
|
+
return (_jsxs("div", { className: "bg-muted text-muted-foreground my-1 min-w-0", children: [_jsxs("div", { className: "inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs", children: [_jsx(BookOpen, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "font-medium", children: "Skills config initialized" }), _jsx(Check, { className: "size-3 shrink-0 text-green-600" })] }), result.repositories && result.repositories.length > 0 && (_jsxs("div", { className: "text-muted-foreground px-2 pb-1 text-[10px]", children: ["Added: ", result.repositories.map((r) => r.name).join(', ')] }))] }, toolCallId));
|
|
409
|
+
},
|
|
410
|
+
},
|
|
411
|
+
};
|
|
412
|
+
/**
|
|
413
|
+
* Get the handler for a tool
|
|
414
|
+
*/
|
|
415
|
+
export function getToolHandler(toolName) {
|
|
416
|
+
const handler = DEFAULT_TOOL_HANDLERS[toolName];
|
|
417
|
+
if (handler) {
|
|
418
|
+
return handler;
|
|
419
|
+
}
|
|
420
|
+
// Default handler for unknown tools
|
|
421
|
+
return {
|
|
422
|
+
loadingLabel: `Using ${toolName}...`,
|
|
423
|
+
marker: ThinkingTimelineMarker.Default,
|
|
424
|
+
inline: false,
|
|
425
|
+
renderOutput: (output, toolCallId) => {
|
|
426
|
+
const outputContent = typeof output === 'string'
|
|
427
|
+
? output
|
|
428
|
+
: output
|
|
429
|
+
? JSON.stringify(output, null, 2)
|
|
430
|
+
: 'No output';
|
|
431
|
+
return (_jsx("div", { className: "min-w-0 px-2 py-1", children: _jsxs("div", { className: "text-muted-foreground min-w-0 font-mono text-xs", children: [_jsxs("div", { className: "truncate font-semibold", children: [toolName, " completed"] }), _jsxs(CodeBlock, { maxHeight: 150, className: "mt-1", children: [outputContent.slice(0, 1000), outputContent.length > 1000 && '\n... (truncated)'] })] }) }, toolCallId));
|
|
432
|
+
},
|
|
433
|
+
};
|
|
434
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type CodeBlockProps = {
|
|
2
|
+
code: string;
|
|
3
|
+
language?: string;
|
|
4
|
+
className?: string;
|
|
5
|
+
theme?: 'light' | 'dark';
|
|
6
|
+
};
|
|
7
|
+
export declare const CodeBlock: import("react").MemoExoticComponent<({ code, language, className, }: CodeBlockProps) => import("react/jsx-runtime").JSX.Element>;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=code-block.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-block.d.ts","sourceRoot":"","sources":["../../src/components/code-block.tsx"],"names":[],"mappings":"AAgDA,KAAK,cAAc,GAAG;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;CACzB,CAAA;AAgHD,eAAO,MAAM,SAAS,uEA1GnB,cAAc,6CAgHhB,CAAA"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { memo, useEffect, useState } from 'react';
|
|
4
|
+
import { cn } from '../lib/utils';
|
|
5
|
+
let highlighterPromise = null;
|
|
6
|
+
async function getHighlighter() {
|
|
7
|
+
if (!highlighterPromise) {
|
|
8
|
+
highlighterPromise = import('shiki').then((shiki) => shiki.createHighlighter({
|
|
9
|
+
themes: ['github-dark', 'github-light'],
|
|
10
|
+
langs: [
|
|
11
|
+
'typescript',
|
|
12
|
+
'javascript',
|
|
13
|
+
'tsx',
|
|
14
|
+
'jsx',
|
|
15
|
+
'json',
|
|
16
|
+
'html',
|
|
17
|
+
'css',
|
|
18
|
+
'bash',
|
|
19
|
+
'shell',
|
|
20
|
+
'python',
|
|
21
|
+
'rust',
|
|
22
|
+
'go',
|
|
23
|
+
'sql',
|
|
24
|
+
'yaml',
|
|
25
|
+
'markdown',
|
|
26
|
+
'diff',
|
|
27
|
+
],
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
// highlighterPromise is guaranteed to be set at this point
|
|
31
|
+
return highlighterPromise;
|
|
32
|
+
}
|
|
33
|
+
const NonMemoizedCodeBlock = ({ code, language = 'text', className, }) => {
|
|
34
|
+
const [highlightedHtml, setHighlightedHtml] = useState(null);
|
|
35
|
+
const [error, setError] = useState(false);
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
let cancelled = false;
|
|
38
|
+
async function highlight() {
|
|
39
|
+
try {
|
|
40
|
+
const highlighter = await getHighlighter();
|
|
41
|
+
if (cancelled)
|
|
42
|
+
return;
|
|
43
|
+
// Normalize language name
|
|
44
|
+
const lang = normalizeLanguage(language);
|
|
45
|
+
// Check if language is supported, fallback to plaintext
|
|
46
|
+
const loadedLangs = highlighter.getLoadedLanguages();
|
|
47
|
+
const supportedLang = loadedLangs.includes(lang) ? lang : 'text';
|
|
48
|
+
// If language wasn't loaded and is supported by shiki, try to load it
|
|
49
|
+
if (!loadedLangs.includes(lang) && lang !== 'text') {
|
|
50
|
+
try {
|
|
51
|
+
await highlighter.loadLanguage(lang);
|
|
52
|
+
if (cancelled)
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// Language not supported, will use plaintext
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const finalLang = highlighter.getLoadedLanguages().includes(lang)
|
|
60
|
+
? lang
|
|
61
|
+
: supportedLang;
|
|
62
|
+
const html = highlighter.codeToHtml(code, {
|
|
63
|
+
lang: finalLang,
|
|
64
|
+
themes: {
|
|
65
|
+
light: 'github-light',
|
|
66
|
+
dark: 'github-dark',
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
if (!cancelled) {
|
|
70
|
+
setHighlightedHtml(html);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
console.error('Shiki highlighting failed:', err);
|
|
75
|
+
if (!cancelled) {
|
|
76
|
+
setError(true);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
highlight();
|
|
81
|
+
return () => {
|
|
82
|
+
cancelled = true;
|
|
83
|
+
};
|
|
84
|
+
}, [code, language]);
|
|
85
|
+
// Fallback: plain code block
|
|
86
|
+
if (error || !highlightedHtml) {
|
|
87
|
+
return (_jsx("pre", { className: cn('bg-muted text-foreground overflow-x-auto rounded-lg p-4 font-mono text-sm', className), children: _jsx("code", { children: code }) }));
|
|
88
|
+
}
|
|
89
|
+
return (_jsx("div", { className: cn('shiki-wrapper overflow-x-auto rounded-lg text-sm', '[&_pre]:overflow-x-auto [&_pre]:p-4', '[&_code]:bg-transparent', className), dangerouslySetInnerHTML: { __html: highlightedHtml } }));
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Normalize common language aliases to shiki's expected names
|
|
93
|
+
*/
|
|
94
|
+
function normalizeLanguage(lang) {
|
|
95
|
+
const aliases = {
|
|
96
|
+
js: 'javascript',
|
|
97
|
+
ts: 'typescript',
|
|
98
|
+
py: 'python',
|
|
99
|
+
rb: 'ruby',
|
|
100
|
+
sh: 'bash',
|
|
101
|
+
zsh: 'bash',
|
|
102
|
+
yml: 'yaml',
|
|
103
|
+
md: 'markdown',
|
|
104
|
+
dockerfile: 'docker',
|
|
105
|
+
plaintext: 'text',
|
|
106
|
+
txt: 'text',
|
|
107
|
+
};
|
|
108
|
+
return aliases[lang.toLowerCase()] ?? lang.toLowerCase();
|
|
109
|
+
}
|
|
110
|
+
export const CodeBlock = memo(NonMemoizedCodeBlock, (prevProps, nextProps) => prevProps.code === nextProps.code &&
|
|
111
|
+
prevProps.language === nextProps.language &&
|
|
112
|
+
prevProps.className === nextProps.className);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
type MarkdownViewerProps = {
|
|
3
|
+
markdown: string;
|
|
4
|
+
className?: string;
|
|
5
|
+
proseSize?: 'xs' | 'sm' | 'base' | 'lg';
|
|
6
|
+
maxHeightToReadMore?: number;
|
|
7
|
+
};
|
|
8
|
+
export declare const Markdown: React.MemoExoticComponent<({ markdown, className, proseSize, maxHeightToReadMore, }: MarkdownViewerProps) => import("react/jsx-runtime").JSX.Element>;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=markdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/components/markdown.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAyB,MAAM,OAAO,CAAA;AAgF7C,KAAK,mBAAmB,GAAG;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAAA;IACvC,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAC7B,CAAA;AAoSD,eAAO,MAAM,QAAQ,uFA5DlB,mBAAmB,6CAmErB,CAAA"}
|