@elizaos/client 1.5.5-alpha.10
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 +21 -0
- package/README.md +350 -0
- package/dist/assets/empty-module-CLMscLYw.js +1 -0
- package/dist/assets/main-BBZ_3lkn.css +5999 -0
- package/dist/assets/main-C5zNUkXH.js +7 -0
- package/dist/assets/main-Dz64ENQg.js +614 -0
- package/dist/assets/react-vendor-DM5m98rr.js +545 -0
- package/dist/assets/ui-vendor-BQCqNqg0.js +1 -0
- package/dist/elizaos-avatar.png +0 -0
- package/dist/elizaos-icon.png +0 -0
- package/dist/elizaos-logo-light.png +0 -0
- package/dist/elizaos.webp +0 -0
- package/dist/favicon.ico +0 -0
- package/dist/images/agents/agent1.png +0 -0
- package/dist/images/agents/agent2.png +0 -0
- package/dist/images/agents/agent3.png +0 -0
- package/dist/images/agents/agent4.png +0 -0
- package/dist/images/agents/agent5.png +0 -0
- package/dist/index.html +14 -0
- package/index.html +24 -0
- package/package.json +159 -0
- package/postcss.config.js +3 -0
- package/public/elizaos-avatar.png +0 -0
- package/public/elizaos-icon.png +0 -0
- package/public/elizaos-logo-light.png +0 -0
- package/public/elizaos.webp +0 -0
- package/public/favicon.ico +0 -0
- package/public/images/agents/agent1.png +0 -0
- package/public/images/agents/agent2.png +0 -0
- package/public/images/agents/agent3.png +0 -0
- package/public/images/agents/agent4.png +0 -0
- package/public/images/agents/agent5.png +0 -0
- package/src/App.tsx +222 -0
- package/src/components/AgentDetailsPanel.tsx +147 -0
- package/src/components/ChatInputArea.tsx +196 -0
- package/src/components/ChatMessageListComponent.tsx +139 -0
- package/src/components/actionTool.tsx +186 -0
- package/src/components/add-agent-card.tsx +77 -0
- package/src/components/agent-action-viewer.tsx +816 -0
- package/src/components/agent-avatar-stack.tsx +121 -0
- package/src/components/agent-card.cy.tsx +259 -0
- package/src/components/agent-card.tsx +177 -0
- package/src/components/agent-creator.tsx +142 -0
- package/src/components/agent-log-viewer.tsx +645 -0
- package/src/components/agent-memory-edit-overlay.tsx +461 -0
- package/src/components/agent-memory-viewer.tsx +504 -0
- package/src/components/agent-settings.tsx +270 -0
- package/src/components/agent-sidebar.tsx +178 -0
- package/src/components/api-key-dialog.tsx +113 -0
- package/src/components/app-sidebar.tsx +685 -0
- package/src/components/array-input.tsx +116 -0
- package/src/components/audio-recorder.tsx +292 -0
- package/src/components/avatar-panel.tsx +141 -0
- package/src/components/character-form.tsx +1138 -0
- package/src/components/chat.tsx +1813 -0
- package/src/components/combobox.tsx +187 -0
- package/src/components/confirmation-dialog.tsx +59 -0
- package/src/components/connection-error-banner.tsx +101 -0
- package/src/components/connection-status.cy.tsx +73 -0
- package/src/components/connection-status.tsx +155 -0
- package/src/components/copy-button.tsx +35 -0
- package/src/components/delete-button.tsx +24 -0
- package/src/components/env-settings.tsx +261 -0
- package/src/components/group-card.tsx +160 -0
- package/src/components/group-panel.tsx +543 -0
- package/src/components/input-copy.tsx +21 -0
- package/src/components/logs-page.tsx +41 -0
- package/src/components/media-content.tsx +385 -0
- package/src/components/memory-graph.tsx +170 -0
- package/src/components/missing-secrets-dialog.tsx +72 -0
- package/src/components/onboarding-tour.tsx +247 -0
- package/src/components/page-title.tsx +8 -0
- package/src/components/plugins-panel.tsx +383 -0
- package/src/components/profile-card.tsx +66 -0
- package/src/components/profile-overlay.tsx +283 -0
- package/src/components/retry-button.tsx +28 -0
- package/src/components/secret-panel.tsx +1505 -0
- package/src/components/server-management.tsx +264 -0
- package/src/components/split-button.tsx +148 -0
- package/src/components/stop-agent-button.tsx +99 -0
- package/src/components/ui/alert-dialog.cy.tsx +333 -0
- package/src/components/ui/alert-dialog.tsx +115 -0
- package/src/components/ui/alert.tsx +49 -0
- package/src/components/ui/avatar.cy.tsx +180 -0
- package/src/components/ui/avatar.tsx +57 -0
- package/src/components/ui/badge.cy.tsx +146 -0
- package/src/components/ui/badge.tsx +43 -0
- package/src/components/ui/button.cy.tsx +177 -0
- package/src/components/ui/button.tsx +56 -0
- package/src/components/ui/card.cy.tsx +160 -0
- package/src/components/ui/card.tsx +73 -0
- package/src/components/ui/chat/animated-markdown.tsx +59 -0
- package/src/components/ui/chat/chat-bubble.tsx +178 -0
- package/src/components/ui/chat/chat-container.tsx +51 -0
- package/src/components/ui/chat/chat-input.cy.tsx +169 -0
- package/src/components/ui/chat/chat-input.tsx +47 -0
- package/src/components/ui/chat/chat-message-list.tsx +61 -0
- package/src/components/ui/chat/chat-tts-button.tsx +199 -0
- package/src/components/ui/chat/code-block.tsx +79 -0
- package/src/components/ui/chat/expandable-chat.tsx +131 -0
- package/src/components/ui/chat/hooks/useAutoScroll.ts +86 -0
- package/src/components/ui/chat/markdown.tsx +209 -0
- package/src/components/ui/chat/message-loading.tsx +48 -0
- package/src/components/ui/checkbox.cy.tsx +170 -0
- package/src/components/ui/checkbox.tsx +30 -0
- package/src/components/ui/collapsible.cy.tsx +283 -0
- package/src/components/ui/collapsible.tsx +9 -0
- package/src/components/ui/command.cy.tsx +313 -0
- package/src/components/ui/command.tsx +143 -0
- package/src/components/ui/dialog.cy.tsx +279 -0
- package/src/components/ui/dialog.tsx +104 -0
- package/src/components/ui/dropdown-menu.cy.tsx +273 -0
- package/src/components/ui/dropdown-menu.tsx +281 -0
- package/src/components/ui/input.cy.tsx +82 -0
- package/src/components/ui/input.tsx +27 -0
- package/src/components/ui/label.cy.tsx +157 -0
- package/src/components/ui/label.tsx +19 -0
- package/src/components/ui/resizable.tsx +42 -0
- package/src/components/ui/scroll-area.cy.tsx +242 -0
- package/src/components/ui/scroll-area.tsx +46 -0
- package/src/components/ui/select.cy.tsx +277 -0
- package/src/components/ui/select.tsx +155 -0
- package/src/components/ui/separator.cy.tsx +145 -0
- package/src/components/ui/separator.tsx +29 -0
- package/src/components/ui/sheet.cy.tsx +324 -0
- package/src/components/ui/sheet.tsx +119 -0
- package/src/components/ui/sidebar.tsx +734 -0
- package/src/components/ui/skeleton.cy.tsx +149 -0
- package/src/components/ui/skeleton.tsx +17 -0
- package/src/components/ui/split-button.cy.tsx +274 -0
- package/src/components/ui/split-button.tsx +112 -0
- package/src/components/ui/switch.tsx +28 -0
- package/src/components/ui/tabs.cy.tsx +271 -0
- package/src/components/ui/tabs.tsx +53 -0
- package/src/components/ui/textarea.cy.tsx +136 -0
- package/src/components/ui/textarea.tsx +26 -0
- package/src/components/ui/toast.cy.tsx +209 -0
- package/src/components/ui/toast.tsx +126 -0
- package/src/components/ui/toaster.tsx +29 -0
- package/src/components/ui/tooltip.cy.tsx +244 -0
- package/src/components/ui/tooltip.tsx +30 -0
- package/src/config/agent-templates.ts +349 -0
- package/src/config/voice-models.ts +181 -0
- package/src/constants.ts +23 -0
- package/src/context/AuthContext.tsx +44 -0
- package/src/context/ConnectionContext.tsx +194 -0
- package/src/entry.tsx +9 -0
- package/src/hooks/__tests__/use-agent-tab-state.test.ts +137 -0
- package/src/hooks/__tests__/use-agent-update.test.tsx +250 -0
- package/src/hooks/__tests__/use-character-convert.test.ts +102 -0
- package/src/hooks/__tests__/use-panel-width-state.test.ts +243 -0
- package/src/hooks/__tests__/use-sidebar-state.test.ts +117 -0
- package/src/hooks/use-agent-management.ts +130 -0
- package/src/hooks/use-agent-tab-state.ts +74 -0
- package/src/hooks/use-agent-update.ts +469 -0
- package/src/hooks/use-character-convert.ts +138 -0
- package/src/hooks/use-confirmation.ts +55 -0
- package/src/hooks/use-delete-agent.ts +123 -0
- package/src/hooks/use-dm-channels.ts +198 -0
- package/src/hooks/use-elevenlabs-voices.ts +83 -0
- package/src/hooks/use-file-upload.ts +224 -0
- package/src/hooks/use-mobile.tsx +19 -0
- package/src/hooks/use-onboarding.tsx +49 -0
- package/src/hooks/use-panel-width-state.ts +147 -0
- package/src/hooks/use-partial-update.ts +288 -0
- package/src/hooks/use-plugin-details.ts +462 -0
- package/src/hooks/use-plugins.ts +119 -0
- package/src/hooks/use-query-hooks.ts +1263 -0
- package/src/hooks/use-server-agents.ts +62 -0
- package/src/hooks/use-server-version.tsx +47 -0
- package/src/hooks/use-sidebar-state.ts +50 -0
- package/src/hooks/use-socket-chat.ts +264 -0
- package/src/hooks/use-toast.ts +260 -0
- package/src/hooks/use-version.tsx +64 -0
- package/src/index.css +146 -0
- package/src/lib/api-client-config.ts +53 -0
- package/src/lib/api-type-mappers.ts +196 -0
- package/src/lib/export-utils.ts +123 -0
- package/src/lib/logger.ts +19 -0
- package/src/lib/media-utils.ts +170 -0
- package/src/lib/pca.test.ts +17 -0
- package/src/lib/pca.ts +52 -0
- package/src/lib/socketio-manager.ts +664 -0
- package/src/lib/utils.ts +168 -0
- package/src/main.tsx +16 -0
- package/src/mocks/empty-module.ts +12 -0
- package/src/mocks/node-module.ts +57 -0
- package/src/polyfills.ts +37 -0
- package/src/routes/agent-detail.tsx +30 -0
- package/src/routes/agent-list.tsx +27 -0
- package/src/routes/agent-settings.tsx +48 -0
- package/src/routes/character-detail.tsx +52 -0
- package/src/routes/character-form.tsx +79 -0
- package/src/routes/character-list.tsx +38 -0
- package/src/routes/chat.tsx +128 -0
- package/src/routes/createAgent.tsx +13 -0
- package/src/routes/group-new.tsx +50 -0
- package/src/routes/group.tsx +29 -0
- package/src/routes/home.tsx +218 -0
- package/src/routes/not-found.tsx +71 -0
- package/src/test/setup.ts +154 -0
- package/src/types/crypto-browserify.d.ts +4 -0
- package/src/types/index.ts +13 -0
- package/src/types/rooms.ts +8 -0
- package/src/types.ts +84 -0
- package/src/vite-env.d.ts +40 -0
- package/tailwind.config.ts +90 -0
- package/tsconfig.json +10 -0
- package/vite.config.ts +102 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { Button } from '@/components/ui/button';
|
|
2
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
|
3
|
+
import {
|
|
4
|
+
Dialog,
|
|
5
|
+
DialogContent,
|
|
6
|
+
DialogDescription,
|
|
7
|
+
DialogFooter,
|
|
8
|
+
DialogHeader,
|
|
9
|
+
DialogTitle,
|
|
10
|
+
} from '@/components/ui/dialog';
|
|
11
|
+
import { Label } from '@/components/ui/label';
|
|
12
|
+
import { ScrollArea } from '@/components/ui/scroll-area';
|
|
13
|
+
import {
|
|
14
|
+
Select,
|
|
15
|
+
SelectContent,
|
|
16
|
+
SelectItem,
|
|
17
|
+
SelectTrigger,
|
|
18
|
+
SelectValue,
|
|
19
|
+
} from '@/components/ui/select';
|
|
20
|
+
import { useAgents, useServers } from '@/hooks/use-query-hooks';
|
|
21
|
+
import { useToast } from '@/hooks/use-toast';
|
|
22
|
+
import { createElizaClient } from '@/lib/api-client-config';
|
|
23
|
+
import type { UUID } from '@elizaos/core';
|
|
24
|
+
import { Loader2, Plus, X } from 'lucide-react';
|
|
25
|
+
import { useEffect, useState } from 'react';
|
|
26
|
+
|
|
27
|
+
interface ServerManagementProps {
|
|
28
|
+
open: boolean;
|
|
29
|
+
onOpenChange: (open: boolean) => void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function ServerManagement({ open, onOpenChange }: ServerManagementProps) {
|
|
33
|
+
const { toast } = useToast();
|
|
34
|
+
const { data: serversData } = useServers();
|
|
35
|
+
const { data: agentsData } = useAgents();
|
|
36
|
+
|
|
37
|
+
const [selectedServerId, setSelectedServerId] = useState<UUID | null>(null);
|
|
38
|
+
const [selectedAgentId, setSelectedAgentId] = useState<UUID | null>(null);
|
|
39
|
+
const [serverAgents, setServerAgents] = useState<Map<UUID, UUID[]>>(new Map());
|
|
40
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
41
|
+
|
|
42
|
+
// Load agents for each server
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
const loadServerAgents = async () => {
|
|
45
|
+
if (!serversData?.data?.servers) return;
|
|
46
|
+
|
|
47
|
+
const newServerAgents = new Map<UUID, UUID[]>();
|
|
48
|
+
|
|
49
|
+
for (const server of serversData.data.servers) {
|
|
50
|
+
try {
|
|
51
|
+
const elizaClient = createElizaClient();
|
|
52
|
+
const response = await elizaClient.agents.getAgentsForServer(server.id);
|
|
53
|
+
if (response.success) {
|
|
54
|
+
newServerAgents.set(server.id, response.data.agents);
|
|
55
|
+
}
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.error(`Failed to load agents for server ${server.id}:`, error);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
setServerAgents(newServerAgents);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
loadServerAgents();
|
|
65
|
+
}, [serversData]);
|
|
66
|
+
|
|
67
|
+
const handleAddAgentToServer = async () => {
|
|
68
|
+
if (!selectedServerId || !selectedAgentId) {
|
|
69
|
+
toast({
|
|
70
|
+
title: 'Error',
|
|
71
|
+
description: 'Please select both a server and an agent',
|
|
72
|
+
variant: 'destructive',
|
|
73
|
+
});
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
setIsLoading(true);
|
|
78
|
+
try {
|
|
79
|
+
const elizaClient = createElizaClient();
|
|
80
|
+
await elizaClient.agents.addAgentToServer(selectedServerId, selectedAgentId);
|
|
81
|
+
|
|
82
|
+
// Update local state
|
|
83
|
+
setServerAgents((prev) => {
|
|
84
|
+
const newMap = new Map(prev);
|
|
85
|
+
const agents = newMap.get(selectedServerId) || [];
|
|
86
|
+
if (!agents.includes(selectedAgentId)) {
|
|
87
|
+
newMap.set(selectedServerId, [...agents, selectedAgentId]);
|
|
88
|
+
}
|
|
89
|
+
return newMap;
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
toast({
|
|
93
|
+
title: 'Success',
|
|
94
|
+
description: 'Agent added to server successfully',
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Reset selection
|
|
98
|
+
setSelectedAgentId(null);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
toast({
|
|
101
|
+
title: 'Error',
|
|
102
|
+
description: error instanceof Error ? error.message : 'Failed to add agent to server',
|
|
103
|
+
variant: 'destructive',
|
|
104
|
+
});
|
|
105
|
+
} finally {
|
|
106
|
+
setIsLoading(false);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const handleRemoveAgentFromServer = async (serverId: UUID, agentId: UUID) => {
|
|
111
|
+
setIsLoading(true);
|
|
112
|
+
try {
|
|
113
|
+
const elizaClient = createElizaClient();
|
|
114
|
+
await elizaClient.agents.removeAgentFromServer(serverId, agentId);
|
|
115
|
+
|
|
116
|
+
// Update local state
|
|
117
|
+
setServerAgents((prev) => {
|
|
118
|
+
const newMap = new Map(prev);
|
|
119
|
+
const agents = newMap.get(serverId) || [];
|
|
120
|
+
newMap.set(
|
|
121
|
+
serverId,
|
|
122
|
+
agents.filter((id) => id !== agentId)
|
|
123
|
+
);
|
|
124
|
+
return newMap;
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
toast({
|
|
128
|
+
title: 'Success',
|
|
129
|
+
description: 'Agent removed from server successfully',
|
|
130
|
+
});
|
|
131
|
+
} catch (error) {
|
|
132
|
+
toast({
|
|
133
|
+
title: 'Error',
|
|
134
|
+
description: error instanceof Error ? error.message : 'Failed to remove agent from server',
|
|
135
|
+
variant: 'destructive',
|
|
136
|
+
});
|
|
137
|
+
} finally {
|
|
138
|
+
setIsLoading(false);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const getAgentName = (agentId: UUID) => {
|
|
143
|
+
const agent = agentsData?.data?.agents?.find((a) => a.id === agentId);
|
|
144
|
+
return agent?.name || agentId;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const getAvailableAgents = () => {
|
|
148
|
+
if (!selectedServerId || !agentsData?.data?.agents) return [];
|
|
149
|
+
|
|
150
|
+
const currentAgents = serverAgents.get(selectedServerId) || [];
|
|
151
|
+
return agentsData.data.agents.filter(
|
|
152
|
+
(agent) => agent.id && !currentAgents.includes(agent.id as UUID)
|
|
153
|
+
);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
return (
|
|
157
|
+
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
158
|
+
<DialogContent className="max-w-4xl max-h-[80vh] overflow-hidden">
|
|
159
|
+
<DialogHeader>
|
|
160
|
+
<DialogTitle>Server Management</DialogTitle>
|
|
161
|
+
<DialogDescription>
|
|
162
|
+
Manage server-agent associations. Add or remove agents from servers to control which
|
|
163
|
+
agents can process messages.
|
|
164
|
+
</DialogDescription>
|
|
165
|
+
</DialogHeader>
|
|
166
|
+
|
|
167
|
+
<div className="space-y-4">
|
|
168
|
+
{/* Server Selection */}
|
|
169
|
+
<div className="space-y-2">
|
|
170
|
+
<Label>Select Server</Label>
|
|
171
|
+
<Select
|
|
172
|
+
value={selectedServerId || undefined}
|
|
173
|
+
onValueChange={(value) => setSelectedServerId(value as UUID)}
|
|
174
|
+
>
|
|
175
|
+
<SelectTrigger>
|
|
176
|
+
<SelectValue placeholder="Choose a server" />
|
|
177
|
+
</SelectTrigger>
|
|
178
|
+
<SelectContent>
|
|
179
|
+
{serversData?.data?.servers.map((server) => (
|
|
180
|
+
<SelectItem key={server.id} value={server.id}>
|
|
181
|
+
{server.name}
|
|
182
|
+
</SelectItem>
|
|
183
|
+
))}
|
|
184
|
+
</SelectContent>
|
|
185
|
+
</Select>
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
{/* Server Agents */}
|
|
189
|
+
{selectedServerId && (
|
|
190
|
+
<Card>
|
|
191
|
+
<CardHeader>
|
|
192
|
+
<CardTitle className="text-lg">Agents in Server</CardTitle>
|
|
193
|
+
<CardDescription>Agents currently associated with this server</CardDescription>
|
|
194
|
+
</CardHeader>
|
|
195
|
+
<CardContent>
|
|
196
|
+
<ScrollArea className="h-[200px]">
|
|
197
|
+
<div className="space-y-2">
|
|
198
|
+
{(serverAgents.get(selectedServerId) || []).length === 0 ? (
|
|
199
|
+
<p className="text-sm text-muted-foreground">No agents in this server</p>
|
|
200
|
+
) : (
|
|
201
|
+
(serverAgents.get(selectedServerId) || []).map((agentId) => (
|
|
202
|
+
<div
|
|
203
|
+
key={agentId}
|
|
204
|
+
className="flex items-center justify-between p-2 rounded-lg border"
|
|
205
|
+
>
|
|
206
|
+
<span className="text-sm font-medium">{getAgentName(agentId)}</span>
|
|
207
|
+
<Button
|
|
208
|
+
variant="ghost"
|
|
209
|
+
size="sm"
|
|
210
|
+
onClick={() => handleRemoveAgentFromServer(selectedServerId, agentId)}
|
|
211
|
+
disabled={isLoading}
|
|
212
|
+
>
|
|
213
|
+
<X className="h-4 w-4" />
|
|
214
|
+
</Button>
|
|
215
|
+
</div>
|
|
216
|
+
))
|
|
217
|
+
)}
|
|
218
|
+
</div>
|
|
219
|
+
</ScrollArea>
|
|
220
|
+
</CardContent>
|
|
221
|
+
</Card>
|
|
222
|
+
)}
|
|
223
|
+
|
|
224
|
+
{/* Add Agent Section */}
|
|
225
|
+
{selectedServerId && (
|
|
226
|
+
<div className="space-y-2">
|
|
227
|
+
<Label>Add Agent to Server</Label>
|
|
228
|
+
<div className="flex gap-2">
|
|
229
|
+
<Select
|
|
230
|
+
value={selectedAgentId || undefined}
|
|
231
|
+
onValueChange={(value) => setSelectedAgentId(value as UUID)}
|
|
232
|
+
>
|
|
233
|
+
<SelectTrigger className="flex-1">
|
|
234
|
+
<SelectValue placeholder="Choose an agent" />
|
|
235
|
+
</SelectTrigger>
|
|
236
|
+
<SelectContent>
|
|
237
|
+
{getAvailableAgents().map((agent) => (
|
|
238
|
+
<SelectItem key={agent.id} value={agent.id!}>
|
|
239
|
+
{agent.name}
|
|
240
|
+
</SelectItem>
|
|
241
|
+
))}
|
|
242
|
+
</SelectContent>
|
|
243
|
+
</Select>
|
|
244
|
+
<Button onClick={handleAddAgentToServer} disabled={!selectedAgentId || isLoading}>
|
|
245
|
+
{isLoading ? (
|
|
246
|
+
<Loader2 className="h-4 w-4 animate-spin" />
|
|
247
|
+
) : (
|
|
248
|
+
<Plus className="h-4 w-4" />
|
|
249
|
+
)}
|
|
250
|
+
</Button>
|
|
251
|
+
</div>
|
|
252
|
+
</div>
|
|
253
|
+
)}
|
|
254
|
+
</div>
|
|
255
|
+
|
|
256
|
+
<DialogFooter>
|
|
257
|
+
<Button variant="outline" onClick={() => onOpenChange(false)}>
|
|
258
|
+
Close
|
|
259
|
+
</Button>
|
|
260
|
+
</DialogFooter>
|
|
261
|
+
</DialogContent>
|
|
262
|
+
</Dialog>
|
|
263
|
+
);
|
|
264
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { ChevronDownIcon } from 'lucide-react';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
|
|
4
|
+
import { Button } from '@/components/ui/button';
|
|
5
|
+
import {
|
|
6
|
+
DropdownMenu,
|
|
7
|
+
DropdownMenuContent,
|
|
8
|
+
DropdownMenuRadioGroup,
|
|
9
|
+
DropdownMenuRadioItem,
|
|
10
|
+
DropdownMenuTrigger,
|
|
11
|
+
} from '@/components/ui/dropdown-menu';
|
|
12
|
+
import { cn } from '@/lib/utils';
|
|
13
|
+
|
|
14
|
+
export interface SplitButtonOption {
|
|
15
|
+
label: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
value?: string;
|
|
18
|
+
onClick?: () => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface SplitButtonProps {
|
|
22
|
+
options: SplitButtonOption[];
|
|
23
|
+
defaultValue?: string;
|
|
24
|
+
value?: string;
|
|
25
|
+
onValueChange?: (value: string) => void;
|
|
26
|
+
onClick?: (option: SplitButtonOption, value: string) => void;
|
|
27
|
+
variant?: 'default' | 'destructive';
|
|
28
|
+
className?: string;
|
|
29
|
+
buttonClassName?: string;
|
|
30
|
+
dropdownClassName?: string;
|
|
31
|
+
disabled?: boolean;
|
|
32
|
+
'aria-label'?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default function SplitButton({
|
|
36
|
+
options,
|
|
37
|
+
defaultValue = '0',
|
|
38
|
+
value,
|
|
39
|
+
onValueChange,
|
|
40
|
+
onClick,
|
|
41
|
+
variant = 'default',
|
|
42
|
+
className = '',
|
|
43
|
+
buttonClassName = '',
|
|
44
|
+
dropdownClassName = '',
|
|
45
|
+
disabled = false,
|
|
46
|
+
'aria-label': ariaLabel = 'Options',
|
|
47
|
+
}: SplitButtonProps) {
|
|
48
|
+
const [internalValue, setInternalValue] = useState(defaultValue);
|
|
49
|
+
|
|
50
|
+
// Use controlled value if provided, otherwise use internal state
|
|
51
|
+
const selectedValue = value !== undefined ? value : internalValue;
|
|
52
|
+
|
|
53
|
+
const handleValueChange = (newValue: string) => {
|
|
54
|
+
if (value === undefined) {
|
|
55
|
+
setInternalValue(newValue);
|
|
56
|
+
}
|
|
57
|
+
onValueChange?.(newValue);
|
|
58
|
+
|
|
59
|
+
// Find the selected option and call its onClick if it exists
|
|
60
|
+
const selectedOption = options.find(
|
|
61
|
+
(opt) => (opt.value || String(options.indexOf(opt))) === newValue
|
|
62
|
+
);
|
|
63
|
+
if (selectedOption?.onClick) {
|
|
64
|
+
selectedOption.onClick();
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const handleMainButtonClick = () => {
|
|
69
|
+
const selectedOption = options[Number(selectedValue)] || options[0];
|
|
70
|
+
if (selectedOption) {
|
|
71
|
+
// Call the option's specific onClick handler
|
|
72
|
+
selectedOption.onClick?.();
|
|
73
|
+
// Call the general onClick handler
|
|
74
|
+
onClick?.(selectedOption, selectedValue);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const selectedOption = options[Number(selectedValue)] || options[0];
|
|
79
|
+
|
|
80
|
+
if (!options.length) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Determine divider classes based on variant
|
|
85
|
+
const dividerClasses =
|
|
86
|
+
variant === 'destructive' ? 'divide-white/20' : 'divide-primary-foreground/30';
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<div
|
|
90
|
+
className={cn(
|
|
91
|
+
'inline-flex divide-x rounded-md shadow-xs rtl:space-x-reverse',
|
|
92
|
+
dividerClasses,
|
|
93
|
+
className
|
|
94
|
+
)}
|
|
95
|
+
>
|
|
96
|
+
<Button
|
|
97
|
+
className={cn(
|
|
98
|
+
'rounded-none shadow-none first:rounded-s-md last:rounded-e-md focus-visible:z-10',
|
|
99
|
+
buttonClassName
|
|
100
|
+
)}
|
|
101
|
+
variant={variant}
|
|
102
|
+
disabled={disabled}
|
|
103
|
+
onClick={handleMainButtonClick}
|
|
104
|
+
>
|
|
105
|
+
{selectedOption.label}
|
|
106
|
+
</Button>
|
|
107
|
+
<DropdownMenu>
|
|
108
|
+
<DropdownMenuTrigger asChild>
|
|
109
|
+
<Button
|
|
110
|
+
className={cn(
|
|
111
|
+
'rounded-none shadow-none first:rounded-s-md last:rounded-e-md focus-visible:z-10',
|
|
112
|
+
buttonClassName
|
|
113
|
+
)}
|
|
114
|
+
variant={variant}
|
|
115
|
+
size="icon"
|
|
116
|
+
aria-label={ariaLabel}
|
|
117
|
+
disabled={disabled}
|
|
118
|
+
>
|
|
119
|
+
<ChevronDownIcon size={16} aria-hidden="true" />
|
|
120
|
+
</Button>
|
|
121
|
+
</DropdownMenuTrigger>
|
|
122
|
+
<DropdownMenuContent
|
|
123
|
+
className={cn('max-w-64 md:max-w-xs', dropdownClassName)}
|
|
124
|
+
side="bottom"
|
|
125
|
+
sideOffset={4}
|
|
126
|
+
align="end"
|
|
127
|
+
>
|
|
128
|
+
<DropdownMenuRadioGroup value={selectedValue} onValueChange={handleValueChange}>
|
|
129
|
+
{options.map((option, index) => (
|
|
130
|
+
<DropdownMenuRadioItem
|
|
131
|
+
key={option.value || option.label}
|
|
132
|
+
value={option.value || String(index)}
|
|
133
|
+
className="items-start [&>span]:pt-1.5"
|
|
134
|
+
>
|
|
135
|
+
<div className="flex flex-col gap-1">
|
|
136
|
+
<span className="text-sm font-medium">{option.label}</span>
|
|
137
|
+
{option.description && (
|
|
138
|
+
<span className="text-muted-foreground text-xs">{option.description}</span>
|
|
139
|
+
)}
|
|
140
|
+
</div>
|
|
141
|
+
</DropdownMenuRadioItem>
|
|
142
|
+
))}
|
|
143
|
+
</DropdownMenuRadioGroup>
|
|
144
|
+
</DropdownMenuContent>
|
|
145
|
+
</DropdownMenu>
|
|
146
|
+
</div>
|
|
147
|
+
);
|
|
148
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Button } from '@/components/ui/button';
|
|
2
|
+
import { useAgentManagement } from '@/hooks/use-agent-management';
|
|
3
|
+
import ConfirmationDialog from '@/components/confirmation-dialog';
|
|
4
|
+
import { useConfirmation } from '@/hooks/use-confirmation';
|
|
5
|
+
import clientLogger from '@/lib/logger';
|
|
6
|
+
import type { Agent } from '@elizaos/core';
|
|
7
|
+
import { Loader2, Square } from 'lucide-react';
|
|
8
|
+
import { useNavigate } from 'react-router-dom';
|
|
9
|
+
|
|
10
|
+
interface StopAgentButtonProps {
|
|
11
|
+
agent: Agent;
|
|
12
|
+
variant?: 'default' | 'destructive' | 'outline';
|
|
13
|
+
size?: 'default' | 'sm' | 'lg' | 'icon';
|
|
14
|
+
className?: string;
|
|
15
|
+
showIcon?: boolean;
|
|
16
|
+
redirectToHome?: boolean;
|
|
17
|
+
onStopComplete?: () => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default function StopAgentButton({
|
|
21
|
+
agent,
|
|
22
|
+
variant = 'destructive',
|
|
23
|
+
size = 'default',
|
|
24
|
+
className = '',
|
|
25
|
+
showIcon = true,
|
|
26
|
+
redirectToHome = false,
|
|
27
|
+
onStopComplete,
|
|
28
|
+
}: StopAgentButtonProps) {
|
|
29
|
+
const { stopAgent, isAgentStopping } = useAgentManagement();
|
|
30
|
+
const navigate = useNavigate();
|
|
31
|
+
const isStoppingAgent = isAgentStopping(agent.id);
|
|
32
|
+
const { confirm, isOpen, onOpenChange, onConfirm, options } = useConfirmation();
|
|
33
|
+
|
|
34
|
+
const handleStopAgent = () => {
|
|
35
|
+
confirm(
|
|
36
|
+
{
|
|
37
|
+
title: 'Stop Agent',
|
|
38
|
+
description: `Are you sure you want to stop "${agent.name}"?`,
|
|
39
|
+
confirmText: 'Stop',
|
|
40
|
+
variant: 'destructive',
|
|
41
|
+
},
|
|
42
|
+
async () => {
|
|
43
|
+
try {
|
|
44
|
+
await stopAgent(agent);
|
|
45
|
+
|
|
46
|
+
// Call the onStopComplete callback if provided
|
|
47
|
+
if (onStopComplete) {
|
|
48
|
+
onStopComplete();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Navigate to homepage if redirectToHome is true
|
|
52
|
+
if (redirectToHome) {
|
|
53
|
+
navigate('/');
|
|
54
|
+
}
|
|
55
|
+
} catch (error) {
|
|
56
|
+
// If error occurs, don't navigate or call callback
|
|
57
|
+
clientLogger.error('Error stopping agent:', error);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<>
|
|
65
|
+
<Button
|
|
66
|
+
type="button"
|
|
67
|
+
variant={variant}
|
|
68
|
+
size={size}
|
|
69
|
+
className={className}
|
|
70
|
+
onClick={handleStopAgent}
|
|
71
|
+
disabled={isStoppingAgent}
|
|
72
|
+
>
|
|
73
|
+
{isStoppingAgent ? (
|
|
74
|
+
<>
|
|
75
|
+
{showIcon && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
|
76
|
+
Stopping...
|
|
77
|
+
</>
|
|
78
|
+
) : (
|
|
79
|
+
<>
|
|
80
|
+
{showIcon && <Square className="mr-2 h-4 w-4" />}
|
|
81
|
+
Stop
|
|
82
|
+
</>
|
|
83
|
+
)}
|
|
84
|
+
</Button>
|
|
85
|
+
|
|
86
|
+
{/* Confirmation Dialog */}
|
|
87
|
+
<ConfirmationDialog
|
|
88
|
+
open={isOpen}
|
|
89
|
+
onOpenChange={onOpenChange}
|
|
90
|
+
title={options?.title || ''}
|
|
91
|
+
description={options?.description || ''}
|
|
92
|
+
confirmText={options?.confirmText}
|
|
93
|
+
cancelText={options?.cancelText}
|
|
94
|
+
variant={options?.variant}
|
|
95
|
+
onConfirm={onConfirm}
|
|
96
|
+
/>
|
|
97
|
+
</>
|
|
98
|
+
);
|
|
99
|
+
}
|