@moldable-ai/ui 0.2.1 → 0.2.3
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/dist/components/chat/chat-message.d.ts +4 -0
- package/dist/components/chat/chat-message.d.ts.map +1 -1
- package/dist/components/chat/chat-message.js +64 -10
- package/dist/components/chat/chat-panel.d.ts +20 -1
- package/dist/components/chat/chat-panel.d.ts.map +1 -1
- package/dist/components/chat/chat-panel.js +20 -5
- package/dist/components/chat/index.d.ts +4 -1
- package/dist/components/chat/index.d.ts.map +1 -1
- package/dist/components/chat/index.js +4 -1
- package/dist/components/chat/tool-approval-context.d.ts +21 -0
- package/dist/components/chat/tool-approval-context.d.ts.map +1 -0
- package/dist/components/chat/tool-approval-context.js +19 -0
- package/dist/components/chat/tool-approval.d.ts +85 -0
- package/dist/components/chat/tool-approval.d.ts.map +1 -0
- package/dist/components/chat/tool-approval.js +80 -0
- package/dist/components/chat/tool-handlers.d.ts +21 -0
- package/dist/components/chat/tool-handlers.d.ts.map +1 -1
- package/dist/components/chat/tool-handlers.js +147 -35
- package/dist/components/chat/tool-progress-context.d.ts +27 -0
- package/dist/components/chat/tool-progress-context.d.ts.map +1 -0
- package/dist/components/chat/tool-progress-context.js +26 -0
- package/dist/components/ui/button.d.ts +3 -2
- package/dist/components/ui/button.d.ts.map +1 -1
- package/dist/components/ui/button.js +6 -5
- package/dist/components/ui/card.d.ts +7 -8
- package/dist/components/ui/card.d.ts.map +1 -1
- package/dist/components/ui/card.js +14 -23
- package/dist/components/ui/checkbox.js +1 -1
- package/dist/components/ui/collapsible.d.ts +3 -3
- package/dist/components/ui/collapsible.d.ts.map +1 -1
- package/dist/components/ui/collapsible.js +3 -11
- package/dist/components/ui/command.d.ts +15 -15
- package/dist/components/ui/command.d.ts.map +1 -1
- package/dist/components/ui/command.js +24 -29
- package/dist/components/ui/dialog.d.ts +17 -13
- package/dist/components/ui/dialog.d.ts.map +1 -1
- package/dist/components/ui/dialog.js +19 -32
- package/dist/components/ui/dropdown-menu.js +6 -6
- package/dist/components/ui/input.d.ts +2 -1
- package/dist/components/ui/input.d.ts.map +1 -1
- package/dist/components/ui/input.js +5 -4
- package/dist/components/ui/label.d.ts +1 -1
- package/dist/components/ui/label.d.ts.map +1 -1
- package/dist/components/ui/label.js +3 -3
- package/dist/components/ui/scroll-area.d.ts +2 -2
- package/dist/components/ui/scroll-area.d.ts.map +1 -1
- package/dist/components/ui/scroll-area.js +7 -9
- package/dist/components/ui/select.d.ts +11 -13
- package/dist/components/ui/select.d.ts.map +1 -1
- package/dist/components/ui/select.js +23 -35
- package/dist/components/ui/switch.d.ts +2 -2
- package/dist/components/ui/switch.d.ts.map +1 -1
- package/dist/components/ui/switch.js +4 -4
- package/dist/components/ui/textarea.d.ts +2 -1
- package/dist/components/ui/textarea.d.ts.map +1 -1
- package/dist/components/ui/textarea.js +5 -4
- package/dist/components/ui/tooltip.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/commands.d.ts +37 -0
- package/dist/lib/commands.d.ts.map +1 -1
- package/dist/lib/commands.js +85 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-message.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-message.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"chat-message.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-message.tsx"],"names":[],"mappings":"AAgCA,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACnC;IACE,IAAI,EAAE,iBAAiB,CAAA;IACvB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;IAEhB,QAAQ,CAAC,EAAE;QACT,EAAE,EAAE,MAAM,CAAA;QACV,QAAQ,CAAC,EAAE,OAAO,CAAA;KACnB,CAAA;CACF,CAAA;AAEL,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAA;IACrC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,eAAe,EAAE,CAAA;CAC1B,CAAA;AAED,KAAK,YAAY,GAAG;IAClB,OAAO,EAAE,WAAW,CAAA;IACpB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AAcD,iBAAS,WAAW,CAAC,EACnB,OAAO,EACP,MAAc,EACd,WAAmB,GACpB,EAAE,YAAY,2CA8bd;AAED,eAAO,MAAM,OAAO,yDAAoB,CAAA;AAExC,wBAAgB,eAAe,4CAiB9B"}
|
|
@@ -6,10 +6,19 @@ import { cn } from '../../lib/utils';
|
|
|
6
6
|
import { Markdown } from '../markdown';
|
|
7
7
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from '../ui/collapsible';
|
|
8
8
|
import { ThinkingTimeline, ThinkingTimelineMarker, } from './thinking-timeline';
|
|
9
|
+
import { ToolApproval, ToolApprovalHeader, ToolApprovalRejected, } from './tool-approval';
|
|
10
|
+
import { useToolApprovalResponse } from './tool-approval-context';
|
|
9
11
|
import { getToolHandler } from './tool-handlers';
|
|
12
|
+
import { useToolProgress } from './tool-progress-context';
|
|
10
13
|
import { AnimatePresence, motion } from 'framer-motion';
|
|
11
14
|
const DEFAULT_ACTIONS_LABEL = 'Thinking';
|
|
15
|
+
// OpenRouter sends these for encrypted reasoning msgs
|
|
16
|
+
const stripRedacted = (text) => text.replace(/\[REDACTED\]/g, '').trim();
|
|
12
17
|
function PureMessage({ message, isLast = false, isStreaming = false, }) {
|
|
18
|
+
// Get tool progress for streaming stdout/stderr
|
|
19
|
+
const toolProgress = useToolProgress();
|
|
20
|
+
// Get approval response handler
|
|
21
|
+
const onApprovalResponse = useToolApprovalResponse();
|
|
13
22
|
// Track open state for each thinking group independently
|
|
14
23
|
const [thinkingGroupStates, setThinkingGroupStates] = useState(new Map());
|
|
15
24
|
// Process parts chronologically, grouping only reasoning into thinking groups
|
|
@@ -69,13 +78,51 @@ function PureMessage({ message, isLast = false, isStreaming = false, }) {
|
|
|
69
78
|
if (toolHandler.inline) {
|
|
70
79
|
// Close any open thinking group first
|
|
71
80
|
closeThinkingGroup();
|
|
81
|
+
// Check for approval states
|
|
82
|
+
const isApprovalRequested = part.state === 'approval-requested';
|
|
83
|
+
const isApprovalResponded = part.state === 'approval-responded' ||
|
|
84
|
+
part.state === 'output-denied';
|
|
85
|
+
const wasRejected = part.approval?.approved === false;
|
|
86
|
+
const wasApproved = part.approval?.approved === true;
|
|
72
87
|
// Render inline tool
|
|
88
|
+
// Also treat approval-responded (when approved) as loading until output arrives
|
|
73
89
|
const isToolLoading = part.state === 'partial-call' ||
|
|
74
90
|
part.state === 'call' ||
|
|
75
|
-
part.state === 'pending'
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
91
|
+
part.state === 'pending' ||
|
|
92
|
+
(isApprovalResponded && wasApproved && part.output === undefined);
|
|
93
|
+
// Check for streaming progress (stdout/stderr from command execution)
|
|
94
|
+
const progress = part.toolCallId
|
|
95
|
+
? toolProgress[part.toolCallId]
|
|
96
|
+
: undefined;
|
|
97
|
+
const hasStreamingOutput = progress && (progress.stdout || progress.stderr);
|
|
98
|
+
let toolContent;
|
|
99
|
+
// Handle approval request state
|
|
100
|
+
if (isApprovalRequested &&
|
|
101
|
+
toolHandler.renderApproval &&
|
|
102
|
+
part.approval?.id &&
|
|
103
|
+
onApprovalResponse) {
|
|
104
|
+
toolContent = toolHandler.renderApproval({
|
|
105
|
+
approvalId: part.approval.id,
|
|
106
|
+
toolCallId: part.toolCallId || key,
|
|
107
|
+
args: part.args,
|
|
108
|
+
}, onApprovalResponse);
|
|
109
|
+
}
|
|
110
|
+
else if (isApprovalResponded && wasRejected) {
|
|
111
|
+
// Show rejection message
|
|
112
|
+
toolContent = (_jsx(ToolApproval, { state: "output-denied", approved: false, children: _jsx(ToolApprovalHeader, { children: _jsx(ToolApprovalRejected, { children: "Command execution was rejected" }) }) }));
|
|
113
|
+
}
|
|
114
|
+
else if (hasStreamingOutput && toolHandler.renderStreaming) {
|
|
115
|
+
// Show streaming output (live stdout/stderr)
|
|
116
|
+
toolContent = toolHandler.renderStreaming(part.args, progress);
|
|
117
|
+
}
|
|
118
|
+
else if (isToolLoading) {
|
|
119
|
+
// Show loading state (arguments streaming or waiting)
|
|
120
|
+
toolContent = toolHandler.renderLoading?.(part.args) || (_jsx("div", { className: "text-muted-foreground text-xs", children: toolHandler.loadingLabel }));
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// Show final output
|
|
124
|
+
toolContent = toolHandler.renderOutput(part.output, part.toolCallId || key);
|
|
125
|
+
}
|
|
79
126
|
items.push({
|
|
80
127
|
type: 'inline-tool',
|
|
81
128
|
id: key,
|
|
@@ -110,15 +157,15 @@ function PureMessage({ message, isLast = false, isStreaming = false, }) {
|
|
|
110
157
|
}
|
|
111
158
|
if (part.type === 'reasoning') {
|
|
112
159
|
const full = part.text ?? '';
|
|
113
|
-
const
|
|
114
|
-
if (
|
|
160
|
+
const cleaned = stripRedacted(full);
|
|
161
|
+
if (cleaned.length === 0) {
|
|
115
162
|
continue;
|
|
116
163
|
}
|
|
117
164
|
const group = ensureThinkingGroup();
|
|
118
165
|
// Extract title from markdown **title** or use first line
|
|
119
|
-
const markdownTitleMatch =
|
|
166
|
+
const markdownTitleMatch = cleaned.match(/\*\*([^*]+)\*\*/);
|
|
120
167
|
const candidateTitle = markdownTitleMatch?.[1];
|
|
121
|
-
const trimmed =
|
|
168
|
+
const trimmed = cleaned;
|
|
122
169
|
const firstLine = trimmed.split(/\n+/)[0];
|
|
123
170
|
const extractedTitle = candidateTitle || firstLine?.slice(0, 50) || DEFAULT_ACTIONS_LABEL;
|
|
124
171
|
group.title = extractedTitle;
|
|
@@ -148,7 +195,13 @@ function PureMessage({ message, isLast = false, isStreaming = false, }) {
|
|
|
148
195
|
contentItems: items,
|
|
149
196
|
hasFinalAssistantText: state.hasText,
|
|
150
197
|
};
|
|
151
|
-
}, [
|
|
198
|
+
}, [
|
|
199
|
+
message.id,
|
|
200
|
+
message.parts,
|
|
201
|
+
message.content,
|
|
202
|
+
toolProgress,
|
|
203
|
+
onApprovalResponse,
|
|
204
|
+
]);
|
|
152
205
|
// Determine if last thinking group is streaming
|
|
153
206
|
const lastThinkingGroup = contentItems
|
|
154
207
|
.filter((item) => item.type === 'thinking')
|
|
@@ -236,8 +289,9 @@ function PureMessage({ message, isLast = false, isStreaming = false, }) {
|
|
|
236
289
|
return (_jsx("div", { className: "w-full min-w-0", children: item.content }, item.id));
|
|
237
290
|
}
|
|
238
291
|
if (item.type === 'text' && item.textPart) {
|
|
239
|
-
return (_jsx("div", { className: cn('flex flex-col gap-4', {
|
|
292
|
+
return (_jsx("div", { className: cn('flex min-w-0 flex-col gap-4', {
|
|
240
293
|
'bg-secondary text-secondary-foreground w-fit rounded-2xl px-4 py-2.5': message.role === 'user',
|
|
294
|
+
'w-full': message.role === 'assistant',
|
|
241
295
|
}), children: _jsx(Markdown, { markdown: item.textPart.text, proseSize: "sm", className: cn('[&_.prose]:leading-relaxed [&_.prose]:text-current', '[&_.prose_pre]:bg-background/80 [&_.prose_code]:text-current', '[&_.prose_p:last-child]:mb-0 [&_.prose_p]:mb-2', message.role === 'user' &&
|
|
242
296
|
'[&_.prose_pre]:bg-secondary-foreground/20') }) }, item.id));
|
|
243
297
|
}
|
|
@@ -3,7 +3,18 @@ import type { ChatMessage } from './chat-message';
|
|
|
3
3
|
import { type ConversationMeta } from './conversation-history';
|
|
4
4
|
import { type ModelOption } from './model-selector';
|
|
5
5
|
import { type ReasoningEffortOption } from './reasoning-effort-selector';
|
|
6
|
+
import { type ApprovalResponseHandler } from './tool-approval-context';
|
|
6
7
|
type ChatStatus = 'ready' | 'submitted' | 'streaming' | 'error';
|
|
8
|
+
/**
|
|
9
|
+
* Progress data for a running tool (command execution with streaming stdout/stderr)
|
|
10
|
+
*/
|
|
11
|
+
export interface ToolProgressData {
|
|
12
|
+
toolCallId: string;
|
|
13
|
+
command: string;
|
|
14
|
+
stdout: string;
|
|
15
|
+
stderr: string;
|
|
16
|
+
status: 'running' | 'complete';
|
|
17
|
+
}
|
|
7
18
|
export interface ChatPanelProps {
|
|
8
19
|
/** Chat messages */
|
|
9
20
|
messages: ChatMessage[];
|
|
@@ -47,6 +58,10 @@ export interface ChatPanelProps {
|
|
|
47
58
|
isExpanded: boolean;
|
|
48
59
|
/** Toggle expanded state */
|
|
49
60
|
onExpandedChange: (expanded: boolean) => void;
|
|
61
|
+
/** Whether the panel is minimized (hidden below fold with only flap visible) */
|
|
62
|
+
isMinimized?: boolean;
|
|
63
|
+
/** Toggle minimized state */
|
|
64
|
+
onMinimizedChange?: (minimized: boolean) => void;
|
|
50
65
|
/** Custom class name */
|
|
51
66
|
className?: string;
|
|
52
67
|
/** Error from chat request */
|
|
@@ -55,10 +70,14 @@ export interface ChatPanelProps {
|
|
|
55
70
|
missingApiKey?: boolean;
|
|
56
71
|
/** Callback to trigger API key setup */
|
|
57
72
|
onAddApiKey?: () => void;
|
|
73
|
+
/** Progress data for running tools (streaming stdout/stderr) */
|
|
74
|
+
toolProgress?: Record<string, ToolProgressData>;
|
|
75
|
+
/** Callback for tool approval responses */
|
|
76
|
+
onApprovalResponse?: ApprovalResponseHandler;
|
|
58
77
|
}
|
|
59
78
|
/**
|
|
60
79
|
* Floating chat panel with model selector
|
|
61
80
|
*/
|
|
62
|
-
export declare function ChatPanel({ messages, status, input, onInputChange, onSubmit, onStop, onNewChat, models, selectedModel, onModelChange, reasoningEffortOptions, selectedReasoningEffort, onReasoningEffortChange, conversations, currentConversationId, onSelectConversation, onDeleteConversation, placeholder, welcomeMessage, isExpanded, onExpandedChange, className, error, missingApiKey, onAddApiKey, }: ChatPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
81
|
+
export declare function ChatPanel({ messages, status, input, onInputChange, onSubmit, onStop, onNewChat, models, selectedModel, onModelChange, reasoningEffortOptions, selectedReasoningEffort, onReasoningEffortChange, conversations, currentConversationId, onSelectConversation, onDeleteConversation, placeholder, welcomeMessage, isExpanded, onExpandedChange, isMinimized, onMinimizedChange, className, error, missingApiKey, onAddApiKey, toolProgress, onApprovalResponse, }: ChatPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
63
82
|
export {};
|
|
64
83
|
//# sourceMappingURL=chat-panel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-panel.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-panel.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,SAAS,EAKf,MAAM,OAAO,CAAA;AAWd,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEjD,OAAO,EAEL,KAAK,gBAAgB,EACtB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,KAAK,WAAW,EAAiB,MAAM,kBAAkB,CAAA;AAClE,OAAO,EACL,KAAK,qBAAqB,EAE3B,MAAM,6BAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"chat-panel.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-panel.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { AlertCircle,
|
|
3
|
+
import { AlertCircle, ChevronDown, MessageCircle, Plus } from 'lucide-react';
|
|
4
4
|
import { useCallback, useEffect, useRef, useState, } from 'react';
|
|
5
5
|
import { cn } from '../../lib/utils';
|
|
6
6
|
import { Button } from '../ui/button';
|
|
@@ -11,11 +11,13 @@ import { Messages } from './chat-messages';
|
|
|
11
11
|
import { ConversationHistory, } from './conversation-history';
|
|
12
12
|
import { ModelSelector } from './model-selector';
|
|
13
13
|
import { ReasoningEffortSelector, } from './reasoning-effort-selector';
|
|
14
|
+
import { ToolApprovalProvider, } from './tool-approval-context';
|
|
15
|
+
import { ToolProgressProvider } from './tool-progress-context';
|
|
14
16
|
import { AnimatePresence, motion } from 'framer-motion';
|
|
15
17
|
/**
|
|
16
18
|
* Floating chat panel with model selector
|
|
17
19
|
*/
|
|
18
|
-
export function ChatPanel({ messages, status, input, onInputChange, onSubmit, onStop, onNewChat, models, selectedModel, onModelChange, reasoningEffortOptions, selectedReasoningEffort, onReasoningEffortChange, conversations, currentConversationId, onSelectConversation, onDeleteConversation, placeholder = 'Ask anything...', welcomeMessage, isExpanded, onExpandedChange, className, error, missingApiKey, onAddApiKey, }) {
|
|
20
|
+
export function ChatPanel({ messages, status, input, onInputChange, onSubmit, onStop, onNewChat, models, selectedModel, onModelChange, reasoningEffortOptions, selectedReasoningEffort, onReasoningEffortChange, conversations, currentConversationId, onSelectConversation, onDeleteConversation, placeholder = 'Ask anything...', welcomeMessage, isExpanded, onExpandedChange, isMinimized = false, onMinimizedChange, className, error, missingApiKey, onAddApiKey, toolProgress = {}, onApprovalResponse, }) {
|
|
19
21
|
const inputRef = useRef(null);
|
|
20
22
|
const messagesEndRef = useRef(null);
|
|
21
23
|
const scrollAreaRef = useRef(null);
|
|
@@ -87,9 +89,22 @@ export function ChatPanel({ messages, status, input, onInputChange, onSubmit, on
|
|
|
87
89
|
document.addEventListener('keydown', handleKeyDown);
|
|
88
90
|
return () => document.removeEventListener('keydown', handleKeyDown);
|
|
89
91
|
}, [isResponding, isExpanded, handleNewChat]);
|
|
90
|
-
|
|
92
|
+
// Handle clicking the minimized flap
|
|
93
|
+
const handleFlapClick = useCallback(() => {
|
|
94
|
+
onMinimizedChange?.(false);
|
|
95
|
+
}, [onMinimizedChange]);
|
|
96
|
+
// Handle minimize button - now goes to minimized state
|
|
97
|
+
const handleMinimize = useCallback(() => {
|
|
98
|
+
if (isExpanded) {
|
|
99
|
+
onExpandedChange(false);
|
|
100
|
+
}
|
|
101
|
+
onMinimizedChange?.(true);
|
|
102
|
+
}, [isExpanded, onExpandedChange, onMinimizedChange]);
|
|
103
|
+
return (_jsxs(_Fragment, { children: [_jsx(AnimatePresence, { children: isExpanded && !isMinimized && (_jsx(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.2 }, className: "fixed inset-0 z-40 bg-black/20", onClick: () => onExpandedChange(false) })) }), _jsx(AnimatePresence, { children: isMinimized && (_jsx(motion.div, { initial: { y: 20, opacity: 0 }, animate: { y: 0, opacity: 1 }, exit: { y: 20, opacity: 0 }, transition: { type: 'tween', duration: 0.2, ease: 'easeOut' }, className: "fixed bottom-0 left-[calc(50%+var(--sidebar-width,0px)/2)] z-50 -translate-x-1/2", children: _jsxs("button", { onClick: handleFlapClick, className: cn('bg-background/95 hover:bg-background group flex cursor-pointer items-center gap-2 rounded-t-xl border border-b-0 px-4 py-2 shadow-lg backdrop-blur transition-colors', 'text-muted-foreground hover:text-foreground'), children: [_jsx(MessageCircle, { className: "size-4" }), _jsx("span", { className: "text-sm font-medium", children: "Chat" }), _jsx("span", { className: "bg-muted text-muted-foreground group-hover:bg-primary/10 group-hover:text-primary rounded px-1.5 py-0.5 text-xs transition-colors", children: "\u2318M" })] }) })) }), _jsx(motion.div, { initial: false, animate: {
|
|
91
104
|
width: isExpanded ? 'min(90vw, 640px)' : '400px',
|
|
92
|
-
|
|
105
|
+
y: isMinimized ? 'calc(100% + 24px)' : 0,
|
|
106
|
+
opacity: isMinimized ? 0 : 1,
|
|
107
|
+
}, transition: { type: 'tween', duration: 0.25, ease: 'easeOut' }, className: cn('pointer-events-none fixed bottom-6 left-[calc(50%+var(--sidebar-width,0px)/2)] z-50 flex -translate-x-1/2 justify-center', isMinimized && 'pointer-events-none', className), children: _jsxs("div", { className: cn('bg-background pointer-events-auto relative w-full border shadow-lg', 'supports-[backdrop-filter]:bg-background/95 backdrop-blur', 'rounded-4xl', isExpanded ? 'overflow-hidden' : 'overflow-visible'), children: [!isExpanded && (_jsx(motion.div, { className: "rounded-4xl pointer-events-none absolute inset-0 z-0", animate: {
|
|
93
108
|
boxShadow: [
|
|
94
109
|
'0 0 0px 0px color-mix(in oklch, var(--primary) 0%, #ff8800 00%)',
|
|
95
110
|
'0 0 20px 0px color-mix(in oklch, var(--primary) 20%, #ff8800 30%)',
|
|
@@ -107,7 +122,7 @@ export function ChatPanel({ messages, status, input, onInputChange, onSubmit, on
|
|
|
107
122
|
selectedReasoningEffort &&
|
|
108
123
|
onReasoningEffortChange && (_jsx(ReasoningEffortSelector, { options: reasoningEffortOptions, selectedEffort: selectedReasoningEffort, onEffortChange: onReasoningEffortChange, disabled: isResponding }))] }), _jsxs("div", { className: "flex items-center gap-1", children: [conversations &&
|
|
109
124
|
conversations.length > 0 &&
|
|
110
|
-
onSelectConversation && (_jsx(ConversationHistory, { conversations: conversations, currentConversationId: currentConversationId, onSelect: onSelectConversation, onDelete: onDeleteConversation, disabled: isResponding })), _jsxs(TooltipProvider, { children: [messages.length > 0 && (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", onClick: handleNewChat, disabled: isResponding, className: "text-muted-foreground hover:text-foreground", children: _jsx(Plus, { className: "size-4" }) }) }), _jsx(TooltipContent, { side: "bottom", children: _jsx("p", { children: "New chat" }) })] })), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", onClick:
|
|
125
|
+
onSelectConversation && (_jsx(ConversationHistory, { conversations: conversations, currentConversationId: currentConversationId, onSelect: onSelectConversation, onDelete: onDeleteConversation, disabled: isResponding })), _jsxs(TooltipProvider, { children: [messages.length > 0 && (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", onClick: handleNewChat, disabled: isResponding, className: "text-muted-foreground hover:text-foreground", children: _jsx(Plus, { className: "size-4" }) }) }), _jsx(TooltipContent, { side: "bottom", children: _jsx("p", { children: "New chat" }) })] })), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", onClick: handleMinimize, className: "text-muted-foreground hover:text-foreground", children: _jsx(ChevronDown, { className: "size-4" }) }) }), _jsx(TooltipContent, { side: "bottom", children: _jsx("p", { children: "Minimize" }) })] })] })] })] }), _jsx(ScrollArea, { ref: scrollAreaRef, className: "min-w-0 flex-1 px-2", children: _jsxs("div", { className: "min-w-0 max-w-full space-y-4 overflow-hidden py-4", children: [messages.length === 0 && welcomeMessage && (_jsx("div", { className: "bg-muted/50 text-muted-foreground mx-2 rounded-2xl p-4 text-sm", children: welcomeMessage })), _jsx(ToolProgressProvider, { value: toolProgress, children: _jsx(ToolApprovalProvider, { onApprovalResponse: onApprovalResponse ?? null, children: _jsx(Messages, { messages: messages, status: status }) }) }), (missingApiKey ||
|
|
111
126
|
(error &&
|
|
112
127
|
error.message?.includes('API_KEY not configured'))) && (_jsxs("div", { className: "border-primary/30 bg-primary/5 mx-2 flex flex-col items-center gap-3 rounded-lg border p-4 text-center", children: [_jsx("div", { className: "bg-primary/10 flex size-10 items-center justify-center rounded-full", children: _jsx(AlertCircle, { className: "text-primary size-5" }) }), _jsxs("div", { children: [_jsx("p", { className: "text-foreground font-medium", children: "API key required" }), _jsx("p", { className: "text-muted-foreground mt-1 text-sm", children: "Add an API key to start chatting with AI" })] }), onAddApiKey && (_jsx(Button, { onClick: onAddApiKey, size: "sm", className: "cursor-pointer", children: "Add API key" }))] })), error &&
|
|
113
128
|
status === 'error' &&
|
|
@@ -4,7 +4,10 @@ export { Messages } from './chat-messages';
|
|
|
4
4
|
export { ModelSelector, type ModelOption } from './model-selector';
|
|
5
5
|
export { ReasoningEffortSelector, type ReasoningEffortOption, } from './reasoning-effort-selector';
|
|
6
6
|
export { ConversationHistory, type ConversationMeta, } from './conversation-history';
|
|
7
|
-
export { ChatPanel, type ChatPanelProps } from './chat-panel';
|
|
7
|
+
export { ChatPanel, type ChatPanelProps, type ToolProgressData, } from './chat-panel';
|
|
8
8
|
export { ThinkingTimeline, ThinkingTimelineMarker, type ThinkingTimelineItem, type ThinkingTimelineProps, } from './thinking-timeline';
|
|
9
9
|
export { getToolHandler, DEFAULT_TOOL_HANDLERS, type ToolHandler, } from './tool-handlers';
|
|
10
|
+
export { ToolProgressProvider, useToolProgress, useToolCallProgress, } from './tool-progress-context';
|
|
11
|
+
export { ToolApprovalProvider, useToolApprovalResponse, type ApprovalResponseHandler, } from './tool-approval-context';
|
|
12
|
+
export { ToolApproval, ToolApprovalHeader, ToolApprovalRequest, ToolApprovalAccepted, ToolApprovalRejected, ToolApprovalActions, ToolApprovalAction, type ToolApprovalState, } from './tool-approval';
|
|
10
13
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/chat/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EACL,OAAO,EACP,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,eAAe,GACrB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC1C,OAAO,EAAE,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAClE,OAAO,EACL,uBAAuB,EACvB,KAAK,qBAAqB,GAC3B,MAAM,6BAA6B,CAAA;AACpC,OAAO,EACL,mBAAmB,EACnB,KAAK,gBAAgB,GACtB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/chat/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EACL,OAAO,EACP,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,eAAe,GACrB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC1C,OAAO,EAAE,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAClE,OAAO,EACL,uBAAuB,EACvB,KAAK,qBAAqB,GAC3B,MAAM,6BAA6B,CAAA;AACpC,OAAO,EACL,mBAAmB,EACnB,KAAK,gBAAgB,GACtB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACL,SAAS,EACT,KAAK,cAAc,EACnB,KAAK,gBAAgB,GACtB,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,KAAK,WAAW,GACjB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,mBAAmB,GACpB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,KAAK,uBAAuB,GAC7B,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EAClB,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAA"}
|
|
@@ -4,6 +4,9 @@ export { Messages } from './chat-messages';
|
|
|
4
4
|
export { ModelSelector } from './model-selector';
|
|
5
5
|
export { ReasoningEffortSelector, } from './reasoning-effort-selector';
|
|
6
6
|
export { ConversationHistory, } from './conversation-history';
|
|
7
|
-
export { ChatPanel } from './chat-panel';
|
|
7
|
+
export { ChatPanel, } from './chat-panel';
|
|
8
8
|
export { ThinkingTimeline, ThinkingTimelineMarker, } from './thinking-timeline';
|
|
9
9
|
export { getToolHandler, DEFAULT_TOOL_HANDLERS, } from './tool-handlers';
|
|
10
|
+
export { ToolProgressProvider, useToolProgress, useToolCallProgress, } from './tool-progress-context';
|
|
11
|
+
export { ToolApprovalProvider, useToolApprovalResponse, } from './tool-approval-context';
|
|
12
|
+
export { ToolApproval, ToolApprovalHeader, ToolApprovalRequest, ToolApprovalAccepted, ToolApprovalRejected, ToolApprovalActions, ToolApprovalAction, } from './tool-approval';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Callback type for responding to tool approval requests
|
|
4
|
+
*/
|
|
5
|
+
export type ApprovalResponseHandler = (params: {
|
|
6
|
+
approvalId: string;
|
|
7
|
+
approved: boolean;
|
|
8
|
+
reason?: string;
|
|
9
|
+
}) => void;
|
|
10
|
+
/**
|
|
11
|
+
* Provider for tool approval handling
|
|
12
|
+
*/
|
|
13
|
+
export declare function ToolApprovalProvider({ onApprovalResponse, children, }: {
|
|
14
|
+
onApprovalResponse: ApprovalResponseHandler | null;
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
/**
|
|
18
|
+
* Hook to get the approval response handler
|
|
19
|
+
*/
|
|
20
|
+
export declare function useToolApprovalResponse(): ApprovalResponseHandler | null;
|
|
21
|
+
//# sourceMappingURL=tool-approval-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-approval-context.d.ts","sourceRoot":"","sources":["../../../src/components/chat/tool-approval-context.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAA6B,MAAM,OAAO,CAAA;AAEjE;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,MAAM,EAAE;IAC7C,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,OAAO,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,KAAK,IAAI,CAAA;AAOV;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,kBAAkB,EAClB,QAAQ,GACT,EAAE;IACD,kBAAkB,EAAE,uBAAuB,GAAG,IAAI,CAAA;IAClD,QAAQ,EAAE,SAAS,CAAA;CACpB,2CAMA;AAED;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,uBAAuB,GAAG,IAAI,CAExE"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext } from 'react';
|
|
4
|
+
/**
|
|
5
|
+
* Context for tool approval handling
|
|
6
|
+
*/
|
|
7
|
+
const ToolApprovalContext = createContext(null);
|
|
8
|
+
/**
|
|
9
|
+
* Provider for tool approval handling
|
|
10
|
+
*/
|
|
11
|
+
export function ToolApprovalProvider({ onApprovalResponse, children, }) {
|
|
12
|
+
return (_jsx(ToolApprovalContext.Provider, { value: onApprovalResponse, children: children }));
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Hook to get the approval response handler
|
|
16
|
+
*/
|
|
17
|
+
export function useToolApprovalResponse() {
|
|
18
|
+
return useContext(ToolApprovalContext);
|
|
19
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Button } from '../ui/button';
|
|
3
|
+
/**
|
|
4
|
+
* State of the tool approval
|
|
5
|
+
*/
|
|
6
|
+
export type ToolApprovalState = 'approval-requested' | 'approval-responded' | 'output-available' | 'output-denied';
|
|
7
|
+
/**
|
|
8
|
+
* Root component for tool approval UI
|
|
9
|
+
*/
|
|
10
|
+
interface ToolApprovalProps {
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
/** Current state of the approval */
|
|
13
|
+
state: ToolApprovalState;
|
|
14
|
+
/** Whether the user approved (for responded/output states) */
|
|
15
|
+
approved?: boolean;
|
|
16
|
+
className?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function ToolApproval({ children, state, approved, className, }: ToolApprovalProps): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
/**
|
|
20
|
+
* Header section with icon and title
|
|
21
|
+
*/
|
|
22
|
+
interface ToolApprovalHeaderProps {
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
className?: string;
|
|
25
|
+
}
|
|
26
|
+
export declare function ToolApprovalHeader({ children, className, }: ToolApprovalHeaderProps): import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
/**
|
|
28
|
+
* Content shown only when approval is being requested
|
|
29
|
+
*/
|
|
30
|
+
interface ToolApprovalRequestProps {
|
|
31
|
+
children: React.ReactNode;
|
|
32
|
+
className?: string;
|
|
33
|
+
}
|
|
34
|
+
export declare function ToolApprovalRequest({ children, className, }: ToolApprovalRequestProps): import("react/jsx-runtime").JSX.Element | null;
|
|
35
|
+
/**
|
|
36
|
+
* Expandable help section explaining why approval is needed
|
|
37
|
+
*/
|
|
38
|
+
interface ToolApprovalHelpProps {
|
|
39
|
+
children: React.ReactNode;
|
|
40
|
+
className?: string;
|
|
41
|
+
}
|
|
42
|
+
export declare function ToolApprovalHelp({ children, className, }: ToolApprovalHelpProps): import("react/jsx-runtime").JSX.Element | null;
|
|
43
|
+
/**
|
|
44
|
+
* Pre-built help content for sandboxing explanation
|
|
45
|
+
*/
|
|
46
|
+
export declare function ToolApprovalSandboxHelp({ className }: {
|
|
47
|
+
className?: string;
|
|
48
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
49
|
+
/**
|
|
50
|
+
* Pre-built help content for dangerous command explanation
|
|
51
|
+
*/
|
|
52
|
+
export declare function ToolApprovalDangerousHelp({ className, }: {
|
|
53
|
+
className?: string;
|
|
54
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
55
|
+
/**
|
|
56
|
+
* Content shown when approval was granted
|
|
57
|
+
*/
|
|
58
|
+
interface ToolApprovalAcceptedProps {
|
|
59
|
+
children?: React.ReactNode;
|
|
60
|
+
className?: string;
|
|
61
|
+
}
|
|
62
|
+
export declare function ToolApprovalAccepted({ children, className, }: ToolApprovalAcceptedProps): import("react/jsx-runtime").JSX.Element | null;
|
|
63
|
+
/**
|
|
64
|
+
* Content shown when approval was rejected
|
|
65
|
+
*/
|
|
66
|
+
interface ToolApprovalRejectedProps {
|
|
67
|
+
children?: React.ReactNode;
|
|
68
|
+
className?: string;
|
|
69
|
+
}
|
|
70
|
+
export declare function ToolApprovalRejected({ children, className, }: ToolApprovalRejectedProps): import("react/jsx-runtime").JSX.Element | null;
|
|
71
|
+
/**
|
|
72
|
+
* Actions container (approve/reject buttons)
|
|
73
|
+
*/
|
|
74
|
+
interface ToolApprovalActionsProps {
|
|
75
|
+
children: React.ReactNode;
|
|
76
|
+
className?: string;
|
|
77
|
+
}
|
|
78
|
+
export declare function ToolApprovalActions({ children, className, }: ToolApprovalActionsProps): import("react/jsx-runtime").JSX.Element | null;
|
|
79
|
+
/**
|
|
80
|
+
* Individual action button
|
|
81
|
+
*/
|
|
82
|
+
type ToolApprovalActionProps = React.ComponentProps<typeof Button>;
|
|
83
|
+
export declare function ToolApprovalAction({ className, size, ...props }: ToolApprovalActionProps): import("react/jsx-runtime").JSX.Element;
|
|
84
|
+
export {};
|
|
85
|
+
//# sourceMappingURL=tool-approval.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-approval.d.ts","sourceRoot":"","sources":["../../../src/components/chat/tool-approval.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAErC;;GAEG;AACH,MAAM,MAAM,iBAAiB,GACzB,oBAAoB,GACpB,oBAAoB,GACpB,kBAAkB,GAClB,eAAe,CAAA;AAenB;;GAEG;AACH,UAAU,iBAAiB;IACzB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,oCAAoC;IACpC,KAAK,EAAE,iBAAiB,CAAA;IACxB,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,SAAS,GACV,EAAE,iBAAiB,2CAanB;AAED;;GAEG;AACH,UAAU,uBAAuB;IAC/B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,kBAAkB,CAAC,EACjC,QAAQ,EACR,SAAS,GACV,EAAE,uBAAuB,2CAqBzB;AAED;;GAEG;AACH,UAAU,wBAAwB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,EACR,SAAS,GACV,EAAE,wBAAwB,kDAQ1B;AAED;;GAEG;AACH,UAAU,qBAAqB;IAC7B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,SAAS,GACV,EAAE,qBAAqB,kDAkCvB;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,2CAc5E;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,EACxC,SAAS,GACV,EAAE;IACD,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,2CAaA;AAED;;GAEG;AACH,UAAU,yBAAyB;IACjC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,oBAAoB,CAAC,EACnC,QAAQ,EACR,SAAS,GACV,EAAE,yBAAyB,kDAqB3B;AAED;;GAEG;AACH,UAAU,yBAAyB;IACjC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,oBAAoB,CAAC,EACnC,QAAQ,EACR,SAAS,GACV,EAAE,yBAAyB,kDAuB3B;AAED;;GAEG;AACH,UAAU,wBAAwB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,EACR,SAAS,GACV,EAAE,wBAAwB,kDAkB1B;AAED;;GAEG;AACH,KAAK,uBAAuB,GAAG,KAAK,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,CAAA;AAElE,wBAAgB,kBAAkB,CAAC,EACjC,SAAS,EACT,IAAW,EACX,GAAG,KAAK,EACT,EAAE,uBAAuB,2CAIzB"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Check, ChevronDown, HelpCircle, ShieldAlert, X } from 'lucide-react';
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
import { cn } from '../../lib/utils';
|
|
6
|
+
import { Button } from '../ui/button';
|
|
7
|
+
const ToolApprovalContext = React.createContext({
|
|
8
|
+
state: 'approval-requested',
|
|
9
|
+
});
|
|
10
|
+
function useToolApprovalContext() {
|
|
11
|
+
return React.useContext(ToolApprovalContext);
|
|
12
|
+
}
|
|
13
|
+
export function ToolApproval({ children, state, approved, className, }) {
|
|
14
|
+
return (_jsx(ToolApprovalContext.Provider, { value: { state, approved }, children: _jsx("div", { className: cn('bg-card border-border my-2 overflow-hidden rounded-lg border', className), children: children }) }));
|
|
15
|
+
}
|
|
16
|
+
export function ToolApprovalHeader({ children, className, }) {
|
|
17
|
+
const { state, approved } = useToolApprovalContext();
|
|
18
|
+
// Show different icons based on state
|
|
19
|
+
const icon = state === 'approval-requested' ? (_jsx(ShieldAlert, { className: "size-3.5 shrink-0 text-amber-500" })) : approved ? (_jsx(Check, { className: "text-success size-3.5 shrink-0" })) : (_jsx(X, { className: "text-destructive size-3.5 shrink-0" }));
|
|
20
|
+
return (_jsxs("div", { className: cn('bg-muted/50 flex items-start gap-2 px-3 py-2', className), children: [_jsx("div", { className: "mt-0.5", children: icon }), _jsx("div", { className: "min-w-0 flex-1", children: children })] }));
|
|
21
|
+
}
|
|
22
|
+
export function ToolApprovalRequest({ children, className, }) {
|
|
23
|
+
const { state } = useToolApprovalContext();
|
|
24
|
+
if (state !== 'approval-requested') {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
return _jsx("div", { className: cn('text-xs', className), children: children });
|
|
28
|
+
}
|
|
29
|
+
export function ToolApprovalHelp({ children, className, }) {
|
|
30
|
+
const { state } = useToolApprovalContext();
|
|
31
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
32
|
+
// Only show when approval is being requested
|
|
33
|
+
if (state !== 'approval-requested') {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
return (_jsxs("div", { className: cn('', className), children: [_jsxs("button", { type: "button", onClick: () => setIsOpen(!isOpen), className: "text-muted-foreground hover:text-foreground flex cursor-pointer items-center gap-1 text-[10px] transition-colors", children: [_jsx(HelpCircle, { className: "size-2.5" }), _jsx("span", { children: "What does this mean?" }), _jsx(ChevronDown, { className: cn('size-2.5 transition-transform', isOpen && 'rotate-180') })] }), isOpen && (_jsx("div", { className: "bg-muted/30 mt-1.5 rounded-md px-2 py-1.5", children: _jsx("p", { className: "text-muted-foreground text-[10px] leading-relaxed", children: children }) }))] }));
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Pre-built help content for sandboxing explanation
|
|
40
|
+
*/
|
|
41
|
+
export function ToolApprovalSandboxHelp({ className }) {
|
|
42
|
+
return (_jsxs(ToolApprovalHelp, { className: className, children: [_jsx("strong", { className: "text-foreground", children: "Sandboxing" }), " restricts commands from accessing the network and writing to sensitive locations. Package managers like", ' ', _jsx("code", { className: "bg-muted rounded px-1 py-0.5", children: "pnpm install" }), " need network access to download packages, so they run unsandboxed.", _jsxs("strong", { className: "text-foreground", children: [' ', "Only approve commands you trust."] })] }));
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Pre-built help content for dangerous command explanation
|
|
46
|
+
*/
|
|
47
|
+
export function ToolApprovalDangerousHelp({ className, }) {
|
|
48
|
+
return (_jsxs(ToolApprovalHelp, { className: className, children: [_jsx("strong", { className: "text-foreground", children: "Dangerous commands" }), " can make irreversible changes to your system, like deleting files recursively or modifying permissions. These commands are flagged based on common patterns (rm -rf, sudo, etc.) to give you a chance to review before execution.", _jsxs("strong", { className: "text-foreground", children: [' ', "Only approve if you understand what the command does."] })] }));
|
|
49
|
+
}
|
|
50
|
+
export function ToolApprovalAccepted({ children, className, }) {
|
|
51
|
+
const { state, approved } = useToolApprovalContext();
|
|
52
|
+
// Show for responded/output states where approved is true
|
|
53
|
+
const shouldShow = (state === 'approval-responded' || state === 'output-available') && approved;
|
|
54
|
+
if (!shouldShow) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
return (_jsx("div", { className: cn('text-success flex items-center gap-1.5 text-xs', className), children: children ?? 'Approved' }));
|
|
58
|
+
}
|
|
59
|
+
export function ToolApprovalRejected({ children, className, }) {
|
|
60
|
+
const { state, approved } = useToolApprovalContext();
|
|
61
|
+
// Show for output-denied or responded with approved=false
|
|
62
|
+
const shouldShow = state === 'output-denied' ||
|
|
63
|
+
((state === 'approval-responded' || state === 'output-available') &&
|
|
64
|
+
approved === false);
|
|
65
|
+
if (!shouldShow) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
return (_jsx("div", { className: cn('text-destructive flex items-center gap-1.5 text-xs', className), children: children ?? 'Rejected' }));
|
|
69
|
+
}
|
|
70
|
+
export function ToolApprovalActions({ children, className, }) {
|
|
71
|
+
const { state } = useToolApprovalContext();
|
|
72
|
+
// Only show actions when approval is being requested
|
|
73
|
+
if (state !== 'approval-requested') {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
return (_jsx("div", { className: cn('border-border flex items-center justify-end gap-2 border-t px-3 py-2', className), children: children }));
|
|
77
|
+
}
|
|
78
|
+
export function ToolApprovalAction({ className, size = 'sm', ...props }) {
|
|
79
|
+
return (_jsx(Button, { className: cn('h-7 text-xs', className), size: size, ...props }));
|
|
80
|
+
}
|
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import { ThinkingTimelineMarker } from './thinking-timeline';
|
|
3
|
+
import { type ApprovalResponseHandler } from './tool-approval-context';
|
|
4
|
+
/**
|
|
5
|
+
* Progress data for streaming tool execution (e.g., command stdout/stderr)
|
|
6
|
+
*/
|
|
7
|
+
export interface ToolStreamingProgress {
|
|
8
|
+
toolCallId: string;
|
|
9
|
+
command?: string;
|
|
10
|
+
stdout: string;
|
|
11
|
+
stderr: string;
|
|
12
|
+
status: 'running' | 'complete';
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Approval info passed to renderApproval
|
|
16
|
+
*/
|
|
17
|
+
export interface ToolApprovalInfo {
|
|
18
|
+
approvalId: string;
|
|
19
|
+
toolCallId: string;
|
|
20
|
+
args: unknown;
|
|
21
|
+
}
|
|
3
22
|
/**
|
|
4
23
|
* Tool handler definition
|
|
5
24
|
*/
|
|
@@ -9,6 +28,8 @@ export type ToolHandler = {
|
|
|
9
28
|
inline?: boolean;
|
|
10
29
|
renderOutput: (output: unknown, toolCallId: string) => ReactNode;
|
|
11
30
|
renderLoading?: (args?: unknown) => ReactNode;
|
|
31
|
+
renderStreaming?: (args: unknown, progress: ToolStreamingProgress) => ReactNode;
|
|
32
|
+
renderApproval?: (approval: ToolApprovalInfo, onRespond: ApprovalResponseHandler) => ReactNode;
|
|
12
33
|
};
|
|
13
34
|
/**
|
|
14
35
|
* Default tool handlers for Moldable tools
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-handlers.d.ts","sourceRoot":"","sources":["../../../src/components/chat/tool-handlers.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tool-handlers.d.ts","sourceRoot":"","sources":["../../../src/components/chat/tool-handlers.tsx"],"names":[],"mappings":"AAoBA,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAA;AAEhD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAU5D,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,yBAAyB,CAAA;AAEtE;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,SAAS,GAAG,UAAU,CAAA;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,OAAO,CAAA;CACd;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IAExB,YAAY,EAAE,MAAM,CAAA;IAEpB,MAAM,CAAC,EAAE,sBAAsB,CAAA;IAE/B,MAAM,CAAC,EAAE,OAAO,CAAA;IAEhB,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,KAAK,SAAS,CAAA;IAGhE,aAAa,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,OAAO,KAAK,SAAS,CAAA;IAG7C,eAAe,CAAC,EAAE,CAChB,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,qBAAqB,KAC5B,SAAS,CAAA;IAGd,cAAc,CAAC,EAAE,CACf,QAAQ,EAAE,gBAAgB,EAC1B,SAAS,EAAE,uBAAuB,KAC/B,SAAS,CAAA;CACf,CAAA;AAkOD;;GAEG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAk1C7D,CAAA;AAoBD;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAmC5D"}
|