feat: display hot key mappings in settings (#173)
This commit is contained in:
parent
d4fc4bef19
commit
bbbe378467
|
|
@ -3,11 +3,12 @@ import type { OrcaHooks } from '../../../../shared/types'
|
|||
import { useAppStore } from '../../store'
|
||||
import { ScrollArea } from '../ui/scroll-area'
|
||||
import { Button } from '../ui/button'
|
||||
import { ArrowLeft, Palette, SlidersHorizontal, SquareTerminal } from 'lucide-react'
|
||||
import { ArrowLeft, Palette, SlidersHorizontal, SquareTerminal, Keyboard } from 'lucide-react'
|
||||
import { getSystemPrefersDark } from '@/lib/terminal-theme'
|
||||
import { SCROLLBACK_PRESETS_MB, getFallbackTerminalFonts } from './SettingsConstants'
|
||||
import { GeneralPane } from './GeneralPane'
|
||||
import { AppearancePane } from './AppearancePane'
|
||||
import { ShortcutsPane } from './ShortcutsPane'
|
||||
import { TerminalPane } from './TerminalPane'
|
||||
import { RepositoryPane } from './RepositoryPane'
|
||||
|
||||
|
|
@ -20,9 +21,9 @@ function Settings(): React.JSX.Element {
|
|||
const updateRepo = useAppStore((s) => s.updateRepo)
|
||||
const removeRepo = useAppStore((s) => s.removeRepo)
|
||||
|
||||
const [selectedPane, setSelectedPane] = useState<'general' | 'appearance' | 'terminal' | 'repo'>(
|
||||
'general'
|
||||
)
|
||||
const [selectedPane, setSelectedPane] = useState<
|
||||
'general' | 'appearance' | 'terminal' | 'shortcuts' | 'repo'
|
||||
>('general')
|
||||
const [selectedRepoId, setSelectedRepoId] = useState<string | null>(null)
|
||||
const [repoHooksMap, setRepoHooksMap] = useState<
|
||||
Record<string, { hasHooks: boolean; hooks: OrcaHooks | null }>
|
||||
|
|
@ -151,6 +152,7 @@ function Settings(): React.JSX.Element {
|
|||
const showGeneralPane = selectedPane === 'general'
|
||||
const showAppearancePane = selectedPane === 'appearance'
|
||||
const showTerminalPane = selectedPane === 'terminal'
|
||||
const showShortcutsPane = selectedPane === 'shortcuts'
|
||||
const showRepoPane = selectedPane === 'repo' && !!selectedRepo
|
||||
const displayedGitUsername = (selectedRepo ?? repos[0])?.gitUsername ?? ''
|
||||
|
||||
|
|
@ -180,6 +182,11 @@ function Settings(): React.JSX.Element {
|
|||
Terminal appearance, previews, and defaults for new panes.
|
||||
</p>
|
||||
</div>
|
||||
) : showShortcutsPane ? (
|
||||
<div className="space-y-1">
|
||||
<h1 className="text-2xl font-semibold">Shortcuts</h1>
|
||||
<p className="text-sm text-muted-foreground">Keyboard shortcuts for common actions.</p>
|
||||
</div>
|
||||
) : selectedRepo ? (
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-3">
|
||||
|
|
@ -249,6 +256,17 @@ function Settings(): React.JSX.Element {
|
|||
<SquareTerminal className="mr-2 size-4" />
|
||||
Terminal
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setSelectedPane('shortcuts')}
|
||||
className={`flex w-full items-center rounded-lg px-3 py-2 text-left text-sm transition-colors ${
|
||||
showShortcutsPane
|
||||
? 'bg-accent font-medium text-accent-foreground'
|
||||
: 'text-muted-foreground hover:bg-muted/60 hover:text-foreground'
|
||||
}`}
|
||||
>
|
||||
<Keyboard className="mr-2 size-4" />
|
||||
Shortcuts
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
|
|
@ -315,6 +333,8 @@ function Settings(): React.JSX.Element {
|
|||
scrollbackMode={scrollbackMode}
|
||||
setScrollbackMode={setScrollbackMode}
|
||||
/>
|
||||
) : showShortcutsPane ? (
|
||||
<ShortcutsPane />
|
||||
) : selectedRepo ? (
|
||||
<RepositoryPane
|
||||
repo={selectedRepo}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,97 @@
|
|||
import React, { useMemo } from 'react'
|
||||
|
||||
type ShortcutItem = {
|
||||
action: string
|
||||
keys: string[]
|
||||
}
|
||||
|
||||
type ShortcutGroup = {
|
||||
title: string
|
||||
items: ShortcutItem[]
|
||||
}
|
||||
|
||||
export function ShortcutsPane(): React.JSX.Element {
|
||||
const isMac = navigator.userAgent.includes('Mac')
|
||||
const mod = isMac ? '⌘' : 'Ctrl'
|
||||
const shift = isMac ? '⇧' : 'Shift'
|
||||
const enter = isMac ? '↵' : 'Enter'
|
||||
|
||||
const groups = useMemo<ShortcutGroup[]>(
|
||||
() => [
|
||||
{
|
||||
title: 'Global',
|
||||
items: [
|
||||
{ action: 'Create worktree', keys: [mod, 'N'] },
|
||||
{ action: 'Toggle File Explorer', keys: [mod, shift, 'E'] },
|
||||
{ action: 'Toggle Search', keys: [mod, shift, 'F'] },
|
||||
{ action: 'Toggle Source Control', keys: [mod, shift, 'G'] }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Terminal Tabs',
|
||||
items: [
|
||||
{ action: 'New tab', keys: [mod, 'T'] },
|
||||
{ action: 'Close active tab / pane', keys: [mod, 'W'] },
|
||||
{ action: 'Next tab', keys: [mod, shift, ']'] },
|
||||
{ action: 'Previous tab', keys: [mod, shift, '['] }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Terminal Panes',
|
||||
items: [
|
||||
{ action: 'Split pane right', keys: [mod, 'D'] },
|
||||
{ action: 'Split pane down', keys: [mod, shift, 'D'] },
|
||||
{ action: 'Close pane (EOF)', keys: ['Ctrl', 'D'] },
|
||||
{ action: 'Focus next pane', keys: [mod, ']'] },
|
||||
{ action: 'Focus previous pane', keys: [mod, '['] },
|
||||
{ action: 'Clear active pane', keys: [mod, 'K'] },
|
||||
{ action: 'Expand / collapse pane', keys: [mod, shift, enter] }
|
||||
]
|
||||
}
|
||||
],
|
||||
[mod, shift, enter]
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<section className="space-y-4">
|
||||
<div className="space-y-1">
|
||||
<h2 className="text-sm font-semibold">Keyboard Shortcuts</h2>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
View common hotkeys used across the application. Shortcuts customization is not
|
||||
currently supported.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-8">
|
||||
{groups.map((group) => (
|
||||
<div key={group.title} className="space-y-3">
|
||||
<h3 className="text-sm font-medium border-b border-border/50 pb-2 text-muted-foreground">
|
||||
{group.title}
|
||||
</h3>
|
||||
<div className="grid gap-2">
|
||||
{group.items.map((item, idx) => (
|
||||
<div key={idx} className="flex items-center justify-between py-1">
|
||||
<span className="text-sm text-foreground">{item.action}</span>
|
||||
<div className="flex items-center gap-1">
|
||||
{item.keys.map((key, kIdx) => (
|
||||
<React.Fragment key={kIdx}>
|
||||
<span className="inline-flex min-w-6 items-center justify-center rounded border border-border/80 bg-secondary/70 px-1.5 py-0.5 text-xs font-medium text-muted-foreground shadow-sm">
|
||||
{key}
|
||||
</span>
|
||||
{!isMac && kIdx < item.keys.length - 1 && (
|
||||
<span className="text-muted-foreground text-xs mx-0.5">+</span>
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Loading…
Reference in New Issue