feat: Add multi-canvas system with project canvas support

- Add project_canvas and canvas_components tables for persistent canvas storage
- Add ProjectCanvas store with full CRUD operations
- Add ProjectCanvasPage for rendering saved canvas with components
- Add ProjectsPage for managing canvas list (create, clone, delete)
- Add HomePage that loads default canvas or falls back to dynamic canvas
- Add toolbar support for displaying canvas as pages with custom icons
- Add component usage validation to prevent deletion of components in use
- Add MCP tools for canvas management (list, create, update, delete, clone)
- Update router with /canvas/:id and /projects routes
- Update Toolbar to show dynamic canvas pages from database
This commit is contained in:
2026-02-13 06:32:46 -06:00
parent 2e64dceb1e
commit 8a017db777
13 changed files with 2016 additions and 13 deletions

View File

@@ -1,9 +1,12 @@
<script setup lang="ts">
import { onMounted } from 'vue'
import { RouterLink, useRoute } from 'vue-router'
import { useCanvasStore } from '../stores/canvas'
import { useProjectCanvasStore } from '../stores/projectCanvas'
const route = useRoute()
const canvasStore = useCanvasStore()
const projectCanvasStore = useProjectCanvasStore()
function clearCanvas() {
const container = document.getElementById('canvas-content')
@@ -25,19 +28,54 @@ function clearCanvas() {
function toggleHistory() {
canvasStore.toggleHistoryPanel()
}
function isCanvasActive(canvasId: string) {
return route.path === `/canvas/${canvasId}`
}
onMounted(() => {
projectCanvasStore.fetchToolbarCanvases()
})
</script>
<template>
<aside class="toolbar">
<!-- Navegación -->
<!-- Navegacion principal -->
<div class="toolbar-section nav-section">
<RouterLink to="/" class="toolbar-btn" :class="{ active: route.path === '/' }" title="Canvas">
<RouterLink to="/" class="toolbar-btn" :class="{ active: route.path === '/' }" title="Home">
<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 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
<polyline points="9 22 9 12 15 12 15 22"/>
</svg>
</RouterLink>
<!-- Canvas dinamicos de la toolbar -->
<RouterLink
v-for="canvas in projectCanvasStore.toolbarCanvases"
:key="canvas.id"
:to="`/canvas/${canvas.id}`"
class="toolbar-btn"
:class="{ active: isCanvasActive(canvas.id) }"
:title="canvas.name"
>
<span v-if="canvas.toolbar_icon" class="toolbar-emoji">{{ canvas.toolbar_icon }}</span>
<svg v-else 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>
</div>
<div class="toolbar-divider"></div>
<!-- Gestion -->
<div class="toolbar-section">
<RouterLink to="/projects" class="toolbar-btn" :class="{ active: route.path === '/projects' }" title="Proyectos">
<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="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/>
</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">
@@ -132,4 +170,9 @@ function toggleHistory() {
background: rgba(99, 102, 241, 0.15);
color: #6366f1;
}
.toolbar-emoji {
font-size: 1.25rem;
line-height: 1;
}
</style>