feat: show installed badge and version in agent dropdown menus

This commit is contained in:
Nathan Flurry 2026-01-27 20:34:10 -08:00
parent f452b46b94
commit c498aeba28
4 changed files with 70 additions and 19 deletions

View file

@ -482,6 +482,10 @@
font-size: 12px; font-size: 12px;
cursor: pointer; cursor: pointer;
transition: all var(--transition); transition: all var(--transition);
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
} }
.sidebar-add-option:hover { .sidebar-add-option:hover {
@ -489,6 +493,42 @@
color: #fff; color: #fff;
} }
.sidebar-add-option:hover .agent-badge {
background: rgba(255, 255, 255, 0.2);
color: #fff;
}
.agent-option-name {
flex: 1;
min-width: 0;
}
.agent-option-badges {
display: flex;
align-items: center;
gap: 4px;
flex-shrink: 0;
}
.agent-badge {
padding: 2px 6px;
border-radius: 4px;
font-size: 10px;
font-weight: 500;
white-space: nowrap;
transition: all var(--transition);
}
.agent-badge.installed {
background: rgba(48, 209, 88, 0.15);
color: var(--success);
}
.agent-badge.version {
background: var(--border-2);
color: var(--muted);
}
.sidebar-add-status { .sidebar-add-status {
padding: 6px 8px; padding: 6px 8px;
font-size: 11px; font-size: 11px;

View file

@ -744,7 +744,6 @@ export default function App() {
} }
}, [modesByAgent, agentId]); }, [modesByAgent, agentId]);
const availableAgents = agents.length ? agents.map((agent) => agent.id) : defaultAgents;
const currentAgent = agents.find((agent) => agent.id === agentId); const currentAgent = agents.find((agent) => agent.id === agentId);
const activeModes = modesByAgent[agentId] ?? []; const activeModes = modesByAgent[agentId] ?? [];
const modesLoading = modesLoadingByAgent[agentId] ?? false; const modesLoading = modesLoadingByAgent[agentId] ?? false;
@ -822,7 +821,7 @@ export default function App() {
onSelectSession={selectSession} onSelectSession={selectSession}
onRefresh={fetchSessions} onRefresh={fetchSessions}
onCreateSession={createNewSession} onCreateSession={createNewSession}
availableAgents={availableAgents} agents={agents.length ? agents : defaultAgents.map((id) => ({ id, installed: false, capabilities: {} }) as AgentInfo)}
agentsLoading={agentsLoading} agentsLoading={agentsLoading}
agentsError={agentsError} agentsError={agentsError}
sessionsLoading={sessionsLoading} sessionsLoading={sessionsLoading}
@ -840,7 +839,7 @@ export default function App() {
onSendMessage={sendMessage} onSendMessage={sendMessage}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
onCreateSession={createNewSession} onCreateSession={createNewSession}
availableAgents={availableAgents} agents={agents.length ? agents : defaultAgents.map((id) => ({ id, installed: false, capabilities: {} }) as AgentInfo)}
agentsLoading={agentsLoading} agentsLoading={agentsLoading}
agentsError={agentsError} agentsError={agentsError}
messagesEndRef={messagesEndRef} messagesEndRef={messagesEndRef}

View file

@ -1,6 +1,6 @@
import { Plus, RefreshCw } from "lucide-react"; import { Plus, RefreshCw } from "lucide-react";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import type { SessionInfo } from "sandbox-agent"; import type { AgentInfo, SessionInfo } from "sandbox-agent";
const SessionSidebar = ({ const SessionSidebar = ({
sessions, sessions,
@ -8,7 +8,7 @@ const SessionSidebar = ({
onSelectSession, onSelectSession,
onRefresh, onRefresh,
onCreateSession, onCreateSession,
availableAgents, agents,
agentsLoading, agentsLoading,
agentsError, agentsError,
sessionsLoading, sessionsLoading,
@ -19,7 +19,7 @@ const SessionSidebar = ({
onSelectSession: (session: SessionInfo) => void; onSelectSession: (session: SessionInfo) => void;
onRefresh: () => void; onRefresh: () => void;
onCreateSession: (agentId: string) => void; onCreateSession: (agentId: string) => void;
availableAgents: string[]; agents: AgentInfo[];
agentsLoading: boolean; agentsLoading: boolean;
agentsError: string | null; agentsError: string | null;
sessionsLoading: boolean; sessionsLoading: boolean;
@ -68,20 +68,26 @@ const SessionSidebar = ({
<div className="sidebar-add-menu"> <div className="sidebar-add-menu">
{agentsLoading && <div className="sidebar-add-status">Loading agents...</div>} {agentsLoading && <div className="sidebar-add-status">Loading agents...</div>}
{agentsError && <div className="sidebar-add-status error">{agentsError}</div>} {agentsError && <div className="sidebar-add-status error">{agentsError}</div>}
{!agentsLoading && !agentsError && availableAgents.length === 0 && ( {!agentsLoading && !agentsError && agents.length === 0 && (
<div className="sidebar-add-status">No agents available.</div> <div className="sidebar-add-status">No agents available.</div>
)} )}
{!agentsLoading && !agentsError && {!agentsLoading && !agentsError &&
availableAgents.map((id) => ( agents.map((agent) => (
<button <button
key={id} key={agent.id}
className="sidebar-add-option" className="sidebar-add-option"
onClick={() => { onClick={() => {
onCreateSession(id); onCreateSession(agent.id);
setShowMenu(false); setShowMenu(false);
}} }}
> >
{agentLabels[id] ?? id} <span className="agent-option-name">{agentLabels[agent.id] ?? agent.id}</span>
{agent.installed && (
<span className="agent-option-badges">
<span className="agent-badge installed">Installed</span>
{agent.version && <span className="agent-badge version">v{agent.version}</span>}
</span>
)}
</button> </button>
))} ))}
</div> </div>

View file

@ -1,6 +1,6 @@
import { MessageSquare, PauseCircle, PlayCircle, Plus, Terminal } from "lucide-react"; import { MessageSquare, PauseCircle, PlayCircle, Plus, Terminal } from "lucide-react";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import type { AgentModeInfo, PermissionEventData, QuestionEventData } from "sandbox-agent"; import type { AgentInfo, AgentModeInfo, PermissionEventData, QuestionEventData } from "sandbox-agent";
import ApprovalsTab from "../debug/ApprovalsTab"; import ApprovalsTab from "../debug/ApprovalsTab";
import ChatInput from "./ChatInput"; import ChatInput from "./ChatInput";
import ChatMessages from "./ChatMessages"; import ChatMessages from "./ChatMessages";
@ -18,7 +18,7 @@ const ChatPanel = ({
onSendMessage, onSendMessage,
onKeyDown, onKeyDown,
onCreateSession, onCreateSession,
availableAgents, agents,
agentsLoading, agentsLoading,
agentsError, agentsError,
messagesEndRef, messagesEndRef,
@ -58,7 +58,7 @@ const ChatPanel = ({
onSendMessage: () => void; onSendMessage: () => void;
onKeyDown: (event: React.KeyboardEvent<HTMLTextAreaElement>) => void; onKeyDown: (event: React.KeyboardEvent<HTMLTextAreaElement>) => void;
onCreateSession: (agentId: string) => void; onCreateSession: (agentId: string) => void;
availableAgents: string[]; agents: AgentInfo[];
agentsLoading: boolean; agentsLoading: boolean;
agentsError: string | null; agentsError: string | null;
messagesEndRef: React.RefObject<HTMLDivElement>; messagesEndRef: React.RefObject<HTMLDivElement>;
@ -188,20 +188,26 @@ const ChatPanel = ({
<div className="empty-state-menu"> <div className="empty-state-menu">
{agentsLoading && <div className="sidebar-add-status">Loading agents...</div>} {agentsLoading && <div className="sidebar-add-status">Loading agents...</div>}
{agentsError && <div className="sidebar-add-status error">{agentsError}</div>} {agentsError && <div className="sidebar-add-status error">{agentsError}</div>}
{!agentsLoading && !agentsError && availableAgents.length === 0 && ( {!agentsLoading && !agentsError && agents.length === 0 && (
<div className="sidebar-add-status">No agents available.</div> <div className="sidebar-add-status">No agents available.</div>
)} )}
{!agentsLoading && !agentsError && {!agentsLoading && !agentsError &&
availableAgents.map((id) => ( agents.map((agent) => (
<button <button
key={id} key={agent.id}
className="sidebar-add-option" className="sidebar-add-option"
onClick={() => { onClick={() => {
onCreateSession(id); onCreateSession(agent.id);
setShowAgentMenu(false); setShowAgentMenu(false);
}} }}
> >
{agentLabels[id] ?? id} <span className="agent-option-name">{agentLabels[agent.id] ?? agent.id}</span>
{agent.installed && (
<span className="agent-option-badges">
<span className="agent-badge installed">Installed</span>
{agent.version && <span className="agent-badge version">v{agent.version}</span>}
</span>
)}
</button> </button>
))} ))}
</div> </div>