diff --git a/AGENTS.md b/AGENTS.md index 24ded422..186cc4f7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -451,7 +451,7 @@ A single store with N properties means every subscriber re-evaluates on every st ## Validation expectations -- Run `bun run type-check` and `bun run lint` before finalizing source-code changes that can affect TypeScript, runtime behavior, builds, lint rules, package resolution, or generated assets. For docs-only or isolated config-only changes, run the narrowest relevant validation instead (for example JSON/schema validation) and do not run the full checks unless the change can affect code execution. +- Run `bun run type-check` and `bun run lint` before finalizing source-code changes that can affect TypeScript, runtime behavior, builds, lint rules, package resolution, or generated assets. Use a sufficiently long tool timeout for these workspace-wide checks (for example 240000ms) so successful package-level results are not lost to a tool timeout. For docs-only or isolated config-only changes, run the narrowest relevant validation instead (for example JSON/schema validation) and do not run the full checks unless the change can affect code execution. - For hot-path changes, verify behavior under streaming or repeated events, not just static render. - For sync or startup changes, verify fresh load, retry/failure, and restart behavior. - For session changes, verify create, stream, abort, permission, archive/delete, and revisit flows when relevant. diff --git a/packages/ui/src/components/layout/Header.tsx b/packages/ui/src/components/layout/Header.tsx index 1ae2aa9d..9a76dff8 100644 --- a/packages/ui/src/components/layout/Header.tsx +++ b/packages/ui/src/components/layout/Header.tsx @@ -1417,16 +1417,6 @@ export const Header: React.FC = ({ toggleSidebar(); }, [blurActiveElement, isMobile, isSessionSwitcherOpen, setSessionSwitcherOpen, toggleSidebar]); - const handleOpenWindowsAppMenu = React.useCallback((event: React.MouseEvent) => { - const rect = event.currentTarget.getBoundingClientRect(); - void invokeDesktop('desktop_show_app_menu', { - x: rect.left, - y: rect.bottom, - }).catch((error) => { - console.warn('[header] failed to open app menu', error); - }); - }, []); - const handleOpenDraftMiniChat = React.useCallback(() => { void invokeDesktop('desktop_open_draft_mini_chat_window', { directory: normalize(openDirectory || activeProject?.path || ''), @@ -2154,15 +2144,6 @@ export const Header: React.FC = ({ className="app-region-no-drag shrink-0 self-stretch transition-[width] duration-200 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none" style={{ width: headerControlsSpacerWidth }} /> - {isWindowsElectronDesktop ? ( - - ) : null} {/* Sidebar toggle + project actions live in the persistent TitlebarLeftControls overlay; the header reserves matching left space via padding (see headerStyle) when the sidebar is collapsed. */} diff --git a/packages/ui/src/components/layout/TitlebarLeftControls.tsx b/packages/ui/src/components/layout/TitlebarLeftControls.tsx index 33636d56..9f29795d 100644 --- a/packages/ui/src/components/layout/TitlebarLeftControls.tsx +++ b/packages/ui/src/components/layout/TitlebarLeftControls.tsx @@ -7,6 +7,7 @@ import { useI18n } from '@/lib/i18n'; import { useProjectActionsContext } from '@/hooks/useProjectActionsContext'; import { ProjectActionsButton } from '@/components/layout/ProjectActionsButton'; import { formatShortcutForDisplay, getEffectiveShortcutCombo } from '@/lib/shortcuts'; +import { invokeDesktop } from '@/lib/desktop'; const ICON_BUTTON_CLASS = 'app-region-no-drag inline-flex h-8 w-8 items-center justify-center gap-2 rounded-md typography-ui-label font-medium text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary hover:bg-interactive-hover transition-colors'; @@ -31,6 +32,22 @@ export const TitlebarLeftControls: React.FC = () => { const clusterRef = React.useRef(null); const toggleShortcut = formatShortcutForDisplay(getEffectiveShortcutCombo('toggle_sidebar', shortcutOverrides)); + const isWindowsElectronDesktop = React.useMemo(() => { + if (typeof window === 'undefined') { + return false; + } + return Boolean(window.__OPENCHAMBER_ELECTRON__) && window.__OPENCHAMBER_PLATFORM__ === 'win32'; + }, []); + + const handleOpenWindowsAppMenu = React.useCallback((event: React.MouseEvent) => { + const rect = event.currentTarget.getBoundingClientRect(); + void invokeDesktop('desktop_show_app_menu', { + x: rect.left, + y: rect.bottom, + }).catch((error) => { + console.warn('[titlebar] failed to open app menu', error); + }); + }, []); React.useEffect(() => { if (typeof document === 'undefined') { @@ -71,6 +88,24 @@ export const TitlebarLeftControls: React.FC = () => { }} >
+ {isWindowsElectronDesktop ? ( + + + + + +

{t('header.actions.openAppMenu')}

+
+
+ ) : null} +