Files
tolaria/src/hooks/useAppCommands.ts

250 lines
8.9 KiB
TypeScript
Raw Normal View History

import { useCallback, useRef } from 'react'
import { useAppKeyboard } from './useAppKeyboard'
import { useCommandRegistry } from './useCommandRegistry'
import type { CommandAction } from './useCommandRegistry'
import { useKeyboardNavigation } from './useKeyboardNavigation'
import { useMenuEvents } from './useMenuEvents'
import type { SidebarSelection, SidebarFilter, ThemeFile, VaultEntry } from '../types'
import type { NoteListFilter } from '../utils/noteListHelpers'
import type { ViewMode } from './useViewMode'
interface Tab { entry: VaultEntry; content: string }
interface AppCommandsConfig {
activeTabPath: string | null
activeTabPathRef: React.MutableRefObject<string | null>
handleCloseTabRef: React.MutableRefObject<(path: string) => void>
tabs: Tab[]
entries: VaultEntry[]
modifiedCount: number
selection: SidebarSelection
onQuickOpen: () => void
onCommandPalette: () => void
feat: full-text search with qmd backend and SearchPanel UI (Cmd+Shift+F) (#64) * feat: add search backend (qmd CLI) and design file - Add search.rs: qmd integration for keyword (BM25), semantic (vsearch), and hybrid search modes via CLI JSON output - Register search_vault Tauri command in lib.rs - Design file with 3 frames: empty, results, no-results states - Fix pre-existing test failures: install @tauri-apps/plugin-opener, add mock in test setup Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add SearchPanel UI with Cmd+Shift+F shortcut - SearchPanel component: full-text search overlay with keyword/semantic mode toggle, debounced search (200ms), arrow key navigation, result count + elapsed time display, note type badges from vault entries - Cmd+Shift+F shortcut registered in useAppKeyboard - Mock search_vault handler in mock-tauri for browser dev mode - SearchResult/SearchResponse types in types.ts - Tests: 15 new tests for SearchPanel + 2 for keyboard shortcut - scrollIntoView mock in test setup for jsdom compatibility Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: correct SearchPanel imports for Tauri/browser dual-mode Use invoke from @tauri-apps/api/core and mockInvoke from mock-tauri with a searchCall wrapper, fixing the TypeScript build error. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: make test_detect_collection_fallback robust when qmd not installed * fix: reset localStorage between App tests to prevent view mode state leak --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 17:09:50 +01:00
onSearch: () => void
onCreateNote: () => void
onOpenDailyNote: () => void
onCreateNoteOfType: (type: string) => void
onSave: () => void
onOpenSettings: () => void
onTrashNote: (path: string) => void
onRestoreNote: (path: string) => void
onArchiveNote: (path: string) => void
onUnarchiveNote: (path: string) => void
onCommitPush: () => void
onResolveConflicts?: () => void
onSetViewMode: (mode: ViewMode) => void
onToggleInspector: () => void
onToggleDiff?: () => void
onToggleRawEditor?: () => void
activeNoteModified: boolean
onZoomIn: () => void
onZoomOut: () => void
onZoomReset: () => void
zoomLevel: number
onSelect: (sel: SidebarSelection) => void
onCloseTab: (path: string) => void
onSwitchTab: (path: string) => void
onReplaceActiveTab: (entry: VaultEntry) => void
onSelectNote: (entry: VaultEntry) => void
feat: back/forward navigation between opened notes (#75) * feat: add useNavigationHistory hook for browser-style back/forward Pure state management hook that tracks a navigation stack of note paths with cursor-based back/forward traversal. Handles edge cases: duplicate pushes (no-op), forward stack cleared on new push, invalid path skipping, and path removal when tabs close. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: integrate back/forward navigation into UI and keyboard shortcuts - Add Back/Forward arrow buttons to TabBar (left of tabs) - Wire useNavigationHistory through App → Editor → TabBar - Add Cmd+[ and Cmd+] keyboard shortcuts via useAppKeyboard - Register Go Back/Go Forward in command palette - History tracks active tab changes, skips closed tabs gracefully - Navigation from history doesn't re-push to stack (ref guard) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add mouse button 3/4 and trackpad swipe for back/forward - Mouse buttons 3 (back) and 4 (forward) trigger navigation - macOS trackpad two-finger horizontal swipe triggers back/forward using accumulated wheel deltaX with a 120px threshold - Debounced reset after 300ms of inactivity Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * design: add back-forward-nav.pen with 3 state frames Shows: default (both disabled), one note visited (back disabled), two notes visited (back enabled). Matches tab bar button placement. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 19:39:12 +01:00
onGoBack?: () => void
onGoForward?: () => void
canGoBack?: boolean
canGoForward?: boolean
themes?: ThemeFile[]
activeThemeId?: string | null
onSwitchTheme?: (themeId: string) => void
onCreateTheme?: () => void
onOpenTheme?: (themeId: string) => void
onOpenVault?: () => void
onCreateType?: () => void
onToggleAIChat?: () => void
onCheckForUpdates?: () => void
onRemoveActiveVault?: () => void
onRestoreGettingStarted?: () => void
onRestoreDefaultThemes?: () => void
isGettingStartedHidden?: boolean
vaultCount?: number
mcpStatus?: string
onInstallMcp?: () => void
onEmptyTrash?: () => void
trashedCount?: number
onReopenClosedTab?: () => void
onReindexVault?: () => void
onReloadVault?: () => void
onRepairVault?: () => void
onSetNoteIcon?: () => void
onRemoveNoteIcon?: () => void
activeNoteHasIcon?: boolean
noteListFilter?: NoteListFilter
onSetNoteListFilter?: (filter: NoteListFilter) => void
onOpenInNewWindow?: () => void
}
/** Sets up keyboard shortcuts, command registry, menu events, and keyboard navigation. */
export function useAppCommands(config: AppCommandsConfig): CommandAction[] {
const entriesRef = useRef(config.entries)
// eslint-disable-next-line react-hooks/refs
entriesRef.current = config.entries
const toggleArchive = useCallback((path: string) => {
const entry = entriesRef.current.find(e => e.path === path)
;(entry?.archived ? config.onUnarchiveNote : config.onArchiveNote)(path)
}, [config.onArchiveNote, config.onUnarchiveNote])
const toggleTrash = useCallback((path: string) => {
const entry = entriesRef.current.find(e => e.path === path)
;(entry?.trashed ? config.onRestoreNote : config.onTrashNote)(path)
}, [config.onTrashNote, config.onRestoreNote])
const { onSelect } = config
const selectFilter = useCallback((filter: SidebarFilter) => {
onSelect({ kind: 'filter', filter })
}, [onSelect])
const viewChanges = useCallback(() => {
onSelect({ kind: 'filter', filter: 'changes' })
}, [onSelect])
useAppKeyboard({
onQuickOpen: config.onQuickOpen,
onCommandPalette: config.onCommandPalette,
feat: full-text search with qmd backend and SearchPanel UI (Cmd+Shift+F) (#64) * feat: add search backend (qmd CLI) and design file - Add search.rs: qmd integration for keyword (BM25), semantic (vsearch), and hybrid search modes via CLI JSON output - Register search_vault Tauri command in lib.rs - Design file with 3 frames: empty, results, no-results states - Fix pre-existing test failures: install @tauri-apps/plugin-opener, add mock in test setup Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add SearchPanel UI with Cmd+Shift+F shortcut - SearchPanel component: full-text search overlay with keyword/semantic mode toggle, debounced search (200ms), arrow key navigation, result count + elapsed time display, note type badges from vault entries - Cmd+Shift+F shortcut registered in useAppKeyboard - Mock search_vault handler in mock-tauri for browser dev mode - SearchResult/SearchResponse types in types.ts - Tests: 15 new tests for SearchPanel + 2 for keyboard shortcut - scrollIntoView mock in test setup for jsdom compatibility Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: correct SearchPanel imports for Tauri/browser dual-mode Use invoke from @tauri-apps/api/core and mockInvoke from mock-tauri with a searchCall wrapper, fixing the TypeScript build error. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: make test_detect_collection_fallback robust when qmd not installed * fix: reset localStorage between App tests to prevent view mode state leak --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 17:09:50 +01:00
onSearch: config.onSearch,
onCreateNote: config.onCreateNote,
onOpenDailyNote: config.onOpenDailyNote,
onSave: config.onSave,
onOpenSettings: config.onOpenSettings,
onTrashNote: toggleTrash,
onArchiveNote: toggleArchive,
onSetViewMode: config.onSetViewMode,
onZoomIn: config.onZoomIn,
onZoomOut: config.onZoomOut,
onZoomReset: config.onZoomReset,
feat: back/forward navigation between opened notes (#75) * feat: add useNavigationHistory hook for browser-style back/forward Pure state management hook that tracks a navigation stack of note paths with cursor-based back/forward traversal. Handles edge cases: duplicate pushes (no-op), forward stack cleared on new push, invalid path skipping, and path removal when tabs close. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: integrate back/forward navigation into UI and keyboard shortcuts - Add Back/Forward arrow buttons to TabBar (left of tabs) - Wire useNavigationHistory through App → Editor → TabBar - Add Cmd+[ and Cmd+] keyboard shortcuts via useAppKeyboard - Register Go Back/Go Forward in command palette - History tracks active tab changes, skips closed tabs gracefully - Navigation from history doesn't re-push to stack (ref guard) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add mouse button 3/4 and trackpad swipe for back/forward - Mouse buttons 3 (back) and 4 (forward) trigger navigation - macOS trackpad two-finger horizontal swipe triggers back/forward using accumulated wheel deltaX with a 120px threshold - Debounced reset after 300ms of inactivity Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * design: add back-forward-nav.pen with 3 state frames Shows: default (both disabled), one note visited (back disabled), two notes visited (back enabled). Matches tab bar button placement. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 19:39:12 +01:00
onGoBack: config.onGoBack,
onGoForward: config.onGoForward,
onToggleAIChat: config.onToggleAIChat,
onToggleRawEditor: config.onToggleRawEditor,
onReopenClosedTab: config.onReopenClosedTab,
onOpenInNewWindow: config.onOpenInNewWindow,
activeTabPathRef: config.activeTabPathRef,
handleCloseTabRef: config.handleCloseTabRef,
})
useMenuEvents({
onSetViewMode: config.onSetViewMode,
onCreateNote: config.onCreateNote,
onCreateType: config.onCreateType,
onOpenDailyNote: config.onOpenDailyNote,
onQuickOpen: config.onQuickOpen,
onSave: config.onSave,
onOpenSettings: config.onOpenSettings,
onToggleInspector: config.onToggleInspector,
onCommandPalette: config.onCommandPalette,
onZoomIn: config.onZoomIn,
onZoomOut: config.onZoomOut,
onZoomReset: config.onZoomReset,
onArchiveNote: toggleArchive,
onTrashNote: toggleTrash,
onSearch: config.onSearch,
onToggleRawEditor: config.onToggleRawEditor,
onToggleDiff: config.onToggleDiff,
onToggleAIChat: config.onToggleAIChat,
onGoBack: config.onGoBack,
onGoForward: config.onGoForward,
onCheckForUpdates: config.onCheckForUpdates,
onSelectFilter: selectFilter,
onOpenVault: config.onOpenVault,
onRemoveActiveVault: config.onRemoveActiveVault,
onRestoreGettingStarted: config.onRestoreGettingStarted,
onCreateTheme: config.onCreateTheme,
onRestoreDefaultThemes: config.onRestoreDefaultThemes,
onCommitPush: config.onCommitPush,
onResolveConflicts: config.onResolveConflicts,
onViewChanges: viewChanges,
onInstallMcp: config.onInstallMcp,
onReindexVault: config.onReindexVault,
onReloadVault: config.onReloadVault,
onRepairVault: config.onRepairVault,
onEmptyTrash: config.onEmptyTrash,
onReopenClosedTab: config.onReopenClosedTab,
onOpenInNewWindow: config.onOpenInNewWindow,
activeTabPathRef: config.activeTabPathRef,
handleCloseTabRef: config.handleCloseTabRef,
activeTabPath: config.activeTabPath,
modifiedCount: config.modifiedCount,
})
const commands = useCommandRegistry({
activeTabPath: config.activeTabPath,
entries: config.entries,
modifiedCount: config.modifiedCount,
onQuickOpen: config.onQuickOpen,
onCreateNote: config.onCreateNote,
onCreateNoteOfType: config.onCreateNoteOfType,
onSave: config.onSave,
onOpenSettings: config.onOpenSettings,
onTrashNote: config.onTrashNote,
onRestoreNote: config.onRestoreNote,
onArchiveNote: config.onArchiveNote,
onUnarchiveNote: config.onUnarchiveNote,
onCommitPush: config.onCommitPush,
onResolveConflicts: config.onResolveConflicts,
onSetViewMode: config.onSetViewMode,
onToggleInspector: config.onToggleInspector,
onToggleDiff: config.onToggleDiff,
onToggleRawEditor: config.onToggleRawEditor,
onToggleAIChat: config.onToggleAIChat,
onOpenVault: config.onOpenVault,
activeNoteModified: config.activeNoteModified,
onZoomIn: config.onZoomIn,
onZoomOut: config.onZoomOut,
onZoomReset: config.onZoomReset,
zoomLevel: config.zoomLevel,
onSelect: config.onSelect,
onOpenDailyNote: config.onOpenDailyNote,
onCloseTab: config.onCloseTab,
feat: back/forward navigation between opened notes (#75) * feat: add useNavigationHistory hook for browser-style back/forward Pure state management hook that tracks a navigation stack of note paths with cursor-based back/forward traversal. Handles edge cases: duplicate pushes (no-op), forward stack cleared on new push, invalid path skipping, and path removal when tabs close. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: integrate back/forward navigation into UI and keyboard shortcuts - Add Back/Forward arrow buttons to TabBar (left of tabs) - Wire useNavigationHistory through App → Editor → TabBar - Add Cmd+[ and Cmd+] keyboard shortcuts via useAppKeyboard - Register Go Back/Go Forward in command palette - History tracks active tab changes, skips closed tabs gracefully - Navigation from history doesn't re-push to stack (ref guard) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add mouse button 3/4 and trackpad swipe for back/forward - Mouse buttons 3 (back) and 4 (forward) trigger navigation - macOS trackpad two-finger horizontal swipe triggers back/forward using accumulated wheel deltaX with a 120px threshold - Debounced reset after 300ms of inactivity Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * design: add back-forward-nav.pen with 3 state frames Shows: default (both disabled), one note visited (back disabled), two notes visited (back enabled). Matches tab bar button placement. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 19:39:12 +01:00
onGoBack: config.onGoBack,
onGoForward: config.onGoForward,
canGoBack: config.canGoBack,
canGoForward: config.canGoForward,
themes: config.themes,
activeThemeId: config.activeThemeId,
onSwitchTheme: config.onSwitchTheme,
onCreateTheme: config.onCreateTheme,
onOpenTheme: config.onOpenTheme,
onCheckForUpdates: config.onCheckForUpdates,
onCreateType: config.onCreateType,
onRemoveActiveVault: config.onRemoveActiveVault,
onRestoreGettingStarted: config.onRestoreGettingStarted,
onRestoreDefaultThemes: config.onRestoreDefaultThemes,
isGettingStartedHidden: config.isGettingStartedHidden,
vaultCount: config.vaultCount,
mcpStatus: config.mcpStatus,
onInstallMcp: config.onInstallMcp,
onEmptyTrash: config.onEmptyTrash,
trashedCount: config.trashedCount,
onReindexVault: config.onReindexVault,
onReloadVault: config.onReloadVault,
onRepairVault: config.onRepairVault,
onSetNoteIcon: config.onSetNoteIcon,
onRemoveNoteIcon: config.onRemoveNoteIcon,
activeNoteHasIcon: config.activeNoteHasIcon,
selection: config.selection,
noteListFilter: config.noteListFilter,
onSetNoteListFilter: config.onSetNoteListFilter,
onOpenInNewWindow: config.onOpenInNewWindow,
})
useKeyboardNavigation({
tabs: config.tabs,
activeTabPath: config.activeTabPath,
entries: config.entries,
selection: config.selection,
onSwitchTab: config.onSwitchTab,
onReplaceActiveTab: config.onReplaceActiveTab,
onSelectNote: config.onSelectNote,
})
return commands
}