Skip to content

Commit 37d03e8

Browse files
committed
Move agent management into profile sidebar
1 parent 9b87d11 commit 37d03e8

14 files changed

Lines changed: 2792 additions & 1078 deletions
Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,76 @@
11
import * as React from "react";
22

3+
import { useAppNavigation } from "@/app/navigation/useAppNavigation";
4+
import { useOpenDmMutation } from "@/features/channels/hooks";
5+
import { UserProfilePanel } from "@/features/profile/ui/UserProfilePanel";
6+
import { useIdentityQuery } from "@/shared/api/hooks";
7+
import type { AgentPersona } from "@/shared/api/types";
8+
import { ProfilePanelProvider } from "@/shared/context/ProfilePanelContext";
9+
import { useThreadPanelWidth } from "@/shared/hooks/useThreadPanelWidth";
310
import { ViewLoadingFallback } from "@/shared/ui/ViewLoadingFallback";
411

512
const AgentsView = React.lazy(async () => {
613
const module = await import("@/features/agents/ui/AgentsView");
714
return { default: module.AgentsView };
815
});
916

17+
type ProfilePanelTarget =
18+
| { kind: "pubkey"; pubkey: string }
19+
| { kind: "persona"; persona: AgentPersona };
20+
1021
export function AgentsScreen() {
22+
const identityQuery = useIdentityQuery();
23+
const [profilePanelTarget, setProfilePanelTarget] =
24+
React.useState<ProfilePanelTarget | null>(null);
25+
const threadPanelWidth = useThreadPanelWidth();
26+
const openDmMutation = useOpenDmMutation();
27+
const { goChannel } = useAppNavigation();
28+
29+
const handleOpenDm = React.useCallback(
30+
async (pubkeys: string[]) => {
31+
const dm = await openDmMutation.mutateAsync({ pubkeys });
32+
await goChannel(dm.id);
33+
},
34+
[goChannel, openDmMutation],
35+
);
36+
1137
return (
12-
<div className="relative flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden">
13-
<React.Suspense fallback={<ViewLoadingFallback kind="agents" />}>
14-
<AgentsView />
15-
</React.Suspense>
16-
</div>
38+
<ProfilePanelProvider
39+
onOpenPersonaProfilePanel={(persona) =>
40+
setProfilePanelTarget({ kind: "persona", persona })
41+
}
42+
onOpenProfilePanel={(pubkey) =>
43+
setProfilePanelTarget({ kind: "pubkey", pubkey })
44+
}
45+
>
46+
<div className="relative flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden">
47+
<div className="flex min-h-0 min-w-0 flex-1 flex-row overflow-hidden">
48+
<React.Suspense fallback={<ViewLoadingFallback kind="agents" />}>
49+
<AgentsView />
50+
</React.Suspense>
51+
{profilePanelTarget ? (
52+
<UserProfilePanel
53+
canResetWidth={threadPanelWidth.canReset}
54+
currentPubkey={identityQuery.data?.pubkey}
55+
onClose={() => setProfilePanelTarget(null)}
56+
onOpenDm={handleOpenDm}
57+
onResetWidth={threadPanelWidth.onResetWidth}
58+
onResizeStart={threadPanelWidth.onResizeStart}
59+
persona={
60+
profilePanelTarget.kind === "persona"
61+
? profilePanelTarget.persona
62+
: undefined
63+
}
64+
pubkey={
65+
profilePanelTarget.kind === "pubkey"
66+
? profilePanelTarget.pubkey
67+
: undefined
68+
}
69+
widthPx={threadPanelWidth.widthPx}
70+
/>
71+
) : null}
72+
</div>
73+
</div>
74+
</ProfilePanelProvider>
1775
);
1876
}

desktop/src/features/agents/ui/AgentsView.tsx

Lines changed: 11 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ import { UnifiedAgentsSection } from "./UnifiedAgentsSection";
1919
import { useManagedAgentActions } from "./useManagedAgentActions";
2020
import { usePersonaActions } from "./usePersonaActions";
2121
import { useTeamActions } from "./useTeamActions";
22+
import { useProfilePanel } from "@/shared/context/ProfilePanelContext";
2223

