feat: Add dynamic Vue 3 components system

- Add dynamicComponents.ts service (~300 lines)
  - CSS scoping with high specificity
  - Async setup support with Suspense
  - Event bus for inter-component communication
  - Shared Pinia store with main app
  - No app overhead (uses render + createVNode)

- Add MCP tools for Vue components
  - render_vue_component
  - save_vue_component
  - load_vue_component
  - list_vue_components
  - delete_vue_component

- Add SQLite table for component persistence
- Add TypeScript declarations for webmcp
- Configure Vite for runtime template compilation
- Add comprehensive README with documentation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-13 04:15:53 -06:00
parent 52c93930e1
commit 075e167389
8 changed files with 1054 additions and 3 deletions

View File

@@ -22,6 +22,21 @@ db.run(`
)
`)
// Tabla para componentes Vue dinámicos
db.run(`
CREATE TABLE IF NOT EXISTS vue_components (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
template TEXT NOT NULL,
setup TEXT,
style TEXT,
props TEXT,
imports TEXT,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
)
`)
console.log('[DB] SQLite inicializado: agent-ui.db')
// API HTTP solamente - WebSocket lo maneja webmcp
@@ -85,6 +100,57 @@ Bun.serve({
return Response.json({ status: 'ok', timestamp: new Date().toISOString() }, { headers: corsHeaders })
}
// API de Componentes Vue
if (url.pathname === '/api/components') {
if (req.method === 'GET') {
const rows = db.query('SELECT * FROM vue_components ORDER BY updated_at DESC').all()
return Response.json(rows, { headers: corsHeaders })
}
if (req.method === 'POST') {
const body = await req.json()
const id = body.id || `comp-${Date.now()}`
const stmt = db.prepare(`
INSERT OR REPLACE INTO vue_components
(id, name, template, setup, style, props, imports, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
`)
stmt.run(
id,
body.name,
body.template,
body.setup || '',
body.style || '',
JSON.stringify(body.props || []),
JSON.stringify(body.imports || [])
)
return Response.json({ success: true, id }, { headers: corsHeaders })
}
if (req.method === 'DELETE') {
db.run('DELETE FROM vue_components')
return Response.json({ success: true }, { headers: corsHeaders })
}
}
// Obtener componente por ID
if (url.pathname.startsWith('/api/components/')) {
const id = url.pathname.split('/').pop()
if (req.method === 'GET') {
const row = db.query('SELECT * FROM vue_components WHERE id = ?').get(id)
if (!row) {
return Response.json({ error: 'Component not found' }, { status: 404, headers: corsHeaders })
}
return Response.json(row, { headers: corsHeaders })
}
if (req.method === 'DELETE') {
db.run('DELETE FROM vue_components WHERE id = ?', [id])
return Response.json({ success: true }, { headers: corsHeaders })
}
}
return new Response('Not Found', { status: 404, headers: corsHeaders })
}
})