diff --git a/src/main/codex-accounts/runtime-home-service.test.ts b/src/main/codex-accounts/runtime-home-service.test.ts index aa905cadb88..3a2f407e6fe 100644 --- a/src/main/codex-accounts/runtime-home-service.test.ts +++ b/src/main/codex-accounts/runtime-home-service.test.ts @@ -66,7 +66,7 @@ function createSettings(overrides: Partial = {}): GlobalSettings terminalScrollbackBytes: 10_000_000, openLinksInApp: false, rightSidebarOpenByDefault: true, - showTitlebarAgentActivity: true, + showTitlebarAppName: true, showTasksButton: true, diffDefaultView: 'inline', notifications: { diff --git a/src/main/codex-accounts/service.test.ts b/src/main/codex-accounts/service.test.ts index 0f66249ed69..e54e37c0ca9 100644 --- a/src/main/codex-accounts/service.test.ts +++ b/src/main/codex-accounts/service.test.ts @@ -60,7 +60,7 @@ function createSettings(overrides: Partial = {}): GlobalSettings terminalScrollbackBytes: 10_000_000, openLinksInApp: false, rightSidebarOpenByDefault: true, - showTitlebarAgentActivity: true, + showTitlebarAppName: true, showTasksButton: true, diffDefaultView: 'inline', notifications: { diff --git a/src/main/index.ts b/src/main/index.ts index ac53966d012..a2cad5f29f4 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -550,7 +550,7 @@ app.whenReady().then(async () => { const ui = store?.getUI() return { showTasksButton: settings?.showTasksButton !== false, - showTitlebarAgentActivity: settings?.showTitlebarAgentActivity !== false, + showTitlebarAppName: settings?.showTitlebarAppName !== false, statusBarVisible: ui?.statusBarVisible !== false } } diff --git a/src/main/ipc/settings.ts b/src/main/ipc/settings.ts index 30fe923b1d4..2029227ef12 100644 --- a/src/main/ipc/settings.ts +++ b/src/main/ipc/settings.ts @@ -18,7 +18,7 @@ const SETTINGS_CHANGED_WHITELIST_SET = new Set(SETTINGS_CHANGED_WHITELIS // items when the backing state changes. const APPEARANCE_MENU_KEYS: readonly (keyof GlobalSettings)[] = [ 'showTasksButton', - 'showTitlebarAgentActivity' + 'showTitlebarAppName' ] export function registerSettingsHandlers(store: Store): void { diff --git a/src/main/menu/register-app-menu.test.ts b/src/main/menu/register-app-menu.test.ts index d28a0b51d56..9bbf4faf9d9 100644 --- a/src/main/menu/register-app-menu.test.ts +++ b/src/main/menu/register-app-menu.test.ts @@ -35,7 +35,7 @@ function buildMenuOptions() { onToggleAppearance: vi.fn(), getAppearanceState: vi.fn(() => ({ showTasksButton: true, - showTitlebarAgentActivity: true, + showTitlebarAppName: true, statusBarVisible: true })) } @@ -194,7 +194,7 @@ describe('registerAppMenu', () => { const options = buildMenuOptions() options.getAppearanceState.mockReturnValue({ showTasksButton: false, - showTitlebarAgentActivity: true, + showTitlebarAppName: true, statusBarVisible: true }) registerAppMenu(options) @@ -209,9 +209,7 @@ describe('registerAppMenu', () => { expect(tasksItem?.type).toBe('checkbox') expect(tasksItem?.checked).toBe(false) - const titlebarItem = appearanceSubmenu.find( - (item) => item.label === 'Show Titlebar Agent Activity' - ) + const titlebarItem = appearanceSubmenu.find((item) => item.label === 'Show Titlebar App Name') expect(titlebarItem?.checked).toBe(true) const statusBarItem = appearanceSubmenu.find((item) => item.label === 'Show Status Bar') @@ -229,8 +227,12 @@ describe('registerAppMenu', () => { appearanceSubmenu .find((item) => item.label === 'Show Tasks Button') ?.click?.({} as never, {} as never, {} as never) + appearanceSubmenu + .find((item) => item.label === 'Show Titlebar App Name') + ?.click?.({} as never, {} as never, {} as never) expect(options.onToggleAppearance).toHaveBeenCalledWith('showTasksButton') + expect(options.onToggleAppearance).toHaveBeenCalledWith('showTitlebarAppName') }) it('routes sidebar toggle items through their callbacks', () => { diff --git a/src/main/menu/register-app-menu.ts b/src/main/menu/register-app-menu.ts index d78a70d8c1b..61d51284ee7 100644 --- a/src/main/menu/register-app-menu.ts +++ b/src/main/menu/register-app-menu.ts @@ -2,7 +2,7 @@ import { BrowserWindow, Menu, app } from 'electron' export type AppearanceMenuState = { showTasksButton: boolean - showTitlebarAgentActivity: boolean + showTitlebarAppName: boolean statusBarVisible: boolean } @@ -181,10 +181,10 @@ function buildAndApplyMenu(options: RegisterAppMenuOptions): void { click: () => onToggleAppearance('showTasksButton') }, { - label: 'Show Titlebar Agent Activity', + label: 'Show Titlebar App Name', type: 'checkbox', - checked: appearance.showTitlebarAgentActivity, - click: () => onToggleAppearance('showTitlebarAgentActivity') + checked: appearance.showTitlebarAppName, + click: () => onToggleAppearance('showTitlebarAppName') } ] } diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index f8bdda4164e..e0540161765 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -1,5 +1,5 @@ /* eslint-disable max-lines */ -import { lazy, Suspense, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react' +import { lazy, Suspense, useEffect, useLayoutEffect, useRef, useState } from 'react' import { getDefaultUIState } from '../../shared/constants' import { @@ -14,10 +14,14 @@ import logo from '../../../resources/logo.svg' import { SYNC_FIT_PANES_EVENT, TOGGLE_TERMINAL_PANE_EXPAND_EVENT } from '@/constants/terminal' import { syncZoomCSSVar } from '@/lib/ui-zoom' import { buildAppFontFamily } from '@/lib/app-font-family' -import { activateTabAndFocusPane } from '@/lib/activate-tab-and-focus-pane' -import { toast } from 'sonner' import { Toaster } from '@/components/ui/sonner' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip' +import { + ContextMenu, + ContextMenuContent, + ContextMenuItem, + ContextMenuTrigger +} from '@/components/ui/context-menu' import { useAppStore } from './store' import { useShallow } from 'zustand/react/shallow' import { useIpcEvents } from './hooks/useIpcEvents' @@ -44,12 +48,8 @@ import { import { useGlobalFileDrop } from './hooks/useGlobalFileDrop' import { registerUpdaterBeforeUnloadBypass } from './lib/updater-beforeunload' import { buildWorkspaceSessionPayload } from './lib/workspace-session' -import { countWorkingAgents, getWorkingAgentsPerWorktree } from './lib/agent-status' -import { activateAndRevealWorktree } from './lib/worktree-activation' import { applyDocumentTheme } from './lib/document-theme' import { isEditableTarget } from './lib/editable-target' -import { Popover, PopoverTrigger, PopoverContent } from '@/components/ui/popover' -import { findWorktreeById, getRepoIdFromWorktreeId } from '@/store/slices/worktree-helpers' import { canGoBackWorktreeHistory, canGoForwardWorktreeHistory @@ -179,19 +179,6 @@ function App(): React.JSX.Element { const activeWorktreeId = useAppStore((s) => s.activeWorktreeId) const tabsByWorktree = useAppStore((s) => s.tabsByWorktree) const activeTabId = useAppStore((s) => s.activeTabId) - const worktreesByRepo = useAppStore((s) => s.worktreesByRepo) - const agentInputs = useAppStore( - useShallow((s) => ({ - tabsByWorktree: s.tabsByWorktree, - runtimePaneTitlesByTabId: s.runtimePaneTitlesByTabId, - worktreesByRepo: s.worktreesByRepo - })) - ) - const activeAgentCount = useMemo(() => countWorkingAgents(agentInputs), [agentInputs]) - const workingAgentsPerWorktree = useMemo( - () => getWorkingAgentsPerWorktree(agentInputs), - [agentInputs] - ) const expandedPaneByTabId = useAppStore((s) => s.expandedPaneByTabId) const canExpandPaneByTabId = useAppStore((s) => s.canExpandPaneByTabId) const workspaceSessionReady = useAppStore((s) => s.workspaceSessionReady) @@ -739,14 +726,7 @@ function App(): React.JSX.Element { }) observer.observe(controls) return () => observer.disconnect() - }, [ - activeAgentCount, - isFullScreen, - settings?.showTitlebarAgentActivity, - showSidebar, - workspaceActive, - sidebarOpen - ]) + }, [isFullScreen, settings?.showTitlebarAppName, showSidebar, workspaceActive, sidebarOpen]) useEffect(() => { if ( @@ -807,6 +787,28 @@ function App(): React.JSX.Element { ) : (
)} + {showSidebar && ( + <> + {settings?.showTitlebarAppName !== false && ( + + +
+ Orca +
+
+ + { + void actions.updateSettings({ showTitlebarAppName: false }) + }} + > + Hide App Name + + +
+ )} + + )} {showSidebar && ( @@ -823,101 +825,6 @@ function App(): React.JSX.Element { )} - {settings?.showTitlebarAgentActivity !== false ? ( - - - - - -
0 ? ' titlebar-agent-hovercard-header-with-list' : ''}`} - > - {activeAgentCount === 0 - ? 'No agents active' - : `${activeAgentCount} ${activeAgentCount === 1 ? 'agent' : 'agents'} active`} -
- {activeAgentCount > 0 && ( -
- {Object.entries(workingAgentsPerWorktree).map(([worktreeId, { agents }]) => { - const wt = findWorktreeById(worktreesByRepo, worktreeId) - // Why: when a transient git error causes worktreesByRepo to - // lose a worktree, the raw worktreeId (uuid::path) is not - // useful. Extract a cross-platform path basename as a - // readable fallback. - const sepIdx = worktreeId.indexOf('::') - const pathPart = sepIdx !== -1 ? worktreeId.slice(sepIdx + 2) : worktreeId - const fallbackName = pathPart.split(/[\\/]/).pop() || pathPart - return ( -
- - {agents.map((agent) => ( - - ))} -
- ) - })} -
- )} - -
-
- ) : null}
{/* Why: Back/Forward traverse mixed worktree + Tasks history, so the cluster is shown wherever the history shortcut is live (terminal or diff --git a/src/renderer/src/assets/main.css b/src/renderer/src/assets/main.css index a5e9f7b82c1..f92b2a37492 100644 --- a/src/renderer/src/assets/main.css +++ b/src/renderer/src/assets/main.css @@ -528,192 +528,23 @@ color: var(--muted-foreground); } -.titlebar-agent-badge { +.titlebar-app-name { -webkit-app-region: no-drag; display: inline-flex; align-items: center; - gap: 6px; - margin-left: 4px; - margin-right: 8px; - padding: 3px 8px; - border-radius: 999px; - border: 1px solid color-mix(in srgb, var(--border) 78%, transparent); - background: color-mix(in srgb, var(--accent) 80%, transparent); - color: var(--foreground); - font-size: 11px; - font-weight: 600; - line-height: 1; - white-space: nowrap; - cursor: pointer; -} - -/* Why: the titlebar is a drag region, and descendant elements inherit that - region unless they opt out. Without this, hovering the dot/count spans - inside the badge showed the default cursor instead of pointer because the - drag region overrode the button's cursor. pointer-events: none forwards - hover/click hit-testing to the button, which already has no-drag set. */ -.titlebar-agent-badge > * { - pointer-events: none; -} - -.titlebar-agent-badge:hover { - background: var(--accent); - border-color: var(--border); -} - -.titlebar-agent-badge-idle { - opacity: 0.55; -} - -.titlebar-agent-badge-dot { - width: 7px; - height: 7px; - border-radius: 999px; - flex-shrink: 0; - background: #16a34a; - box-shadow: 0 0 0 3px color-mix(in srgb, #16a34a 18%, transparent); -} - -.titlebar-agent-badge-dot-idle { - background: var(--muted-foreground); - box-shadow: none; -} - -.titlebar-agent-badge-count { - min-width: 1ch; -} - -.titlebar-agent-badge-label { + height: 24px; + margin-left: 2px; + margin-right: 4px; + padding: 0 6px; + border-radius: 4px; color: var(--muted-foreground); - font-weight: 500; -} - -/* ─── Agent hover card (titlebar badge popover) ───────────────────── */ - -.titlebar-agent-hovercard { - width: auto; - min-width: 180px; - max-width: 280px; - padding: 8px 0 0; -} - -.titlebar-agent-hovercard-header { - padding: 0 12px 6px; - font-size: 11px; - font-weight: 500; - color: var(--muted-foreground); -} - -.titlebar-agent-hovercard-header-with-list { - border-bottom: 1px solid var(--border); - margin-bottom: 4px; -} - -.titlebar-agent-hovercard-hide { - display: flex; - align-items: center; - width: 100%; - padding: 5px 12px 8px; - margin-top: 4px; - border-top: 1px solid var(--border); - background: none; - border-left: none; - border-right: none; - border-bottom: none; - color: var(--muted-foreground); - font-size: 11px; - cursor: pointer; - border-radius: 0; -} - -.titlebar-agent-hovercard-hide:hover { - color: var(--foreground); - background: var(--accent); -} - -.titlebar-agent-hovercard-list { - display: flex; - flex-direction: column; -} - -.titlebar-agent-hovercard-worktree { - display: flex; - align-items: center; - gap: 8px; - padding: 5px 12px; - background: none; - border: none; - color: var(--foreground); - font-size: 13px; - font-weight: 500; - text-align: left; - cursor: pointer; - border-radius: 0; - width: 100%; -} - -.titlebar-agent-hovercard-worktree:hover { - background: var(--accent); -} - -.titlebar-agent-hovercard-agent { - display: flex; - align-items: center; - justify-content: space-between; - padding: 3px 12px 3px 24px; font-size: 12px; - color: var(--muted-foreground); - background: none; - border: none; - text-align: left; - width: 100%; + font-weight: 600; + line-height: 24px; + white-space: nowrap; cursor: pointer; } -.titlebar-agent-hovercard-agent:hover { - background: var(--accent); - color: var(--foreground); -} - -.titlebar-agent-hovercard-agent-label { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.titlebar-agent-hovercard-agent-dot { - width: 6px; - height: 6px; - border-radius: 999px; - flex-shrink: 0; - background: #16a34a; -} - -.titlebar-agent-hovercard-name { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - min-width: 0; -} - -.titlebar-agent-hovercard-count { - flex-shrink: 0; - display: inline-flex; - align-items: center; - gap: 4px; - font-size: 11px; - color: var(--muted-foreground); - font-variant-numeric: tabular-nums; -} - -.titlebar-agent-hovercard-dot { - display: inline-block; - width: 6px; - height: 6px; - border-radius: 999px; - background: #16a34a; -} - .titlebar-icon-button { -webkit-app-region: no-drag; background: none; diff --git a/src/renderer/src/components/settings/AppearancePane.tsx b/src/renderer/src/components/settings/AppearancePane.tsx index 295daf5ef97..7cf84bca1a8 100644 --- a/src/renderer/src/components/settings/AppearancePane.tsx +++ b/src/renderer/src/components/settings/AppearancePane.tsx @@ -114,9 +114,9 @@ const LAYOUT_ENTRIES: SettingsSearchEntry[] = [ const TITLEBAR_ENTRIES: SettingsSearchEntry[] = [ { - title: 'Titlebar Agent Activity', - description: 'Show the number of active agents in the titlebar.', - keywords: ['titlebar', 'agent', 'badge', 'active', 'count', 'status'] + title: 'Titlebar App Name', + description: 'Show Orca in the titlebar.', + keywords: ['titlebar', 'orca', 'app', 'name', 'brand'] } ] @@ -289,32 +289,30 @@ export function AppearancePane({
- -

- Show the number of active agents in the titlebar. -

+ +

Show Orca in the titlebar.

diff --git a/src/shared/constants.ts b/src/shared/constants.ts index 0650c342d31..17184820a89 100644 --- a/src/shared/constants.ts +++ b/src/shared/constants.ts @@ -196,7 +196,7 @@ export function getDefaultSettings(homedir: string): GlobalSettings { terminalScrollbackBytes: 10_000_000, openLinksInApp: true, rightSidebarOpenByDefault: true, - showTitlebarAgentActivity: true, + showTitlebarAppName: true, showTasksButton: true, notifications: getDefaultNotificationSettings(), diffDefaultView: 'inline', diff --git a/src/shared/types.ts b/src/shared/types.ts index 715129a1653..16498cf089f 100644 --- a/src/shared/types.ts +++ b/src/shared/types.ts @@ -1145,8 +1145,8 @@ export type GlobalSettings = { * until the user explicitly wants worktree-scoped in-app browsing. */ openLinksInApp: boolean rightSidebarOpenByDefault: boolean - /** Whether to show the live agent activity count badge in the titlebar. */ - showTitlebarAgentActivity: boolean + /** Whether to show the Orca app name in the titlebar. */ + showTitlebarAppName: boolean /** Why: some users do not use the Tasks feature and prefer to keep the * left sidebar free of its button entirely. Hiding the button here also * removes it from keyboard navigation. */