feat: Add theme system with visual editor

- Backend: themes table and API endpoints (CRUD, export, design-tokens)
- Theme store with preview, apply, and persistence
- ThemesPage with collapsible variables editor and live preview
- Components: ColorPicker (HSL), VariableEditor, ThemePreview, ThemeListItem
- Integration: $theme helper for dynamic components, get_design_tokens MCP tool
- Navigation: /themes route with palette icon in toolbar

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-13 05:10:18 -06:00
parent d1c0f62fc3
commit b880038b07
19 changed files with 3358 additions and 11 deletions

View File

@@ -18,6 +18,7 @@ import {
} from 'vue'
import { setActivePinia, type Pinia } from 'pinia'
import { useCanvasStore } from '../stores/canvas'
import { useThemeStore } from '../stores/theme'
const API_URL = 'http://localhost:4101'
@@ -171,6 +172,12 @@ function getCanvasStore() {
return useCanvasStore()
}
function getThemeStore() {
const globalPinia = (window as any).__pinia as Pinia | undefined
if (globalPinia) setActivePinia(globalPinia)
return useThemeStore()
}
const dynamicHelpers = {
$emit: (event: string, ...args: any[]) => eventBus.emit(event, ...args),
$on: (event: string, cb: EventCallback) => eventBus.on(event, cb),
@@ -182,7 +189,15 @@ const dynamicHelpers = {
list: () => componentsApi.getAll(),
save: (comp: VueComponentDefinition) => componentsApi.save(comp),
},
$theme: {
getVariable: (name: string) => getComputedStyle(document.documentElement).getPropertyValue(`--${name}`).trim(),
setVariable: (name: string, value: string) => document.documentElement.style.setProperty(`--${name}`, value),
getTokens: () => getThemeStore().designTokens,
getActiveTheme: () => getThemeStore().activeTheme,
getVariables: () => getThemeStore().currentVariables,
},
useCanvasStore: getCanvasStore,
useThemeStore: getThemeStore,
$nextTick: nextTick,
}