@alpaca-editor/core 1.0.4172 → 1.0.4174
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/agents-view/AgentsView.d.ts +5 -0
- package/dist/agents-view/AgentsView.js +213 -0
- package/dist/agents-view/AgentsView.js.map +1 -0
- package/dist/components/ui/context-menu.js +4 -4
- package/dist/components/ui/context-menu.js.map +1 -1
- package/dist/config/config.js +56 -1
- package/dist/config/config.js.map +1 -1
- package/dist/editor/ConfirmationDialog.js +2 -1
- package/dist/editor/ConfirmationDialog.js.map +1 -1
- package/dist/editor/ContentTree.d.ts +2 -1
- package/dist/editor/ContentTree.js +18 -3
- package/dist/editor/ContentTree.js.map +1 -1
- package/dist/editor/ContextMenu.js +1 -1
- package/dist/editor/ContextMenu.js.map +1 -1
- package/dist/editor/FieldList.js +7 -3
- package/dist/editor/FieldList.js.map +1 -1
- package/dist/editor/FieldListField.d.ts +3 -2
- package/dist/editor/FieldListField.js +4 -4
- package/dist/editor/FieldListField.js.map +1 -1
- package/dist/editor/FieldListFieldWithFallbacks.d.ts +2 -1
- package/dist/editor/FieldListFieldWithFallbacks.js +5 -2
- package/dist/editor/FieldListFieldWithFallbacks.js.map +1 -1
- package/dist/editor/MainLayout.js +1 -1
- package/dist/editor/QuickItemSwitcher.d.ts +9 -0
- package/dist/editor/QuickItemSwitcher.js +74 -0
- package/dist/editor/QuickItemSwitcher.js.map +1 -0
- package/dist/editor/ai/AgentCostDisplay.js +7 -11
- package/dist/editor/ai/AgentCostDisplay.js.map +1 -1
- package/dist/editor/ai/AgentProfilesOverview.d.ts +9 -0
- package/dist/editor/ai/AgentProfilesOverview.js +16 -0
- package/dist/editor/ai/AgentProfilesOverview.js.map +1 -0
- package/dist/editor/ai/AgentTerminal.js +285 -748
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/Agents.js +112 -54
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/AiResponseMessage.d.ts +2 -1
- package/dist/editor/ai/AiResponseMessage.js +4 -2
- package/dist/editor/ai/AiResponseMessage.js.map +1 -1
- package/dist/editor/ai/ContextInfoBar.js +17 -17
- package/dist/editor/ai/useAgentStatus.js +7 -0
- package/dist/editor/ai/useAgentStatus.js.map +1 -1
- package/dist/editor/client/EditorShell.js +230 -4
- package/dist/editor/client/EditorShell.js.map +1 -1
- package/dist/editor/client/hooks/useSocketMessageHandler.js +0 -12
- package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -1
- package/dist/editor/client/ui/EditorChrome.js +1 -1
- package/dist/editor/client/ui/EditorChrome.js.map +1 -1
- package/dist/editor/commands/itemCommands.js +1 -0
- package/dist/editor/commands/itemCommands.js.map +1 -1
- package/dist/editor/control-center/parhelia-setup/Overview.d.ts +1 -0
- package/dist/editor/control-center/parhelia-setup/Overview.js +91 -0
- package/dist/editor/control-center/parhelia-setup/Overview.js.map +1 -0
- package/dist/editor/field-types/AttachmentEditor.js +3 -6
- package/dist/editor/field-types/AttachmentEditor.js.map +1 -1
- package/dist/editor/field-types/DropLinkEditor.js +2 -2
- package/dist/editor/field-types/DropLinkEditor.js.map +1 -1
- package/dist/editor/field-types/DropListEditor.js +2 -1
- package/dist/editor/field-types/DropListEditor.js.map +1 -1
- package/dist/editor/field-types/InternalLinkFieldEditor.js +3 -3
- package/dist/editor/field-types/InternalLinkFieldEditor.js.map +1 -1
- package/dist/editor/field-types/MultiLineText.d.ts +2 -1
- package/dist/editor/field-types/MultiLineText.js +2 -2
- package/dist/editor/field-types/MultiLineText.js.map +1 -1
- package/dist/editor/field-types/RawEditor.d.ts +2 -1
- package/dist/editor/field-types/RawEditor.js +2 -2
- package/dist/editor/field-types/RawEditor.js.map +1 -1
- package/dist/editor/field-types/SingleLineText.d.ts +2 -1
- package/dist/editor/field-types/SingleLineText.js +2 -2
- package/dist/editor/field-types/SingleLineText.js.map +1 -1
- package/dist/editor/field-types/TreeListEditor.js +9 -7
- package/dist/editor/field-types/TreeListEditor.js.map +1 -1
- package/dist/editor/media-selector/MediaFolderBrowser.js +68 -7
- package/dist/editor/media-selector/MediaFolderBrowser.js.map +1 -1
- package/dist/editor/media-selector/TreeSelector.js +1 -1
- package/dist/editor/media-selector/TreeSelector.js.map +1 -1
- package/dist/editor/menubar/ActiveUsers.js +2 -2
- package/dist/editor/menubar/FavoritesControls.js +2 -2
- package/dist/editor/menubar/FavoritesControls.js.map +1 -1
- package/dist/editor/page-editor-chrome/FrameMenu.js +10 -1
- package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
- package/dist/editor/page-viewer/pageModelSkeletonBuilder.js +14 -0
- package/dist/editor/page-viewer/pageModelSkeletonBuilder.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +21 -3
- package/dist/editor/services/agentService.js +24 -8
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/services/aiService.d.ts +6 -1
- package/dist/editor/services/aiService.js +36 -5
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/services/contentService.d.ts +1 -1
- package/dist/editor/services/contentService.js +4 -2
- package/dist/editor/services/contentService.js.map +1 -1
- package/dist/editor/services/editService.d.ts +5 -1
- package/dist/editor/services/editService.js +1 -1
- package/dist/editor/services/editService.js.map +1 -1
- package/dist/editor/services/setupService.d.ts +21 -0
- package/dist/editor/services/setupService.js +10 -0
- package/dist/editor/services/setupService.js.map +1 -0
- package/dist/editor/sidebar/ComponentTree.js +15 -1
- package/dist/editor/sidebar/ComponentTree.js.map +1 -1
- package/dist/editor/sidebar/SidebarView.js +1 -1
- package/dist/editor/sidebar/SidebarView.js.map +1 -1
- package/dist/editor/ui/ItemSearch.d.ts +1 -0
- package/dist/editor/ui/ItemSearch.js +2 -2
- package/dist/editor/ui/ItemSearch.js.map +1 -1
- package/dist/editor/ui/PerfectTree.d.ts +5 -1
- package/dist/editor/ui/PerfectTree.js +308 -29
- package/dist/editor/ui/PerfectTree.js.map +1 -1
- package/dist/editor/utils/keyboardNavigation.d.ts +2 -0
- package/dist/editor/utils/keyboardNavigation.js +80 -2
- package/dist/editor/utils/keyboardNavigation.js.map +1 -1
- package/dist/editor/views/SingleEditView.js +6 -4
- package/dist/editor/views/SingleEditView.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/splash-screen/SplashScreen.js +78 -4
- package/dist/splash-screen/SplashScreen.js.map +1 -1
- package/dist/styles.css +157 -23
- package/dist/types.d.ts +13 -0
- package/package.json +1 -1
- package/src/agents-view/AgentsView.tsx +431 -0
- package/src/components/ui/context-menu.tsx +4 -4
- package/src/config/config.tsx +61 -0
- package/src/editor/ConfirmationDialog.tsx +42 -10
- package/src/editor/ContentTree.tsx +20 -1
- package/src/editor/ContextMenu.tsx +4 -1
- package/src/editor/FieldList.tsx +10 -4
- package/src/editor/FieldListField.tsx +7 -0
- package/src/editor/FieldListFieldWithFallbacks.tsx +10 -0
- package/src/editor/MainLayout.tsx +1 -1
- package/src/editor/QuickItemSwitcher.tsx +217 -0
- package/src/editor/ai/AgentCostDisplay.tsx +59 -60
- package/src/editor/ai/AgentProfilesOverview.tsx +81 -0
- package/src/editor/ai/AgentTerminal.tsx +321 -775
- package/src/editor/ai/Agents.tsx +157 -91
- package/src/editor/ai/AiResponseMessage.tsx +12 -1
- package/src/editor/ai/ContextInfoBar.tsx +17 -17
- package/src/editor/ai/useAgentStatus.ts +6 -0
- package/src/editor/client/EditorShell.tsx +288 -3
- package/src/editor/client/hooks/useSocketMessageHandler.ts +0 -15
- package/src/editor/client/ui/EditorChrome.tsx +1 -1
- package/src/editor/commands/itemCommands.tsx +1 -0
- package/src/editor/control-center/parhelia-setup/Overview.tsx +184 -0
- package/src/editor/field-types/AttachmentEditor.tsx +13 -50
- package/src/editor/field-types/DropLinkEditor.tsx +2 -2
- package/src/editor/field-types/DropListEditor.tsx +2 -1
- package/src/editor/field-types/InternalLinkFieldEditor.tsx +3 -3
- package/src/editor/field-types/MultiLineText.tsx +3 -0
- package/src/editor/field-types/RawEditor.tsx +3 -0
- package/src/editor/field-types/SingleLineText.tsx +3 -0
- package/src/editor/field-types/TreeListEditor.tsx +32 -24
- package/src/editor/media-selector/MediaFolderBrowser.tsx +93 -9
- package/src/editor/media-selector/TreeSelector.tsx +1 -0
- package/src/editor/menubar/ActiveUsers.tsx +2 -2
- package/src/editor/menubar/FavoritesControls.tsx +5 -5
- package/src/editor/page-editor-chrome/FrameMenu.tsx +10 -1
- package/src/editor/page-viewer/pageModelSkeletonBuilder.ts +17 -0
- package/src/editor/services/agentService.ts +46 -11
- package/src/editor/services/aiService.ts +48 -7
- package/src/editor/services/contentService.ts +4 -1
- package/src/editor/services/editService.ts +8 -3
- package/src/editor/services/setupService.ts +35 -0
- package/src/editor/sidebar/ComponentTree.tsx +16 -1
- package/src/editor/sidebar/SidebarView.tsx +1 -1
- package/src/editor/ui/ItemSearch.tsx +3 -1
- package/src/editor/ui/PerfectTree.tsx +393 -42
- package/src/editor/utils/keyboardNavigation.ts +97 -1
- package/src/editor/views/SingleEditView.tsx +27 -13
- package/src/revision.ts +2 -2
- package/src/splash-screen/SplashScreen.tsx +134 -2
- package/src/types.ts +15 -0
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
import React, { useState, useEffect } from "react";
|
|
2
|
+
import {
|
|
3
|
+
AgentDetails,
|
|
4
|
+
getActiveAgents,
|
|
5
|
+
getAgent,
|
|
6
|
+
closeAgent,
|
|
7
|
+
deleteAgent,
|
|
8
|
+
} from "../editor/services/agentService";
|
|
9
|
+
import { getAgentStatusConfig } from "../editor/ai/AgentStatusBadge";
|
|
10
|
+
import { cn } from "../lib/utils";
|
|
11
|
+
import { Trash, X, RefreshCw, Play } from "lucide-react";
|
|
12
|
+
import { SimpleIconButton } from "../editor/ui/SimpleIconButton";
|
|
13
|
+
import { useEditContext } from "../editor/client/editContext";
|
|
14
|
+
import { SecretAgentIcon } from "../editor/ui/Icons";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Agents overview view that displays all non-closed agents for the current user
|
|
18
|
+
* grouped by profile with interactive controls
|
|
19
|
+
*/
|
|
20
|
+
export function AgentsView() {
|
|
21
|
+
const editContext = useEditContext();
|
|
22
|
+
const [agents, setAgents] = useState<AgentDetails[]>([]);
|
|
23
|
+
const [loading, setLoading] = useState(true);
|
|
24
|
+
const [error, setError] = useState<string | null>(null);
|
|
25
|
+
const [refreshing, setRefreshing] = useState(false);
|
|
26
|
+
const [searchTerm, setSearchTerm] = useState("");
|
|
27
|
+
|
|
28
|
+
const loadAgents = async () => {
|
|
29
|
+
try {
|
|
30
|
+
setError(null);
|
|
31
|
+
// Get basic agent list
|
|
32
|
+
const basicAgents = await getActiveAgents(1000);
|
|
33
|
+
|
|
34
|
+
// Fetch full details for each agent
|
|
35
|
+
const detailsPromises = basicAgents.map((agent) =>
|
|
36
|
+
getAgent(agent.id).catch(() => null),
|
|
37
|
+
);
|
|
38
|
+
const agentDetails = await Promise.all(detailsPromises);
|
|
39
|
+
|
|
40
|
+
// Filter out any failed fetches and set the agents
|
|
41
|
+
const validAgents = agentDetails.filter(
|
|
42
|
+
(a): a is AgentDetails => a !== null,
|
|
43
|
+
);
|
|
44
|
+
setAgents(validAgents);
|
|
45
|
+
} catch (err: any) {
|
|
46
|
+
console.error("Failed to load agents:", err);
|
|
47
|
+
setError(
|
|
48
|
+
err?.message || "Failed to load agents. Please try again later.",
|
|
49
|
+
);
|
|
50
|
+
} finally {
|
|
51
|
+
setLoading(false);
|
|
52
|
+
setRefreshing(false);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
loadAgents();
|
|
58
|
+
}, []);
|
|
59
|
+
|
|
60
|
+
// Subscribe to websocket messages for real-time agent updates
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (!editContext?.addSocketMessageListener) return;
|
|
63
|
+
|
|
64
|
+
const unsubscribe = editContext.addSocketMessageListener(
|
|
65
|
+
(message: { type: string; payload: any }) => {
|
|
66
|
+
if (message.type === "agent:run:start") {
|
|
67
|
+
const { agentId, agentName } = message.payload;
|
|
68
|
+
|
|
69
|
+
if (!agentId || !agentName) {
|
|
70
|
+
console.warn(
|
|
71
|
+
"Invalid agent:run:start message payload:",
|
|
72
|
+
message.payload,
|
|
73
|
+
);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
setAgents((prevAgents) => {
|
|
78
|
+
// Check if agent already exists
|
|
79
|
+
const existingAgentIndex = prevAgents.findIndex(
|
|
80
|
+
(agent) => agent.id === agentId,
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
if (existingAgentIndex !== -1) {
|
|
84
|
+
// Update existing agent
|
|
85
|
+
const updatedAgents = [...prevAgents];
|
|
86
|
+
const existingAgent = updatedAgents[existingAgentIndex]!;
|
|
87
|
+
updatedAgents[existingAgentIndex] = {
|
|
88
|
+
...existingAgent,
|
|
89
|
+
name: agentName,
|
|
90
|
+
status: "running" as const,
|
|
91
|
+
updatedDate: new Date().toISOString(),
|
|
92
|
+
};
|
|
93
|
+
return updatedAgents;
|
|
94
|
+
} else {
|
|
95
|
+
// Add new agent - reload to get full details
|
|
96
|
+
loadAgents();
|
|
97
|
+
return prevAgents;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
} else if (message.type === "agent:status:changed") {
|
|
101
|
+
// Update agent status in real-time
|
|
102
|
+
const { agentId, status } = message.payload || {};
|
|
103
|
+
if (!agentId) return;
|
|
104
|
+
|
|
105
|
+
setAgents((prevAgents) => {
|
|
106
|
+
const existingIndex = prevAgents.findIndex((a) => a.id === agentId);
|
|
107
|
+
|
|
108
|
+
if (existingIndex !== -1) {
|
|
109
|
+
const updatedAgents = [...prevAgents];
|
|
110
|
+
const existingAgent = updatedAgents[existingIndex]!;
|
|
111
|
+
updatedAgents[existingIndex] = {
|
|
112
|
+
...existingAgent,
|
|
113
|
+
status,
|
|
114
|
+
updatedDate: new Date().toISOString(),
|
|
115
|
+
};
|
|
116
|
+
return updatedAgents;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return prevAgents;
|
|
120
|
+
});
|
|
121
|
+
} else if (message.type === "agent:run:closed") {
|
|
122
|
+
// Remove agent from list when closed
|
|
123
|
+
const { agentId } = message.payload || {};
|
|
124
|
+
if (!agentId) return;
|
|
125
|
+
|
|
126
|
+
setAgents((prevAgents) => prevAgents.filter((a) => a.id !== agentId));
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
return unsubscribe;
|
|
132
|
+
}, [editContext?.addSocketMessageListener]);
|
|
133
|
+
|
|
134
|
+
const handleRefresh = async () => {
|
|
135
|
+
setRefreshing(true);
|
|
136
|
+
await loadAgents();
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const handleResumeAgent = (agent: AgentDetails) => {
|
|
140
|
+
editContext?.switchView("page-editor");
|
|
141
|
+
editContext?.setShowAgentsPanel?.(true);
|
|
142
|
+
// Delay for panel mount, then dispatch event to open existing agent
|
|
143
|
+
setTimeout(() => {
|
|
144
|
+
window.dispatchEvent(
|
|
145
|
+
new CustomEvent("editor:openAgent", {
|
|
146
|
+
detail: { agentId: agent.id },
|
|
147
|
+
}),
|
|
148
|
+
);
|
|
149
|
+
}, 500);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const handleCloseAgent = async (agentId: string, agentName: string) => {
|
|
153
|
+
editContext?.confirm({
|
|
154
|
+
header: "Close Agent",
|
|
155
|
+
message: `Are you sure you want to close "${agentName}"? This will stop its execution if running.`,
|
|
156
|
+
acceptLabel: "Close Agent",
|
|
157
|
+
accept: async () => {
|
|
158
|
+
try {
|
|
159
|
+
await closeAgent(agentId);
|
|
160
|
+
setAgents((prev) => prev.filter((a) => a.id !== agentId));
|
|
161
|
+
} catch (err: any) {
|
|
162
|
+
console.error("Failed to close agent:", err);
|
|
163
|
+
editContext?.showToast(
|
|
164
|
+
`Failed to close agent: ${err?.message || "Unknown error"}`,
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
showCancel: true,
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const handleDeleteAgent = async (
|
|
173
|
+
agentId: string,
|
|
174
|
+
agentName: string,
|
|
175
|
+
event: React.MouseEvent,
|
|
176
|
+
) => {
|
|
177
|
+
event.stopPropagation();
|
|
178
|
+
|
|
179
|
+
editContext?.confirm({
|
|
180
|
+
header: "Delete Agent",
|
|
181
|
+
message: `Are you sure you want to permanently delete "${agentName}"? This action cannot be undone.`,
|
|
182
|
+
acceptLabel: "Delete Agent",
|
|
183
|
+
accept: async () => {
|
|
184
|
+
try {
|
|
185
|
+
await deleteAgent(agentId);
|
|
186
|
+
setAgents((prev) => prev.filter((a) => a.id !== agentId));
|
|
187
|
+
} catch (err: any) {
|
|
188
|
+
console.error("Failed to delete agent:", err);
|
|
189
|
+
editContext?.showToast(
|
|
190
|
+
`Failed to delete agent: ${err?.message || "Unknown error"}`,
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
showCancel: true,
|
|
195
|
+
});
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const formatDateTime = (dateString: string) => {
|
|
199
|
+
try {
|
|
200
|
+
const date = new Date(dateString);
|
|
201
|
+
return date.toLocaleString();
|
|
202
|
+
} catch {
|
|
203
|
+
return dateString;
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const filteredAgents = agents.filter((agent) => {
|
|
208
|
+
if (!searchTerm.trim()) return true;
|
|
209
|
+
const search = searchTerm.toLowerCase();
|
|
210
|
+
return (
|
|
211
|
+
agent.name.toLowerCase().includes(search) ||
|
|
212
|
+
agent.profileName?.toLowerCase().includes(search) ||
|
|
213
|
+
agent.id.toLowerCase().includes(search)
|
|
214
|
+
);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// Group agents by profile
|
|
218
|
+
const groupedAgents = filteredAgents.reduce(
|
|
219
|
+
(acc, agent) => {
|
|
220
|
+
const profileName = agent.profileName || "Unknown Profile";
|
|
221
|
+
if (!acc[profileName]) {
|
|
222
|
+
acc[profileName] = [];
|
|
223
|
+
}
|
|
224
|
+
acc[profileName].push(agent);
|
|
225
|
+
return acc;
|
|
226
|
+
},
|
|
227
|
+
{} as Record<string, AgentDetails[]>,
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
// Sort profile groups by name
|
|
231
|
+
const sortedProfileGroups = Object.entries(groupedAgents).sort(([a], [b]) =>
|
|
232
|
+
a.localeCompare(b),
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
if (loading) {
|
|
236
|
+
return (
|
|
237
|
+
<div className="flex h-full items-center justify-center">
|
|
238
|
+
<div className="text-sm text-gray-500">Loading agents...</div>
|
|
239
|
+
</div>
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (error) {
|
|
244
|
+
return (
|
|
245
|
+
<div className="flex h-full items-center justify-center">
|
|
246
|
+
<div className="max-w-md text-center">
|
|
247
|
+
<div className="mb-4 text-sm text-red-600">{error}</div>
|
|
248
|
+
<button
|
|
249
|
+
onClick={handleRefresh}
|
|
250
|
+
className="rounded bg-blue-500 px-4 py-2 text-xs text-white hover:bg-blue-600"
|
|
251
|
+
>
|
|
252
|
+
Retry
|
|
253
|
+
</button>
|
|
254
|
+
</div>
|
|
255
|
+
</div>
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return (
|
|
260
|
+
<div className="flex h-full flex-col">
|
|
261
|
+
{/* Header */}
|
|
262
|
+
<div className="border-b border-gray-200 bg-white p-4">
|
|
263
|
+
<div className="mb-3 flex items-center justify-between">
|
|
264
|
+
<div>
|
|
265
|
+
<h2 className="text-lg font-semibold text-gray-900">My Agents</h2>
|
|
266
|
+
<p className="text-xs text-gray-500">
|
|
267
|
+
Showing {filteredAgents.length} active agent
|
|
268
|
+
{filteredAgents.length !== 1 ? "s" : ""}
|
|
269
|
+
</p>
|
|
270
|
+
</div>
|
|
271
|
+
<SimpleIconButton
|
|
272
|
+
onClick={handleRefresh}
|
|
273
|
+
icon={
|
|
274
|
+
<RefreshCw
|
|
275
|
+
className={cn("size-4", refreshing && "animate-spin")}
|
|
276
|
+
strokeWidth={1.5}
|
|
277
|
+
/>
|
|
278
|
+
}
|
|
279
|
+
label="Refresh"
|
|
280
|
+
disabled={refreshing}
|
|
281
|
+
className="text-gray-600 hover:text-gray-800"
|
|
282
|
+
/>
|
|
283
|
+
</div>
|
|
284
|
+
|
|
285
|
+
{/* Search */}
|
|
286
|
+
<input
|
|
287
|
+
type="text"
|
|
288
|
+
placeholder="Search by name, profile, or agent ID..."
|
|
289
|
+
value={searchTerm}
|
|
290
|
+
onChange={(e) => setSearchTerm(e.target.value)}
|
|
291
|
+
className="w-full rounded border border-gray-200 px-3 py-2 text-sm focus:border-blue-500 focus:outline-none"
|
|
292
|
+
/>
|
|
293
|
+
</div>
|
|
294
|
+
|
|
295
|
+
{/* Agents List */}
|
|
296
|
+
<div className="flex-1 overflow-auto">
|
|
297
|
+
{filteredAgents.length === 0 ? (
|
|
298
|
+
<div className="flex h-full items-center justify-center text-sm text-gray-500">
|
|
299
|
+
{searchTerm.trim()
|
|
300
|
+
? "No agents match your search"
|
|
301
|
+
: "No active agents found"}
|
|
302
|
+
</div>
|
|
303
|
+
) : (
|
|
304
|
+
<div className="space-y-6 p-4">
|
|
305
|
+
{sortedProfileGroups.map(([profileName, profileAgents]) => (
|
|
306
|
+
<div key={profileName}>
|
|
307
|
+
<h3 className="mb-2 text-xs font-semibold tracking-wide text-gray-500 uppercase">
|
|
308
|
+
{profileName} ({profileAgents.length})
|
|
309
|
+
</h3>
|
|
310
|
+
<div className="space-y-2">
|
|
311
|
+
{profileAgents.map((agent) => {
|
|
312
|
+
const statusConfig = getAgentStatusConfig(agent);
|
|
313
|
+
return (
|
|
314
|
+
<div
|
|
315
|
+
key={agent.id}
|
|
316
|
+
className="cursor-pointer rounded-lg border border-gray-200 bg-white p-3 shadow-sm transition-shadow hover:shadow-md"
|
|
317
|
+
onClick={() => {
|
|
318
|
+
// Only select the agent in the agents panel; do not switch views
|
|
319
|
+
window.dispatchEvent(
|
|
320
|
+
new CustomEvent("editor:selectAgent", {
|
|
321
|
+
detail: { agentId: agent.id },
|
|
322
|
+
}),
|
|
323
|
+
);
|
|
324
|
+
}}
|
|
325
|
+
>
|
|
326
|
+
<div className="flex items-start justify-between">
|
|
327
|
+
{/* Agent Icon */}
|
|
328
|
+
<div className="mr-3 flex-shrink-0">
|
|
329
|
+
{agent.profileSvgIcon ? (
|
|
330
|
+
<div
|
|
331
|
+
className="flex h-6 w-6 items-center justify-center text-gray-400 [&>svg]:h-full [&>svg]:w-full"
|
|
332
|
+
dangerouslySetInnerHTML={{
|
|
333
|
+
__html: agent.profileSvgIcon,
|
|
334
|
+
}}
|
|
335
|
+
/>
|
|
336
|
+
) : (
|
|
337
|
+
<SecretAgentIcon
|
|
338
|
+
size={24}
|
|
339
|
+
strokeWidth={1}
|
|
340
|
+
className="text-gray-400"
|
|
341
|
+
/>
|
|
342
|
+
)}
|
|
343
|
+
</div>
|
|
344
|
+
|
|
345
|
+
<div className="min-w-0 flex-1">
|
|
346
|
+
{/* Agent Name & Status */}
|
|
347
|
+
<div className="mb-1 flex items-center gap-2">
|
|
348
|
+
<div
|
|
349
|
+
className={cn(
|
|
350
|
+
"h-2 w-2 flex-shrink-0 rounded-full",
|
|
351
|
+
statusConfig.color,
|
|
352
|
+
statusConfig.shouldPulse && "animate-pulse",
|
|
353
|
+
)}
|
|
354
|
+
title={statusConfig.label}
|
|
355
|
+
/>
|
|
356
|
+
<h4 className="truncate font-medium text-gray-900">
|
|
357
|
+
{agent.name}
|
|
358
|
+
</h4>
|
|
359
|
+
</div>
|
|
360
|
+
|
|
361
|
+
{/* Agent Details */}
|
|
362
|
+
<div className="space-y-1 text-xs text-gray-600">
|
|
363
|
+
<div className="flex items-center gap-2">
|
|
364
|
+
<span className="font-medium">Updated:</span>
|
|
365
|
+
<span>{formatDateTime(agent.updatedDate)}</span>
|
|
366
|
+
</div>
|
|
367
|
+
{agent.messageCount > 0 && (
|
|
368
|
+
<div className="flex items-center gap-2">
|
|
369
|
+
<span className="font-medium">Messages:</span>
|
|
370
|
+
<span>{agent.messageCount}</span>
|
|
371
|
+
</div>
|
|
372
|
+
)}
|
|
373
|
+
{agent.totalCost > 0 && (
|
|
374
|
+
<div className="flex items-center gap-2">
|
|
375
|
+
<span className="font-medium">Cost:</span>
|
|
376
|
+
<span>${agent.totalCost.toFixed(4)}</span>
|
|
377
|
+
<span className="text-gray-400">
|
|
378
|
+
({agent.totalTokensUsed.toLocaleString()}{" "}
|
|
379
|
+
tokens)
|
|
380
|
+
</span>
|
|
381
|
+
</div>
|
|
382
|
+
)}
|
|
383
|
+
</div>
|
|
384
|
+
</div>
|
|
385
|
+
|
|
386
|
+
{/* Actions */}
|
|
387
|
+
<div className="ml-3 flex gap-1">
|
|
388
|
+
<SimpleIconButton
|
|
389
|
+
onClick={(e) => {
|
|
390
|
+
e.stopPropagation();
|
|
391
|
+
handleResumeAgent(agent);
|
|
392
|
+
}}
|
|
393
|
+
icon={
|
|
394
|
+
<Play className="size-3" strokeWidth={1.5} />
|
|
395
|
+
}
|
|
396
|
+
label="Resume Agent"
|
|
397
|
+
className="text-blue-600 opacity-60 hover:text-blue-700 hover:opacity-100"
|
|
398
|
+
/>
|
|
399
|
+
<SimpleIconButton
|
|
400
|
+
onClick={(e) => {
|
|
401
|
+
e.stopPropagation();
|
|
402
|
+
handleCloseAgent(agent.id, agent.name);
|
|
403
|
+
}}
|
|
404
|
+
icon={<X className="size-3" strokeWidth={1.5} />}
|
|
405
|
+
label="Close Agent"
|
|
406
|
+
className="text-gray-600 opacity-60 hover:text-gray-800 hover:opacity-100"
|
|
407
|
+
/>
|
|
408
|
+
<SimpleIconButton
|
|
409
|
+
onClick={(e) =>
|
|
410
|
+
handleDeleteAgent(agent.id, agent.name, e)
|
|
411
|
+
}
|
|
412
|
+
icon={
|
|
413
|
+
<Trash className="size-3" strokeWidth={1.5} />
|
|
414
|
+
}
|
|
415
|
+
label="Delete Agent"
|
|
416
|
+
className="text-red-600 opacity-60 hover:text-red-700 hover:opacity-100"
|
|
417
|
+
/>
|
|
418
|
+
</div>
|
|
419
|
+
</div>
|
|
420
|
+
</div>
|
|
421
|
+
);
|
|
422
|
+
})}
|
|
423
|
+
</div>
|
|
424
|
+
</div>
|
|
425
|
+
))}
|
|
426
|
+
</div>
|
|
427
|
+
)}
|
|
428
|
+
</div>
|
|
429
|
+
</div>
|
|
430
|
+
);
|
|
431
|
+
}
|
|
@@ -326,7 +326,7 @@ function ContextMenuItem({
|
|
|
326
326
|
data-variant={variant}
|
|
327
327
|
data-disabled={disabled ? "" : undefined}
|
|
328
328
|
className={cn(
|
|
329
|
-
"hover:
|
|
329
|
+
"hover:text-accent-foreground focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-xs outline-hidden select-none hover:bg-[#f6eeff] focus:bg-[#f6eeff] data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
330
330
|
className,
|
|
331
331
|
)}
|
|
332
332
|
onMouseDown={handleMouseDown}
|
|
@@ -361,7 +361,7 @@ function ContextMenuCheckboxItem({
|
|
|
361
361
|
data-testid="context-menu-checkbox-item"
|
|
362
362
|
data-disabled={(rest as any).disabled ? "" : undefined}
|
|
363
363
|
className={cn(
|
|
364
|
-
"hover:
|
|
364
|
+
"hover:text-accent-foreground focus:text-accent-foreground relative flex cursor-pointer items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-xs outline-hidden select-none hover:bg-[#f6eeff] focus:bg-[#f6eeff] data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
365
365
|
className,
|
|
366
366
|
)}
|
|
367
367
|
onClick={handle}
|
|
@@ -410,7 +410,7 @@ function ContextMenuRadioItem({
|
|
|
410
410
|
data-testid="context-menu-radio-item"
|
|
411
411
|
data-disabled={(rest as any).disabled ? "" : undefined}
|
|
412
412
|
className={cn(
|
|
413
|
-
"hover:
|
|
413
|
+
"hover:text-accent-foreground focus:text-accent-foreground relative flex cursor-pointer items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-xs outline-hidden select-none hover:bg-[#f6eeff] focus:bg-[#f6eeff] data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
414
414
|
className,
|
|
415
415
|
)}
|
|
416
416
|
onClick={handle}
|
|
@@ -539,7 +539,7 @@ function ContextMenuSubTrigger({
|
|
|
539
539
|
data-inset={inset}
|
|
540
540
|
data-disabled={disabled ? "" : undefined}
|
|
541
541
|
className={cn(
|
|
542
|
-
"hover:
|
|
542
|
+
"hover:text-accent-foreground focus:text-accent-foreground flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-xs outline-hidden select-none hover:bg-[#f6eeff] focus:bg-[#f6eeff] data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
543
543
|
className,
|
|
544
544
|
)}
|
|
545
545
|
onMouseEnter={openSub}
|
package/src/config/config.tsx
CHANGED
|
@@ -47,6 +47,7 @@ import { Titlebar } from "../editor/Titlebar";
|
|
|
47
47
|
import { NoWriteLanguageAccess } from "../editor/editor-warnings/NoLanguageWriteAccess";
|
|
48
48
|
import { NoWorkflowWriteAccess } from "../editor/editor-warnings/NoWorkflowWriteAccess";
|
|
49
49
|
import { SplashScreen } from "../splash-screen/SplashScreen";
|
|
50
|
+
import { AgentsView } from "../agents-view/AgentsView";
|
|
50
51
|
import { createEditToolbar } from "../editor/menubar/ToolbarFactory";
|
|
51
52
|
|
|
52
53
|
import { Validation } from "../editor/sidebar/Validation";
|
|
@@ -81,6 +82,7 @@ import { About } from "../editor/control-center/About";
|
|
|
81
82
|
import { WebSocketMessages } from "../editor/control-center/WebSocketMessages";
|
|
82
83
|
import { LatestFeedback } from "../editor/control-center/LatestFeedback";
|
|
83
84
|
import { AllAgentsPanel } from "../editor/control-center/AllAgentsPanel";
|
|
85
|
+
import { ParheliaSetupOverview } from "../editor/control-center/parhelia-setup/Overview";
|
|
84
86
|
import { DbSetupStep } from "../editor/control-center/setup-steps/DbSetupStep";
|
|
85
87
|
import { SettingsSetupStep } from "../editor/control-center/setup-steps/SettingsSetupStep";
|
|
86
88
|
import { AiSetupStep } from "../editor/control-center/setup-steps/AiSetupStep";
|
|
@@ -137,6 +139,8 @@ import {
|
|
|
137
139
|
Video,
|
|
138
140
|
X,
|
|
139
141
|
Expand,
|
|
142
|
+
Download,
|
|
143
|
+
Bot,
|
|
140
144
|
} from "lucide-react";
|
|
141
145
|
|
|
142
146
|
import { Completions } from "../editor/sidebar/Completions";
|
|
@@ -360,6 +364,47 @@ const openImageMediaItemButton: ClientFieldButton = {
|
|
|
360
364
|
description: "Opens the media item in the content editor",
|
|
361
365
|
};
|
|
362
366
|
|
|
367
|
+
const openAttachmentButton: ClientFieldButton = {
|
|
368
|
+
label: "Open",
|
|
369
|
+
icon: (<ExternalLink strokeWidth={1} size={16} />) as React.ReactNode,
|
|
370
|
+
visible: ({ field }) => !!(field.value as any)?.src,
|
|
371
|
+
clientAction: ({ field }) => {
|
|
372
|
+
const src = (field.value as any)?.src;
|
|
373
|
+
if (src) {
|
|
374
|
+
window.open(src, "_blank", "noopener,noreferrer");
|
|
375
|
+
}
|
|
376
|
+
},
|
|
377
|
+
isGenerator: false,
|
|
378
|
+
id: "open-attachment",
|
|
379
|
+
description: "Opens the attachment in a new tab",
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
const downloadAttachmentButton: ClientFieldButton = {
|
|
383
|
+
label: "Download",
|
|
384
|
+
icon: (<Download strokeWidth={1} size={16} />) as React.ReactNode,
|
|
385
|
+
visible: ({ field }) => !!(field.value as any)?.src,
|
|
386
|
+
clientAction: ({ field }) => {
|
|
387
|
+
const src = (field.value as any)?.src;
|
|
388
|
+
const fileName = (field.value as any)?.fileName;
|
|
389
|
+
const mimeType = (field.value as any)?.mimeType;
|
|
390
|
+
|
|
391
|
+
if (src) {
|
|
392
|
+
const link = document.createElement("a");
|
|
393
|
+
link.href = src;
|
|
394
|
+
link.download = fileName || "";
|
|
395
|
+
if (mimeType) {
|
|
396
|
+
link.type = mimeType;
|
|
397
|
+
}
|
|
398
|
+
document.body.appendChild(link);
|
|
399
|
+
link.click();
|
|
400
|
+
document.body.removeChild(link);
|
|
401
|
+
}
|
|
402
|
+
},
|
|
403
|
+
isGenerator: false,
|
|
404
|
+
id: "download-attachment",
|
|
405
|
+
description: "Downloads the attachment file",
|
|
406
|
+
};
|
|
407
|
+
|
|
363
408
|
const pageEditorRightSidebar = {
|
|
364
409
|
title: "COMPONENT NAVIGATOR & HISTORY",
|
|
365
410
|
panels: [
|
|
@@ -576,6 +621,7 @@ export const getConfiguration = (): EditorConfiguration => {
|
|
|
576
621
|
},
|
|
577
622
|
attachment: {
|
|
578
623
|
editor: AttachmentEditor,
|
|
624
|
+
buttons: [openAttachmentButton, downloadAttachmentButton],
|
|
579
625
|
},
|
|
580
626
|
},
|
|
581
627
|
editorWarnings: [
|
|
@@ -628,6 +674,12 @@ export const getConfiguration = (): EditorConfiguration => {
|
|
|
628
674
|
icon: <Settings strokeWidth={1} />,
|
|
629
675
|
content: <Setup />,
|
|
630
676
|
},
|
|
677
|
+
{
|
|
678
|
+
id: "parhelia-setup",
|
|
679
|
+
title: "Parhelia Setup",
|
|
680
|
+
icon: <Settings strokeWidth={1} />,
|
|
681
|
+
content: <ParheliaSetupOverview />,
|
|
682
|
+
},
|
|
631
683
|
{
|
|
632
684
|
id: "info",
|
|
633
685
|
title: "System Info",
|
|
@@ -707,6 +759,15 @@ export const getConfiguration = (): EditorConfiguration => {
|
|
|
707
759
|
defaultCenterPanelView: <NewPage />,
|
|
708
760
|
hideViewSelector: true,
|
|
709
761
|
},
|
|
762
|
+
{
|
|
763
|
+
name: "agents-overview",
|
|
764
|
+
title: "My Agents",
|
|
765
|
+
icon: <Bot strokeWidth={1} />,
|
|
766
|
+
|
|
767
|
+
defaultCenterPanelView: <AgentsView />,
|
|
768
|
+
hideViewSelector: false,
|
|
769
|
+
menuBar: <></>,
|
|
770
|
+
},
|
|
710
771
|
{
|
|
711
772
|
name: "page-editor",
|
|
712
773
|
title: "Edit",
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
DialogFooter,
|
|
8
8
|
} from "../components/ui/dialog";
|
|
9
9
|
import { Button } from "../components/ui/button";
|
|
10
|
+
import { AlertTriangle } from "lucide-react";
|
|
10
11
|
|
|
11
12
|
export type ConfirmationDialogProps = {};
|
|
12
13
|
|
|
@@ -62,29 +63,60 @@ const ConfirmationDialog = forwardRef<
|
|
|
62
63
|
return (
|
|
63
64
|
<Dialog open={visible} onOpenChange={setVisible}>
|
|
64
65
|
<DialogContent
|
|
65
|
-
className="sm:max-w-[
|
|
66
|
+
className="gap-0 p-0 sm:max-w-[480px]"
|
|
66
67
|
data-testid="confirmation-dialog"
|
|
67
68
|
>
|
|
68
|
-
<DialogHeader>
|
|
69
|
-
<DialogTitle
|
|
69
|
+
<DialogHeader className="space-y-0 px-6 pt-3 pb-2">
|
|
70
|
+
<DialogTitle className="text-lg font-semibold">
|
|
71
|
+
{props?.header || "Confirmation"}
|
|
72
|
+
</DialogTitle>
|
|
70
73
|
</DialogHeader>
|
|
71
|
-
<div className="
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
<div className="px-6 py-4">
|
|
75
|
+
<div className="flex gap-4">
|
|
76
|
+
{props?.icon ? (
|
|
77
|
+
<div className="flex-shrink-0 pt-0.5 text-amber-500">
|
|
78
|
+
{props.icon}
|
|
79
|
+
</div>
|
|
80
|
+
) : (
|
|
81
|
+
<div className="flex-shrink-0 pt-0.5">
|
|
82
|
+
<AlertTriangle
|
|
83
|
+
className="h-5 w-5 text-amber-500"
|
|
84
|
+
strokeWidth={1.5}
|
|
85
|
+
/>
|
|
86
|
+
</div>
|
|
87
|
+
)}
|
|
88
|
+
<div className="flex-1 space-y-2">
|
|
89
|
+
<div className="text-sm leading-relaxed text-gray-700">
|
|
90
|
+
{props?.message}
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
74
94
|
</div>
|
|
75
|
-
<DialogFooter className="gap-2 border-t
|
|
95
|
+
<DialogFooter className="gap-2 border-t bg-gray-50 px-6 py-3 sm:gap-2">
|
|
76
96
|
{props?.showCancel && (
|
|
77
|
-
<Button
|
|
97
|
+
<Button
|
|
98
|
+
variant="outline"
|
|
99
|
+
onClick={handleCancel}
|
|
100
|
+
className="min-w-[90px]"
|
|
101
|
+
>
|
|
78
102
|
Cancel
|
|
79
103
|
</Button>
|
|
80
104
|
)}
|
|
81
105
|
{props?.rejectLabel && (
|
|
82
|
-
<Button
|
|
106
|
+
<Button
|
|
107
|
+
variant="outline"
|
|
108
|
+
onClick={handleReject}
|
|
109
|
+
className="min-w-[90px]"
|
|
110
|
+
>
|
|
83
111
|
{props.rejectLabel}
|
|
84
112
|
</Button>
|
|
85
113
|
)}
|
|
86
114
|
{showAccept && (
|
|
87
|
-
<Button
|
|
115
|
+
<Button
|
|
116
|
+
onClick={handleAccept}
|
|
117
|
+
variant="destructive"
|
|
118
|
+
className="min-w-[90px]"
|
|
119
|
+
>
|
|
88
120
|
{props?.acceptLabel || "Yes"}
|
|
89
121
|
</Button>
|
|
90
122
|
)}
|