feat(shortcuts): add assignable shortcut to toggle sleeping workspaces

Adds an unbound sidebar.sleepingWorkspaces.toggle keybinding action, wires it to the global shortcut dispatcher, and shows assigned shortcut labels in the workspace-board filter menu.

Also resolves the PR against current main and removes an unnecessary exhaustive-switch fallback that blocked current lint.
This commit is contained in:
gatsby74 2026-06-23 23:26:01 +02:00 committed by GitHub
parent 30a1a52459
commit 0880021665
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 94 additions and 14 deletions

View File

@ -1604,6 +1604,21 @@ function App(): React.JSX.Element {
return
}
// Toggle the "show sleeping workspaces" sidebar filter without opening the
// filters menu (issue #5209). When revealing them, open the left sidebar
// so the now-visible sleeping worktrees are actually reachable.
if (matchShortcut('sidebar.sleepingWorkspaces.toggle')) {
input.preventDefault()
notifyTerminalCapture('sidebar.sleepingWorkspaces.toggle')
const store = useAppStore.getState()
const nextShowSleeping = !store.showSleepingWorkspaces
store.setShowSleepingWorkspaces(nextShowSleeping)
if (nextShowSleeping) {
store.setSidebarOpen(true)
}
return
}
// Why: rename the active terminal tab. Cmd+R is free in the app/terminal
// focus zone because the browser pane owns its own Cmd+R reload and that
// focus never reaches this renderer-window handler. Only terminal tabs

View File

