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

@@ -31,6 +31,14 @@ export const useProjectCanvasStore = defineStore('projectCanvas', () => {
canvases.value.find(c => c.type === 'dynamic')
)
const activeCanvasesList = computed(() =>
canvases.value.filter(c => c.status !== 'archived')
)
const archivedCanvases = computed(() =>
canvases.value.filter(c => c.status === 'archived')
)
const canvasCount = computed(() => canvases.value.length)
const hasActiveCanvas = computed(() => activeCanvas.value !== null)
@@ -38,11 +46,12 @@ export const useProjectCanvasStore = defineStore('projectCanvas', () => {
const hasDefaultCanvas = computed(() => defaultCanvas.value !== null)
// Actions
async function fetchCanvases() {
async function fetchCanvases(includeArchived = false) {
loading.value = true
error.value = null
try {
const res = await fetch(`${API_URL}/api/canvas`)
const url = includeArchived ? `${API_URL}/api/canvas?include_archived=true` : `${API_URL}/api/canvas`
const res = await fetch(url)
if (!res.ok) throw new Error('Failed to fetch canvases')
canvases.value = await res.json()
} catch (e) {
@@ -168,6 +177,10 @@ export const useProjectCanvasStore = defineStore('projectCanvas', () => {
}
}
async function restoreCanvas(id: string): Promise<boolean> {
return updateCanvas(id, { status: 'active' } as any)
}
async function cloneCanvas(id: string, newName?: string): Promise<string | null> {
saving.value = true
error.value = null
@@ -304,6 +317,8 @@ export const useProjectCanvasStore = defineStore('projectCanvas', () => {
projectCanvases,
systemCanvases,
dynamicCanvas,
activeCanvasesList,
archivedCanvases,
canvasCount,
hasActiveCanvas,
hasDefaultCanvas,
@@ -315,6 +330,7 @@ export const useProjectCanvasStore = defineStore('projectCanvas', () => {
createCanvas,
updateCanvas,
deleteCanvas,
restoreCanvas,
cloneCanvas,
fetchCanvasComponents,
addComponentToCanvas,