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:
2026-02-15 01:57:04 -06:00
parent 9a636e26a7
commit d5ee533db9
16 changed files with 1055 additions and 653 deletions

View File

@@ -14,6 +14,7 @@ import { initToolRegistry, activatePageTools, initToolsOnRefresh } from './servi
import { setTerminalControls } from './services/tools/handlers/terminalHandlers'
import { setResponseControls } from './services/tools/handlers/responseHandlers'
import { useCanvasStore } from './stores/canvas'
import { useProjectCanvasStore } from './stores/projectCanvas'
const route = useRoute()
const router = useRouter()
@@ -63,6 +64,7 @@ const terminalRef = ref<InstanceType<typeof FloatingTerminal> | null>(null)
const responseRef = ref<InstanceType<typeof FloatingResponse> | null>(null)
const voiceRef = ref<InstanceType<typeof FloatingVoice> | null>(null)
const canvasStore = useCanvasStore()
const projectCanvasStore = useProjectCanvasStore()
// Voice FAB push-to-talk state
const voicePTTActive = ref(false)
@@ -238,7 +240,7 @@ function triggerToolFlash() {
}, 500)
}
type PageName = 'home' | 'canvas' | 'components' | 'themes' | 'projects' | 'project-canvas' | 'database' | 'source' | 'terminal' | 'tools'
type PageName = 'home' | 'canvas' | 'components' | 'themes' | 'project-canvas' | 'database' | 'source' | 'terminal' | 'tools'
onMounted(async () => {
// Connect to WebSocket for Claude status updates
@@ -350,6 +352,11 @@ watch(() => route.name, (newPage) => {
<header class="app-header">
<div class="header-left">
<h1 class="logo">Agent UI</h1>
<template v-if="projectCanvasStore.activeCanvas && route.name === 'project-canvas'">
<span class="header-sep">/</span>
<span class="header-canvas-name">{{ projectCanvasStore.activeCanvas.name }}</span>
<span v-if="projectCanvasStore.activeCanvas.is_system" class="header-canvas-badge">Sistema</span>
</template>
<button class="debug-btn" :class="{ active: showDebugConsole }" @click="showDebugConsole = !showDebugConsole" title="Debug Console">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/>
@@ -561,6 +568,27 @@ watch(() => route.name, (newPage) => {
margin: 0;
}
.header-sep {
color: var(--text-muted);
font-size: 1.1rem;
opacity: 0.4;
}
.header-canvas-name {
font-size: 0.9rem;
font-weight: 500;
color: var(--text-secondary);
}
.header-canvas-badge {
padding: 0.125rem 0.5rem;
background: rgba(99, 102, 241, 0.15);
color: #6366f1;
font-size: 0.6875rem;
font-weight: 500;
border-radius: 999px;
}
.refresh-btn {
display: flex;
align-items: center;