Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 11 additions & 65 deletions apps/desktop/src/app/command-center/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ import {
BookmarkFilled,
Download,
MessageCircle,
Trash2,
Wrench
Trash2
} from '@/lib/icons'
import { exportSession } from '@/lib/session-export'
import { cn } from '@/lib/utils'
Expand All @@ -31,14 +30,9 @@ import { useRouteEnumParam } from '../hooks/use-route-enum-param'
import { OverlayMain, OverlayNavItem, OverlaySidebar, OverlaySplitLayout } from '../overlays/overlay-split-layout'
import { OverlayView } from '../overlays/overlay-view'

import { MaintenancePanel } from './maintenance'
export type CommandCenterSection = 'sessions' | 'system' | 'usage'

export type CommandCenterSection = 'maintenance' | 'sessions' | 'system' | 'usage'

const SECTIONS = ['sessions', 'system', 'usage', 'maintenance'] as const satisfies readonly CommandCenterSection[]

const LOG_FILES = ['agent', 'errors', 'gateway', 'desktop'] as const
const LOG_LEVELS = ['ALL', 'INFO', 'WARNING', 'ERROR'] as const
const SECTIONS = ['sessions', 'system', 'usage'] as const satisfies readonly CommandCenterSection[]

const USAGE_PERIODS = [7, 30, 90] as const
type UsagePeriod = (typeof USAGE_PERIODS)[number]
Expand Down Expand Up @@ -131,9 +125,6 @@ export function CommandCenterView({ initialSection, onClose, onDeleteSession, on
const [query, setQuery] = useState('')
const [status, setStatus] = useState<StatusResponse | null>(null)
const [logs, setLogs] = useState<string[]>([])
const [logFile, setLogFile] = useState<(typeof LOG_FILES)[number]>('agent')
const [logLevel, setLogLevel] = useState<(typeof LOG_LEVELS)[number]>('ALL')
const [logQuery, setLogQuery] = useState('')
const [systemLoading, setSystemLoading] = useState(false)
const [systemError, setSystemError] = useState('')
const [systemAction, setSystemAction] = useState<ActionStatusResponse | null>(null)
Expand Down Expand Up @@ -174,9 +165,8 @@ export function CommandCenterView({ initialSection, onClose, onDeleteSession, on
const [nextStatus, nextLogs] = await Promise.all([
getStatus(),
getLogs({
file: logFile,
level: logLevel,
lines: 200
file: 'agent',
lines: 120
})
])

Expand All @@ -187,7 +177,7 @@ export function CommandCenterView({ initialSection, onClose, onDeleteSession, on
} finally {
setSystemLoading(false)
}
}, [logFile, logLevel])
}, [])

const refreshUsage = useCallback(async (days: UsagePeriod) => {
const requestId = usageRequestRef.current + 1
Expand All @@ -213,12 +203,10 @@ export function CommandCenterView({ initialSection, onClose, onDeleteSession, on
}, [])

useEffect(() => {
// Refetch when the panel opens and whenever the log file/level filters
// change (refreshSystem's identity tracks them).
if (section === 'system') {
if (section === 'system' && !status && !systemLoading) {
void refreshSystem()
}
}, [refreshSystem, section])
}, [refreshSystem, section, status, systemLoading])

useEffect(() => {
if (section === 'usage') {
Expand All @@ -236,17 +224,6 @@ export function CommandCenterView({ initialSection, onClose, onDeleteSession, on

const sessionListHasResults = filteredSessions.length > 0

// Client-side substring filter over the fetched tail (matches `hermes logs --search`).
const visibleLogs = useMemo(() => {
const needle = logQuery.trim().toLowerCase()

if (!needle) {
return logs
}

return logs.filter(line => line.toLowerCase().includes(needle))
}, [logQuery, logs])

const runSystemAction = useCallback(
async (kind: 'restart' | 'update') => {
setSystemError('')
Expand Down Expand Up @@ -295,15 +272,7 @@ export function CommandCenterView({ initialSection, onClose, onDeleteSession, on
{SECTIONS.map(value => (
<OverlayNavItem
active={section === value}
icon={
value === 'sessions'
? MessageCircle
: value === 'system'
? Activity
: value === 'maintenance'
? Wrench
: BarChart3
}
icon={value === 'sessions' ? MessageCircle : value === 'system' ? Activity : BarChart3}
key={value}
label={cc.sections[value]}
onClick={() => setSection(value)}
Expand Down Expand Up @@ -399,8 +368,6 @@ export function CommandCenterView({ initialSection, onClose, onDeleteSession, on
period={usagePeriod}
usage={usage}
/>
) : section === 'maintenance' ? (
<MaintenancePanel />
) : (
<div className="grid min-h-0 flex-1 grid-rows-[auto_minmax(0,1fr)] gap-4">
<div>
Expand Down Expand Up @@ -449,31 +416,10 @@ export function CommandCenterView({ initialSection, onClose, onDeleteSession, on
</div>

<div className="flex min-h-0 flex-col pt-2">
<div className="mb-2 flex flex-wrap items-center justify-between gap-2">
<div className="mb-2 flex items-center justify-between">
<span className="text-[0.625rem] font-medium uppercase tracking-[0.08em] text-(--ui-text-tertiary)">
{cc.recentLogs}
</span>
<div className="flex items-center gap-2">
<SegmentedControl
onChange={id => setLogFile(id)}
options={LOG_FILES.map(value => ({ id: value, label: value }))}
value={logFile}
/>
<SegmentedControl
onChange={id => setLogLevel(id)}
options={LOG_LEVELS.map(value => ({
id: value,
label: value === 'ALL' ? 'all' : value.toLowerCase()
}))}
value={logLevel}
/>
<SearchField
containerClassName="w-44"
onChange={next => setLogQuery(next)}
placeholder={cc.logSearchPlaceholder}
value={logQuery}
/>
</div>
{systemError && (
<span className="inline-flex items-center gap-1 text-[length:var(--conversation-caption-font-size)] text-destructive">
<AlertCircle className="size-3.5" />
Expand All @@ -485,7 +431,7 @@ export function CommandCenterView({ initialSection, onClose, onDeleteSession, on
className="min-h-0 flex-1 overflow-auto whitespace-pre-wrap wrap-break-word rounded-lg border border-(--ui-stroke-tertiary) bg-(--ui-bg-quinary) p-3 font-mono text-[0.65rem] leading-relaxed text-(--ui-text-tertiary)"
data-selectable-text="true"
>
{visibleLogs.length ? visibleLogs.join('\n') : cc.noLogs}
{logs.length ? logs.join('\n') : cc.noLogs}
</pre>
</div>
</div>
Expand Down
Loading
Loading