@chaaskit/client 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/favicon.svg +11 -0
- package/dist/index.html +17 -0
- package/dist/lib/LoadingSkeletons-IcIC2JPq.js +132 -0
- package/dist/lib/LoadingSkeletons-IcIC2JPq.js.map +1 -0
- package/dist/lib/ServerThemeProvider-DNF0LAyk.js +42 -0
- package/dist/lib/ServerThemeProvider-DNF0LAyk.js.map +1 -0
- package/dist/lib/extensions.js +10 -0
- package/dist/lib/extensions.js.map +1 -0
- package/dist/lib/favicon.svg +11 -0
- package/dist/lib/index.js +74126 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/logo.svg +12 -0
- package/dist/lib/routes/AcceptInviteRoute.js +19 -0
- package/dist/lib/routes/AcceptInviteRoute.js.map +1 -0
- package/dist/lib/routes/AdminDashboardRoute.js +19 -0
- package/dist/lib/routes/AdminDashboardRoute.js.map +1 -0
- package/dist/lib/routes/AdminTeamRoute.js +19 -0
- package/dist/lib/routes/AdminTeamRoute.js.map +1 -0
- package/dist/lib/routes/AdminTeamsRoute.js +19 -0
- package/dist/lib/routes/AdminTeamsRoute.js.map +1 -0
- package/dist/lib/routes/AdminUsersRoute.js +19 -0
- package/dist/lib/routes/AdminUsersRoute.js.map +1 -0
- package/dist/lib/routes/ApiKeysRoute.js +19 -0
- package/dist/lib/routes/ApiKeysRoute.js.map +1 -0
- package/dist/lib/routes/AutomationsRoute.js +19 -0
- package/dist/lib/routes/AutomationsRoute.js.map +1 -0
- package/dist/lib/routes/ChatRoute.js +19 -0
- package/dist/lib/routes/ChatRoute.js.map +1 -0
- package/dist/lib/routes/DocumentsRoute.js +19 -0
- package/dist/lib/routes/DocumentsRoute.js.map +1 -0
- package/dist/lib/routes/OAuthConsentRoute.js +19 -0
- package/dist/lib/routes/OAuthConsentRoute.js.map +1 -0
- package/dist/lib/routes/PricingRoute.js +19 -0
- package/dist/lib/routes/PricingRoute.js.map +1 -0
- package/dist/lib/routes/PrivacyRoute.js +19 -0
- package/dist/lib/routes/PrivacyRoute.js.map +1 -0
- package/dist/lib/routes/TeamSettingsRoute.js +19 -0
- package/dist/lib/routes/TeamSettingsRoute.js.map +1 -0
- package/dist/lib/routes/TermsRoute.js +19 -0
- package/dist/lib/routes/TermsRoute.js.map +1 -0
- package/dist/lib/routes/VerifyEmailRoute.js +19 -0
- package/dist/lib/routes/VerifyEmailRoute.js.map +1 -0
- package/dist/lib/routes.js +79 -0
- package/dist/lib/routes.js.map +1 -0
- package/dist/lib/ssr-utils.js +29 -0
- package/dist/lib/ssr-utils.js.map +1 -0
- package/dist/lib/ssr.js +60 -0
- package/dist/lib/ssr.js.map +1 -0
- package/dist/lib/styles.css +2410 -0
- package/dist/lib/useExtensions-B5nX_8XD.js +155 -0
- package/dist/lib/useExtensions-B5nX_8XD.js.map +1 -0
- package/dist/logo.svg +12 -0
- package/package.json +84 -0
- package/src/components/AgentSelector.tsx +90 -0
- package/src/components/BranchModal.tsx +129 -0
- package/src/components/ClientOnly.tsx +27 -0
- package/src/components/ExportMenu.tsx +122 -0
- package/src/components/LoadingSkeletons.tsx +110 -0
- package/src/components/MCPCredentialsSection.tsx +309 -0
- package/src/components/MentionChip.tsx +149 -0
- package/src/components/MentionDropdown.tsx +175 -0
- package/src/components/MentionInput.tsx +293 -0
- package/src/components/MessageItem.tsx +300 -0
- package/src/components/MessageList.tsx +159 -0
- package/src/components/OAuthAppsSection.tsx +124 -0
- package/src/components/ProjectFolder.tsx +141 -0
- package/src/components/ProjectModal.tsx +296 -0
- package/src/components/SSRMessageList.tsx +153 -0
- package/src/components/SearchModal.tsx +173 -0
- package/src/components/SettingsModal.tsx +412 -0
- package/src/components/ShareModal.tsx +280 -0
- package/src/components/Sidebar.tsx +491 -0
- package/src/components/TeamSwitcher.tsx +273 -0
- package/src/components/ToolCallDisplay.tsx +473 -0
- package/src/components/ToolConfirmationModal.tsx +130 -0
- package/src/components/UsageChart.tsx +177 -0
- package/src/components/content/CodeBlock.tsx +69 -0
- package/src/components/content/MarkdownRenderer.tsx +64 -0
- package/src/components/content/SSRMarkdownRenderer.tsx +158 -0
- package/src/contexts/AuthContext.tsx +119 -0
- package/src/contexts/ConfigContext.tsx +214 -0
- package/src/contexts/ProjectContext.tsx +167 -0
- package/src/contexts/ServerConfigProvider.tsx +41 -0
- package/src/contexts/ServerThemeProvider.tsx +47 -0
- package/src/contexts/TeamContext.tsx +255 -0
- package/src/contexts/ThemeContext.tsx +113 -0
- package/src/extensions/index.ts +15 -0
- package/src/extensions/registry.ts +187 -0
- package/src/extensions/useExtensions.ts +52 -0
- package/src/hooks/useAppPath.ts +34 -0
- package/src/hooks/useBasePath.ts +13 -0
- package/src/hooks/useKeyboardShortcuts.ts +50 -0
- package/src/hooks/useMentionSearch.ts +106 -0
- package/src/index.tsx +116 -0
- package/src/layouts/MainLayout.tsx +98 -0
- package/src/pages/AcceptInvitePage.tsx +175 -0
- package/src/pages/AdminDashboardPage.tsx +362 -0
- package/src/pages/AdminTeamPage.tsx +304 -0
- package/src/pages/AdminTeamsPage.tsx +242 -0
- package/src/pages/AdminUsersPage.tsx +385 -0
- package/src/pages/ApiKeysPage.tsx +449 -0
- package/src/pages/ChatPage.tsx +310 -0
- package/src/pages/DocumentsPage.tsx +577 -0
- package/src/pages/LoginPage.tsx +232 -0
- package/src/pages/OAuthConsentPage.tsx +234 -0
- package/src/pages/PricingPage.tsx +314 -0
- package/src/pages/PrivacyPage.tsx +65 -0
- package/src/pages/RegisterPage.tsx +153 -0
- package/src/pages/ScheduledPromptsPage.tsx +702 -0
- package/src/pages/SharedThreadPage.tsx +116 -0
- package/src/pages/TeamSettingsPage.tsx +1085 -0
- package/src/pages/TermsPage.tsx +82 -0
- package/src/pages/VerifyEmailPage.tsx +202 -0
- package/src/routes/AcceptInviteRoute.tsx +24 -0
- package/src/routes/AdminDashboardRoute.tsx +24 -0
- package/src/routes/AdminTeamRoute.tsx +24 -0
- package/src/routes/AdminTeamsRoute.tsx +24 -0
- package/src/routes/AdminUsersRoute.tsx +24 -0
- package/src/routes/ApiKeysRoute.tsx +24 -0
- package/src/routes/AutomationsRoute.tsx +24 -0
- package/src/routes/ChatRoute.tsx +28 -0
- package/src/routes/DocumentsRoute.tsx +24 -0
- package/src/routes/OAuthConsentRoute.tsx +24 -0
- package/src/routes/PricingRoute.tsx +24 -0
- package/src/routes/PrivacyRoute.tsx +24 -0
- package/src/routes/TeamSettingsRoute.tsx +24 -0
- package/src/routes/TermsRoute.tsx +24 -0
- package/src/routes/VerifyEmailRoute.tsx +24 -0
- package/src/routes/index.ts +57 -0
- package/src/ssr-utils.tsx +84 -0
- package/src/ssr.ts +123 -0
- package/src/stores/chatStore.ts +670 -0
- package/src/styles/index.css +254 -0
- package/src/utils/api.ts +78 -0
- package/src/vite-env.d.ts +13 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { useParams, useNavigate, Link } from 'react-router';
|
|
3
|
+
import { Send, Paperclip, Loader2, AlertCircle, X, GitBranch, ArrowLeft, Folder } from 'lucide-react';
|
|
4
|
+
import { useConfig } from '../contexts/ConfigContext';
|
|
5
|
+
import { useProject } from '../contexts/ProjectContext';
|
|
6
|
+
import { useChatStore } from '../stores/chatStore';
|
|
7
|
+
import { useAppPath } from '../hooks/useAppPath';
|
|
8
|
+
import MessageList from '../components/MessageList';
|
|
9
|
+
import ToolConfirmationModal from '../components/ToolConfirmationModal';
|
|
10
|
+
import AgentSelector from '../components/AgentSelector';
|
|
11
|
+
import MentionInput, { type MentionInputHandle } from '../components/MentionInput';
|
|
12
|
+
|
|
13
|
+
export default function ChatPage() {
|
|
14
|
+
const { threadId } = useParams();
|
|
15
|
+
const navigate = useNavigate();
|
|
16
|
+
const appPath = useAppPath();
|
|
17
|
+
const config = useConfig();
|
|
18
|
+
const { projects, currentProjectId, projectsEnabled } = useProject();
|
|
19
|
+
const inputRef = useRef<MentionInputHandle>(null);
|
|
20
|
+
const [input, setInput] = useState('');
|
|
21
|
+
const [files, setFiles] = useState<File[]>([]);
|
|
22
|
+
const [error, setError] = useState<string | null>(null);
|
|
23
|
+
|
|
24
|
+
const {
|
|
25
|
+
currentThread,
|
|
26
|
+
threads,
|
|
27
|
+
isStreaming,
|
|
28
|
+
streamingContent,
|
|
29
|
+
pendingToolCalls,
|
|
30
|
+
completedToolCalls,
|
|
31
|
+
pendingConfirmation,
|
|
32
|
+
availableAgents,
|
|
33
|
+
loadThread,
|
|
34
|
+
sendMessage,
|
|
35
|
+
clearCurrentThread,
|
|
36
|
+
confirmTool,
|
|
37
|
+
loadAgents,
|
|
38
|
+
} = useChatStore();
|
|
39
|
+
|
|
40
|
+
// Find parent thread info if this is a branch
|
|
41
|
+
const parentThread = currentThread?.parentThreadId
|
|
42
|
+
? threads.find((t) => t.id === currentThread.parentThreadId)
|
|
43
|
+
: null;
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (threadId) {
|
|
47
|
+
loadThread(threadId);
|
|
48
|
+
} else {
|
|
49
|
+
clearCurrentThread();
|
|
50
|
+
}
|
|
51
|
+
}, [threadId]);
|
|
52
|
+
|
|
53
|
+
// Navigate to thread URL when a new thread is created from the welcome screen
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
// Wait for real thread ID (not 'pending' placeholder)
|
|
56
|
+
if (!threadId && currentThread?.id && currentThread.id !== 'pending') {
|
|
57
|
+
navigate(appPath(`/thread/${currentThread.id}`), { replace: true });
|
|
58
|
+
}
|
|
59
|
+
}, [threadId, currentThread?.id, navigate, appPath]);
|
|
60
|
+
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
// Load available agents on mount
|
|
63
|
+
loadAgents();
|
|
64
|
+
}, [loadAgents]);
|
|
65
|
+
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
// Focus input on mount
|
|
68
|
+
inputRef.current?.focus();
|
|
69
|
+
}, []);
|
|
70
|
+
|
|
71
|
+
async function handleSubmit(e: React.FormEvent) {
|
|
72
|
+
e.preventDefault();
|
|
73
|
+
if (!input.trim() && files.length === 0) return;
|
|
74
|
+
if (isStreaming) return;
|
|
75
|
+
|
|
76
|
+
const content = input.trim();
|
|
77
|
+
const attachedFiles = [...files];
|
|
78
|
+
|
|
79
|
+
setInput('');
|
|
80
|
+
setFiles([]);
|
|
81
|
+
setError(null);
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
await sendMessage(content, attachedFiles);
|
|
85
|
+
} catch (err) {
|
|
86
|
+
// Parse error message
|
|
87
|
+
let errorMessage = 'Failed to send message. Please try again.';
|
|
88
|
+
if (err instanceof Error) {
|
|
89
|
+
if (err.message.includes('429') || err.message.toLowerCase().includes('usage limit')) {
|
|
90
|
+
errorMessage = 'You\'ve reached your monthly message limit. Please upgrade your plan to continue.';
|
|
91
|
+
} else if (err.message.includes('401') || err.message.includes('403')) {
|
|
92
|
+
errorMessage = 'Please sign in to continue.';
|
|
93
|
+
} else {
|
|
94
|
+
errorMessage = err.message;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
setError(errorMessage);
|
|
98
|
+
// Restore input so user doesn't lose their message
|
|
99
|
+
setInput(content);
|
|
100
|
+
setFiles(attachedFiles);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function handleKeyDown(e: React.KeyboardEvent) {
|
|
105
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
106
|
+
e.preventDefault();
|
|
107
|
+
handleSubmit(e);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function handleFileSelect(e: React.ChangeEvent<HTMLInputElement>) {
|
|
112
|
+
const selectedFiles = Array.from(e.target.files || []);
|
|
113
|
+
setFiles((prev) => [...prev, ...selectedFiles]);
|
|
114
|
+
e.target.value = '';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function removeFile(index: number) {
|
|
118
|
+
setFiles((prev) => prev.filter((_, i) => i !== index));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function handleSamplePrompt(prompt: string) {
|
|
122
|
+
setInput(prompt);
|
|
123
|
+
inputRef.current?.focus();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const showWelcome = !currentThread || currentThread.messages.length === 0;
|
|
127
|
+
const showAgentSelector = showWelcome && availableAgents.length > 1;
|
|
128
|
+
|
|
129
|
+
// Show project indicator when:
|
|
130
|
+
// 1. Viewing a thread that belongs to a project, OR
|
|
131
|
+
// 2. Creating a new chat with a project selected
|
|
132
|
+
const activeProjectId = currentThread?.projectId || (showWelcome ? currentProjectId : null);
|
|
133
|
+
const activeProject = activeProjectId ? projects.find(p => p.id === activeProjectId) : null;
|
|
134
|
+
const showProjectIndicator = projectsEnabled && activeProject;
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<div className="flex h-full flex-col">
|
|
138
|
+
{/* Project Context Bar */}
|
|
139
|
+
{showProjectIndicator && activeProject && (
|
|
140
|
+
<div className="flex items-center gap-2 border-b border-border bg-background-secondary px-4 py-2">
|
|
141
|
+
<Folder
|
|
142
|
+
size={14}
|
|
143
|
+
style={{ color: activeProject.color }}
|
|
144
|
+
fill={activeProject.color}
|
|
145
|
+
/>
|
|
146
|
+
<span className="text-xs text-text-primary font-medium">{activeProject.name}</span>
|
|
147
|
+
{showWelcome && (
|
|
148
|
+
<span className="text-xs text-text-muted">• New chat will be added to this project</span>
|
|
149
|
+
)}
|
|
150
|
+
</div>
|
|
151
|
+
)}
|
|
152
|
+
|
|
153
|
+
{/* Branch Breadcrumb */}
|
|
154
|
+
{currentThread?.parentThreadId && (
|
|
155
|
+
<div className="flex items-center gap-2 border-b border-border bg-background-secondary px-4 py-2">
|
|
156
|
+
<GitBranch size={14} className="text-text-muted" />
|
|
157
|
+
<span className="text-xs text-text-muted">Branched from</span>
|
|
158
|
+
<Link
|
|
159
|
+
to={`/thread/${currentThread.parentThreadId}`}
|
|
160
|
+
className="flex items-center gap-1 text-xs text-primary hover:underline"
|
|
161
|
+
>
|
|
162
|
+
<ArrowLeft size={12} />
|
|
163
|
+
{parentThread?.title || 'Parent conversation'}
|
|
164
|
+
</Link>
|
|
165
|
+
</div>
|
|
166
|
+
)}
|
|
167
|
+
|
|
168
|
+
{/* Messages or Welcome */}
|
|
169
|
+
<div className="min-h-0 flex-1 overflow-y-auto">
|
|
170
|
+
{showWelcome ? (
|
|
171
|
+
<div className="flex min-h-full flex-col items-center justify-center px-4 py-8 sm:px-6">
|
|
172
|
+
<div className="max-w-2xl text-center">
|
|
173
|
+
<h1 className="mb-2 text-2xl font-bold text-text-primary sm:text-3xl">
|
|
174
|
+
{config.ui.welcomeTitle}
|
|
175
|
+
</h1>
|
|
176
|
+
<p className="mb-6 text-sm text-text-secondary sm:mb-8 sm:text-base">
|
|
177
|
+
{config.ui.welcomeSubtitle}
|
|
178
|
+
</p>
|
|
179
|
+
|
|
180
|
+
{/* Agent Selector - only shown when multiple agents available */}
|
|
181
|
+
{showAgentSelector && (
|
|
182
|
+
<div className="mb-6 flex justify-center">
|
|
183
|
+
<AgentSelector />
|
|
184
|
+
</div>
|
|
185
|
+
)}
|
|
186
|
+
|
|
187
|
+
{/* Sample Prompts */}
|
|
188
|
+
{config.ui.samplePrompts.length > 0 && (
|
|
189
|
+
<div className="flex flex-wrap justify-center gap-2">
|
|
190
|
+
{config.ui.samplePrompts.map((prompt, index) => (
|
|
191
|
+
<button
|
|
192
|
+
key={index}
|
|
193
|
+
onClick={() => handleSamplePrompt(prompt.prompt)}
|
|
194
|
+
className="rounded-lg border border-border bg-background-secondary px-3 py-2 text-sm text-text-secondary hover:border-primary hover:text-primary active:bg-primary/10 sm:px-4"
|
|
195
|
+
>
|
|
196
|
+
{prompt.label}
|
|
197
|
+
</button>
|
|
198
|
+
))}
|
|
199
|
+
</div>
|
|
200
|
+
)}
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
) : (
|
|
204
|
+
<MessageList
|
|
205
|
+
messages={currentThread.messages}
|
|
206
|
+
streamingContent={isStreaming ? streamingContent : undefined}
|
|
207
|
+
pendingToolCalls={isStreaming ? pendingToolCalls : undefined}
|
|
208
|
+
completedToolCalls={isStreaming ? completedToolCalls : undefined}
|
|
209
|
+
/>
|
|
210
|
+
)}
|
|
211
|
+
</div>
|
|
212
|
+
|
|
213
|
+
{/* Input Area */}
|
|
214
|
+
<div
|
|
215
|
+
className="flex-shrink-0 border-t border-border bg-background p-3 sm:p-4"
|
|
216
|
+
style={{ paddingBottom: 'max(0.75rem, env(safe-area-inset-bottom))' }}
|
|
217
|
+
>
|
|
218
|
+
{/* Error Banner */}
|
|
219
|
+
{error && (
|
|
220
|
+
<div className="mb-3 flex items-start gap-2 rounded-lg border border-error/30 bg-error/10 p-3 text-sm text-error">
|
|
221
|
+
<AlertCircle size={18} className="mt-0.5 flex-shrink-0" />
|
|
222
|
+
<span className="flex-1">{error}</span>
|
|
223
|
+
<button
|
|
224
|
+
onClick={() => setError(null)}
|
|
225
|
+
className="flex-shrink-0 p-0.5 hover:bg-error/20 rounded"
|
|
226
|
+
aria-label="Dismiss error"
|
|
227
|
+
>
|
|
228
|
+
<X size={16} />
|
|
229
|
+
</button>
|
|
230
|
+
</div>
|
|
231
|
+
)}
|
|
232
|
+
|
|
233
|
+
{/* Attached Files */}
|
|
234
|
+
{files.length > 0 && (
|
|
235
|
+
<div className="mb-2 flex flex-wrap gap-2">
|
|
236
|
+
{files.map((file, index) => (
|
|
237
|
+
<div
|
|
238
|
+
key={index}
|
|
239
|
+
className="flex items-center gap-2 rounded-lg bg-background-secondary px-3 py-1.5 text-sm"
|
|
240
|
+
>
|
|
241
|
+
<span className="max-w-[120px] truncate sm:max-w-[150px]">{file.name}</span>
|
|
242
|
+
<button
|
|
243
|
+
onClick={() => removeFile(index)}
|
|
244
|
+
className="p-0.5 text-text-muted hover:text-error"
|
|
245
|
+
aria-label={`Remove ${file.name}`}
|
|
246
|
+
>
|
|
247
|
+
×
|
|
248
|
+
</button>
|
|
249
|
+
</div>
|
|
250
|
+
))}
|
|
251
|
+
</div>
|
|
252
|
+
)}
|
|
253
|
+
|
|
254
|
+
<form onSubmit={handleSubmit} className="flex items-start gap-2">
|
|
255
|
+
{/* File Upload (uploads as documents with @mentions) */}
|
|
256
|
+
{config.documents?.enabled && (
|
|
257
|
+
<label className="flex-shrink-0 cursor-pointer rounded-lg p-2.5 text-text-secondary hover:bg-background-secondary hover:text-text-primary active:bg-background-secondary">
|
|
258
|
+
<Paperclip size={20} />
|
|
259
|
+
<input
|
|
260
|
+
type="file"
|
|
261
|
+
className="hidden"
|
|
262
|
+
multiple
|
|
263
|
+
accept=".txt,.md,.csv,.json"
|
|
264
|
+
onChange={handleFileSelect}
|
|
265
|
+
/>
|
|
266
|
+
</label>
|
|
267
|
+
)}
|
|
268
|
+
|
|
269
|
+
{/* Text Input */}
|
|
270
|
+
<div className="flex-1 rounded-lg border border-input-border bg-input-background focus-within:border-primary">
|
|
271
|
+
<MentionInput
|
|
272
|
+
ref={inputRef}
|
|
273
|
+
value={input}
|
|
274
|
+
onChange={setInput}
|
|
275
|
+
onKeyDown={handleKeyDown}
|
|
276
|
+
placeholder={config.ui.inputPlaceholder}
|
|
277
|
+
rows={1}
|
|
278
|
+
maxHeight={200}
|
|
279
|
+
autoGrow
|
|
280
|
+
className="max-h-[200px] min-h-[44px] w-full resize-none bg-transparent px-3 py-3 text-base text-text-primary placeholder-text-muted focus:outline-none sm:px-4 sm:text-sm"
|
|
281
|
+
/>
|
|
282
|
+
</div>
|
|
283
|
+
|
|
284
|
+
{/* Send Button */}
|
|
285
|
+
<button
|
|
286
|
+
type="submit"
|
|
287
|
+
disabled={isStreaming || (!input.trim() && files.length === 0)}
|
|
288
|
+
className="flex h-11 w-11 flex-shrink-0 items-center justify-center rounded-lg bg-primary text-white disabled:opacity-50 active:bg-primary-hover"
|
|
289
|
+
>
|
|
290
|
+
{isStreaming ? (
|
|
291
|
+
<Loader2 size={20} className="animate-spin" />
|
|
292
|
+
) : (
|
|
293
|
+
<Send size={20} />
|
|
294
|
+
)}
|
|
295
|
+
</button>
|
|
296
|
+
</form>
|
|
297
|
+
</div>
|
|
298
|
+
|
|
299
|
+
{/* Tool Confirmation Modal */}
|
|
300
|
+
{pendingConfirmation && (
|
|
301
|
+
<ToolConfirmationModal
|
|
302
|
+
confirmation={pendingConfirmation}
|
|
303
|
+
onConfirm={(approved, scope) => {
|
|
304
|
+
confirmTool(pendingConfirmation.confirmationId, approved, scope);
|
|
305
|
+
}}
|
|
306
|
+
/>
|
|
307
|
+
)}
|
|
308
|
+
</div>
|
|
309
|
+
);
|
|
310
|
+
}
|