feat: Add canvas gallery with soft delete, snapshots and components
Replace the empty dynamic canvas placeholder with a gallery showing saved canvases, snapshots and Vue components. Users can create new canvases, restore snapshots, load components, and manage canvas toolbar/archive settings from the gallery. - Backend: soft delete (archive) instead of hard delete, status column - Frontend: CanvasGallery component with grid, search, settings popover - Show canvas name in global header when viewing a project canvas - Remove ProjectsPage (replaced by gallery), clean all references - MCP tools: project category available on canvas page, update handlers
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, onUnmounted } from 'vue'
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { renderInlineComponent, type VueComponentDefinition } from '../services/dynamicComponents'
|
||||
import { useCanvasStore } from '../stores/canvas'
|
||||
import CanvasGallery from './CanvasGallery.vue'
|
||||
|
||||
const canvasStore = useCanvasStore()
|
||||
const showGallery = ref(true)
|
||||
|
||||
function handleLoadComponent(e: Event) {
|
||||
const detail = (e as CustomEvent).detail
|
||||
@@ -12,6 +14,9 @@ function handleLoadComponent(e: Event) {
|
||||
const container = document.getElementById('canvas-content')
|
||||
if (!container) return
|
||||
|
||||
// Hide gallery when MCP renders content
|
||||
showGallery.value = false
|
||||
|
||||
const placeholder = container.querySelector('.canvas-placeholder')
|
||||
if (placeholder) placeholder.remove()
|
||||
|
||||
@@ -32,26 +37,42 @@ function handleLoadComponent(e: Event) {
|
||||
canvasStore.addToHistory({ tool: 'load_vue_component', args: detail, timestamp: Date.now() })
|
||||
}
|
||||
|
||||
function handleContentRendered() {
|
||||
showGallery.value = false
|
||||
}
|
||||
|
||||
function handleClearCanvas() {
|
||||
showGallery.value = true
|
||||
const container = document.getElementById('canvas-content')
|
||||
if (container) {
|
||||
// Remove all non-gallery content
|
||||
const children = Array.from(container.children)
|
||||
for (const child of children) {
|
||||
if (!(child as HTMLElement).classList?.contains('canvas-placeholder')) {
|
||||
child.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('load-vue-component', handleLoadComponent)
|
||||
window.addEventListener('clear-canvas', handleClearCanvas)
|
||||
window.addEventListener('canvas-content-rendered', handleContentRendered)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('load-vue-component', handleLoadComponent)
|
||||
window.removeEventListener('clear-canvas', handleClearCanvas)
|
||||
window.removeEventListener('canvas-content-rendered', handleContentRendered)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="canvas-container">
|
||||
<div id="canvas-content" class="canvas-content">
|
||||
<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">
|
||||
<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>Haz clic en el cuadrado azul (abajo derecha) para conectar con Claude Code</span>
|
||||
<div v-if="showGallery" class="canvas-placeholder">
|
||||
<CanvasGallery @snapshot-restored="showGallery = false" @component-loaded="showGallery = false" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -75,28 +96,7 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.canvas-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
min-height: 400px;
|
||||
color: var(--text-muted);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.canvas-placeholder svg {
|
||||
margin-bottom: 1rem;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.canvas-placeholder p {
|
||||
font-size: 1.25rem;
|
||||
margin: 0 0 0.5rem 0;
|
||||
}
|
||||
|
||||
.canvas-placeholder span {
|
||||
font-size: 0.875rem;
|
||||
opacity: 0.7;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user