feat: add Cmd+Shift+I shortcut to toggle properties panel, default closed

- Add Cmd+Shift+I keyboard shortcut in useAppKeyboard for toggling the
  properties/inspector panel
- Change default inspectorCollapsed to true (panel closed on fresh install)
- Add shortcut hint "⌘⇧I" to Properties button tooltips in breadcrumb
  bar and inspector header
- Show shortcut in command palette for "Toggle Properties Panel"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
lucaronin
2026-03-30 18:45:45 +02:00
parent b229d59437
commit d3e2d45261
8 changed files with 43 additions and 7 deletions

View File

@@ -146,7 +146,7 @@ function BreadcrumbActions({ entry, showDiffToggle, diffMode, diffLoading, onTog
<button
className="flex items-center justify-center border-none bg-transparent p-0 text-muted-foreground cursor-pointer hover:text-foreground transition-colors"
onClick={onToggleInspector}
title="Open Properties"
title="Properties (⌘⇧I)"
>
<SlidersHorizontal size={16} />
</button>

View File

@@ -92,14 +92,14 @@ function InspectorHeader({ collapsed, onToggle }: { collapsed: boolean; onToggle
return (
<div className="flex shrink-0 items-center border-b border-border" style={{ height: 45, padding: '6px 12px', gap: 8, cursor: 'default' }} onMouseDown={onMouseDown}>
{collapsed ? (
<button className="shrink-0 border-none bg-transparent p-1 text-muted-foreground cursor-pointer hover:text-foreground" onClick={onToggle}>
<button className="shrink-0 border-none bg-transparent p-1 text-muted-foreground cursor-pointer hover:text-foreground" onClick={onToggle} title="Properties (⌘⇧I)">
<SlidersHorizontal size={16} />
</button>
) : (
<>
<SlidersHorizontal size={16} className="shrink-0 text-muted-foreground" />
<span className="flex-1 text-muted-foreground" style={{ fontSize: 13, fontWeight: 600 }}>Properties</span>
<button className="shrink-0 border-none bg-transparent p-1 text-muted-foreground cursor-pointer hover:text-foreground" onClick={onToggle}>
<button className="shrink-0 border-none bg-transparent p-1 text-muted-foreground cursor-pointer hover:text-foreground" onClick={onToggle} title="Close Properties (⌘⇧I)">
<X size={16} />
</button>
</>

View File

@@ -26,7 +26,7 @@ export function buildViewCommands(config: ViewCommandsConfig): CommandAction[] {
{ id: 'view-editor', label: 'Editor Only', group: 'View', shortcut: '⌘1', keywords: ['layout', 'focus'], enabled: true, execute: () => onSetViewMode('editor-only') },
{ id: 'view-editor-list', label: 'Editor + Note List', group: 'View', shortcut: '⌘2', keywords: ['layout'], enabled: true, execute: () => onSetViewMode('editor-list') },
{ id: 'view-all', label: 'Full Layout', group: 'View', shortcut: '⌘3', keywords: ['layout', 'sidebar'], enabled: true, execute: () => onSetViewMode('all') },
{ id: 'toggle-inspector', label: 'Toggle Properties Panel', group: 'View', keywords: ['properties', 'inspector', 'panel', 'right', 'sidebar'], enabled: true, execute: onToggleInspector },
{ id: 'toggle-inspector', label: 'Toggle Properties Panel', group: 'View', shortcut: '⌘⇧I', keywords: ['properties', 'inspector', 'panel', 'right', 'sidebar'], enabled: true, execute: onToggleInspector },
{ id: 'toggle-diff', label: 'Toggle Diff Mode', group: 'View', keywords: ['diff', 'changes', 'git', 'compare', 'version'], enabled: hasActiveNote && activeNoteModified, execute: () => onToggleDiff?.() },
{ id: 'toggle-raw-editor', label: 'Toggle Raw Editor', group: 'View', keywords: ['raw', 'source', 'markdown', 'frontmatter', 'code', 'textarea'], enabled: hasActiveNote, execute: () => onToggleRawEditor?.() },
{ id: 'toggle-ai-panel', label: 'Toggle AI Panel', group: 'View', shortcut: '⌘I', keywords: ['ai', 'agent', 'chat', 'assistant', 'contextual'], enabled: true, execute: () => onToggleAIChat?.() },

View File

@@ -111,6 +111,7 @@ export function useAppCommands(config: AppCommandsConfig): CommandAction[] {
onGoForward: config.onGoForward,
onToggleAIChat: config.onToggleAIChat,
onToggleRawEditor: config.onToggleRawEditor,
onToggleInspector: config.onToggleInspector,
onOpenInNewWindow: config.onOpenInNewWindow,
activeTabPathRef: config.activeTabPathRef,
})

View File

@@ -200,4 +200,22 @@ describe('useAppKeyboard', () => {
fireKey('o', { metaKey: true, shiftKey: true })
expect(onOpenInNewWindow).toHaveBeenCalled()
})
it('Cmd+Shift+I triggers toggle inspector', () => {
const actions = makeActions()
const onToggleInspector = vi.fn()
renderHook(() => useAppKeyboard({ ...actions, onToggleInspector }))
fireKey('i', { metaKey: true, shiftKey: true })
expect(onToggleInspector).toHaveBeenCalled()
})
it('Cmd+Shift+I does not trigger AI chat toggle', () => {
const actions = makeActions()
const onToggleAIChat = vi.fn()
const onToggleInspector = vi.fn()
renderHook(() => useAppKeyboard({ ...actions, onToggleAIChat, onToggleInspector }))
fireKey('i', { metaKey: true, shiftKey: true })
expect(onToggleInspector).toHaveBeenCalled()
expect(onToggleAIChat).not.toHaveBeenCalled()
})
})

View File

@@ -19,6 +19,7 @@ interface KeyboardActions {
onGoForward?: () => void
onToggleAIChat?: () => void
onToggleRawEditor?: () => void
onToggleInspector?: () => void
onOpenInNewWindow?: () => void
activeTabPathRef: React.MutableRefObject<string | null>
}
@@ -64,7 +65,7 @@ function handleCmdKey(e: KeyboardEvent, keyMap: Record<string, ShortcutHandler>)
export function useAppKeyboard({
onQuickOpen, onCommandPalette, onSearch, onCreateNote, onOpenDailyNote, onSave, onOpenSettings, onTrashNote, onArchiveNote,
onSetViewMode, onZoomIn, onZoomOut, onZoomReset, onGoBack, onGoForward, onToggleAIChat, onToggleRawEditor, onOpenInNewWindow, activeTabPathRef,
onSetViewMode, onZoomIn, onZoomOut, onZoomReset, onGoBack, onGoForward, onToggleAIChat, onToggleRawEditor, onToggleInspector, onOpenInNewWindow, activeTabPathRef,
}: KeyboardActions) {
useEffect(() => {
const withActiveTab = (fn: (path: string) => void): ShortcutHandler => () => {
@@ -99,6 +100,12 @@ export function useAppKeyboard({
onSearch()
return
}
// Cmd+Shift+I: toggle properties/inspector panel
if ((e.metaKey || e.ctrlKey) && e.shiftKey && (e.key === 'i' || e.key === 'I')) {
e.preventDefault()
onToggleInspector?.()
return
}
// Cmd+Shift+O: open active note in new window
if ((e.metaKey || e.ctrlKey) && e.shiftKey && (e.key === 'o' || e.key === 'O')) {
e.preventDefault()
@@ -111,5 +118,5 @@ export function useAppKeyboard({
}
window.addEventListener('keydown', handleKeyDown)
return () => window.removeEventListener('keydown', handleKeyDown)
}, [onQuickOpen, onCommandPalette, onSearch, onCreateNote, onOpenDailyNote, onSave, onOpenSettings, onTrashNote, onArchiveNote, activeTabPathRef, onSetViewMode, onZoomIn, onZoomOut, onZoomReset, onGoBack, onGoForward, onToggleAIChat, onToggleRawEditor, onOpenInNewWindow])
}, [onQuickOpen, onCommandPalette, onSearch, onCreateNote, onOpenDailyNote, onSave, onOpenSettings, onTrashNote, onArchiveNote, activeTabPathRef, onSetViewMode, onZoomIn, onZoomOut, onZoomReset, onGoBack, onGoForward, onToggleAIChat, onToggleRawEditor, onToggleInspector, onOpenInNewWindow])
}

View File

@@ -52,4 +52,14 @@ describe('useLayoutPanels', () => {
act(() => result.current.handleInspectorResize(-500))
expect(result.current.inspectorWidth).toBe(500)
})
it('defaults inspector to collapsed', () => {
const { result } = renderHook(() => useLayoutPanels())
expect(result.current.inspectorCollapsed).toBe(true)
})
it('accepts initial inspector collapsed override', () => {
const { result } = renderHook(() => useLayoutPanels({ initialInspectorCollapsed: false }))
expect(result.current.inspectorCollapsed).toBe(false)
})
})

View File

@@ -11,7 +11,7 @@ export function useLayoutPanels(options?: { initialInspectorCollapsed?: boolean
const [sidebarWidth, setSidebarWidth] = useState(250)
const [noteListWidth, setNoteListWidth] = useState(300)
const [inspectorWidth, setInspectorWidth] = useState(280)
const [inspectorCollapsed, setInspectorCollapsed] = useState(options?.initialInspectorCollapsed ?? false)
const [inspectorCollapsed, setInspectorCollapsed] = useState(options?.initialInspectorCollapsed ?? true)
const handleSidebarResize = useCallback((delta: number) => setSidebarWidth((w) => Math.max(COLUMN_MIN_WIDTHS.sidebar, Math.min(400, w + delta))), [])
const handleNoteListResize = useCallback((delta: number) => setNoteListWidth((w) => Math.max(COLUMN_MIN_WIDTHS.noteList, Math.min(500, w + delta))), [])
const handleInspectorResize = useCallback((delta: number) => setInspectorWidth((w) => Math.max(COLUMN_MIN_WIDTHS.inspector, Math.min(500, w - delta))), [])