diff --git a/apps/desktop/src/app/chat/sidebar/index.tsx b/apps/desktop/src/app/chat/sidebar/index.tsx index 68db378003..4b3e22fa2e 100644 --- a/apps/desktop/src/app/chat/sidebar/index.tsx +++ b/apps/desktop/src/app/chat/sidebar/index.tsx @@ -14,7 +14,7 @@ import { } from '@/components/ui/sidebar' import { Skeleton } from '@/components/ui/skeleton' import type { SessionInfo } from '@/hermes' -import { Brain, ChevronDown, Clock, Layers3, MessageCircle, Plus, RefreshCw, Users } from '@/lib/icons' +import { Brain, ChevronDown, Layers3, MessageCircle, Plus, RefreshCw } from '@/lib/icons' import { cn } from '@/lib/utils' import { $pinnedSessionIds, @@ -28,14 +28,7 @@ import { } from '@/store/layout' import { $selectedStoredSessionId, $sessions, $sessionsLoading, $workingSessionIds } from '@/store/session' -import { - type AppView, - ARTIFACTS_ROUTE, - CRON_ROUTE, - MESSAGING_ROUTE, - PROFILES_ROUTE, - SKILLS_ROUTE -} from '../../routes' +import { type AppView, ARTIFACTS_ROUTE, MESSAGING_ROUTE, SKILLS_ROUTE } from '../../routes' import { SidebarPanelLabel } from '../../shell/sidebar-label' import type { SidebarNavItem } from '../../types' @@ -50,9 +43,7 @@ const SIDEBAR_NAV: SidebarNavItem[] = [ }, { id: 'skills', label: 'Skills', icon: Brain, route: SKILLS_ROUTE }, { id: 'messaging', label: 'Messaging', icon: MessageCircle, route: MESSAGING_ROUTE }, - { id: 'artifacts', label: 'Artifacts', icon: Layers3, route: ARTIFACTS_ROUTE }, - { id: 'cron', label: 'Cron', icon: Clock, route: CRON_ROUTE }, - { id: 'profiles', label: 'Profiles', icon: Users, route: PROFILES_ROUTE } + { id: 'artifacts', label: 'Artifacts', icon: Layers3, route: ARTIFACTS_ROUTE } ] interface ChatSidebarProps extends React.ComponentProps { @@ -126,9 +117,7 @@ export function ChatSidebar({ const active = (item.id === 'skills' && currentView === 'skills') || (item.id === 'messaging' && currentView === 'messaging') || - (item.id === 'artifacts' && currentView === 'artifacts') || - (item.id === 'cron' && currentView === 'cron') || - (item.id === 'profiles' && currentView === 'profiles') + (item.id === 'artifacts' && currentView === 'artifacts') return ( diff --git a/apps/desktop/src/app/shell/app-shell.tsx b/apps/desktop/src/app/shell/app-shell.tsx index 005d53f81c..09e8b85307 100644 --- a/apps/desktop/src/app/shell/app-shell.tsx +++ b/apps/desktop/src/app/shell/app-shell.tsx @@ -78,7 +78,7 @@ export function AppShell({ // between the pane-tool cluster and the system cluster so they don't sit // flush against each other. Modeled as N gaps (N - 1 inner + 1 trailing) // to keep the formula generic for any pane-tool count. - const SYSTEM_TOOL_COUNT = 3 + const SYSTEM_TOOL_COUNT = 4 const paneToolCount = titlebarTools?.filter(tool => !tool.hidden).length ?? 0 const systemToolsWidth = `calc(${SYSTEM_TOOL_COUNT} * (var(--titlebar-control-size) + 0.25rem))` diff --git a/apps/desktop/src/app/shell/hooks/use-statusbar-items.tsx b/apps/desktop/src/app/shell/hooks/use-statusbar-items.tsx index e598a4dbc4..a0afb9b821 100644 --- a/apps/desktop/src/app/shell/hooks/use-statusbar-items.tsx +++ b/apps/desktop/src/app/shell/hooks/use-statusbar-items.tsx @@ -4,7 +4,7 @@ import { useCallback, useMemo, useState } from 'react' import type { CommandCenterSection } from '@/app/command-center' import { GatewayMenuPanel } from '@/app/shell/gateway-menu-panel' import { restartGateway } from '@/hermes' -import { Activity, AlertCircle, Command, Cpu, FolderOpen, GitBranch, Hash, Loader2, Sparkles } from '@/lib/icons' +import { Activity, AlertCircle, Clock, Command, Cpu, FolderOpen, GitBranch, Hash, Loader2, Sparkles } from '@/lib/icons' import { compactPath, contextBarLabel, LiveDuration, usageContextLabel } from '@/lib/statusbar' import { cn } from '@/lib/utils' import { $desktopActionTasks } from '@/store/activity' @@ -25,6 +25,7 @@ import { import { $desktopVersion, $updateApply, $updateStatus, setUpdateOverlayOpen } from '@/store/updates' import type { StatusResponse } from '@/types/hermes' +import { CRON_ROUTE } from '../../routes' import type { StatusbarItem } from '../statusbar-controls' interface StatusbarItemsOptions { @@ -203,6 +204,14 @@ export function useStatusbarItems({ onSelect: openAgents, title: agentsOpen ? 'Close agents' : 'Open agents', variant: 'action' + }, + { + icon: , + id: 'cron', + label: 'Cron', + title: 'Open cron jobs', + to: CRON_ROUTE, + variant: 'action' } ], [ diff --git a/apps/desktop/src/app/shell/titlebar-controls.tsx b/apps/desktop/src/app/shell/titlebar-controls.tsx index 960ac0eb22..6817bd444c 100644 --- a/apps/desktop/src/app/shell/titlebar-controls.tsx +++ b/apps/desktop/src/app/shell/titlebar-controls.tsx @@ -2,12 +2,22 @@ import { useStore } from '@nanostores/react' import type { ComponentProps, ReactNode } from 'react' import { useNavigate } from 'react-router-dom' +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger +} from '@/components/ui/dropdown-menu' import { triggerHaptic } from '@/lib/haptics' -import { FolderOpen, NotebookTabs, Settings, Volume2, VolumeX } from '@/lib/icons' +import { FolderOpen, NotebookTabs, Settings, Users, Volume2, VolumeX } from '@/lib/icons' import { cn } from '@/lib/utils' import { $hapticsMuted, toggleHapticsMuted } from '@/store/haptics' import { $fileBrowserOpen, $sidebarOpen, toggleFileBrowserOpen, toggleSidebarOpen } from '@/store/layout' +import { PROFILES_ROUTE } from '../routes' + import { titlebarButtonClass } from './titlebar' export interface TitlebarTool { @@ -95,6 +105,8 @@ export function TitlebarControls({ leftTools = [], tools = [], onOpenSettings }: ] const visibleSystemTools = systemTools.filter(tool => !tool.hidden) + const settingsTool = visibleSystemTools.find(tool => tool.id === 'settings') + const visibleSystemToolsBeforeSettings = visibleSystemTools.filter(tool => tool.id !== 'settings') const visiblePaneTools = tools.filter(tool => !tool.hidden) return ( @@ -133,14 +145,55 @@ export function TitlebarControls({ leftTools = [], tools = [], onOpenSettings }: aria-label="App controls" className="fixed right-(--titlebar-tools-right) top-(--titlebar-controls-top) z-70 flex flex-row items-center justify-end gap-x-1 pointer-events-auto select-none [-webkit-app-region:no-drag]" > - {visibleSystemTools.map(tool => ( + {visibleSystemToolsBeforeSettings.map(tool => ( ))} + + {settingsTool && } ) } +function ProfilesMenuButton({ navigate }: { navigate: ReturnType }) { + return ( + + + + + + +
Profiles
+
+ Advanced Hermes environments for separate personas, config, skills, and SOUL.md. +
+
+ + { + triggerHaptic('open') + navigate(PROFILES_ROUTE) + }} + > + + Manage profiles + +
+
+ ) +} + function TitlebarToolButton({ navigate, tool }: { navigate: ReturnType; tool: TitlebarTool }) { const className = cn( titlebarButtonClass, diff --git a/apps/desktop/src/app/types.ts b/apps/desktop/src/app/types.ts index 7cc5f2d939..36ea97d874 100644 --- a/apps/desktop/src/app/types.ts +++ b/apps/desktop/src/app/types.ts @@ -54,10 +54,8 @@ export type CommandDispatchResponse = export type SidebarNavId = | 'artifacts' | 'command-center' - | 'cron' | 'messaging' | 'new-session' - | 'profiles' | 'settings' | 'skills'