feat(04-01): wire ContextSwitcher into SidebarHeader and add keyboard shortcut
- Add ContextSwitcher to SidebarHeader Row 1 with visual separator - Add Cmd+Shift+K shortcut to cycle through available contexts - Add SSH status listener in App.tsx to refresh context list on changes - Shortcut guard: disabled during active context switch Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ca60158d34
commit
58f4be0a61
3 changed files with 45 additions and 2 deletions
|
|
@ -24,6 +24,15 @@ export const App = (): React.JSX.Element => {
|
|||
void useStore.getState().initializeContextSystem();
|
||||
}, []);
|
||||
|
||||
// Refresh available contexts when SSH connection state changes
|
||||
useEffect(() => {
|
||||
if (!window.electronAPI.ssh?.onStatus) return;
|
||||
const cleanup = window.electronAPI.ssh.onStatus(() => {
|
||||
void useStore.getState().fetchAvailableContexts();
|
||||
});
|
||||
return cleanup;
|
||||
}, []);
|
||||
|
||||
// Initialize IPC event listeners (notifications, file changes)
|
||||
useEffect(() => {
|
||||
const cleanup = initializeNotificationListeners();
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import { truncateMiddle } from '@renderer/utils/stringUtils';
|
|||
import { Check, ChevronDown, GitBranch, PanelLeft } from 'lucide-react';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
|
||||
import { ContextSwitcher } from '../common/ContextSwitcher';
|
||||
import { WorktreeBadge } from '../common/WorktreeBadge';
|
||||
|
||||
import type { Worktree, WorktreeSource } from '@renderer/types/data';
|
||||
|
|
@ -325,7 +326,7 @@ export const SidebarHeader = (): React.JSX.Element => {
|
|||
{/* ROW 1: Project Identity (Title Bar / Drag Region) */}
|
||||
<div
|
||||
ref={projectDropdownRef}
|
||||
className="relative flex select-none items-center justify-between pr-2"
|
||||
className="relative flex select-none items-center gap-2 pr-2"
|
||||
style={
|
||||
{
|
||||
height: `${HEADER_ROW1_HEIGHT}px`,
|
||||
|
|
@ -334,6 +335,16 @@ export const SidebarHeader = (): React.JSX.Element => {
|
|||
} as React.CSSProperties
|
||||
}
|
||||
>
|
||||
{/* Context Switcher - workspace indicator */}
|
||||
<ContextSwitcher />
|
||||
|
||||
{/* Visual separator */}
|
||||
<div
|
||||
className="h-4 w-px"
|
||||
style={{ backgroundColor: 'var(--color-border)' }}
|
||||
/>
|
||||
|
||||
{/* Project name dropdown button */}
|
||||
<button
|
||||
onClick={() => setIsProjectDropdownOpen(!isProjectDropdownOpen)}
|
||||
className="flex min-w-0 items-center gap-2 transition-opacity hover:opacity-80"
|
||||
|
|
@ -356,7 +367,7 @@ export const SidebarHeader = (): React.JSX.Element => {
|
|||
onClick={toggleSidebar}
|
||||
onMouseEnter={() => setIsCollapseHovered(true)}
|
||||
onMouseLeave={() => setIsCollapseHovered(false)}
|
||||
className="shrink-0 rounded-md p-1.5 transition-colors"
|
||||
className="ml-auto shrink-0 rounded-md p-1.5 transition-colors"
|
||||
style={
|
||||
{
|
||||
WebkitAppRegion: 'no-drag',
|
||||
|
|
|
|||
|
|
@ -38,6 +38,10 @@ export function useKeyboardShortcuts(): void {
|
|||
focusPane,
|
||||
splitPane,
|
||||
closePane,
|
||||
availableContexts,
|
||||
activeContextId,
|
||||
switchContext,
|
||||
isContextSwitching,
|
||||
} = useStore(
|
||||
useShallow((s) => ({
|
||||
openTabs: s.openTabs,
|
||||
|
|
@ -61,6 +65,10 @@ export function useKeyboardShortcuts(): void {
|
|||
focusPane: s.focusPane,
|
||||
splitPane: s.splitPane,
|
||||
closePane: s.closePane,
|
||||
availableContexts: s.availableContexts,
|
||||
activeContextId: s.activeContextId,
|
||||
switchContext: s.switchContext,
|
||||
isContextSwitching: s.isContextSwitching,
|
||||
}))
|
||||
);
|
||||
|
||||
|
|
@ -205,6 +213,17 @@ export function useKeyboardShortcuts(): void {
|
|||
return;
|
||||
}
|
||||
|
||||
// Cmd+Shift+K: Cycle to next workspace context
|
||||
if (event.key === 'k' && event.shiftKey) {
|
||||
event.preventDefault();
|
||||
if (!isContextSwitching && availableContexts.length > 1) {
|
||||
const currentIndex = availableContexts.findIndex((c) => c.id === activeContextId);
|
||||
const nextIndex = (currentIndex + 1) % availableContexts.length;
|
||||
void switchContext(availableContexts[nextIndex].id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Cmd+K: Open command palette for global search
|
||||
if (event.key === 'k') {
|
||||
event.preventDefault();
|
||||
|
|
@ -280,5 +299,9 @@ export function useKeyboardShortcuts(): void {
|
|||
focusPane,
|
||||
splitPane,
|
||||
closePane,
|
||||
availableContexts,
|
||||
activeContextId,
|
||||
switchContext,
|
||||
isContextSwitching,
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue