- webmcp.ts: Add tool tracking, unregisterTool(), clearAllTools() - tools/canvasTools.ts: render_html, render_vue_component - tools/componentTools.ts: save/load/list/delete_vue_component - tools/themeTools.ts: get_design_tokens, get_active_theme, set_theme_variable, save_theme - tools/globalTools.ts: get_current_page, navigate_to, list_available_tools - toolRegistry.ts: Orchestrates tool registration per page - App.vue: Initializes WebMCP once, watches route for tool changes - Canvas.vue: Removed tool registration (now handled by registry) Tools are now registered when entering a page and unregistered when leaving. Refresh initializes tools correctly for the current page. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
148 lines
4.7 KiB
TypeScript
148 lines
4.7 KiB
TypeScript
import { useCanvasStore } from '../../stores/canvas'
|
|
import { registerTool, unregisterTools } from '../webmcp'
|
|
import {
|
|
renderInlineComponent,
|
|
componentsApi,
|
|
type VueComponentDefinition
|
|
} from '../dynamicComponents'
|
|
|
|
export const COMPONENT_TOOLS = [
|
|
'save_vue_component',
|
|
'load_vue_component',
|
|
'list_vue_components',
|
|
'delete_vue_component'
|
|
]
|
|
|
|
function getCanvasContainer() {
|
|
return document.getElementById('canvas-content')
|
|
}
|
|
|
|
function removePlaceholder(container: HTMLElement) {
|
|
const placeholder = container.querySelector('.canvas-placeholder')
|
|
if (placeholder) placeholder.remove()
|
|
}
|
|
|
|
export function registerComponentTools() {
|
|
const canvasStore = useCanvasStore()
|
|
|
|
// save_vue_component
|
|
registerTool(
|
|
'save_vue_component',
|
|
'Guarda un componente Vue en la base de datos para reutilizarlo después',
|
|
{
|
|
type: 'object',
|
|
properties: {
|
|
id: { type: 'string', description: 'ID único del componente (se genera automáticamente si no se proporciona)' },
|
|
name: { type: 'string', description: 'Nombre del componente' },
|
|
template: { type: 'string', description: 'Template HTML del componente' },
|
|
setup: { type: 'string', description: 'Código de la función setup' },
|
|
style: { type: 'string', description: 'CSS del componente' },
|
|
props: { type: 'array', items: { type: 'string' }, description: 'Lista de props' },
|
|
imports: { type: 'array', items: { type: 'string' }, description: 'Funciones de Vue necesarias' }
|
|
},
|
|
required: ['name', 'template']
|
|
},
|
|
async (args: Omit<VueComponentDefinition, 'id'> & { id?: string }) => {
|
|
try {
|
|
const result = await componentsApi.save({
|
|
id: args.id || `comp-${Date.now()}`,
|
|
name: args.name,
|
|
template: args.template,
|
|
setup: args.setup,
|
|
style: args.style,
|
|
props: args.props,
|
|
imports: args.imports
|
|
})
|
|
canvasStore.addToHistory({ tool: 'save_vue_component', args, timestamp: Date.now() })
|
|
return `Componente "${args.name}" guardado con ID: ${result.id}`
|
|
} catch (e: any) {
|
|
return `Error al guardar: ${e.message}`
|
|
}
|
|
}
|
|
)
|
|
|
|
// load_vue_component
|
|
registerTool(
|
|
'load_vue_component',
|
|
'Carga un componente Vue guardado desde la base de datos y lo renderiza',
|
|
{
|
|
type: 'object',
|
|
properties: {
|
|
id: { type: 'string', description: 'ID del componente a cargar' },
|
|
componentProps: { type: 'object', description: 'Props para pasar al componente' },
|
|
mode: { type: 'string', enum: ['replace', 'append'], description: 'replace: limpia el canvas, append: agrega al final' }
|
|
},
|
|
required: ['id']
|
|
},
|
|
async (args: { id: string; componentProps?: Record<string, any>; mode?: string }) => {
|
|
try {
|
|
const definition = await componentsApi.getById(args.id)
|
|
if (!definition) {
|
|
return `Error: Componente con ID "${args.id}" no encontrado`
|
|
}
|
|
|
|
const container = getCanvasContainer()
|
|
if (!container) return 'Error: canvas no encontrado'
|
|
|
|
removePlaceholder(container)
|
|
|
|
const isAppend = args.mode === 'append'
|
|
const result = renderInlineComponent(definition, container, args.componentProps || {}, isAppend)
|
|
;(window as any).__vueComponentUnmount = result.unmount
|
|
|
|
canvasStore.addToHistory({ tool: 'load_vue_component', args, timestamp: Date.now() })
|
|
return `Componente "${definition.name}" cargado y renderizado`
|
|
} catch (e: any) {
|
|
return `Error: ${e.message}`
|
|
}
|
|
}
|
|
)
|
|
|
|
// list_vue_components
|
|
registerTool(
|
|
'list_vue_components',
|
|
'Lista todos los componentes Vue guardados en la base de datos',
|
|
{
|
|
type: 'object',
|
|
properties: {}
|
|
},
|
|
async () => {
|
|
try {
|
|
const components = await componentsApi.getAll()
|
|
if (components.length === 0) {
|
|
return 'No hay componentes guardados'
|
|
}
|
|
const list = components.map(c => `- ${c.id}: ${c.name}`).join('\n')
|
|
return `Componentes guardados:\n${list}`
|
|
} catch (e: any) {
|
|
return `Error: ${e.message}`
|
|
}
|
|
}
|
|
)
|
|
|
|
// delete_vue_component
|
|
registerTool(
|
|
'delete_vue_component',
|
|
'Elimina un componente Vue de la base de datos',
|
|
{
|
|
type: 'object',
|
|
properties: {
|
|
id: { type: 'string', description: 'ID del componente a eliminar' }
|
|
},
|
|
required: ['id']
|
|
},
|
|
async (args: { id: string }) => {
|
|
try {
|
|
await componentsApi.delete(args.id)
|
|
return `Componente "${args.id}" eliminado`
|
|
} catch (e: any) {
|
|
return `Error: ${e.message}`
|
|
}
|
|
}
|
|
)
|
|
}
|
|
|
|
export function unregisterComponentTools() {
|
|
unregisterTools(COMPONENT_TOOLS)
|
|
}
|