mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-09 08:21:50 +00:00
refactor(desktop): make /agents subagent-only, drop sidebar + dead sections
Activity rail and History stub were both noise. Strip the split layout, sidebar, route enum, and the rail/stub helpers — the overlay is now just the spawn tree, centered in a max-w-3xl column so it stops claiming the whole screen for one section's worth of content.
This commit is contained in:
parent
b96bee7f5c
commit
5dd4fb05c6
1 changed files with 14 additions and 130 deletions
|
|
@ -5,21 +5,10 @@ import { useElapsedSeconds } from '@/components/chat/activity-timer'
|
|||
import { ActivityTimerText } from '@/components/chat/activity-timer-text'
|
||||
import { BrailleSpinner } from '@/components/ui/braille-spinner'
|
||||
import { FadeText } from '@/components/ui/fade-text'
|
||||
import {
|
||||
Activity,
|
||||
AlertCircle,
|
||||
CheckCircle2,
|
||||
Layers3,
|
||||
Loader2,
|
||||
type LucideIcon,
|
||||
RefreshCw,
|
||||
Sparkles
|
||||
} from '@/lib/icons'
|
||||
import { AlertCircle, CheckCircle2, Sparkles } from '@/lib/icons'
|
||||
import { useEnterAnimation } from '@/lib/use-enter-animation'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { $desktopActionTasks, buildRailTasks, type RailTask, type RailTaskStatus } from '@/store/activity'
|
||||
import { $previewServerRestart } from '@/store/preview'
|
||||
import { $activeSessionId, $sessions, $workingSessionIds } from '@/store/session'
|
||||
import { $activeSessionId } from '@/store/session'
|
||||
import {
|
||||
$subagentsBySession,
|
||||
buildSubagentTree,
|
||||
|
|
@ -28,39 +17,8 @@ import {
|
|||
type SubagentStreamEntry
|
||||
} from '@/store/subagents'
|
||||
|
||||
import { useRouteEnumParam } from '../hooks/use-route-enum-param'
|
||||
import { OverlayMain, OverlayNavItem, OverlaySidebar, OverlaySplitLayout } from '../overlays/overlay-split-layout'
|
||||
import { OverlayView } from '../overlays/overlay-view'
|
||||
|
||||
type AgentsSection = 'tree' | 'activity' | 'history'
|
||||
|
||||
interface SectionDef {
|
||||
description: string
|
||||
icon: LucideIcon
|
||||
id: AgentsSection
|
||||
label: string
|
||||
}
|
||||
|
||||
const SECTIONS: readonly SectionDef[] = [
|
||||
{ description: 'Live subagent spawn tree for the current turn', icon: Layers3, id: 'tree', label: 'Spawn tree' },
|
||||
{ description: 'Background work across sessions and the desktop', icon: Activity, id: 'activity', label: 'Activity' },
|
||||
{ description: 'Past spawn snapshots, replay, and diff', icon: RefreshCw, id: 'history', label: 'History' }
|
||||
]
|
||||
|
||||
const SECTION_IDS = SECTIONS.map(s => s.id) as readonly AgentsSection[]
|
||||
|
||||
const RAIL_TONE: Record<RailTaskStatus, string> = {
|
||||
error: 'text-destructive',
|
||||
running: 'text-foreground',
|
||||
success: 'text-emerald-500'
|
||||
}
|
||||
|
||||
const RAIL_ICON: Record<RailTaskStatus, LucideIcon> = {
|
||||
error: AlertCircle,
|
||||
running: Loader2,
|
||||
success: Sparkles
|
||||
}
|
||||
|
||||
// Mirrors statusGlyph() in tool-fallback.tsx so subagent rows speak the
|
||||
// same visual vocabulary as the chat tool blocks.
|
||||
function statusGlyph(status: SubagentStatus): ReactNode {
|
||||
|
|
@ -114,27 +72,13 @@ function streamGlyph(entry: SubagentStreamEntry): ReactNode {
|
|||
}
|
||||
|
||||
interface AgentsViewProps {
|
||||
initialSection?: AgentsSection
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
export function AgentsView({ initialSection = 'tree', onClose }: AgentsViewProps) {
|
||||
const [section, setSection] = useRouteEnumParam('section', SECTION_IDS, initialSection)
|
||||
|
||||
export function AgentsView({ onClose }: AgentsViewProps) {
|
||||
const activeSessionId = useStore($activeSessionId)
|
||||
const sessions = useStore($sessions)
|
||||
const workingSessionIds = useStore($workingSessionIds)
|
||||
const previewRestart = useStore($previewServerRestart)
|
||||
const desktopActionTasks = useStore($desktopActionTasks)
|
||||
const subagentsBySession = useStore($subagentsBySession)
|
||||
|
||||
const activityTasks = useMemo(
|
||||
() => buildRailTasks(workingSessionIds, sessions, previewRestart, desktopActionTasks),
|
||||
[desktopActionTasks, previewRestart, sessions, workingSessionIds]
|
||||
)
|
||||
|
||||
const active = SECTIONS.find(s => s.id === section) ?? SECTIONS[0]!
|
||||
|
||||
const activeSubagents = useMemo(
|
||||
() => (activeSessionId ? (subagentsBySession[activeSessionId] ?? []) : []),
|
||||
[activeSessionId, subagentsBySession]
|
||||
|
|
@ -143,35 +87,17 @@ export function AgentsView({ initialSection = 'tree', onClose }: AgentsViewProps
|
|||
const tree = useMemo(() => buildSubagentTree(activeSubagents), [activeSubagents])
|
||||
|
||||
return (
|
||||
<OverlayView closeLabel="Close agents" onClose={onClose}>
|
||||
<OverlaySplitLayout>
|
||||
<OverlaySidebar>
|
||||
{SECTIONS.map(s => (
|
||||
<OverlayNavItem
|
||||
active={s.id === section}
|
||||
icon={s.icon}
|
||||
key={s.id}
|
||||
label={s.label}
|
||||
onClick={() => setSection(s.id)}
|
||||
/>
|
||||
))}
|
||||
</OverlaySidebar>
|
||||
|
||||
<OverlayMain>
|
||||
<header className="mb-4">
|
||||
<h2 className="text-sm font-semibold text-foreground">{active.label}</h2>
|
||||
<p className="text-xs text-muted-foreground">{active.description}</p>
|
||||
</header>
|
||||
|
||||
{section === 'tree' ? (
|
||||
<SubagentTree tree={tree} />
|
||||
) : section === 'activity' ? (
|
||||
<ActivityList tasks={activityTasks} />
|
||||
) : (
|
||||
<SectionStub label={active.label} />
|
||||
)}
|
||||
</OverlayMain>
|
||||
</OverlaySplitLayout>
|
||||
<OverlayView
|
||||
closeLabel="Close agents"
|
||||
contentClassName="px-5 pt-5 pb-4 sm:px-6"
|
||||
onClose={onClose}
|
||||
rootClassName="mx-auto max-w-3xl"
|
||||
>
|
||||
<header className="mb-3 shrink-0">
|
||||
<h2 className="text-sm font-semibold text-foreground">Spawn tree</h2>
|
||||
<p className="text-xs text-muted-foreground/80">Live subagent activity for the current turn.</p>
|
||||
</header>
|
||||
<SubagentTree tree={tree} />
|
||||
</OverlayView>
|
||||
)
|
||||
}
|
||||
|
|
@ -449,45 +375,3 @@ function SubagentRow({ node, depth = 0, nowMs }: { node: SubagentNode; depth?: n
|
|||
)
|
||||
}
|
||||
|
||||
function ActivityList({ tasks }: { tasks: readonly RailTask[] }) {
|
||||
if (tasks.length === 0) {
|
||||
return (
|
||||
<p className="py-4 text-sm text-muted-foreground/75">
|
||||
No background activity. Long-running tools, preview restarts, and parallel sessions surface here.
|
||||
</p>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="grid min-h-0 gap-2 overflow-y-auto pr-1">
|
||||
{tasks.map(task => {
|
||||
const Icon = RAIL_ICON[task.status]
|
||||
|
||||
return (
|
||||
<div className="flex items-start gap-2.5" key={task.id}>
|
||||
<Icon
|
||||
className={cn(
|
||||
'mt-0.5 size-3.5 shrink-0',
|
||||
RAIL_TONE[task.status],
|
||||
task.status === 'running' && 'animate-spin'
|
||||
)}
|
||||
/>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="truncate text-sm font-medium text-foreground/90">{task.label}</div>
|
||||
{task.detail ? <div className="truncate text-xs text-muted-foreground/75">{task.detail}</div> : null}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function SectionStub({ label }: { label: string }) {
|
||||
return (
|
||||
<div className="grid place-items-center gap-3 py-12 text-center">
|
||||
<Sparkles className="size-6 text-muted-foreground/60" />
|
||||
<p className="text-sm font-medium text-foreground/90">{label} — coming soon</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue