feat: Add additional theme management MCP tools
- list_themes: List all available themes (system + custom) - switch_theme: Change active theme by name or ID - set_default_theme: Set a theme as the default startup theme - delete_theme: Remove custom themes (protects system themes) - reset_theme: Discard unsaved changes - export_theme: Export theme as JSON for backup/sharing Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,13 @@ export const THEME_TOOLS = [
|
||||
'get_design_tokens',
|
||||
'get_active_theme',
|
||||
'set_theme_variable',
|
||||
'save_theme'
|
||||
'save_theme',
|
||||
'list_themes',
|
||||
'switch_theme',
|
||||
'set_default_theme',
|
||||
'delete_theme',
|
||||
'reset_theme',
|
||||
'export_theme'
|
||||
]
|
||||
|
||||
export function registerThemeTools() {
|
||||
@@ -205,6 +211,232 @@ export function registerThemeTools() {
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// list_themes
|
||||
registerTool(
|
||||
'list_themes',
|
||||
'Lista todos los temas disponibles (del sistema y personalizados)',
|
||||
{
|
||||
type: 'object',
|
||||
properties: {}
|
||||
},
|
||||
async () => {
|
||||
try {
|
||||
const themeStore = useThemeStore()
|
||||
await themeStore.fetchThemes()
|
||||
|
||||
const themes = themeStore.themes
|
||||
if (themes.length === 0) {
|
||||
return 'No hay temas disponibles'
|
||||
}
|
||||
|
||||
const systemThemes = themes.filter(t => t.is_system)
|
||||
const userThemes = themes.filter(t => !t.is_system)
|
||||
|
||||
let result = `Temas disponibles (${themes.length}):\n\n`
|
||||
|
||||
if (systemThemes.length > 0) {
|
||||
result += `[SISTEMA]\n`
|
||||
result += systemThemes.map(t =>
|
||||
` - ${t.name} (${t.id})${t.is_default ? ' [DEFAULT]' : ''}`
|
||||
).join('\n')
|
||||
result += '\n\n'
|
||||
}
|
||||
|
||||
if (userThemes.length > 0) {
|
||||
result += `[PERSONALIZADOS]\n`
|
||||
result += userThemes.map(t =>
|
||||
` - ${t.name} (${t.id})${t.is_default ? ' [DEFAULT]' : ''}`
|
||||
).join('\n')
|
||||
}
|
||||
|
||||
return result
|
||||
} catch (e: any) {
|
||||
return `Error: ${e.message}`
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// switch_theme
|
||||
registerTool(
|
||||
'switch_theme',
|
||||
'Cambia al tema especificado por nombre o ID',
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
theme: {
|
||||
type: 'string',
|
||||
description: 'Nombre o ID del tema a activar'
|
||||
}
|
||||
},
|
||||
required: ['theme']
|
||||
},
|
||||
async (args: { theme: string }) => {
|
||||
try {
|
||||
const themeStore = useThemeStore()
|
||||
await themeStore.fetchThemes()
|
||||
|
||||
// Find theme by ID or name
|
||||
const theme = themeStore.themes.find(t =>
|
||||
t.id === args.theme || t.name.toLowerCase() === args.theme.toLowerCase()
|
||||
)
|
||||
|
||||
if (!theme) {
|
||||
const available = themeStore.themes.map(t => t.name).join(', ')
|
||||
return `Tema "${args.theme}" no encontrado.\nDisponibles: ${available}`
|
||||
}
|
||||
|
||||
themeStore.selectTheme(theme)
|
||||
return `Tema cambiado a "${theme.name}"\nID: ${theme.id}`
|
||||
} catch (e: any) {
|
||||
return `Error: ${e.message}`
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// set_default_theme
|
||||
registerTool(
|
||||
'set_default_theme',
|
||||
'Establece un tema como el default (se cargará automáticamente al iniciar)',
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
theme: {
|
||||
type: 'string',
|
||||
description: 'Nombre o ID del tema a establecer como default'
|
||||
}
|
||||
},
|
||||
required: ['theme']
|
||||
},
|
||||
async (args: { theme: string }) => {
|
||||
try {
|
||||
const themeStore = useThemeStore()
|
||||
await themeStore.fetchThemes()
|
||||
|
||||
const theme = themeStore.themes.find(t =>
|
||||
t.id === args.theme || t.name.toLowerCase() === args.theme.toLowerCase()
|
||||
)
|
||||
|
||||
if (!theme) {
|
||||
const available = themeStore.themes.map(t => t.name).join(', ')
|
||||
return `Tema "${args.theme}" no encontrado.\nDisponibles: ${available}`
|
||||
}
|
||||
|
||||
await themeStore.setDefaultTheme(theme.id)
|
||||
return `Tema "${theme.name}" establecido como default.\nSe cargará automáticamente al iniciar la aplicación.`
|
||||
} catch (e: any) {
|
||||
return `Error: ${e.message}`
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// delete_theme
|
||||
registerTool(
|
||||
'delete_theme',
|
||||
'Elimina un tema personalizado (no se pueden eliminar temas del sistema)',
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
theme: {
|
||||
type: 'string',
|
||||
description: 'Nombre o ID del tema a eliminar'
|
||||
}
|
||||
},
|
||||
required: ['theme']
|
||||
},
|
||||
async (args: { theme: string }) => {
|
||||
try {
|
||||
const themeStore = useThemeStore()
|
||||
await themeStore.fetchThemes()
|
||||
|
||||
const theme = themeStore.themes.find(t =>
|
||||
t.id === args.theme || t.name.toLowerCase() === args.theme.toLowerCase()
|
||||
)
|
||||
|
||||
if (!theme) {
|
||||
return `Tema "${args.theme}" no encontrado`
|
||||
}
|
||||
|
||||
if (theme.is_system) {
|
||||
return `No se puede eliminar "${theme.name}" porque es un tema del sistema`
|
||||
}
|
||||
|
||||
const success = await themeStore.deleteTheme(theme.id)
|
||||
if (success) {
|
||||
return `Tema "${theme.name}" eliminado correctamente`
|
||||
} else {
|
||||
return `Error al eliminar el tema "${theme.name}"`
|
||||
}
|
||||
} catch (e: any) {
|
||||
return `Error: ${e.message}`
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// reset_theme
|
||||
registerTool(
|
||||
'reset_theme',
|
||||
'Descarta todos los cambios no guardados y restaura el tema activo original',
|
||||
{
|
||||
type: 'object',
|
||||
properties: {}
|
||||
},
|
||||
() => {
|
||||
try {
|
||||
const themeStore = useThemeStore()
|
||||
|
||||
if (!themeStore.previewTheme) {
|
||||
return 'No hay cambios pendientes para descartar'
|
||||
}
|
||||
|
||||
const themeName = themeStore.activeTheme?.name || 'desconocido'
|
||||
themeStore.resetPreview()
|
||||
return `Cambios descartados. Tema "${themeName}" restaurado a su estado original.`
|
||||
} catch (e: any) {
|
||||
return `Error: ${e.message}`
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// export_theme
|
||||
registerTool(
|
||||
'export_theme',
|
||||
'Exporta un tema como JSON para respaldo o compartir',
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
theme: {
|
||||
type: 'string',
|
||||
description: 'Nombre o ID del tema a exportar. Si no se especifica, exporta el tema activo.'
|
||||
}
|
||||
}
|
||||
},
|
||||
async (args: { theme?: string }) => {
|
||||
try {
|
||||
const themeStore = useThemeStore()
|
||||
|
||||
let theme = themeStore.activeTheme
|
||||
|
||||
if (args.theme) {
|
||||
await themeStore.fetchThemes()
|
||||
theme = themeStore.themes.find(t =>
|
||||
t.id === args.theme || t.name.toLowerCase() === args.theme!.toLowerCase()
|
||||
) || null
|
||||
}
|
||||
|
||||
if (!theme) {
|
||||
return args.theme
|
||||
? `Tema "${args.theme}" no encontrado`
|
||||
: 'No hay tema activo para exportar'
|
||||
}
|
||||
|
||||
const exported = themeStore.exportTheme(theme)
|
||||
return `Tema "${theme.name}" exportado:\n\n${exported}`
|
||||
} catch (e: any) {
|
||||
return `Error: ${e.message}`
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export function unregisterThemeTools() {
|
||||
|
||||
Reference in New Issue
Block a user