2324
export function AgentsView() {
25+
const { openPersonaProfilePanel, openProfilePanel } = useProfilePanel();
2426
const agents = useManagedAgentActions();
2527
const personas = usePersonaActions();
2628
const teamActions = useTeamActions(
@@ -34,14 +36,6 @@ export function AgentsView() {
3436
},
3537
);
3638

37-
const isActionPending =
38-
agents.isPending ||
39-
personas.isPending ||
40-
teamActions.exportTeamJsonMutation.isPending ||
41-
teamActions.createTeamMutation.isPending ||
42-
teamActions.updateTeamMutation.isPending ||
43-
teamActions.deleteTeamMutation.isPending;
44-
4539
return (
4640
<>
4741
<div
@@ -56,56 +50,21 @@ export function AgentsView() {
5650
actionErrorMessage={agents.actionErrorMessage}
5751
actionNoticeMessage={agents.actionNoticeMessage}
5852
agents={agents.managedAgents}
59-
channelIdToName={agents.channelIdToName}
60-
channelsByPubkey={agents.channelsByPubkey}
6153
agentsError={
6254
agents.managedAgentsQuery.error instanceof Error
6355
? agents.managedAgentsQuery.error
6456
: null
6557
}
66-
isActionPending={isActionPending}
6758
isAgentsLoading={agents.managedAgentsQuery.isLoading}
68-
logContent={agents.managedAgentLogQuery.data?.content ?? null}
69-
logError={
70-
agents.managedAgentLogQuery.error instanceof Error
71-
? agents.managedAgentLogQuery.error
72-
: null
73-
}
74-
logLoading={agents.managedAgentLogQuery.isLoading}
75-
personaLabelsById={personas.personaLabelsById}
76-
presenceLoaded={agents.managedPresenceQuery.isSuccess}
77-
presenceLookup={agents.managedPresenceQuery.data ?? {}}
78-
onAddToChannel={(agent) => {
79-
agents.setActionNoticeMessage(null);
80-
agents.setActionErrorMessage(null);
81-
agents.setAgentToAddToChannel(agent);
82-
}}
83-
onBulkRemoveStopped={() => {
84-
void agents.handleBulkRemoveStopped();
85-
}}
86-
onBulkStopRunning={() => {
87-
void agents.handleBulkStopRunning();
88-
}}
8959
onCreateAgent={() => {
9060
agents.setIsCreateOpen(true);
9161
}}
92-
onDeleteAgent={(pubkey) => {
93-
void agents.handleDelete(pubkey);
94-
}}
95-
onSelectLogAgent={agents.setLogAgentPubkey}
96-
onStartAgent={(pubkey) => {
97-
void agents.handleStart(pubkey);
98-
}}
99-
onStopAgent={(pubkey) => {
100-
void agents.handleStop(pubkey);
62+
onOpenAgentProfile={(pubkey) => {
63+
openProfilePanel?.(pubkey);
10164
}}
102-
onToggleStartOnAppLaunch={(pubkey, startOnAppLaunch) => {
103-
void agents.handleToggleStartOnAppLaunch(
104-
pubkey,
105-
startOnAppLaunch,
106-
);
65+
onOpenPersonaProfile={(persona) => {
66+
openPersonaProfilePanel?.(persona);
10767
}}
108-
selectedLogAgentPubkey={agents.logAgentPubkey}
10968
// Persona props
11069
canChooseCatalog={personas.catalogPersonas.length > 0}
11170
personas={personas.libraryPersonas}
@@ -128,13 +87,6 @@ export function AgentsView() {
12887
isPersonasPending={personas.isPending}
12988
onCreatePersona={personas.openCreate}
13089
onChooseCatalog={personas.openCatalog}
131-
onDuplicatePersona={personas.openDuplicate}
132-
onEditPersona={personas.openEdit}
133-
onExportPersona={personas.handleExport}
134-
onDeactivatePersona={(persona) => {
135-
void personas.handleSetActive(persona, false, "library");
136-
}}
137-
onDeletePersona={personas.openDelete}
13890
onImportPersonaFile={(fileBytes, fileName) => {
13991
void personas.handleImportFile(fileBytes, fileName);
14092
}}
@@ -157,10 +109,12 @@ export function AgentsView() {
157109
onDuplicate={teamActions.openDuplicateDialog}
158110
onEdit={teamActions.openEditDialog}
159111
onExport={teamActions.handleExportTeam}
160-
onImportFile={teamActions.handleImportFile}
161-
onInstallFromDirectory={teamActions.handleInstallFromDirectory}
162112
onSync={teamActions.handleSyncTeam}
163113
onRevealInFinder={teamActions.handleRevealInFinder}
114+
onImportFile={(fileBytes, fileName) => {
115+
void teamActions.handleImportFile(fileBytes, fileName);
116+
}}
117+
onInstallFromDirectory={teamActions.handleInstallFromDirectory}
164118
onAddToChannel={teamActions.setTeamToAddToChannel}
165119
personas={personas.libraryPersonas}
166120
teams={teamActions.teams}
@@ -219,10 +173,7 @@ export function AgentsView() {
219173
isImportPending={
220174
personas.personaImportActions.isApplyingPersonaImportUpdate
221175
}
222-
isPending={
223-
personas.createPersonaMutation.isPending ||
224-
personas.updatePersonaMutation.isPending
225-
}
176+
isPending={personas.isPending}
226177
runtimes={personas.acpRuntimesQuery.data ?? []}
227178
runtimesLoading={personas.acpRuntimesQuery.isLoading}
228179
onImportUpdateFile={

0 commit comments

Comments
 (0)