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:
parent
30a1a52459
commit
0880021665
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
Loading…
Reference in New Issue