@moldable-ai/ui 0.2.6 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +75 -71
- package/dist/components/chat/chat-input.d.ts +16 -3
- package/dist/components/chat/chat-input.d.ts.map +1 -1
- package/dist/components/chat/chat-input.js +25 -8
- package/dist/components/chat/chat-message.d.ts +11 -0
- package/dist/components/chat/chat-message.d.ts.map +1 -1
- package/dist/components/chat/chat-message.js +503 -158
- package/dist/components/chat/chat-panel.d.ts +26 -2
- package/dist/components/chat/chat-panel.d.ts.map +1 -1
- package/dist/components/chat/chat-panel.js +69 -33
- package/dist/components/chat/conversation-history.d.ts +6 -1
- package/dist/components/chat/conversation-history.d.ts.map +1 -1
- package/dist/components/chat/conversation-history.js +6 -6
- package/dist/components/chat/index.d.ts +2 -2
- package/dist/components/chat/index.d.ts.map +1 -1
- package/dist/components/chat/index.js +1 -1
- package/dist/components/chat/model-selector.d.ts +3 -1
- package/dist/components/chat/model-selector.d.ts.map +1 -1
- package/dist/components/chat/model-selector.js +2 -2
- package/dist/components/chat/reasoning-effort-selector.d.ts +4 -1
- package/dist/components/chat/reasoning-effort-selector.d.ts.map +1 -1
- package/dist/components/chat/reasoning-effort-selector.js +6 -6
- package/dist/components/chat/tool-handlers.d.ts.map +1 -1
- package/dist/components/chat/tool-handlers.js +62 -6
- package/dist/components/markdown.js +2 -2
- package/dist/components/ui/command.d.ts +2 -1
- package/dist/components/ui/command.d.ts.map +1 -1
- package/dist/components/ui/command.js +2 -2
- package/dist/components/ui/scroll-area.d.ts +1 -1
- package/dist/components/ui/scroll-area.d.ts.map +1 -1
- package/dist/components/ui/scroll-area.js +5 -3
- 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 +57 -0
- package/dist/lib/commands.d.ts.map +1 -1
- package/dist/lib/commands.js +74 -0
- package/package.json +2 -2
- package/src/styles/index.css +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ChangeEvent, type FormEvent, type ReactNode } from 'react';
|
|
2
|
+
import { type ChatImageAttachment, type ChatSubmitMode } from './chat-input';
|
|
2
3
|
import { type ChatMessage } from './chat-message';
|
|
3
4
|
import { type MessageCheckpoint } from './chat-messages';
|
|
4
5
|
import { type ConversationMeta } from './conversation-history';
|
|
@@ -16,17 +17,26 @@ export interface ToolProgressData {
|
|
|
16
17
|
stderr: string;
|
|
17
18
|
status: 'running' | 'complete';
|
|
18
19
|
}
|
|
20
|
+
export interface QueuedChatMessage {
|
|
21
|
+
id: string;
|
|
22
|
+
preview: string;
|
|
23
|
+
attachmentCount: number;
|
|
24
|
+
status?: 'queued' | 'failed';
|
|
25
|
+
error?: string;
|
|
26
|
+
}
|
|
19
27
|
export interface ChatPanelProps {
|
|
20
28
|
/** Chat messages */
|
|
21
29
|
messages: ChatMessage[];
|
|
22
30
|
/** Current chat status */
|
|
23
31
|
status: ChatStatus;
|
|
32
|
+
/** Whether chat work is active or queued, blocking conversation-level controls */
|
|
33
|
+
isTurnInFlight?: boolean;
|
|
24
34
|
/** Input value */
|
|
25
35
|
input: string;
|
|
26
36
|
/** Handle input change */
|
|
27
37
|
onInputChange: (e: ChangeEvent<HTMLTextAreaElement>) => void;
|
|
28
38
|
/** Handle form submit */
|
|
29
|
-
onSubmit: (e?: FormEvent<HTMLFormElement
|
|
39
|
+
onSubmit: (e?: FormEvent<HTMLFormElement>, mode?: ChatSubmitMode) => void;
|
|
30
40
|
/** Handle stop generation */
|
|
31
41
|
onStop?: () => void;
|
|
32
42
|
/** Handle new chat */
|
|
@@ -87,10 +97,24 @@ export interface ChatPanelProps {
|
|
|
87
97
|
restoringMessageId?: string | null;
|
|
88
98
|
/** Callback when restore is requested for a message */
|
|
89
99
|
onRestoreCheckpoint?: (messageId: string) => void;
|
|
100
|
+
/** Images attached to the pending user message */
|
|
101
|
+
imageAttachments?: ChatImageAttachment[];
|
|
102
|
+
/** Remove a pending image attachment */
|
|
103
|
+
onRemoveImageAttachment?: (id: string) => void;
|
|
104
|
+
/** Whether a dragged image is currently over the chat drop target */
|
|
105
|
+
isImageDropActive?: boolean;
|
|
106
|
+
/** Follow-up messages queued to send after the active response finishes */
|
|
107
|
+
queuedMessages?: QueuedChatMessage[];
|
|
108
|
+
/** Remove a queued follow-up before it sends */
|
|
109
|
+
onRemoveQueuedMessage?: (id: string) => void;
|
|
110
|
+
/** Retry a queued follow-up after a send failure */
|
|
111
|
+
onRetryQueuedMessage?: (id: string) => void;
|
|
112
|
+
/** Send a queued follow-up into the active response immediately */
|
|
113
|
+
onSteerQueuedMessage?: (id: string) => void;
|
|
90
114
|
}
|
|
91
115
|
/**
|
|
92
116
|
* Floating chat panel with model selector
|
|
93
117
|
*/
|
|
94
|
-
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, isEditingApp, onEditingAppChange, showEditingAppToggle, checkpoints, restoringMessageId, onRestoreCheckpoint, }: ChatPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
118
|
+
export declare function ChatPanel({ messages, status, isTurnInFlight, 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, isEditingApp, onEditingAppChange, showEditingAppToggle, checkpoints, restoringMessageId, onRestoreCheckpoint, imageAttachments, onRemoveImageAttachment, isImageDropActive, queuedMessages, onRemoveQueuedMessage, onRetryQueuedMessage, onSteerQueuedMessage, }: ChatPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
95
119
|
export {};
|
|
96
120
|
//# 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":"
|
|
1
|
+
{"version":3,"file":"chat-panel.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-panel.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx,
|
|
3
|
-
import { AlertCircle, ChevronDown, Code2, MessageCircle,
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { AlertCircle, ChevronDown, Code2, CornerDownRight, Image as ImageIcon, ListPlus, MessageCircle, X, } from 'lucide-react';
|
|
4
4
|
import { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
|
|
5
5
|
import { cn } from '../../lib/utils';
|
|
6
6
|
import { Button } from '../ui/button';
|
|
7
7
|
import { ScrollArea } from '../ui/scroll-area';
|
|
8
8
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from '../ui/tooltip';
|
|
9
|
-
import { ChatInput } from './chat-input';
|
|
9
|
+
import { ChatInput, } from './chat-input';
|
|
10
10
|
import { ThinkingMessage } from './chat-message';
|
|
11
11
|
import { MessageRow, computeIsThinking, } from './chat-messages';
|
|
12
12
|
import { ConversationHistory, } from './conversation-history';
|
|
@@ -19,7 +19,7 @@ import { Virtualizer } from 'virtua';
|
|
|
19
19
|
/**
|
|
20
20
|
* Floating chat panel with model selector
|
|
21
21
|
*/
|
|
22
|
-
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, isEditingApp = true, onEditingAppChange, showEditingAppToggle = false, checkpoints, restoringMessageId, onRestoreCheckpoint, }) {
|
|
22
|
+
export function ChatPanel({ messages, status, isTurnInFlight, 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, isEditingApp = true, onEditingAppChange, showEditingAppToggle = false, checkpoints, restoringMessageId, onRestoreCheckpoint, imageAttachments = [], onRemoveImageAttachment, isImageDropActive = false, queuedMessages = [], onRemoveQueuedMessage, onRetryQueuedMessage, onSteerQueuedMessage, }) {
|
|
23
23
|
const inputRef = useRef(null);
|
|
24
24
|
const scrollAreaRef = useRef(null);
|
|
25
25
|
const viewportRef = useRef(null);
|
|
@@ -29,6 +29,10 @@ export function ChatPanel({ messages, status, input, onInputChange, onSubmit, on
|
|
|
29
29
|
const [viewportVersion, setViewportVersion] = useState(0);
|
|
30
30
|
const [isAtBottom, setIsAtBottom] = useState(true);
|
|
31
31
|
const isResponding = status === 'streaming' || status === 'submitted';
|
|
32
|
+
const controlsDisabled = isTurnInFlight ?? isResponding;
|
|
33
|
+
const composerControls = (_jsxs(_Fragment, { children: [_jsx(ModelSelector, { models: models, selectedModel: selectedModel, onModelChange: onModelChange, disabled: controlsDisabled, dropdownAlign: "end", dropdownSide: "top" }), reasoningEffortOptions &&
|
|
34
|
+
selectedReasoningEffort &&
|
|
35
|
+
onReasoningEffortChange && (_jsx(ReasoningEffortSelector, { options: reasoningEffortOptions, selectedEffort: selectedReasoningEffort, onEffortChange: onReasoningEffortChange, disabled: controlsDisabled, showLabel: true, dropdownAlign: "end", dropdownSide: "top" }))] }));
|
|
32
36
|
// Capture the Radix viewport element so virtua can use it as the scroll container
|
|
33
37
|
useEffect(() => {
|
|
34
38
|
const scrollArea = scrollAreaRef.current;
|
|
@@ -67,12 +71,25 @@ export function ChatPanel({ messages, status, input, onInputChange, onSubmit, on
|
|
|
67
71
|
return () => clearTimeout(timer);
|
|
68
72
|
}
|
|
69
73
|
}, [isExpanded]);
|
|
70
|
-
const
|
|
74
|
+
const showMissingApiKeyPrompt = missingApiKey ||
|
|
75
|
+
(error &&
|
|
76
|
+
(error.message?.includes('API_KEY not configured') ||
|
|
77
|
+
error.message?.includes('credential required in Vault') ||
|
|
78
|
+
error.message?.includes('credential not configured in Vault')));
|
|
79
|
+
const submitDisabled = Boolean(showMissingApiKeyPrompt);
|
|
80
|
+
const handleSubmit = useCallback((e, mode) => {
|
|
71
81
|
e?.preventDefault();
|
|
72
|
-
if (
|
|
82
|
+
if (submitDisabled) {
|
|
83
|
+
if (!isExpanded) {
|
|
84
|
+
onExpandedChange(true);
|
|
85
|
+
}
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (!input.trim() && imageAttachments.length === 0)
|
|
73
89
|
return;
|
|
74
|
-
|
|
75
|
-
|
|
90
|
+
const submitMode = mode ?? (isResponding ? 'steer' : 'send');
|
|
91
|
+
// Steer interrupts the current response and sends the new instruction now.
|
|
92
|
+
if (submitMode === 'steer' && isResponding && onStop) {
|
|
76
93
|
onStop();
|
|
77
94
|
}
|
|
78
95
|
if (!isExpanded) {
|
|
@@ -80,26 +97,45 @@ export function ChatPanel({ messages, status, input, onInputChange, onSubmit, on
|
|
|
80
97
|
}
|
|
81
98
|
// Reset to bottom when user sends a message
|
|
82
99
|
setIsAtBottom(true);
|
|
83
|
-
onSubmit(e);
|
|
84
|
-
}, [
|
|
100
|
+
onSubmit(e, submitMode);
|
|
101
|
+
}, [
|
|
102
|
+
imageAttachments.length,
|
|
103
|
+
input,
|
|
104
|
+
isResponding,
|
|
105
|
+
isExpanded,
|
|
106
|
+
onExpandedChange,
|
|
107
|
+
onSubmit,
|
|
108
|
+
onStop,
|
|
109
|
+
submitDisabled,
|
|
110
|
+
]);
|
|
85
111
|
const handleNewChat = useCallback(() => {
|
|
86
112
|
onNewChat?.();
|
|
87
113
|
inputRef.current?.focus();
|
|
88
114
|
}, [onNewChat]);
|
|
115
|
+
const composerStartControls = (_jsxs(_Fragment, { children: [conversations &&
|
|
116
|
+
(conversations.length > 0 || onNewChat) &&
|
|
117
|
+
onSelectConversation && (_jsx(ConversationHistory, { conversations: conversations, currentConversationId: currentConversationId, onSelect: onSelectConversation, onDelete: onDeleteConversation, onNewChat: handleNewChat, newChatDisabled: controlsDisabled, disabled: controlsDisabled, dropdownAlign: "start", dropdownSide: "top", tooltipSide: "top" })), showEditingAppToggle && onEditingAppChange && (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", onClick: (event) => {
|
|
118
|
+
event.preventDefault();
|
|
119
|
+
onEditingAppChange(!isEditingApp);
|
|
120
|
+
}, className: cn('cursor-pointer', isEditingApp
|
|
121
|
+
? 'text-primary bg-primary/10 hover:bg-primary/20'
|
|
122
|
+
: 'text-muted-foreground hover:text-foreground'), children: _jsx(Code2, { className: "size-4" }) }) }), _jsx(TooltipContent, { side: "top", children: _jsx("p", { children: isEditingApp
|
|
123
|
+
? 'Editing this app (click to disable)'
|
|
124
|
+
: 'Not editing app (click to enable)' }) })] }) }))] }));
|
|
89
125
|
// Keyboard shortcut: Cmd+Shift+O for new chat
|
|
90
126
|
useEffect(() => {
|
|
91
127
|
const handleKeyDown = (e) => {
|
|
92
128
|
if (e.key === 'o' && e.metaKey && e.shiftKey) {
|
|
93
129
|
e.preventDefault();
|
|
94
|
-
// Only create new chat if
|
|
95
|
-
if (!
|
|
130
|
+
// Only create new chat if conversation controls are currently available.
|
|
131
|
+
if (!controlsDisabled && isExpanded) {
|
|
96
132
|
handleNewChat();
|
|
97
133
|
}
|
|
98
134
|
}
|
|
99
135
|
};
|
|
100
136
|
document.addEventListener('keydown', handleKeyDown);
|
|
101
137
|
return () => document.removeEventListener('keydown', handleKeyDown);
|
|
102
|
-
}, [
|
|
138
|
+
}, [controlsDisabled, isExpanded, handleNewChat]);
|
|
103
139
|
// Handle clicking the minimized flap
|
|
104
140
|
const handleFlapClick = useCallback(() => {
|
|
105
141
|
onMinimizedChange?.(false);
|
|
@@ -113,11 +149,7 @@ export function ChatPanel({ messages, status, input, onInputChange, onSubmit, on
|
|
|
113
149
|
}, [isExpanded, onExpandedChange, onMinimizedChange]);
|
|
114
150
|
const isStreaming = status === 'streaming' || status === 'submitted';
|
|
115
151
|
const isThinking = useMemo(() => computeIsThinking(messages, status), [messages, status]);
|
|
116
|
-
const
|
|
117
|
-
(error && error.message?.includes('API_KEY not configured'));
|
|
118
|
-
const showErrorMessage = Boolean(error) &&
|
|
119
|
-
status === 'error' &&
|
|
120
|
-
!error?.message?.includes('API_KEY not configured');
|
|
152
|
+
const showErrorMessage = Boolean(error) && status === 'error' && !showMissingApiKeyPrompt;
|
|
121
153
|
const lastMessageId = messages[messages.length - 1]?.id;
|
|
122
154
|
const listItems = useMemo(() => {
|
|
123
155
|
const items = [];
|
|
@@ -150,12 +182,12 @@ export function ChatPanel({ messages, status, input, onInputChange, onSubmit, on
|
|
|
150
182
|
return (_jsx("div", { className: "px-2 pb-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 })) }));
|
|
151
183
|
case 'message': {
|
|
152
184
|
const checkpoint = checkpoints?.get(item.message.id);
|
|
153
|
-
return (_jsx("div", { className: "px-2 pb-
|
|
185
|
+
return (_jsx("div", { className: "px-2 pb-3", children: _jsx(MessageRow, { message: item.message, isLast: item.message.id === lastMessageId, isStreaming: isStreaming, checkpoint: checkpoint, restoringMessageId: restoringMessageId, onRestoreCheckpoint: onRestoreCheckpoint }) }));
|
|
154
186
|
}
|
|
155
187
|
case 'thinking':
|
|
156
|
-
return (_jsx("div", { className: "px-2 pb-
|
|
188
|
+
return (_jsx("div", { className: "px-2 pb-3", children: _jsx(ThinkingMessage, {}) }));
|
|
157
189
|
case 'missing-api-key':
|
|
158
|
-
return (_jsx("div", { className: "px-2 pb-4", children: _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" }))] }) }));
|
|
190
|
+
return (_jsx("div", { className: "px-2 pb-4", children: _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 Vault to start chatting with AI" })] }), onAddApiKey && (_jsx(Button, { onClick: onAddApiKey, size: "sm", className: "cursor-pointer", children: "Add API key" }))] }) }));
|
|
159
191
|
case 'error':
|
|
160
192
|
return (_jsx("div", { className: "px-2 pb-4", children: error && (_jsxs("div", { className: "border-destructive/30 bg-destructive/10 text-destructive mx-2 flex items-start gap-2 rounded-lg border p-3 text-sm", children: [_jsx(AlertCircle, { className: "mt-0.5 size-4 shrink-0" }), _jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "font-medium", children: "Request failed" }), _jsx("p", { className: "text-destructive/80 mt-0.5 break-words", children: error.message || 'An unknown error occurred' })] })] })) }));
|
|
161
193
|
}
|
|
@@ -202,11 +234,11 @@ export function ChatPanel({ messages, status, input, onInputChange, onSubmit, on
|
|
|
202
234
|
width: isExpanded ? 'min(90vw, 640px)' : '400px',
|
|
203
235
|
y: isMinimized ? 'calc(100% + 24px)' : 0,
|
|
204
236
|
opacity: isMinimized ? 0 : 1,
|
|
205
|
-
}, 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/80 pointer-events-auto relative w-full shadow-[0_8px_40px_-12px_rgba(0,0,0,0.15)]', isExpanded
|
|
237
|
+
}, 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", { "data-chat-drop-target": true, className: cn('bg-background/80 pointer-events-auto relative w-full shadow-[0_8px_40px_-12px_rgba(0,0,0,0.15)]', isExpanded
|
|
206
238
|
? 'bg-background'
|
|
207
239
|
: 'bg-background/80 supports-[backdrop-filter]:backdrop-blur-xl', 'rounded-[28px]', isExpanded ? 'overflow-hidden' : 'overflow-visible'), children: [!isExpanded && (_jsx("div", { className: "pointer-events-none absolute inset-0 z-0 rounded-[inherit]", style: {
|
|
208
240
|
background: 'linear-gradient(134deg, rgba(255,255,255,0.08) 0%, rgba(255,255,255,0.02) 50%, transparent 55%)',
|
|
209
|
-
} })), _jsx("div", { className: "pointer-events-none absolute inset-0 z-50 rounded-[inherit]", style: {
|
|
241
|
+
} })), isImageDropActive && (_jsxs("div", { className: "bg-background/90 border-primary/50 text-foreground absolute inset-0 z-[70] flex flex-col items-center justify-center gap-2 rounded-[inherit] border-2 border-dashed backdrop-blur-sm", children: [_jsx(ImageIcon, { className: "text-primary size-7" }), _jsxs("div", { className: "text-center", children: [_jsx("p", { className: "text-sm font-medium", children: "Drop images to attach" }), _jsx("p", { className: "text-muted-foreground text-xs", children: "They stay on disk and are read when sent" })] })] })), _jsx("div", { className: "pointer-events-none absolute inset-0 z-50 rounded-[inherit]", style: {
|
|
210
242
|
padding: '1px',
|
|
211
243
|
background: 'linear-gradient(135deg, rgba(255,255,255,0.25) 0%, rgba(255,255,255,0.1) 20%, rgba(255,255,255,0.05) 45%, rgba(255,255,255,0.05) 55%, rgba(255,255,255,0.1) 80%, rgba(255,255,255,0.25) 100%)',
|
|
212
244
|
mask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',
|
|
@@ -227,17 +259,21 @@ export function ChatPanel({ messages, status, input, onInputChange, onSubmit, on
|
|
|
227
259
|
} })), _jsxs(motion.div, { initial: false, animate: {
|
|
228
260
|
height: isExpanded ? 'min(60vh, 480px)' : '0px',
|
|
229
261
|
opacity: isExpanded ? 1 : 0,
|
|
230
|
-
}, transition: { type: 'tween', duration: 0.2, ease: 'easeOut' }, className: "relative z-10 flex flex-col overflow-hidden", style: { pointerEvents: isExpanded ? 'auto' : 'none' }, children: [
|
|
231
|
-
selectedReasoningEffort &&
|
|
232
|
-
onReasoningEffortChange && (_jsx(ReasoningEffortSelector, { options: reasoningEffortOptions, selectedEffort: selectedReasoningEffort, onEffortChange: onReasoningEffortChange, disabled: isResponding })), showEditingAppToggle && onEditingAppChange && (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", onClick: () => onEditingAppChange(!isEditingApp), className: cn('ml-1', isEditingApp
|
|
233
|
-
? 'text-primary bg-primary/10 hover:bg-primary/20'
|
|
234
|
-
: 'text-muted-foreground hover:text-foreground'), children: _jsx(Code2, { className: "size-4" }) }) }), _jsx(TooltipContent, { side: "bottom", children: _jsx("p", { children: isEditingApp
|
|
235
|
-
? 'Editing this app (click to disable)'
|
|
236
|
-
: 'Not editing app (click to enable)' }) })] }) }))] }), _jsxs("div", { className: "flex items-center gap-1", children: [conversations &&
|
|
237
|
-
conversations.length > 0 &&
|
|
238
|
-
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: _jsx("div", { className: "min-w-0 max-w-full overflow-hidden py-4", children: _jsx(ToolProgressProvider, { value: toolProgress, children: _jsx(ToolApprovalProvider, { onApprovalResponse: onApprovalResponse ?? null, children: viewportVersion > 0 ? (_jsx(Virtualizer, { ref: virtualizerRef, data: listItems, scrollRef: viewportRef, bufferSize: 300, children: (item) => renderVirtualItem(item) }, viewportVersion)) : (listItems.map((item) => (_jsx("div", { children: renderVirtualItem(item) }, item.key)))) }) }) }) })] }), _jsx("div", { className: "relative z-10", onClick: () => {
|
|
262
|
+
}, transition: { type: 'tween', duration: 0.2, ease: 'easeOut' }, className: "relative z-10 flex flex-col overflow-hidden", style: { pointerEvents: isExpanded ? 'auto' : 'none' }, children: [_jsx("div", { className: "absolute right-3 top-3 z-20", children: _jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", onClick: handleMinimize, className: "text-muted-foreground hover:text-foreground cursor-pointer", children: _jsx(ChevronDown, { className: "size-4" }) }) }), _jsx(TooltipContent, { side: "bottom", children: _jsx("p", { children: "Minimize" }) })] }) }) }), _jsx(ScrollArea, { ref: scrollAreaRef, className: "min-h-0 min-w-0 flex-1 px-2", children: _jsx("div", { className: "min-w-0 max-w-full overflow-hidden py-4 pr-10", children: _jsx(ToolProgressProvider, { value: toolProgress, children: _jsx(ToolApprovalProvider, { onApprovalResponse: onApprovalResponse ?? null, children: viewportVersion > 0 ? (_jsx(Virtualizer, { ref: virtualizerRef, data: listItems, scrollRef: viewportRef, bufferSize: 300, children: (item) => renderVirtualItem(item) }, viewportVersion)) : (listItems.map((item) => (_jsx("div", { children: renderVirtualItem(item) }, item.key)))) }) }) }) })] }), _jsxs("div", { className: "relative z-10", onClick: () => {
|
|
239
263
|
if (!isExpanded) {
|
|
240
264
|
onExpandedChange(true);
|
|
241
265
|
}
|
|
242
|
-
}, children:
|
|
266
|
+
}, children: [queuedMessages.length > 0 && (_jsxs("div", { className: "border-border/70 bg-background/95 border-t px-4 py-2", children: [_jsxs("div", { className: "text-muted-foreground mb-2 flex items-center gap-2 text-xs font-medium", children: [_jsx(ListPlus, { className: "size-3.5" }), _jsx("span", { children: "Queued follow-ups" })] }), _jsx("div", { className: "space-y-1.5", children: queuedMessages.map((message) => (_jsxs("div", { className: "bg-muted/60 text-foreground flex min-h-9 items-center gap-2 rounded-lg px-2.5 py-1.5 text-xs", children: [_jsxs("span", { className: "min-w-0 flex-1 truncate", children: [message.preview, message.attachmentCount > 0 &&
|
|
267
|
+
` (${message.attachmentCount} image${message.attachmentCount === 1 ? '' : 's'})`] }), message.status === 'failed' &&
|
|
268
|
+
message.error &&
|
|
269
|
+
onRetryQueuedMessage && (_jsxs("button", { type: "button", onClick: (event) => {
|
|
270
|
+
event.stopPropagation();
|
|
271
|
+
onRetryQueuedMessage(message.id);
|
|
272
|
+
}, className: "text-muted-foreground hover:bg-accent hover:text-foreground flex h-7 shrink-0 cursor-pointer items-center gap-1.5 rounded-md px-2 text-xs transition-colors", "aria-label": "Retry queued follow-up", children: [_jsx(CornerDownRight, { className: "size-3.5" }), _jsx("span", { children: "Retry" })] })), isResponding && onSteerQueuedMessage && (_jsxs("button", { type: "button", onClick: (event) => {
|
|
273
|
+
event.stopPropagation();
|
|
274
|
+
onSteerQueuedMessage(message.id);
|
|
275
|
+
}, className: "text-muted-foreground hover:bg-accent hover:text-foreground flex h-7 shrink-0 cursor-pointer items-center gap-1.5 rounded-md px-2 text-xs transition-colors", "aria-label": "Steer active response with queued follow-up", children: [_jsx(CornerDownRight, { className: "size-3.5" }), _jsx("span", { children: "Steer" })] })), onRemoveQueuedMessage && (_jsx("button", { type: "button", onClick: (event) => {
|
|
276
|
+
event.stopPropagation();
|
|
277
|
+
onRemoveQueuedMessage(message.id);
|
|
278
|
+
}, className: "text-muted-foreground hover:bg-accent hover:text-foreground flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-md transition-colors", "aria-label": "Remove queued follow-up", children: _jsx(X, { className: "size-3.5" }) }))] }, message.id))) })] })), _jsx(ChatInput, { input: input, onInputChange: onInputChange, onSubmit: handleSubmit, isResponding: isResponding, placeholder: placeholder, inputRef: inputRef, onStop: onStop, compact: !isExpanded, imageAttachments: imageAttachments, onRemoveImageAttachment: onRemoveImageAttachment, footerStartControls: isExpanded ? composerStartControls : null, footerControls: isExpanded ? composerControls : null, submitDisabled: submitDisabled })] })] }) })] }));
|
|
243
279
|
}
|
|
@@ -10,9 +10,14 @@ interface ConversationHistoryProps {
|
|
|
10
10
|
currentConversationId?: string | null;
|
|
11
11
|
onSelect: (id: string) => void;
|
|
12
12
|
onDelete?: (id: string) => void;
|
|
13
|
+
onNewChat?: () => void;
|
|
14
|
+
newChatDisabled?: boolean;
|
|
13
15
|
disabled?: boolean;
|
|
14
16
|
className?: string;
|
|
17
|
+
dropdownAlign?: 'start' | 'center' | 'end';
|
|
18
|
+
dropdownSide?: 'top' | 'right' | 'bottom' | 'left';
|
|
19
|
+
tooltipSide?: 'top' | 'right' | 'bottom' | 'left';
|
|
15
20
|
}
|
|
16
|
-
export declare function ConversationHistory({ conversations, currentConversationId, onSelect, onDelete, disabled, className, }: ConversationHistoryProps): import("react/jsx-runtime").JSX.Element | null;
|
|
21
|
+
export declare function ConversationHistory({ conversations, currentConversationId, onSelect, onDelete, onNewChat, newChatDisabled, disabled, className, dropdownAlign, dropdownSide, tooltipSide, }: ConversationHistoryProps): import("react/jsx-runtime").JSX.Element | null;
|
|
17
22
|
export {};
|
|
18
23
|
//# sourceMappingURL=conversation-history.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation-history.d.ts","sourceRoot":"","sources":["../../../src/components/chat/conversation-history.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"conversation-history.d.ts","sourceRoot":"","sources":["../../../src/components/chat/conversation-history.tsx"],"names":[],"mappings":"AAkBA,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,UAAU,wBAAwB;IAChC,aAAa,EAAE,gBAAgB,EAAE,CAAA;IACjC,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrC,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/B,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAA;IAC1C,YAAY,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;IAClD,WAAW,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;CAClD;AAiBD,wBAAgB,mBAAmB,CAAC,EAClC,aAAa,EACb,qBAAqB,EACrB,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,eAAuB,EACvB,QAAgB,EAChB,SAAS,EACT,aAAuB,EACvB,YAAuB,EACvB,WAAsB,GACvB,EAAE,wBAAwB,kDA6F1B"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Clock, Trash2 } from 'lucide-react';
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Clock, Plus, Trash2 } from 'lucide-react';
|
|
3
3
|
import { cn } from '../../lib/utils';
|
|
4
4
|
import { Button } from '../ui/button';
|
|
5
|
-
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from '../ui/dropdown-menu';
|
|
5
|
+
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuTrigger, } from '../ui/dropdown-menu';
|
|
6
6
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from '../ui/tooltip';
|
|
7
7
|
function formatRelativeTime(dateStr) {
|
|
8
8
|
const date = new Date(dateStr);
|
|
@@ -21,11 +21,11 @@ function formatRelativeTime(dateStr) {
|
|
|
21
21
|
return `${diffDays}d ago`;
|
|
22
22
|
return date.toLocaleDateString();
|
|
23
23
|
}
|
|
24
|
-
export function ConversationHistory({ conversations, currentConversationId, onSelect, onDelete, disabled = false, className, }) {
|
|
25
|
-
if (conversations.length === 0) {
|
|
24
|
+
export function ConversationHistory({ conversations, currentConversationId, onSelect, onDelete, onNewChat, newChatDisabled = false, disabled = false, className, dropdownAlign = 'start', dropdownSide = 'bottom', tooltipSide = 'bottom', }) {
|
|
25
|
+
if (conversations.length === 0 && !onNewChat) {
|
|
26
26
|
return null;
|
|
27
27
|
}
|
|
28
|
-
return (_jsx(TooltipProvider, { children: _jsxs(DropdownMenu, { children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", disabled: disabled, className: cn('text-muted-foreground hover:text-foreground', className), children: _jsx(Clock, { className: "size-4" }) }) }) }), _jsx(TooltipContent, { side:
|
|
28
|
+
return (_jsx(TooltipProvider, { children: _jsxs(DropdownMenu, { children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", disabled: disabled, className: cn('text-muted-foreground hover:text-foreground cursor-pointer', className), children: _jsx(Clock, { className: "size-4" }) }) }) }), _jsx(TooltipContent, { side: tooltipSide, children: _jsx("p", { children: "Conversation history" }) })] }), _jsxs(DropdownMenuContent, { align: dropdownAlign, side: dropdownSide, className: "w-64", children: [onNewChat && (_jsxs(_Fragment, { children: [_jsxs(DropdownMenuItem, { onClick: onNewChat, disabled: newChatDisabled, title: "New conversation (Cmd+Shift+O)", className: "flex cursor-pointer items-center gap-2", children: [_jsx(Plus, { className: "size-4" }), _jsx("span", { children: "New conversation" }), _jsx(DropdownMenuShortcut, { children: "\u2318\u21E7O" })] }), conversations.length > 0 && _jsx(DropdownMenuSeparator, {})] })), conversations.length > 0 && (_jsxs(_Fragment, { children: [_jsx("div", { className: "text-muted-foreground px-2 py-1.5 text-xs font-medium", children: "Recent conversations" }), _jsx(DropdownMenuSeparator, {})] })), _jsx("div", { className: "max-h-64 overflow-y-auto", children: conversations.slice(0, 20).map((conv) => (_jsxs(DropdownMenuItem, { onClick: () => onSelect(conv.id), className: cn('group flex cursor-pointer items-start gap-2 py-2', conv.id === currentConversationId && 'bg-accent'), children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "truncate text-sm", children: conv.title }), _jsxs("p", { className: "text-muted-foreground text-xs", children: [formatRelativeTime(conv.updatedAt), " \u00B7 ", conv.messageCount, ' ', "messages"] })] }), onDelete && (_jsx(Button, { variant: "ghost", size: "icon-sm", onClick: (e) => {
|
|
29
29
|
e.stopPropagation();
|
|
30
30
|
onDelete(conv.id);
|
|
31
31
|
}, className: "text-muted-foreground hover:text-destructive size-6 shrink-0 opacity-0 group-hover:opacity-100", children: _jsx(Trash2, { className: "size-3" }) }))] }, conv.id))) })] })] }) }));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { ChatInput } from './chat-input';
|
|
1
|
+
export { ChatInput, type ChatImageAttachment, type ChatSubmitMode, } from './chat-input';
|
|
2
2
|
export { Message, ThinkingMessage, type ChatMessage, type ChatMessagePart, } from './chat-message';
|
|
3
3
|
export { Messages, type MessageCheckpoint } from './chat-messages';
|
|
4
4
|
export { CheckpointBadge, type CheckpointBadgeProps } from './checkpoint-badge';
|
|
@@ -6,7 +6,7 @@ export { RestoreDialog, type CheckpointInfo, type RestoreDialogProps, } from './
|
|
|
6
6
|
export { ModelSelector, type ModelOption } from './model-selector';
|
|
7
7
|
export { ReasoningEffortSelector, type ReasoningEffortOption, } from './reasoning-effort-selector';
|
|
8
8
|
export { ConversationHistory, type ConversationMeta, } from './conversation-history';
|
|
9
|
-
export { ChatPanel, type ChatPanelProps, type ToolProgressData, } from './chat-panel';
|
|
9
|
+
export { ChatPanel, type ChatPanelProps, type QueuedChatMessage, type ToolProgressData, } from './chat-panel';
|
|
10
10
|
export { ThinkingTimeline, ThinkingTimelineMarker, type ThinkingTimelineItem, type ThinkingTimelineProps, } from './thinking-timeline';
|
|
11
11
|
export { getToolHandler, DEFAULT_TOOL_HANDLERS, type ToolHandler, } from './tool-handlers';
|
|
12
12
|
export { ToolProgressProvider, useToolProgress, useToolCallProgress, } from './tool-progress-context';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/chat/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/chat/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,KAAK,mBAAmB,EACxB,KAAK,cAAc,GACpB,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,OAAO,EACP,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,eAAe,GACrB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,QAAQ,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAClE,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAC/E,OAAO,EACL,aAAa,EACb,KAAK,cAAc,EACnB,KAAK,kBAAkB,GACxB,MAAM,kBAAkB,CAAA;AACzB,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,iBAAiB,EACtB,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"}
|
|
@@ -10,7 +10,9 @@ type ModelSelectorProps = {
|
|
|
10
10
|
onModelChange: (modelId: string) => void;
|
|
11
11
|
disabled?: boolean;
|
|
12
12
|
className?: string;
|
|
13
|
+
dropdownAlign?: 'start' | 'center' | 'end';
|
|
14
|
+
dropdownSide?: 'top' | 'right' | 'bottom' | 'left';
|
|
13
15
|
};
|
|
14
|
-
export declare function ModelSelector({ models, selectedModel, onModelChange, disabled, className, }: ModelSelectorProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export declare function ModelSelector({ models, selectedModel, onModelChange, disabled, className, dropdownAlign, dropdownSide, }: ModelSelectorProps): import("react/jsx-runtime").JSX.Element;
|
|
15
17
|
export {};
|
|
16
18
|
//# sourceMappingURL=model-selector.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-selector.d.ts","sourceRoot":"","sources":["../../../src/components/chat/model-selector.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAUtC,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,SAAS,CAAA;CAChB,CAAA;AAED,KAAK,kBAAkB,GAAG;IACxB,MAAM,EAAE,WAAW,EAAE,CAAA;IACrB,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"model-selector.d.ts","sourceRoot":"","sources":["../../../src/components/chat/model-selector.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAUtC,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,SAAS,CAAA;CAChB,CAAA;AAED,KAAK,kBAAkB,GAAG;IACxB,MAAM,EAAE,WAAW,EAAE,CAAA;IACrB,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAA;IAC1C,YAAY,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;CACnD,CAAA;AAED,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EACN,aAAa,EACb,aAAa,EACb,QAAgB,EAChB,SAAS,EACT,aAAuB,EACvB,YAAuB,GACxB,EAAE,kBAAkB,2CAyCpB"}
|
|
@@ -3,7 +3,7 @@ import { Check, ChevronDown } from 'lucide-react';
|
|
|
3
3
|
import { cn } from '../../lib/utils';
|
|
4
4
|
import { Button } from '../ui/button';
|
|
5
5
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '../ui/dropdown-menu';
|
|
6
|
-
export function ModelSelector({ models, selectedModel, onModelChange, disabled = false, className, }) {
|
|
6
|
+
export function ModelSelector({ models, selectedModel, onModelChange, disabled = false, className, dropdownAlign = 'start', dropdownSide = 'bottom', }) {
|
|
7
7
|
const currentModel = models.find((m) => m.id === selectedModel) ?? models[0];
|
|
8
|
-
return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", size: "sm", disabled: disabled, className: cn('text-muted-foreground hover:text-foreground h-
|
|
8
|
+
return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", size: "sm", disabled: disabled, className: cn('text-muted-foreground hover:text-foreground h-8 min-w-0 max-w-[180px] cursor-pointer gap-1.5 rounded-full px-2.5 text-xs font-medium', className), children: [_jsx("span", { children: currentModel?.icon }), _jsx("span", { className: "min-w-0 truncate", children: currentModel?.name }), _jsx(ChevronDown, { className: "size-3 opacity-50" })] }) }), _jsx(DropdownMenuContent, { align: dropdownAlign, side: dropdownSide, className: "min-w-[160px]", children: models.map((model) => (_jsxs(DropdownMenuItem, { onClick: () => onModelChange(model.id), className: "flex cursor-pointer items-center gap-2", children: [_jsx("span", { children: model.icon }), _jsx("span", { className: "flex-1", children: model.name }), model.id === selectedModel && (_jsx(Check, { className: "text-primary size-4" }))] }, model.id))) })] }));
|
|
9
9
|
}
|
|
@@ -8,7 +8,10 @@ type ReasoningEffortSelectorProps = {
|
|
|
8
8
|
onEffortChange: (effort: string) => void;
|
|
9
9
|
disabled?: boolean;
|
|
10
10
|
className?: string;
|
|
11
|
+
showLabel?: boolean;
|
|
12
|
+
dropdownAlign?: 'start' | 'center' | 'end';
|
|
13
|
+
dropdownSide?: 'top' | 'right' | 'bottom' | 'left';
|
|
11
14
|
};
|
|
12
|
-
export declare function ReasoningEffortSelector({ options, selectedEffort, onEffortChange, disabled, className, }: ReasoningEffortSelectorProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export declare function ReasoningEffortSelector({ options, selectedEffort, onEffortChange, disabled, className, showLabel, dropdownAlign, dropdownSide, }: ReasoningEffortSelectorProps): import("react/jsx-runtime").JSX.Element;
|
|
13
16
|
export {};
|
|
14
17
|
//# sourceMappingURL=reasoning-effort-selector.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reasoning-effort-selector.d.ts","sourceRoot":"","sources":["../../../src/components/chat/reasoning-effort-selector.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"reasoning-effort-selector.d.ts","sourceRoot":"","sources":["../../../src/components/chat/reasoning-effort-selector.tsx"],"names":[],"mappings":"AAWA,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED,KAAK,4BAA4B,GAAG;IAClC,OAAO,EAAE,qBAAqB,EAAE,CAAA;IAChC,cAAc,EAAE,MAAM,CAAA;IACtB,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,aAAa,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAA;IAC1C,YAAY,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;CACnD,CAAA;AAED,wBAAgB,uBAAuB,CAAC,EACtC,OAAO,EACP,cAAc,EACd,cAAc,EACd,QAAgB,EAChB,SAAS,EACT,SAAiB,EACjB,aAAuB,EACvB,YAAuB,GACxB,EAAE,4BAA4B,2CAoD9B"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Brain, Check } from 'lucide-react';
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Brain, Check, ChevronDown } from 'lucide-react';
|
|
3
3
|
import { cn } from '../../lib/utils';
|
|
4
4
|
import { Button } from '../ui/button';
|
|
5
|
-
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '../ui/dropdown-menu';
|
|
6
|
-
|
|
7
|
-
export function ReasoningEffortSelector({ options, selectedEffort, onEffortChange, disabled = false, className, }) {
|
|
5
|
+
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuTrigger, } from '../ui/dropdown-menu';
|
|
6
|
+
export function ReasoningEffortSelector({ options, selectedEffort, onEffortChange, disabled = false, className, showLabel = false, dropdownAlign = 'start', dropdownSide = 'bottom', }) {
|
|
8
7
|
const currentOption = options.find((o) => o.value === selectedEffort) ?? options[1]; // Default to medium
|
|
9
|
-
return (
|
|
8
|
+
return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", disabled: disabled, className: cn('text-muted-foreground hover:text-foreground cursor-pointer', showLabel &&
|
|
9
|
+
'h-8 w-auto gap-1.5 rounded-full px-3 text-xs font-medium', className), children: showLabel ? (_jsxs(_Fragment, { children: [_jsx("span", { children: currentOption?.label }), _jsx(ChevronDown, { className: "size-3 opacity-50" })] })) : (_jsx(Brain, { className: "size-4" })) }) }), _jsxs(DropdownMenuContent, { align: dropdownAlign, side: dropdownSide, className: "min-w-[140px]", children: [_jsx(DropdownMenuLabel, { className: "text-muted-foreground font-normal", children: "Reasoning" }), options.map((option) => (_jsxs(DropdownMenuItem, { onClick: () => onEffortChange(option.value), className: "flex cursor-pointer items-center gap-2", children: [!showLabel && _jsx(Brain, { className: "text-muted-foreground size-3.5" }), _jsx("span", { className: "flex-1", children: option.label }), option.value === selectedEffort && (_jsx(Check, { className: "text-primary size-4" }))] }, option.value)))] })] }));
|
|
10
10
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-handlers.d.ts","sourceRoot":"","sources":["../../../src/components/chat/tool-handlers.tsx"],"names":[],"mappings":"AAuBA,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAA;AAEhD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAW5D,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;
|
|
1
|
+
{"version":3,"file":"tool-handlers.d.ts","sourceRoot":"","sources":["../../../src/components/chat/tool-handlers.tsx"],"names":[],"mappings":"AAuBA,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAA;AAEhD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAW5D,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;AAoSD;;GAEG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAg/D7D,CAAA;AAoBD;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAmC5D"}
|
|
@@ -85,10 +85,66 @@ function getFileName(path) {
|
|
|
85
85
|
const parts = path.split('/');
|
|
86
86
|
return parts[parts.length - 1] || path;
|
|
87
87
|
}
|
|
88
|
+
function appApiScopes(output) {
|
|
89
|
+
return (output.apps?.flatMap((app) => app.api?.capabilities?.flatMap((capability) => capability.scopes ?? []) ?? []) ?? []);
|
|
90
|
+
}
|
|
91
|
+
function appApiItemCount(result) {
|
|
92
|
+
if (Array.isArray(result)) {
|
|
93
|
+
return result.length;
|
|
94
|
+
}
|
|
95
|
+
if (result && typeof result === 'object') {
|
|
96
|
+
const record = result;
|
|
97
|
+
for (const key of ['items', 'records', 'meetings', 'results', 'data']) {
|
|
98
|
+
const value = record[key];
|
|
99
|
+
if (Array.isArray(value)) {
|
|
100
|
+
return value.length;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return 1;
|
|
104
|
+
}
|
|
105
|
+
return result == null ? 0 : 1;
|
|
106
|
+
}
|
|
88
107
|
/**
|
|
89
108
|
* Default tool handlers for Moldable tools
|
|
90
109
|
*/
|
|
91
110
|
export const DEFAULT_TOOL_HANDLERS = {
|
|
111
|
+
listMoldableAppApi: {
|
|
112
|
+
loadingLabel: 'Listing app APIs...',
|
|
113
|
+
marker: ThinkingTimelineMarker.Default,
|
|
114
|
+
inline: true,
|
|
115
|
+
renderLoading: () => (_jsx(LoadingIndicator, { icon: Database, children: "Listing app APIs..." })),
|
|
116
|
+
renderOutput: (output, toolCallId) => {
|
|
117
|
+
if (output === undefined || output === null) {
|
|
118
|
+
return (_jsx(LoadingIndicator, { icon: Database, children: "Listing app APIs..." }, toolCallId));
|
|
119
|
+
}
|
|
120
|
+
const result = output;
|
|
121
|
+
const apps = result.apps ?? [];
|
|
122
|
+
const scopes = appApiScopes(result);
|
|
123
|
+
const hasError = result.success === false;
|
|
124
|
+
return (_jsx("div", { className: "my-1 min-w-0 max-w-xl", children: _jsxs("div", { className: cn('inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs', hasError
|
|
125
|
+
? 'bg-destructive/10 text-destructive'
|
|
126
|
+
: 'bg-muted text-muted-foreground'), children: [_jsx(Database, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "shrink-0 font-medium", children: hasError ? 'App API list failed' : 'Listing app APIs' }), !hasError && (_jsxs("span", { className: "truncate", children: [apps.length, " app", apps.length === 1 ? '' : 's', ", ", scopes.length, ' ', "function", scopes.length === 1 ? '' : 's'] })), hasError ? (_jsx(X, { className: "size-3 shrink-0" })) : (_jsx(Check, { className: "size-3 shrink-0 text-green-600" }))] }) }, toolCallId));
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
callMoldableAppApi: {
|
|
130
|
+
loadingLabel: 'Calling app API...',
|
|
131
|
+
marker: ThinkingTimelineMarker.Default,
|
|
132
|
+
inline: true,
|
|
133
|
+
renderLoading: () => (_jsx(LoadingIndicator, { icon: Database, children: "Fetching app data..." })),
|
|
134
|
+
renderOutput: (output, toolCallId) => {
|
|
135
|
+
if (output === undefined || output === null) {
|
|
136
|
+
return (_jsx(LoadingIndicator, { icon: Database, children: "Fetching app data..." }, toolCallId));
|
|
137
|
+
}
|
|
138
|
+
const response = output;
|
|
139
|
+
const ok = response.ok !== false && !response.error;
|
|
140
|
+
const itemCount = ok ? appApiItemCount(response.result) : 0;
|
|
141
|
+
return (_jsx("div", { className: "my-1 min-w-0 max-w-xl", children: _jsxs("div", { className: cn('inline-flex max-w-full items-center gap-2 rounded-md px-2 py-1 text-xs', ok
|
|
142
|
+
? 'bg-muted text-muted-foreground'
|
|
143
|
+
: 'bg-destructive/10 text-destructive'), children: [_jsx(Database, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "shrink-0 font-medium", children: ok ? 'Fetched app data' : 'App API failed' }), _jsx("span", { className: "min-w-0 truncate", children: ok
|
|
144
|
+
? `${itemCount} item${itemCount === 1 ? '' : 's'}`
|
|
145
|
+
: response.error?.message || response.error?.code || 'Error' }), ok ? (_jsx(Check, { className: "size-3 shrink-0 text-green-600" })) : (_jsx(X, { className: "size-3 shrink-0" }))] }) }, toolCallId));
|
|
146
|
+
},
|
|
147
|
+
},
|
|
92
148
|
readFile: {
|
|
93
149
|
loadingLabel: 'Reading file...',
|
|
94
150
|
marker: ThinkingTimelineMarker.File,
|
|
@@ -563,8 +619,8 @@ export const DEFAULT_TOOL_HANDLERS = {
|
|
|
563
619
|
marker: ThinkingTimelineMarker.Default,
|
|
564
620
|
inline: true,
|
|
565
621
|
renderLoading: (args) => {
|
|
566
|
-
const { appId } = (args ?? {});
|
|
567
|
-
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(Database, { className: "size-3.5 shrink-0 animate-pulse" }), _jsxs("span", { className: "truncate", children: ["Deleting data for ", appId || 'app', "..."] })] }));
|
|
622
|
+
const { appId, workspaceId } = (args ?? {});
|
|
623
|
+
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(Database, { className: "size-3.5 shrink-0 animate-pulse" }), _jsxs("span", { className: "truncate", children: ["Deleting data for ", appId || 'app', workspaceId ? ` in ${workspaceId}` : '', "..."] })] }));
|
|
568
624
|
},
|
|
569
625
|
renderOutput: (output, toolCallId) => {
|
|
570
626
|
const result = (output ?? {});
|
|
@@ -572,13 +628,13 @@ export const DEFAULT_TOOL_HANDLERS = {
|
|
|
572
628
|
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(Database, { className: "size-3.5 shrink-0 animate-pulse" }), _jsx("span", { className: "truncate", children: "Deleting app data..." })] }, toolCallId));
|
|
573
629
|
}
|
|
574
630
|
if (result.success === false) {
|
|
575
|
-
return (_jsxs("div", { className: "bg-destructive/10 text-destructive my-1
|
|
631
|
+
return (_jsxs("div", { className: "bg-destructive/10 text-destructive my-1 min-w-0 rounded-md", children: [_jsxs("div", { className: "flex items-center gap-2 px-2 py-1.5 text-xs", children: [_jsx(Database, { className: "size-3.5 shrink-0" }), _jsx("span", { className: "truncate", children: result.error || 'Failed to delete app data' })] }), (result.workspaceName || result.workspaceId) && (_jsxs("div", { className: "border-destructive/20 border-t px-2 py-1 text-[10px]", children: ["Workspace:", ' ', _jsx("span", { className: "font-medium", children: result.workspaceName || result.workspaceId }), result.workspaceName && result.workspaceId && (_jsxs("span", { className: "text-destructive/70", children: [' ', "(", result.workspaceId, ")"] }))] }))] }, toolCallId));
|
|
576
632
|
}
|
|
577
|
-
return (_jsxs("div", { className: "bg-muted text-muted-foreground my-1
|
|
633
|
+
return (_jsxs("div", { className: "bg-muted text-muted-foreground my-1 min-w-0 rounded-md", children: [_jsxs("div", { className: "flex items-center gap-2 px-2 py-1.5 text-xs", children: [_jsx(Database, { className: "size-3.5 shrink-0" }), _jsxs("span", { className: "font-medium", children: ["Data deleted for ", result.appId] }), _jsx(Check, { className: "size-3 shrink-0 text-green-600" })] }), (result.workspaceName || result.workspaceId) && (_jsxs("div", { className: "border-border/50 border-t px-2 py-1 text-[10px]", children: ["Workspace:", ' ', _jsx("span", { className: "font-medium", children: result.workspaceName || result.workspaceId }), result.workspaceName && result.workspaceId && (_jsxs("span", { className: "text-muted-foreground/70", children: [' ', "(", result.workspaceId, ")"] }))] }))] }, toolCallId));
|
|
578
634
|
},
|
|
579
635
|
renderApproval: (approval, onRespond) => {
|
|
580
|
-
const { appId } = (approval.args ?? {});
|
|
581
|
-
return (_jsxs(ToolApproval, { state: "approval-requested", children: [_jsx(ToolApprovalHeader, { children: _jsxs(ToolApprovalRequest, { children: [_jsxs("div", { className: "mb-1 flex items-center gap-1.5 text-xs font-medium text-amber-600", children: [_jsx(AlertTriangle, { className: "size-3.5" }), "Delete app data"] }), _jsxs("div", { className: "text-muted-foreground mb-2 text-[10px]", children: ["Delete all data for ", _jsx("strong", { children: appId }), " in
|
|
636
|
+
const { appId, workspaceId } = (approval.args ?? {});
|
|
637
|
+
return (_jsxs(ToolApproval, { state: "approval-requested", children: [_jsx(ToolApprovalHeader, { children: _jsxs(ToolApprovalRequest, { children: [_jsxs("div", { className: "mb-1 flex items-center gap-1.5 text-xs font-medium text-amber-600", children: [_jsx(AlertTriangle, { className: "size-3.5" }), "Delete app data"] }), _jsxs("div", { className: "text-muted-foreground mb-2 text-[10px]", children: ["Delete all data for ", _jsx("strong", { children: appId }), " in workspace ID", ' ', _jsx("strong", { children: workspaceId || 'unknown' }), "?"] }), _jsxs("div", { className: "bg-muted/50 mb-2 grid gap-1 rounded px-2 py-1.5 text-[10px]", children: [_jsxs("div", { className: "flex min-w-0 items-center justify-between gap-2", children: [_jsx("span", { className: "text-muted-foreground", children: "App" }), _jsx("code", { className: "bg-background/70 truncate rounded px-1 font-mono", children: appId || 'unknown' })] }), _jsxs("div", { className: "flex min-w-0 items-center justify-between gap-2", children: [_jsx("span", { className: "text-muted-foreground", children: "Target workspace ID" }), _jsx("code", { className: "bg-background/70 truncate rounded px-1 font-mono", children: workspaceId || 'unknown' })] })] }), _jsx("div", { className: "rounded bg-amber-500/10 px-2 py-1.5 text-[10px] text-amber-700", children: "This permanently deletes this app's database, files, and cache only in the target workspace. The app will remain installed and start fresh there." })] }) }), _jsxs(ToolApprovalActions, { children: [_jsx(ToolApprovalAction, { variant: "outline", onClick: () => onRespond({
|
|
582
638
|
approvalId: approval.approvalId,
|
|
583
639
|
approved: false,
|
|
584
640
|
reason: 'User cancelled',
|