diff --git a/src/components/CommandPalette.tsx b/src/components/CommandPalette.tsx
index 08c805ea..ba360460 100644
--- a/src/components/CommandPalette.tsx
+++ b/src/components/CommandPalette.tsx
@@ -51,7 +51,7 @@ export function CommandPalette({ open, commands, onClose }: CommandPaletteProps)
useEffect(() => {
if (open) {
setQuery('') // eslint-disable-line react-hooks/set-state-in-effect -- reset on open
- setSelectedIndex(0) // eslint-disable-line react-hooks/set-state-in-effect -- reset on open
+ setSelectedIndex(0)
setTimeout(() => inputRef.current?.focus(), 50)
}
}, [open])
diff --git a/src/components/SearchPanel.tsx b/src/components/SearchPanel.tsx
index 093bc7dc..29ede4a3 100644
--- a/src/components/SearchPanel.tsx
+++ b/src/components/SearchPanel.tsx
@@ -209,7 +209,6 @@ function SearchContent({
onMouseEnter={() => onHover(i)}
>
- {/* eslint-disable-next-line react-hooks/static-components -- icon from static map lookup */}
{result.title}
{noteType && (
diff --git a/src/hooks/useCommandRegistry.test.ts b/src/hooks/useCommandRegistry.test.ts
index 1f3b2d34..78493c16 100644
--- a/src/hooks/useCommandRegistry.test.ts
+++ b/src/hooks/useCommandRegistry.test.ts
@@ -437,6 +437,46 @@ describe('useCommandRegistry', () => {
const groups = new Set(result.current.map(c => c.group))
expect(groups).toContain('Appearance')
})
+
+ it('generates open-theme commands for each theme when onOpenTheme is provided', () => {
+ const onOpenTheme = vi.fn()
+ const { result } = renderHook(() => useCommandRegistry(makeConfig({
+ themes: themeFixtures, activeThemeId: 'default', onOpenTheme,
+ })))
+ const openDefault = result.current.find(c => c.id === 'open-theme-default')
+ const openDark = result.current.find(c => c.id === 'open-theme-dark')
+ expect(openDefault).toBeDefined()
+ expect(openDefault!.label).toBe('Edit Default Theme')
+ expect(openDefault!.group).toBe('Appearance')
+ expect(openDefault!.enabled).toBe(true)
+ expect(openDark).toBeDefined()
+ expect(openDark!.label).toBe('Edit Dark Theme')
+ })
+
+ it('omits open-theme commands when onOpenTheme is not provided', () => {
+ const { result } = renderHook(() => useCommandRegistry(makeConfig({
+ themes: themeFixtures, activeThemeId: 'default',
+ })))
+ expect(result.current.find(c => c.id === 'open-theme-default')).toBeUndefined()
+ expect(result.current.find(c => c.id === 'open-theme-dark')).toBeUndefined()
+ })
+
+ it('calls onOpenTheme with correct themeId when open-theme command executes', () => {
+ const onOpenTheme = vi.fn()
+ const { result } = renderHook(() => useCommandRegistry(makeConfig({
+ themes: themeFixtures, activeThemeId: 'default', onOpenTheme,
+ })))
+ result.current.find(c => c.id === 'open-theme-dark')!.execute()
+ expect(onOpenTheme).toHaveBeenCalledWith('dark')
+ })
+
+ it('open-theme command is always enabled regardless of active theme', () => {
+ const onOpenTheme = vi.fn()
+ const { result } = renderHook(() => useCommandRegistry(makeConfig({
+ themes: themeFixtures, activeThemeId: 'default', onOpenTheme,
+ })))
+ expect(result.current.find(c => c.id === 'open-theme-default')!.enabled).toBe(true)
+ })
})
})
diff --git a/src/hooks/useThemeManager.ts b/src/hooks/useThemeManager.ts
index 95d3d2e3..3ef828ea 100644
--- a/src/hooks/useThemeManager.ts
+++ b/src/hooks/useThemeManager.ts
@@ -296,7 +296,6 @@ export function useThemeManager(
const entry = entries.find(e => e.path === activeThemeId)
if (!entry || !isEntryRemoved(entry)) return
clearTheme()
- // eslint-disable-next-line react-hooks/set-state-in-effect -- sync fallback when active theme is deleted
setActiveThemeId(null)
if (vaultPath) tauriCall('set_active_theme', { vaultPath, themeId: null }).catch(() => {})
}, [entries, activeThemeId, clearTheme, vaultPath, setActiveThemeId])