From f033b7dbfbe81dc5b0dbafe3c7eef7d25b6718ae Mon Sep 17 00:00:00 2001 From: brooklyn! Date: Sat, 6 Jun 2026 16:32:47 -0500 Subject: [PATCH] feat(desktop): unified overlay design system, BrandMark & onboarding redesign (#40708) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(desktop): unify dialog/overlay buttons on shared Button component Replace raw + ) +} + +function EmptyPanel({ action, description, title }: { action?: ReactNode; description: string; title?: string }) { + return ( +
+
+ {title && ( +
{title}
+ )} +
+ {description} +
+ {action &&
{action}
} +
+
+ ) +} + +export function CommandCenterView({ initialSection, onClose, onDeleteSession, onOpenSession }: CommandCenterViewProps) { const { t } = useI18n() const cc = t.commandCenter const sessions = useStore($sessions) @@ -161,8 +122,6 @@ export function CommandCenterView({ const [section, setSection] = useRouteEnumParam('section', SECTIONS, initialSection ?? 'sessions') const [query, setQuery] = useState('') - const [searchLoading, setSearchLoading] = useState(false) - const [searchGroups, setSearchGroups] = useState([]) const [status, setStatus] = useState(null) const [logs, setLogs] = useState([]) const [systemLoading, setSystemLoading] = useState(false) @@ -172,78 +131,30 @@ export function CommandCenterView({ const [usage, setUsage] = useState(null) const [usageLoading, setUsageLoading] = useState(false) const [usageError, setUsageError] = useState('') - const searchRequestRef = useRef(0) const usageRequestRef = useRef(0) const debouncedQuery = useDebouncedValue(query.trim(), 180) - const sessionsById = useMemo(() => new Map(sessions.map(session => [session.id, session])), [sessions]) + const filteredSessions = useMemo(() => { + const sorted = [...sessions].sort((a, b) => { + const left = a.last_active || a.started_at || 0 + const right = b.last_active || b.started_at || 0 - const filteredSessions = useMemo( - () => - [...sessions].sort((a, b) => { - const left = a.last_active || a.started_at || 0 - const right = b.last_active || b.started_at || 0 + return right - left + }) - return right - left - }), - [sessions] - ) + const needle = debouncedQuery.toLowerCase() - const searchProviders = useMemo( - () => [ - { - id: 'navigation', - label: cc.providerNavigate, - search: async searchQuery => { - const routeHits: RouteSearchHit[] = NAV_ROUTES.filter(entry => - matchesSearchQuery(searchQuery, cc.nav[entry.key].title, cc.nav[entry.key].detail, entry.route) - ).map(entry => ({ - detail: cc.nav[entry.key].detail, - kind: 'route', - route: entry.route, - title: cc.nav[entry.key].title - })) + if (!needle) { + return sorted + } - const sectionHits: SectionSearchHit[] = SECTIONS.filter(section => - matchesSearchQuery( - searchQuery, - cc.sectionEntries[section].title, - cc.sectionEntries[section].detail, - cc.sections[section] - ) - ).map(section => ({ - detail: cc.sectionEntries[section].detail, - kind: 'section', - section, - title: cc.sectionEntries[section].title - })) + return sorted.filter(session => { + const haystack = `${sessionTitle(session)} ${session.id}`.toLowerCase() - return [...routeHits, ...sectionHits] - } - }, - { - id: 'sessions', - label: cc.providerSessions, - search: async searchQuery => { - const response = await searchSessions(searchQuery) - - return response.results.map(result => { - const { detail, title } = splitSessionSearchResult(result, sessionsById) - - return { - detail, - kind: 'session', - sessionId: result.session_id, - snippet: result.snippet || '', - title - } satisfies SessionSearchHit - }) - } - } - ], - [cc, sessionsById] - ) + return haystack.includes(needle) + }) + }, [debouncedQuery, sessions]) const refreshSystem = useCallback(async () => { setSystemLoading(true) @@ -290,42 +201,6 @@ export function CommandCenterView({ } }, []) - useEffect(() => { - if (!debouncedQuery) { - setSearchGroups([]) - setSearchLoading(false) - - return - } - - const requestId = searchRequestRef.current + 1 - searchRequestRef.current = requestId - setSearchLoading(true) - - void Promise.all( - searchProviders.map(async provider => ({ - id: provider.id, - label: provider.label, - results: await provider.search(debouncedQuery) - })) - ) - .then(groups => { - if (searchRequestRef.current === requestId) { - setSearchGroups(groups.filter(group => group.results.length > 0)) - } - }) - .catch(() => { - if (searchRequestRef.current === requestId) { - setSearchGroups([]) - } - }) - .finally(() => { - if (searchRequestRef.current === requestId) { - setSearchLoading(false) - } - }) - }, [debouncedQuery, searchProviders]) - useEffect(() => { if (section === 'system' && !status && !systemLoading) { void refreshSystem() @@ -346,8 +221,6 @@ export function CommandCenterView({ } }) - const showGlobalSearchResults = debouncedQuery.length > 0 - const hasGlobalSearchResults = searchGroups.length > 0 const sessionListHasResults = filteredSessions.length > 0 const runSystemAction = useCallback( @@ -391,40 +264,8 @@ export function CommandCenterView({ [cc, refreshSystem] ) - const handleSearchSelect = useCallback( - (result: CommandCenterSearchResult) => { - if (result.kind === 'route') { - onNavigateRoute(result.route) - - return - } - - if (result.kind === 'section') { - setSection(result.section) - setQuery('') - - return - } - - onOpenSession(result.sessionId) - }, - [onNavigateRoute, onOpenSession, setSection] - ) - return ( - setQuery(next)} - placeholder={cc.searchPlaceholder} - value={query} - /> - } - onClose={onClose} - > + {SECTIONS.map(value => ( @@ -439,178 +280,100 @@ export function CommandCenterView({ -
-
-

{cc.sections[section]}

-

{cc.sectionDescriptions[section]}

+
+
+

+ {cc.sections[section]} +

+

+ {cc.sectionDescriptions[section]} +

- {section === 'system' && ( - void refreshSystem()}> - - {systemLoading ? cc.refreshing : cc.refresh} - - )} - {section === 'usage' && ( - void refreshUsage(usagePeriod)}> - - {usageLoading ? cc.refreshing : cc.refresh} - - )} -
- - {showGlobalSearchResults ? ( -
- {!hasGlobalSearchResults ? ( - {cc.noResults} - ) : ( -
- {searchGroups.map(group => ( -
-

- {group.label} -

- {group.results.map(result => { - if (result.kind === 'session') { - const pinned = pinnedSessionIds.includes(result.sessionId) - - return ( - - -
- { - event.preventDefault() - event.stopPropagation() - pinned ? unpinSession(result.sessionId) : pinSession(result.sessionId) - }} - title={pinned ? cc.unpinSession : cc.pinSession} - > - {pinned ? ( - - ) : ( - - )} - - { - event.preventDefault() - event.stopPropagation() - void exportSession(result.sessionId, { title: result.title }) - }} - title={cc.exportSession} - > - - - { - event.preventDefault() - event.stopPropagation() - void onDeleteSession(result.sessionId) - }} - title={cc.deleteSession} - > - - -
-
- ) - } - - return ( - - ) - })} -
- ))} -
+
+ {section === 'sessions' && ( + setQuery(next)} + placeholder={cc.searchPlaceholder} + value={query} + /> + )} + {section === 'usage' && ( + setUsagePeriod(Number(id) as UsagePeriod)} + options={USAGE_PERIODS.map(value => ({ id: String(value), label: cc.days(value) }))} + value={String(usagePeriod)} + /> )}
- ) : section === 'sessions' ? ( +
+ + {section === 'sessions' ? (
{!sessionListHasResults ? ( - {cc.noSessions} + ) : ( -
+
    {filteredSessions.map(session => { - const pinned = pinnedSessionIds.includes(session.id) + const pinId = sessionPinId(session) + const pinned = pinnedSessionIds.includes(pinId) return ( - +
  • - (pinned ? unpinSession(session.id) : pinSession(session.id))} - title={pinned ? cc.unpinSession : cc.pinSession} - > - {pinned ? : } - - void exportSession(session.id, { session, title: sessionTitle(session) })} - title={cc.exportSession} - > - - - void onDeleteSession(session.id)} - title={cc.deleteSession} - > - - - +
    + (pinned ? unpinSession(pinId) : pinSession(pinId))} + title={pinned ? cc.unpinSession : cc.pinSession} + > + {pinned ? ( + + ) : ( + + )} + + void exportSession(session.id, { session, title: sessionTitle(session) })} + title={cc.exportSession} + > + + + void onDeleteSession(session.id)} + title={cc.deleteSession} + > + + +
    +
  • ) })} -
+ )}
) : section === 'usage' ? ( void refreshUsage(usagePeriod)} period={usagePeriod} usage={usage} /> ) : ( -
- +
+
{status ? (
@@ -622,49 +385,51 @@ export function CommandCenterView({ status.gateway_running ? 'bg-emerald-500' : 'bg-amber-500' )} /> - + {status.gateway_running ? cc.gatewayRunning : cc.gatewayStopped}
-
+
{cc.hermesActiveSessions(status.version, status.active_sessions)}
- void runSystemAction('restart')}> + +
{systemAction && ( -
+
{systemAction.name} ·{' '} {systemAction.running ? cc.actionRunning : systemAction.exit_code === 0 ? cc.actionDone : cc.actionFailed}
)}
) : ( -
{cc.loadingStatus}
+ )} - +
- +
- {cc.recentLogs} + + {cc.recentLogs} + {systemError && ( - + {systemError} )}
-
+                
                   {logs.length ? logs.join('\n') : cc.noLogs}
                 
- +
)} @@ -708,13 +473,12 @@ function formatInteger(value: null | number | undefined): string { interface UsagePanelProps { error: string loading: boolean - onPeriodChange: (period: UsagePeriod) => void onRefresh: () => void period: UsagePeriod usage: AnalyticsResponse | null } -function UsagePanel({ error, loading, onPeriodChange, onRefresh, period, usage }: UsagePanelProps) { +function UsagePanel({ error, loading, onRefresh, period, usage }: UsagePanelProps) { const { t } = useI18n() const cc = t.commandCenter const daily = useMemo(() => usage?.daily ?? [], [usage]) @@ -730,171 +494,161 @@ function UsagePanel({ error, loading, onPeriodChange, onRefresh, period, usage } return daily.reduce((acc, entry) => Math.max(acc, (entry.input_tokens || 0) + (entry.output_tokens || 0)), 1) }, [daily]) - return ( -
- -
- {USAGE_PERIODS.map(value => ( - - ))} -
- {error && ( - - - {error} - - )} -
- - - {totals ? ( -
- - - - 0 ? cc.actualCost(formatCost(totals.total_actual_cost)) : undefined} - label={cc.statCost} - value={formatCost(totals.total_estimated_cost)} - /> -
- ) : loading ? ( -
{cc.loadingUsage}
+ if (!totals) { + return ( +
+ {loading ? ( + ) : ( -
- {cc.noUsage(period)}{' '} - -
+ + {cc.retry} + + } + description={cc.noUsage(period)} + /> )} - +
+ ) + } -
- -
- {cc.dailyTokens} - - - {cc.input} - - - {cc.output} - + return ( +
+ {error && ( + + + {error} + + )} + +
+ + + + 0 ? cc.actualCost(formatCost(totals.total_actual_cost)) : undefined} + label={cc.statCost} + value={formatCost(totals.total_estimated_cost)} + /> +
+ +
+
+ + {cc.dailyTokens} + + + + {cc.input} + + {cc.output} + + +
+ {daily.length === 0 ? ( +
+ {cc.noDailyActivity}
- {daily.length === 0 ? ( -
{cc.noDailyActivity}
- ) : ( - <> -
- {daily.map(entry => { - const total = (entry.input_tokens || 0) + (entry.output_tokens || 0) - const inputH = Math.round(((entry.input_tokens || 0) / maxTokens) * 96) - const outputH = Math.round(((entry.output_tokens || 0) / maxTokens) * 96) + ) : ( + <> +
+ {daily.map(entry => { + const inputH = Math.round(((entry.input_tokens || 0) / maxTokens) * 96) + const outputH = Math.round(((entry.output_tokens || 0) / maxTokens) * 96) - return ( + return ( +
-
0 ? 1 : 0) }} - /> -
0 ? 1 : 0) }} - /> -
- ) - })} -
-
- {daily[0]?.day} - {daily[daily.length - 1]?.day} -
- - )} - + className="w-full rounded-t-[1px] bg-[color:var(--dt-primary)]/50" + style={{ height: Math.max(inputH, entry.input_tokens > 0 ? 1 : 0) }} + /> +
0 ? 1 : 0) }} + /> +
+ ) + })} +
+
+ {daily[0]?.day} + {daily[daily.length - 1]?.day} +
+ + )} +
- -
-
-
- {cc.topModels} -
- {byModel.length === 0 ? ( -
{cc.noModelUsage}
- ) : ( -
    - {byModel.slice(0, 6).map(entry => ( -
  • - {entry.model} - - {formatTokens((entry.input_tokens || 0) + (entry.output_tokens || 0))} ·{' '} - {formatCost(entry.estimated_cost)} - -
  • - ))} -
- )} -
- -
-
- {cc.topSkills} -
- {topSkills.length === 0 ? ( -
{cc.noSkillActivity}
- ) : ( -
    - {topSkills.slice(0, 6).map(entry => ( -
  • - {entry.skill} - - {cc.actions(entry.total_count.toLocaleString())} - -
  • - ))} -
- )} -
-
-
+
+ ({ + key: entry.model, + label: entry.model, + value: `${formatTokens((entry.input_tokens || 0) + (entry.output_tokens || 0))} · ${formatCost(entry.estimated_cost)}` + }))} + title={cc.topModels} + /> + ({ + key: entry.skill, + label: entry.skill, + value: cc.actions(entry.total_count.toLocaleString()) + }))} + title={cc.topSkills} + />
) } +function UsageList({ + emptyLabel, + rows, + title +}: { + emptyLabel: string + rows: Array<{ key: string; label: string; value: string }> + title: string +}) { + return ( +
+
+ {title} +
+ {rows.length === 0 ? ( +
+ {emptyLabel} +
+ ) : ( +
    + {rows.map(row => ( +
  • + {row.label} + {row.value} +
  • + ))} +
+ )} +
+ ) +} + function UsageStat({ hint, label, value }: { hint?: string; label: string; value: string }) { return (
-
{label}
-
{value}
- {hint &&
{hint}
} +
{label}
+
{value}
+ {hint &&
{hint}
}
) } diff --git a/apps/desktop/src/app/cron/index.tsx b/apps/desktop/src/app/cron/index.tsx index dcf852e6aa5..075dd3380b7 100644 --- a/apps/desktop/src/app/cron/index.tsx +++ b/apps/desktop/src/app/cron/index.tsx @@ -434,7 +434,7 @@ export function CronView({ onClose, setStatusbarItemGroup: _setStatusbarItemGrou {c.newCron}
-
+
{visibleJobs.map(job => ( 0 && (
+ {profiles.map(profile => ( + setSelectedName(profile.name)} + profile={profile} + /> + ))} + {profiles.length === 0 && ( +

{p.noProfiles}

+ )} + -
- {!profiles ? ( - - ) : ( -
- +
+ )} + + + )} -
- {selected ? ( - setPendingDelete(selected)} - onRename={newName => handleRename(selected.name, newName)} - profile={selected} - /> - ) : ( -
-
- -

{p.selectPrompt}

-
-
- )} -
-
- )} -
- - setCreateOpen(false)} onCreate={async (name, cloneFromDefault) => handleCreate(name, cloneFromDefault)} open={createOpen} @@ -261,7 +219,6 @@ export function ProfilesView({ - ) } @@ -273,7 +230,7 @@ function ProfileRow({ active, onSelect, profile }: { active: boolean; onSelect: return (
-
+
{profile.model ? ( <> @@ -475,9 +432,7 @@ function SoulEditor({ profileName }: { profileName: string }) {
{loading ? ( -
- {p.loadingSoul} -
+ ) : (