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

@@ -1,6 +1,8 @@
<script setup lang="ts">
import { RouterLink, useRoute } from 'vue-router'
import { useCanvasStore } from '../stores/canvas'
const route = useRoute()
const canvasStore = useCanvasStore()
function clearCanvas() {
@@ -8,13 +10,13 @@ function clearCanvas() {
if (container) {
container.innerHTML = `
<div class="canvas-placeholder">
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<rect x="3" y="3" width="18" height="18" rx="2"/>
<path d="M3 9h18"/>
<path d="M9 21V9"/>
</svg>
<p>Canvas listo</p>
<span>Claude Code puede renderizar contenido aquí usando las herramientas MCP</span>
<span>Claude Code puede renderizar contenido aquí</span>
</div>
`
}
@@ -27,9 +29,42 @@ function toggleHistory() {
<template>
<aside class="toolbar">
<!-- Navegación -->
<div class="toolbar-section nav-section">
<RouterLink to="/" class="toolbar-btn" :class="{ active: route.path === '/' }" title="Canvas">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="3" width="18" height="18" rx="2"/>
<path d="M3 9h18"/>
<path d="M9 21V9"/>
</svg>
</RouterLink>
<RouterLink to="/components" class="toolbar-btn" :class="{ active: route.path === '/components' }" title="Componentes">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/>
<polyline points="3.29 7 12 12 20.71 7"/>
<line x1="12" y1="22" x2="12" y2="12"/>
</svg>
</RouterLink>
<RouterLink to="/themes" class="toolbar-btn" :class="{ active: route.path === '/themes' }" title="Temas">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="13.5" cy="6.5" r="2.5"/>
<circle cx="19" cy="11.5" r="2.5"/>
<circle cx="17" cy="18.5" r="2.5"/>
<circle cx="8.5" cy="17.5" r="2.5"/>
<circle cx="5" cy="10.5" r="2.5"/>
<path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.93 0 1.82-.13 2.67-.36"/>
</svg>
</RouterLink>
</div>
<div class="toolbar-divider"></div>
<!-- Acciones -->
<div class="toolbar-section">
<button class="toolbar-btn" @click="clearCanvas" title="Limpiar canvas">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M3 6h18"/>
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/>
<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/>
@@ -37,7 +72,7 @@ function toggleHistory() {
</button>
<button class="toolbar-btn" @click="toggleHistory" title="Historial">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 8v4l3 3"/>
<circle cx="12" cy="12" r="10"/>
</svg>
@@ -54,6 +89,7 @@ function toggleHistory() {
padding: 0.75rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.toolbar-section {
@@ -62,6 +98,12 @@ function toggleHistory() {
gap: 0.5rem;
}
.toolbar-divider {
height: 1px;
background: var(--border-color);
margin: 0.25rem 0;
}
.toolbar-btn {
width: 40px;
height: 40px;
@@ -74,6 +116,7 @@ function toggleHistory() {
color: var(--text-secondary);
cursor: pointer;
transition: all 0.15s ease;
text-decoration: none;
}
.toolbar-btn:hover {
@@ -84,4 +127,9 @@ function toggleHistory() {
.toolbar-btn:active {
transform: scale(0.95);
}
.toolbar-btn.active {
background: rgba(99, 102, 241, 0.15);
color: #6366f1;
}
</style>