From fe5e86d514dd09ca1b93062aadb8e1103716cc2a Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Thu, 25 Dec 2025 11:43:14 +0700 Subject: [PATCH] Add agent list sidebar on large screens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Show AgentList as a left sidebar (280px) when viewing an agent on screens >= 768px wide. The current agent is highlighted with a primary-colored border and accent background. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../src/app/agent/[serverId]/[agentId].tsx | 108 ++++++++++++------ packages/app/src/components/agent-list.tsx | 13 ++- 2 files changed, 84 insertions(+), 37 deletions(-) diff --git a/packages/app/src/app/agent/[serverId]/[agentId].tsx b/packages/app/src/app/agent/[serverId]/[agentId].tsx index 01c638bb9..c300f3068 100644 --- a/packages/app/src/app/agent/[serverId]/[agentId].tsx +++ b/packages/app/src/app/agent/[serverId]/[agentId].tsx @@ -19,6 +19,8 @@ import { MoreVertical, GitBranch, Folder, RotateCcw, PlusCircle, Download, Users import { BackHeader } from "@/components/headers/back-header"; import { AgentStreamView } from "@/components/agent-stream-view"; import { AgentInputArea } from "@/components/agent-input-area"; +import { AgentList } from "@/components/agent-list"; +import { useAggregatedAgents } from "@/hooks/use-aggregated-agents"; import { CreateAgentModal, ImportAgentModal, type CreateAgentInitialValues } from "@/components/create-agent-modal"; import { useDaemonConnections } from "@/contexts/daemon-connections-context"; import type { ConnectionStatus } from "@/contexts/daemon-connections-context"; @@ -133,11 +135,17 @@ type AgentScreenContentProps = { onBack: () => void; }; +const SIDEBAR_WIDTH = 280; +const LARGE_SCREEN_BREAKPOINT = 768; + function AgentScreenContent({ serverId, agentId, onBack }: AgentScreenContentProps) { const { theme } = useUnistyles(); const insets = useSafeAreaInsets(); const router = useRouter(); const { width: windowWidth, height: windowHeight } = useWindowDimensions(); + const isLargeScreen = windowWidth >= LARGE_SCREEN_BREAKPOINT; + + const { agents: aggregatedAgents, isRevalidating, refreshAll } = useAggregatedAgents(); const [menuVisible, setMenuVisible] = useState(false); const [menuPosition, setMenuPosition] = useState({ top: 0, left: 0 }); const [menuContentHeight, setMenuContentHeight] = useState(0); @@ -531,43 +539,60 @@ function AgentScreenContent({ serverId, agentId, onBack }: AgentScreenContentPro return ( <> - {/* Header */} - - - - - - } - /> - - {/* Content Area with Keyboard Animation */} - - - {isInitializing ? ( - - - Loading agent... - - ) : ( - + {/* Sidebar - only on large screens */} + {isLargeScreen && ( + + - )} - - + + )} - {/* Agent Input Area */} - {!isInitializing && agent && resolvedAgentId && ( - - )} + {/* Main agent panel */} + + {/* Header */} + + + + + + } + /> + + {/* Content Area with Keyboard Animation */} + + + {isInitializing ? ( + + + Loading agent... + + ) : ( + + )} + + + + {/* Agent Input Area */} + {!isInitializing && agent && resolvedAgentId && ( + + )} + + {/* Dropdown Menu */} ({ flex: 1, backgroundColor: theme.colors.background, }, + mainLayout: { + flex: 1, + }, + mainLayoutRow: { + flexDirection: "row", + }, + sidebar: { + borderRightWidth: 1, + borderRightColor: theme.colors.border, + }, + agentPanel: { + flex: 1, + }, contentContainer: { flex: 1, overflow: "hidden", diff --git a/packages/app/src/components/agent-list.tsx b/packages/app/src/components/agent-list.tsx index 8cc213488..79d91db40 100644 --- a/packages/app/src/components/agent-list.tsx +++ b/packages/app/src/components/agent-list.tsx @@ -12,9 +12,10 @@ interface AgentListProps { agents: AggregatedAgent[]; isRefreshing?: boolean; onRefresh?: () => void; + selectedAgentId?: string; } -export function AgentList({ agents, isRefreshing = false, onRefresh }: AgentListProps) { +export function AgentList({ agents, isRefreshing = false, onRefresh, selectedAgentId }: AgentListProps) { const { theme } = useUnistyles(); const [actionAgent, setActionAgent] = useState(null); @@ -87,11 +88,13 @@ export function AgentList({ agents, isRefreshing = false, onRefresh }: AgentList const timeAgo = formatTimeAgo(agent.lastActivityAt); const providerLabel = getAgentProviderDefinition(agent.provider).label; const isRunning = agent.status === "running"; + const isSelected = selectedAgentId === agent.id; return ( [ styles.agentItem, + isSelected && styles.agentItemSelected, pressed && styles.agentItemPressed, ]} onPress={() => handleAgentPress(agent.serverId, agent.id)} @@ -157,7 +160,7 @@ export function AgentList({ agents, isRefreshing = false, onRefresh }: AgentList ); }, - [handleAgentLongPress, handleAgentPress, theme.colors.muted, theme.colors.mutedForeground, theme.colors.primary, theme.colors.primaryForeground] + [handleAgentLongPress, handleAgentPress, selectedAgentId, theme.colors.muted, theme.colors.mutedForeground, theme.colors.primary, theme.colors.primaryForeground] ); const keyExtractor = useCallback( @@ -247,6 +250,12 @@ const styles = StyleSheet.create((theme) => ({ borderRadius: theme.borderRadius.lg, marginBottom: theme.spacing[2], backgroundColor: theme.colors.muted, + borderWidth: 1, + borderColor: "transparent", + }, + agentItemSelected: { + backgroundColor: theme.colors.accent, + borderColor: theme.colors.primary, }, agentItemPressed: { opacity: 0.7,