@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.
Files changed (117) hide show
  1. package/LICENSE +101 -0
  2. package/dist/components/chat/chat-input.d.ts +14 -0
  3. package/dist/components/chat/chat-input.d.ts.map +1 -0
  4. package/dist/components/chat/chat-input.js +36 -0
  5. package/dist/components/chat/chat-message.d.ts +30 -0
  6. package/dist/components/chat/chat-message.d.ts.map +1 -0
  7. package/dist/components/chat/chat-message.js +250 -0
  8. package/dist/components/chat/chat-messages.d.ts +10 -0
  9. package/dist/components/chat/chat-messages.d.ts.map +1 -0
  10. package/dist/components/chat/chat-messages.js +30 -0
  11. package/dist/components/chat/chat-panel.d.ts +60 -0
  12. package/dist/components/chat/chat-panel.d.ts.map +1 -0
  13. package/dist/components/chat/chat-panel.js +115 -0
  14. package/dist/components/chat/conversation-history.d.ts +18 -0
  15. package/dist/components/chat/conversation-history.d.ts.map +1 -0
  16. package/dist/components/chat/conversation-history.js +32 -0
  17. package/dist/components/chat/index.d.ts +10 -0
  18. package/dist/components/chat/index.d.ts.map +1 -0
  19. package/dist/components/chat/index.js +9 -0
  20. package/dist/components/chat/model-selector.d.ts +16 -0
  21. package/dist/components/chat/model-selector.d.ts.map +1 -0
  22. package/dist/components/chat/model-selector.js +9 -0
  23. package/dist/components/chat/reasoning-effort-selector.d.ts +14 -0
  24. package/dist/components/chat/reasoning-effort-selector.d.ts.map +1 -0
  25. package/dist/components/chat/reasoning-effort-selector.js +10 -0
  26. package/dist/components/chat/thinking-timeline.d.ts +21 -0
  27. package/dist/components/chat/thinking-timeline.d.ts.map +1 -0
  28. package/dist/components/chat/thinking-timeline.js +46 -0
  29. package/dist/components/chat/tool-handlers.d.ts +21 -0
  30. package/dist/components/chat/tool-handlers.d.ts.map +1 -0
  31. package/dist/components/chat/tool-handlers.js +434 -0
  32. package/dist/components/code-block.d.ts +9 -0
  33. package/dist/components/code-block.d.ts.map +1 -0
  34. package/dist/components/code-block.js +112 -0
  35. package/dist/components/markdown.d.ts +10 -0
  36. package/dist/components/markdown.d.ts.map +1 -0
  37. package/dist/components/markdown.js +189 -0
  38. package/dist/components/ui/alert-dialog.d.ts +15 -0
  39. package/dist/components/ui/alert-dialog.d.ts.map +1 -0
  40. package/dist/components/ui/alert-dialog.js +38 -0
  41. package/dist/components/ui/badge.d.ts +10 -0
  42. package/dist/components/ui/badge.d.ts.map +1 -0
  43. package/dist/components/ui/badge.js +22 -0
  44. package/dist/components/ui/button.d.ts +12 -0
  45. package/dist/components/ui/button.d.ts.map +1 -0
  46. package/dist/components/ui/button.js +35 -0
  47. package/dist/components/ui/card.d.ts +9 -0
  48. package/dist/components/ui/card.d.ts.map +1 -0
  49. package/dist/components/ui/card.js +16 -0
  50. package/dist/components/ui/checkbox.d.ts +5 -0
  51. package/dist/components/ui/checkbox.d.ts.map +1 -0
  52. package/dist/components/ui/checkbox.js +9 -0
  53. package/dist/components/ui/collapsible.d.ts +6 -0
  54. package/dist/components/ui/collapsible.d.ts.map +1 -0
  55. package/dist/components/ui/collapsible.js +5 -0
  56. package/dist/components/ui/command.d.ts +19 -0
  57. package/dist/components/ui/command.d.ts.map +1 -0
  58. package/dist/components/ui/command.js +29 -0
  59. package/dist/components/ui/dialog.d.ts +20 -0
  60. package/dist/components/ui/dialog.d.ts.map +1 -0
  61. package/dist/components/ui/dialog.js +23 -0
  62. package/dist/components/ui/dropdown-menu.d.ts +26 -0
  63. package/dist/components/ui/dropdown-menu.d.ts.map +1 -0
  64. package/dist/components/ui/dropdown-menu.js +51 -0
  65. package/dist/components/ui/index.d.ts +19 -0
  66. package/dist/components/ui/index.d.ts.map +1 -0
  67. package/dist/components/ui/index.js +18 -0
  68. package/dist/components/ui/input.d.ts +5 -0
  69. package/dist/components/ui/input.d.ts.map +1 -0
  70. package/dist/components/ui/input.js +8 -0
  71. package/dist/components/ui/label.d.ts +5 -0
  72. package/dist/components/ui/label.d.ts.map +1 -0
  73. package/dist/components/ui/label.js +8 -0
  74. package/dist/components/ui/popover.d.ts +8 -0
  75. package/dist/components/ui/popover.d.ts.map +1 -0
  76. package/dist/components/ui/popover.js +16 -0
  77. package/dist/components/ui/scroll-area.d.ts +6 -0
  78. package/dist/components/ui/scroll-area.d.ts.map +1 -0
  79. package/dist/components/ui/scroll-area.js +11 -0
  80. package/dist/components/ui/select.d.ts +14 -0
  81. package/dist/components/ui/select.d.ts.map +1 -0
  82. package/dist/components/ui/select.js +26 -0
  83. package/dist/components/ui/switch.d.ts +5 -0
  84. package/dist/components/ui/switch.d.ts.map +1 -0
  85. package/dist/components/ui/switch.js +8 -0
  86. package/dist/components/ui/tabs.d.ts +8 -0
  87. package/dist/components/ui/tabs.d.ts.map +1 -0
  88. package/dist/components/ui/tabs.js +16 -0
  89. package/dist/components/ui/textarea.d.ts +5 -0
  90. package/dist/components/ui/textarea.d.ts.map +1 -0
  91. package/dist/components/ui/textarea.js +8 -0
  92. package/dist/components/ui/tooltip.d.ts +8 -0
  93. package/dist/components/ui/tooltip.d.ts.map +1 -0
  94. package/dist/components/ui/tooltip.js +17 -0
  95. package/dist/components/widget-layout.d.ts +10 -0
  96. package/dist/components/widget-layout.d.ts.map +1 -0
  97. package/dist/components/widget-layout.js +30 -0
  98. package/dist/index.d.ts +10 -0
  99. package/dist/index.d.ts.map +1 -0
  100. package/dist/index.js +18 -0
  101. package/dist/lib/commands.d.ts +74 -0
  102. package/dist/lib/commands.d.ts.map +1 -0
  103. package/dist/lib/commands.js +73 -0
  104. package/dist/lib/commands.test.d.ts +2 -0
  105. package/dist/lib/commands.test.d.ts.map +1 -0
  106. package/dist/lib/commands.test.js +111 -0
  107. package/dist/lib/theme.d.ts +25 -0
  108. package/dist/lib/theme.d.ts.map +1 -0
  109. package/dist/lib/theme.js +103 -0
  110. package/dist/lib/utils.d.ts +3 -0
  111. package/dist/lib/utils.d.ts.map +1 -0
  112. package/dist/lib/utils.js +5 -0
  113. package/dist/lib/workspace.d.ts +41 -0
  114. package/dist/lib/workspace.d.ts.map +1 -0
  115. package/dist/lib/workspace.js +82 -0
  116. package/package.json +87 -0
  117. 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"}