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:
@@ -7,18 +7,19 @@ function parseCanvas(row: any) {
|
||||
is_default: !!row.is_default,
|
||||
is_system: !!row.is_system,
|
||||
show_in_toolbar: !!row.show_in_toolbar,
|
||||
status: row.status || 'active',
|
||||
config: row.config ? JSON.parse(row.config) : null,
|
||||
tools: row.tools ? JSON.parse(row.tools) : []
|
||||
}
|
||||
}
|
||||
|
||||
export function handleToolbarCanvas() {
|
||||
const rows = db.query('SELECT * FROM project_canvas WHERE show_in_toolbar = 1 ORDER BY toolbar_order ASC, name ASC').all()
|
||||
const rows = db.query('SELECT * FROM project_canvas WHERE show_in_toolbar = 1 AND (status = \'active\' OR status IS NULL) ORDER BY toolbar_order ASC, name ASC').all()
|
||||
return jsonResponse((rows as any[]).map(parseCanvas))
|
||||
}
|
||||
|
||||
export function handleDefaultCanvas() {
|
||||
const row = db.query('SELECT * FROM project_canvas WHERE is_default = 1 LIMIT 1').get() as any
|
||||
const row = db.query('SELECT * FROM project_canvas WHERE is_default = 1 AND (status = \'active\' OR status IS NULL) LIMIT 1').get() as any
|
||||
if (!row) {
|
||||
return jsonResponse({ hasDefault: false })
|
||||
}
|
||||
@@ -27,7 +28,10 @@ export function handleDefaultCanvas() {
|
||||
|
||||
export async function handleCanvas(req: Request) {
|
||||
if (req.method === 'GET') {
|
||||
const rows = db.query('SELECT * FROM project_canvas ORDER BY is_system DESC, is_default DESC, name ASC').all()
|
||||
const url = new URL(req.url)
|
||||
const includeArchived = url.searchParams.get('include_archived') === 'true'
|
||||
const whereClause = includeArchived ? '' : 'WHERE (status = \'active\' OR status IS NULL)'
|
||||
const rows = db.query(`SELECT * FROM project_canvas ${whereClause} ORDER BY is_system DESC, is_default DESC, name ASC`).all()
|
||||
return jsonResponse((rows as any[]).map(parseCanvas))
|
||||
}
|
||||
|
||||
@@ -129,6 +133,7 @@ export async function handleCanvasById(req: Request, id: string, action?: string
|
||||
if (body.show_in_toolbar !== undefined) { updates.push('show_in_toolbar = ?'); values.push(body.show_in_toolbar ? 1 : 0) }
|
||||
if (body.toolbar_icon !== undefined) { updates.push('toolbar_icon = ?'); values.push(body.toolbar_icon) }
|
||||
if (body.toolbar_order !== undefined) { updates.push('toolbar_order = ?'); values.push(body.toolbar_order) }
|
||||
if (body.status !== undefined) { updates.push('status = ?'); values.push(body.status) }
|
||||
}
|
||||
|
||||
if (updates.length > 0) {
|
||||
@@ -141,13 +146,13 @@ export async function handleCanvasById(req: Request, id: string, action?: string
|
||||
return jsonResponse({ success: true, id })
|
||||
}
|
||||
|
||||
// DELETE /api/canvas/:id
|
||||
// DELETE /api/canvas/:id (soft delete - archive)
|
||||
if (req.method === 'DELETE' && !action) {
|
||||
const canvas = db.query('SELECT is_system FROM project_canvas WHERE id = ?').get(id) as { is_system: number } | null
|
||||
if (canvas?.is_system) {
|
||||
return errorResponse('Cannot delete system canvas', 403)
|
||||
}
|
||||
db.run('DELETE FROM project_canvas WHERE id = ?', [id])
|
||||
db.run('UPDATE project_canvas SET status = \'archived\', show_in_toolbar = 0, updated_at = CURRENT_TIMESTAMP WHERE id = ?', [id])
|
||||
return jsonResponse({ success: true })
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user