@ -13,10 +13,12 @@ import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuTrigger
} from '@/components/ui/dropdown-menu'
import { Tooltip, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip'
import RepoBadgeLabel from '@/components/repo/RepoBadgeLabel'
import { useShortcutLabel } from '@/hooks/useShortcutLabel'
import { searchRepos } from '@/lib/repo-search'
import { cn } from '@/lib/utils'
import { DEFAULT_SHOW_SLEEPING_WORKSPACES } from '../../../../shared/constants'
@ -37,6 +39,9 @@ const SidebarFilter = React.memo(function SidebarFilter({
}: SidebarFilterProps) {
const showSleepingWorkspaces = useAppStore((s) => s.showSleepingWorkspaces)
const setShowSleepingWorkspaces = useAppStore((s) => s.setShowSleepingWorkspaces)
// Surface the user-assigned shortcut here so the filter menu doubles as its
// discovery point ('Unassigned' until they bind one in Settings → Shortcuts).
const sleepingShortcut = useShortcutLabel('sidebar.sleepingWorkspaces.toggle')
const hideDefaultBranchWorkspace = useAppStore((s) => s.hideDefaultBranchWorkspace)
const setHideDefaultBranchWorkspace = useAppStore((s) => s.setHideDefaultBranchWorkspace)
const hideAutomationGeneratedWorkspaces = useAppStore((s) => s.hideAutomationGeneratedWorkspaces)
@ -183,6 +188,7 @@ const SidebarFilter = React.memo(function SidebarFilter({
label={translate('auto.components.sidebar.SidebarFilter.638a2d221d', 'Hide sleeping')}
checked={!showSleepingWorkspaces}
onChange={(hideSleeping) => setShowSleepingWorkspaces(!hideSleeping)}
shortcutLabel={sleepingShortcut === 'Unassigned' ? undefined : sleepingShortcut}
/>
<FilterToggleRow
icon={<GitBranch className="size-3.5" />}
@ -333,12 +339,14 @@ function FilterToggleRow({
icon,
label,
checked,
onChange
onChange,
shortcutLabel
}: {
icon: React.ReactNode
label: string
checked: boolean
onChange: (next: boolean) => void
shortcutLabel?: string
}) {
return (
<button
@ -352,19 +360,22 @@ function FilterToggleRow({
<span className="text-muted-foreground">{icon}</span>
{label}
</span>
<span
aria-hidden
className={cn(
'relative h-3.5 w-6 shrink-0 rounded-full transition-colors',
checked ? 'bg-primary' : 'bg-muted-foreground/30'
)}
>
<span className="inline-flex items-center gap-2">
{shortcutLabel ? <DropdownMenuShortcut>{shortcutLabel}</DropdownMenuShortcut> : null}
<span
aria-hidden
className={cn(
'absolute top-0.5 left-0.5 size-2.5 rounded-full bg-background shadow-sm transition-transform',
checked && 'translate-x-2.5'
'relative h-3.5 w-6 shrink-0 rounded-full transition-colors',
checked ? 'bg-primary' : 'bg-muted-foreground/30'
)}
/>
>
<span
className={cn(
'absolute top-0.5 left-0.5 size-2.5 rounded-full bg-background shadow-sm transition-transform',
checked && 'translate-x-2.5'
)}
/>
</span>
</span>
</button>
)

View File

@ -65,8 +65,5 @@ export function getTaskPageGitHubPRIconTone(item: GitHubWorkItemStatusItem): str
return 'text-purple-600 dark:text-purple-300'
case 'closed':
return 'text-rose-600 dark:text-rose-300'
default:
// Fallback for any unexpected state
return 'text-muted-foreground'
}
}

View File

@ -456,6 +456,42 @@ describe('keybindings', () => {
)
})
it('keeps the sleeping-workspaces toggle unassigned until users customize it', () => {
const binding = {
key: 's',
code: 'KeyS',
control: true,
meta: false,
alt: true,
shift: false
}
// Ships unbound on every platform (issue #5209): assign-it-yourself.
expect(getEffectiveKeybindingsForAction('sidebar.sleepingWorkspaces.toggle', 'darwin')).toEqual(
[]
)
expect(getEffectiveKeybindingsForAction('sidebar.sleepingWorkspaces.toggle', 'linux')).toEqual(
[]
)
expect(getEffectiveKeybindingsForAction('sidebar.sleepingWorkspaces.toggle', 'win32')).toEqual(
[]
)
expect(keybindingMatchesAction('sidebar.sleepingWorkspaces.toggle', binding, 'linux')).toBe(
false
)
expect(
keybindingMatchesAction('sidebar.sleepingWorkspaces.toggle', binding, 'linux', {
'sidebar.sleepingWorkspaces.toggle': ['Mod+Alt+S']
})
).toBe(true)
const definition = getKeybindingDefinition('sidebar.sleepingWorkspaces.toggle')
expect(definition?.title).toBe('Toggle Sleeping Workspaces')
expect(definition?.searchKeywords).toEqual(
expect.arrayContaining(['sleeping', 'workspaces', 'filter'])
)
})
it('defines floating workspace panel action metadata', () => {
const actionIds = [
'floatingWorkspace.maximize' as KeybindingActionId,

View File

@ -48,6 +48,7 @@ export type KeybindingActionId =
| 'sidebar.sourceControl.toggle'
| 'sidebar.checks.toggle'
| 'sidebar.ports.toggle'
| 'sidebar.sleepingWorkspaces.toggle'
| 'sidebar.focusWorktreeList'
| 'floatingTerminal.toggle'
| 'floatingWorkspace.maximize'
@ -393,6 +394,26 @@ export const KEYBINDING_DEFINITIONS: readonly KeybindingDefinition[] = [
win32: []
}
},
{
id: 'sidebar.sleepingWorkspaces.toggle',
title: 'Toggle Sleeping Workspaces',
group: 'Global',
scope: 'global',
searchKeywords: [
'shortcut',
'sidebar',
'sleeping',
'asleep',
'workspaces',
'worktree',
'filter',
'show',
'hide'
],
// Why: ship unbound — issue #5209 asks to "assign a shortcut", so we avoid
// claiming a cross-platform chord and let users bind it in Settings.
defaultBindings: platformBindings([])
},
{
id: 'sidebar.focusWorktreeList',
title: 'Focus worktree list',