feat: expose all theme.json properties in theme editor

Add ThemePropertyEditor component that surfaces all customizable
properties from theme.json — typography, headings, lists, code blocks,
blockquote, table, and horizontal rule — organized into collapsible
sections with appropriate input types (number, color, select, text).

- themeSchema.ts: derives flat property list from theme.json with
  auto-detected input types, units, and select options
- ThemePropertyEditor.tsx: sectioned editor with collapsible sections,
  keyboard-accessible toggles, debounced live updates
- ThemeManager: add updateThemeProperty() and activeThemeContent
- SettingsPanel: show property editor below theme list when active

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
lucaronin
2026-03-05 14:39:19 +01:00
parent 338f073421
commit a364cbc2bd
7 changed files with 856 additions and 2 deletions

View File

@@ -117,10 +117,13 @@ export interface ThemeManager {
themes: ThemeFile[]
activeThemeId: string | null
activeTheme: ThemeFile | null
activeThemeContent: string | undefined
isDark: boolean
switchTheme: (themeId: string) => Promise<void>
createTheme: (name?: string) => Promise<string>
reloadThemes: () => Promise<void>
/** Update a single frontmatter property on the active theme note. */
updateThemeProperty: (key: string, value: string) => Promise<void>
}
/** Manages loading and persisting the active theme path from vault settings. */
@@ -195,6 +198,7 @@ export function useThemeManager(
vaultPath: string | null,
entries: VaultEntry[],
allContent: Record<string, string>,
updateContent?: (path: string, content: string) => void,
): ThemeManager {
// Ensure default theme files exist on vault open (creates theme/ dir + defaults if missing)
useEffect(() => {
@@ -268,5 +272,21 @@ export function useThemeManager(
const reloadThemes = useCallback(async () => { await reload() }, [reload])
return { themes, activeThemeId, activeTheme, isDark, switchTheme, createTheme, reloadThemes }
const updateThemeProperty = useCallback(async (key: string, value: string) => {
if (!activeThemeId) return
try {
const newContent = await tauriCall<string>('update_frontmatter', {
path: activeThemeId,
key,
value,
})
updateContent?.(activeThemeId, newContent)
} catch (err) { console.error('Failed to update theme property:', err) }
}, [activeThemeId, updateContent])
return {
themes, activeThemeId, activeTheme,
activeThemeContent: cachedThemeContent,
isDark, switchTheme, createTheme, reloadThemes, updateThemeProperty,
}
}