From 3e83d314f0449a8f7115c19585d2e5b73995439f Mon Sep 17 00:00:00 2001 From: lucaronin Date: Tue, 3 Mar 2026 00:31:10 +0100 Subject: [PATCH] feat: add "New Type" command to command palette (Cmd+K) --- .claude-pid | 2 +- src/App.tsx | 1 + src/hooks/useAppCommands.ts | 2 ++ src/hooks/useCommandRegistry.test.ts | 35 ++++++++++++++++++++++++++++ src/hooks/useCommandRegistry.ts | 5 +++- 5 files changed, 43 insertions(+), 2 deletions(-) diff --git a/.claude-pid b/.claude-pid index 45ca854b..b06eaf3f 100644 --- a/.claude-pid +++ b/.claude-pid @@ -1 +1 @@ -21351 +3852 diff --git a/src/App.tsx b/src/App.tsx index 5cd7c3ca..28bafdee 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -325,6 +325,7 @@ function App() { if (entry) notes.handleSelectNote(entry) }, onOpenVault: vaultSwitcher.handleOpenLocalFolder, + onCreateType: dialogs.openCreateType, onToggleAIChat: dialogs.toggleAIChat, onCheckForUpdates: handleCheckForUpdates, isUpdating: updateStatus.state === 'downloading' || updateStatus.state === 'ready', diff --git a/src/hooks/useAppCommands.ts b/src/hooks/useAppCommands.ts index d4c364b7..918387d2 100644 --- a/src/hooks/useAppCommands.ts +++ b/src/hooks/useAppCommands.ts @@ -53,6 +53,7 @@ interface AppCommandsConfig { onCreateTheme?: () => void onOpenTheme?: (themeId: string) => void onOpenVault?: () => void + onCreateType?: () => void onToggleAIChat?: () => void onCheckForUpdates?: () => void isUpdating?: boolean @@ -152,6 +153,7 @@ export function useAppCommands(config: AppCommandsConfig): CommandAction[] { onCreateTheme: config.onCreateTheme, onOpenTheme: config.onOpenTheme, onOpenVault: config.onOpenVault, + onCreateType: config.onCreateType, onToggleAIChat: config.onToggleAIChat, onCheckForUpdates: config.onCheckForUpdates, isUpdating: config.isUpdating, diff --git a/src/hooks/useCommandRegistry.test.ts b/src/hooks/useCommandRegistry.test.ts index 7c817fb3..50f918ea 100644 --- a/src/hooks/useCommandRegistry.test.ts +++ b/src/hooks/useCommandRegistry.test.ts @@ -316,6 +316,41 @@ describe('useCommandRegistry', () => { }) }) + describe('create-type command', () => { + it('has create-type command in Note group when onCreateType is provided', () => { + const onCreateType = vi.fn() + const { result } = renderHook(() => useCommandRegistry(makeConfig({ onCreateType }))) + const cmd = result.current.find(c => c.id === 'create-type') + expect(cmd).toBeDefined() + expect(cmd!.label).toBe('New Type') + expect(cmd!.group).toBe('Note') + expect(cmd!.enabled).toBe(true) + }) + + it('is disabled when onCreateType is not provided', () => { + const { result } = renderHook(() => useCommandRegistry(makeConfig())) + const cmd = result.current.find(c => c.id === 'create-type') + expect(cmd).toBeDefined() + expect(cmd!.enabled).toBe(false) + }) + + it('calls onCreateType when executed', () => { + const onCreateType = vi.fn() + const { result } = renderHook(() => useCommandRegistry(makeConfig({ onCreateType }))) + result.current.find(c => c.id === 'create-type')!.execute() + expect(onCreateType).toHaveBeenCalled() + }) + + it('has relevant keywords for discoverability', () => { + const onCreateType = vi.fn() + const { result } = renderHook(() => useCommandRegistry(makeConfig({ onCreateType }))) + const cmd = result.current.find(c => c.id === 'create-type') + expect(cmd!.keywords).toContain('new') + expect(cmd!.keywords).toContain('create') + expect(cmd!.keywords).toContain('type') + }) + }) + describe('type-aware commands', () => { it('generates "New [Type]" commands from vault entries', () => { const entries = [ diff --git a/src/hooks/useCommandRegistry.ts b/src/hooks/useCommandRegistry.ts index ee9453ee..8cf70c7c 100644 --- a/src/hooks/useCommandRegistry.ts +++ b/src/hooks/useCommandRegistry.ts @@ -25,6 +25,7 @@ interface CommandRegistryConfig { onSave: () => void onOpenSettings: () => void onOpenVault?: () => void + onCreateType?: () => void onTrashNote: (path: string) => void onRestoreNote: (path: string) => void onArchiveNote: (path: string) => void @@ -178,6 +179,7 @@ export function useCommandRegistry(config: CommandRegistryConfig): CommandAction onGoBack, onGoForward, canGoBack, canGoForward, themes, activeThemeId, onSwitchTheme, onCreateTheme, onOpenTheme, onCheckForUpdates, isUpdating, + onCreateType, } = config const hasActiveNote = activeTabPath !== null @@ -205,6 +207,7 @@ export function useCommandRegistry(config: CommandRegistryConfig): CommandAction // Note actions (contextual) { id: 'create-note', label: 'Create New Note', group: 'Note', shortcut: '⌘N', keywords: ['new', 'add'], enabled: true, execute: onCreateNote }, + { id: 'create-type', label: 'New Type', group: 'Note', keywords: ['new', 'create', 'type', 'template'], enabled: !!onCreateType, execute: () => onCreateType?.() }, { id: 'open-daily-note', label: "Open Today's Note", group: 'Note', shortcut: '⌘J', keywords: ['daily', 'journal', 'today'], enabled: true, execute: onOpenDailyNote }, { id: 'save-note', label: 'Save Note', group: 'Note', shortcut: '⌘S', keywords: ['write'], enabled: hasActiveNote, execute: onSave }, { id: 'close-tab', label: 'Close Tab', group: 'Note', shortcut: '⌘W', keywords: [], enabled: hasActiveNote, execute: () => { if (activeTabPath) onCloseTab(activeTabPath) } }, @@ -241,7 +244,7 @@ export function useCommandRegistry(config: CommandRegistryConfig): CommandAction return cmds }, [ hasActiveNote, activeTabPath, isArchived, isTrashed, modifiedCount, - onQuickOpen, onCreateNote, onCreateNoteOfType, onSave, onOpenSettings, + onQuickOpen, onCreateNote, onCreateNoteOfType, onCreateType, onSave, onOpenSettings, onTrashNote, onRestoreNote, onArchiveNote, onUnarchiveNote, onCommitPush, onSetViewMode, onToggleInspector, onToggleRawEditor, onToggleAIChat, onOpenVault, onCheckForUpdates, isUpdating,