feat(ui): move project actions from the titlebar overlay into the header before Open in
This commit is contained in:
@@ -46,6 +46,8 @@ import {
|
|||||||
import type { SessionContextUsage } from '@/stores/types/sessionTypes';
|
import type { SessionContextUsage } from '@/stores/types/sessionTypes';
|
||||||
import { DesktopHostSwitcherDialog } from '@/components/desktop/DesktopHostSwitcher';
|
import { DesktopHostSwitcherDialog } from '@/components/desktop/DesktopHostSwitcher';
|
||||||
import { OpenInAppButton } from '@/components/desktop/OpenInAppButton';
|
import { OpenInAppButton } from '@/components/desktop/OpenInAppButton';
|
||||||
|
import { ProjectActionsButton } from '@/components/layout/ProjectActionsButton';
|
||||||
|
import { useProjectActionsContext } from '@/hooks/useProjectActionsContext';
|
||||||
import { SessionSwitcherDropdown } from '@/components/session/SessionSwitcherDropdown';
|
import { SessionSwitcherDropdown } from '@/components/session/SessionSwitcherDropdown';
|
||||||
import { SessionTabsStrip, type SessionTabMenuArgs } from './SessionTabsStrip';
|
import { SessionTabsStrip, type SessionTabMenuArgs } from './SessionTabsStrip';
|
||||||
import { canUseElectronDesktopIPC, invokeDesktop, isDesktopLocalOriginActive, isDesktopShell, isVSCodeRuntime, startDesktopWindowDrag, type UpdateInfo } from '@/lib/desktop';
|
import { canUseElectronDesktopIPC, invokeDesktop, isDesktopLocalOriginActive, isDesktopShell, isVSCodeRuntime, startDesktopWindowDrag, type UpdateInfo } from '@/lib/desktop';
|
||||||
@@ -998,27 +1000,9 @@ export const Header: React.FC = () => {
|
|||||||
return normalize(openDirectory || activeProject?.path || '');
|
return normalize(openDirectory || activeProject?.path || '');
|
||||||
}, [activeProject?.path, openDirectory]);
|
}, [activeProject?.path, openDirectory]);
|
||||||
|
|
||||||
const activeProjectRef = React.useMemo(() => {
|
// Same resolution the titlebar overlay used to own: worktree → session →
|
||||||
if (!activeProject) {
|
// draft → project path, sticky across session switches.
|
||||||
return null;
|
const projectActionsContext = useProjectActionsContext();
|
||||||
}
|
|
||||||
return { id: activeProject.id, path: activeProject.path };
|
|
||||||
}, [activeProject]);
|
|
||||||
|
|
||||||
const lastProjectActionsContextRef = React.useRef<{
|
|
||||||
projectRef: { id: string; path: string };
|
|
||||||
directory: string;
|
|
||||||
} | null>(null);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (!activeProjectRef || !actionDirectory) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
lastProjectActionsContextRef.current = {
|
|
||||||
projectRef: activeProjectRef,
|
|
||||||
directory: actionDirectory,
|
|
||||||
};
|
|
||||||
}, [actionDirectory, activeProjectRef]);
|
|
||||||
|
|
||||||
|
|
||||||
const planModeEnabled = useFeatureFlagsStore((state) => state.planModeEnabled);
|
const planModeEnabled = useFeatureFlagsStore((state) => state.planModeEnabled);
|
||||||
@@ -1295,6 +1279,13 @@ export const Header: React.FC = () => {
|
|||||||
|
|
||||||
const desktopSidebarActions = (
|
const desktopSidebarActions = (
|
||||||
<>
|
<>
|
||||||
|
{projectActionsContext ? (
|
||||||
|
<ProjectActionsButton
|
||||||
|
projectRef={projectActionsContext.projectRef}
|
||||||
|
directory={projectActionsContext.directory}
|
||||||
|
className="mr-2"
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
<OpenInAppButton directory={actionDirectory} className="mr-1" />
|
<OpenInAppButton directory={actionDirectory} className="mr-1" />
|
||||||
{/* Instances only exist in the desktop app. On web the menu was left
|
{/* Instances only exist in the desktop app. On web the menu was left
|
||||||
holding a single dev-only shutdown action, which is not a reason to
|
holding a single dev-only shutdown action, which is not a reason to
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import React from 'react';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Strip at the top of the desktop left sidebar that reserves room for the
|
* Strip at the top of the desktop left sidebar that reserves room for the
|
||||||
* persistent {@link TitlebarLeftControls} overlay (sidebar toggle + project
|
* persistent {@link TitlebarLeftControls} overlay (sidebar toggle), so the
|
||||||
* actions), so the session list starts below them. Its height tracks the
|
* session list starts below it. Its height tracks the
|
||||||
* header via `--oc-header-height`.
|
* header via `--oc-header-height`.
|
||||||
*
|
*
|
||||||
* Split into two regions so the strip stays a window drag area while the
|
* Split into two regions so the strip stays a window drag area while the
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import { Icon } from '@/components/icon/Icon';
|
|||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { useUIStore } from '@/stores/useUIStore';
|
import { useUIStore } from '@/stores/useUIStore';
|
||||||
import { useI18n } from '@/lib/i18n';
|
import { useI18n } from '@/lib/i18n';
|
||||||
import { useProjectActionsContext } from '@/hooks/useProjectActionsContext';
|
|
||||||
import { ProjectActionsButton } from '@/components/layout/ProjectActionsButton';
|
|
||||||
import { WindowsWindowControls } from '@/components/desktop/WindowsWindowControls';
|
import { WindowsWindowControls } from '@/components/desktop/WindowsWindowControls';
|
||||||
import { formatShortcutForDisplay, getEffectiveShortcutCombo } from '@/lib/shortcuts';
|
import { formatShortcutForDisplay, getEffectiveShortcutCombo } from '@/lib/shortcuts';
|
||||||
import { invokeDesktop } from '@/lib/desktop';
|
import { invokeDesktop } from '@/lib/desktop';
|
||||||
@@ -15,7 +13,7 @@ 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';
|
'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';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persistent top-left titlebar controls (sidebar toggle + project actions).
|
* Persistent top-left titlebar controls (app menu on frameless chrome + sidebar toggle).
|
||||||
*
|
*
|
||||||
* Rendered exactly once as an absolutely-positioned overlay above both the
|
* Rendered exactly once as an absolutely-positioned overlay above both the
|
||||||
* sidebar and the header, so the buttons never migrate / re-mount between the
|
* sidebar and the header, so the buttons never migrate / re-mount between the
|
||||||
@@ -29,7 +27,6 @@ export const TitlebarLeftControls: React.FC = () => {
|
|||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const toggleSidebar = useUIStore((state) => state.toggleSidebar);
|
const toggleSidebar = useUIStore((state) => state.toggleSidebar);
|
||||||
const shortcutOverrides = useUIStore((state) => state.shortcutOverrides);
|
const shortcutOverrides = useUIStore((state) => state.shortcutOverrides);
|
||||||
const projectActionsContext = useProjectActionsContext();
|
|
||||||
const clusterRef = React.useRef<HTMLDivElement | null>(null);
|
const clusterRef = React.useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
const toggleShortcut = formatShortcutForDisplay(getEffectiveShortcutCombo('toggle_sidebar', shortcutOverrides));
|
const toggleShortcut = formatShortcutForDisplay(getEffectiveShortcutCombo('toggle_sidebar', shortcutOverrides));
|
||||||
@@ -123,13 +120,6 @@ export const TitlebarLeftControls: React.FC = () => {
|
|||||||
<p>{t('header.actions.openSessionsWithShortcut', { shortcut: toggleShortcut })}</p>
|
<p>{t('header.actions.openSessionsWithShortcut', { shortcut: toggleShortcut })}</p>
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
{projectActionsContext ? (
|
|
||||||
<ProjectActionsButton
|
|
||||||
projectRef={projectActionsContext.projectRef}
|
|
||||||
directory={projectActionsContext.directory}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user