fix: live-reload CSS vars when saving active theme note in editor
When user edits a theme note directly in the editor and presses Cmd+S, the app now immediately re-applies CSS variables — no manual reload needed. Added notifyThemeSaved(path, content) to ThemeManager; wired into onNotePersisted callback so saving the active theme updates cachedThemeContent, triggering useThemeApplier. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -306,10 +306,16 @@ function App() {
|
||||
triggerIncrementalIndex()
|
||||
}, [vault, triggerIncrementalIndex])
|
||||
|
||||
const { notifyThemeSaved } = themeManager
|
||||
const onNotePersisted = useCallback((path: string, content: string) => {
|
||||
vault.clearUnsaved(path)
|
||||
notifyThemeSaved(path, content)
|
||||
}, [vault, notifyThemeSaved])
|
||||
|
||||
const { handleSave: handleSaveRaw, handleContentChange, savePendingForPath, savePending } = useEditorSaveWithLinks({
|
||||
updateEntry: vault.updateEntry,
|
||||
setTabs: notes.setTabs, setToastMessage, onAfterSave,
|
||||
onNotePersisted: vault.clearUnsaved,
|
||||
onNotePersisted,
|
||||
})
|
||||
useEffect(() => { contentChangeRef.current = handleContentChange }, [handleContentChange])
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ interface EditorSaveConfig {
|
||||
setTabs: (fn: SetStateAction<any[]>) => void
|
||||
setToastMessage: (msg: string | null) => void
|
||||
onAfterSave?: () => void
|
||||
/** Called after content is persisted — used to clear unsaved state. */
|
||||
onNotePersisted?: (path: string) => void
|
||||
/** Called after content is persisted — used to clear unsaved state and live-reload themes. */
|
||||
onNotePersisted?: (path: string, content: string) => void
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -41,8 +41,9 @@ export function useEditorSave({ updateVaultContent, setTabs, setToastMessage, on
|
||||
if (!pending) return false
|
||||
if (pathFilter && pending.path !== pathFilter) return false
|
||||
await saveNote(pending.path, pending.content)
|
||||
const savedContent = pending.content
|
||||
pendingContentRef.current = null
|
||||
onNotePersisted?.(pending.path)
|
||||
onNotePersisted?.(pending.path, savedContent)
|
||||
return true
|
||||
}, [saveNote, onNotePersisted])
|
||||
|
||||
@@ -53,7 +54,7 @@ export function useEditorSave({ updateVaultContent, setTabs, setToastMessage, on
|
||||
const saved = await flushPending()
|
||||
if (!saved && unsavedFallback) {
|
||||
await saveNote(unsavedFallback.path, unsavedFallback.content)
|
||||
onNotePersisted?.(unsavedFallback.path)
|
||||
onNotePersisted?.(unsavedFallback.path, unsavedFallback.content)
|
||||
setToastMessage('Saved')
|
||||
onAfterSave()
|
||||
return
|
||||
|
||||
@@ -8,7 +8,7 @@ export function useEditorSaveWithLinks(config: {
|
||||
setTabs: Parameters<typeof useEditorSave>[0]['setTabs']
|
||||
setToastMessage: (msg: string | null) => void
|
||||
onAfterSave: () => void
|
||||
onNotePersisted?: (path: string) => void
|
||||
onNotePersisted?: (path: string, content: string) => void
|
||||
}) {
|
||||
const { updateEntry } = config
|
||||
const saveContent = useCallback((path: string, content: string) => {
|
||||
|
||||
@@ -441,6 +441,55 @@ describe('useThemeManager', () => {
|
||||
expect(result.current.isDark).toBe(false)
|
||||
})
|
||||
|
||||
it('notifyThemeSaved updates CSS vars when active theme is saved', async () => {
|
||||
const { result } = renderHook(() =>
|
||||
useThemeManager('/vault', entries, allContent)
|
||||
)
|
||||
await waitFor(() => {
|
||||
expect(result.current.activeThemeId).toBe(THEME_PATH_DEFAULT)
|
||||
})
|
||||
expect(document.documentElement.style.getPropertyValue('--background')).toBe('#FFFFFF')
|
||||
|
||||
const updatedContent = `---
|
||||
type: Theme
|
||||
Description: Light theme
|
||||
background: "#1a1a2e"
|
||||
foreground: "#e0e0e0"
|
||||
primary: "#155DFF"
|
||||
sidebar: "#2a2a3e"
|
||||
text-primary: "#e0e0e0"
|
||||
---
|
||||
|
||||
# Default Theme
|
||||
`
|
||||
act(() => {
|
||||
result.current.notifyThemeSaved(THEME_PATH_DEFAULT, updatedContent)
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
expect(document.documentElement.style.getPropertyValue('--background')).toBe('#1a1a2e')
|
||||
})
|
||||
expect(document.documentElement.style.getPropertyValue('--sidebar')).toBe('#2a2a3e')
|
||||
})
|
||||
|
||||
it('notifyThemeSaved is a no-op for non-active theme path', async () => {
|
||||
const { result } = renderHook(() =>
|
||||
useThemeManager('/vault', entries, allContent)
|
||||
)
|
||||
await waitFor(() => {
|
||||
expect(result.current.activeThemeId).toBe(THEME_PATH_DEFAULT)
|
||||
})
|
||||
expect(document.documentElement.style.getPropertyValue('--background')).toBe('#FFFFFF')
|
||||
|
||||
act(() => {
|
||||
result.current.notifyThemeSaved(THEME_PATH_DARK, DARK_THEME_CONTENT)
|
||||
})
|
||||
|
||||
// Background should still be the default theme's white, not dark
|
||||
await new Promise(r => setTimeout(r, 50))
|
||||
expect(document.documentElement.style.getPropertyValue('--background')).toBe('#FFFFFF')
|
||||
})
|
||||
|
||||
it('calls ensure_vault_themes on mount with vaultPath', async () => {
|
||||
renderHook(() => useThemeManager('/vault', entries, allContent))
|
||||
await waitFor(() => {
|
||||
|
||||
@@ -124,6 +124,8 @@ export interface ThemeManager {
|
||||
reloadThemes: () => Promise<void>
|
||||
/** Update a single frontmatter property on the active theme note. */
|
||||
updateThemeProperty: (key: string, value: string) => Promise<void>
|
||||
/** Notify that the active theme note was saved with new content (live-reload on Cmd+S). */
|
||||
notifyThemeSaved: (path: string, content: string) => void
|
||||
}
|
||||
|
||||
/** Manages loading and persisting the active theme path from vault settings. */
|
||||
@@ -275,6 +277,10 @@ export function useThemeManager(
|
||||
|
||||
const reloadThemes = useCallback(async () => { await reload() }, [reload])
|
||||
|
||||
const notifyThemeSaved = useCallback((path: string, content: string) => {
|
||||
if (path === activeThemeId) setCachedThemeContent(content)
|
||||
}, [activeThemeId])
|
||||
|
||||
const updateThemeProperty = useCallback(async (key: string, value: string) => {
|
||||
if (!activeThemeId) return
|
||||
try {
|
||||
@@ -290,6 +296,6 @@ export function useThemeManager(
|
||||
return {
|
||||
themes, activeThemeId, activeTheme,
|
||||
activeThemeContent: cachedThemeContent,
|
||||
isDark, switchTheme, createTheme, reloadThemes, updateThemeProperty,
|
||||
isDark, switchTheme, createTheme, reloadThemes, updateThemeProperty, notifyThemeSaved,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user