feat: Add canvas snapshots to save and restore full canvas state
Implements save/restore system that captures HTML base, injected CSS, executed scripts, and floating Vue windows with their full definitions. Adds 4 MCP tools, backend CRUD API, Pinia store, and script logger.
This commit is contained in:
@@ -10,6 +10,7 @@ import { handleTables, handleStats, handleTableSchema, handleTableData, handleQu
|
||||
import { handleWhisperRoutes } from './whisper'
|
||||
import { handleRecordingsRoutes } from './recordings'
|
||||
import { handleClaudeStatus } from './claude-status'
|
||||
import { handleSnapshots, handleSnapshotById } from './snapshots'
|
||||
import { handleGitStatus, handleGitDiff, handleGitLog, handleGitLogCommit, handleGitCompare, handleGitBranches, handleGitCurrentBranch, handleGitTree, handleGitFile } from './git'
|
||||
|
||||
export async function handleRequest(req: Request): Promise<Response> {
|
||||
@@ -145,6 +146,18 @@ export async function handleRequest(req: Request): Promise<Response> {
|
||||
if (res) return res
|
||||
}
|
||||
|
||||
// Snapshots
|
||||
if (path === '/api/snapshots') {
|
||||
const res = await handleSnapshots(req)
|
||||
if (res) return res
|
||||
}
|
||||
|
||||
if (path.startsWith('/api/snapshots/')) {
|
||||
const id = path.split('/').pop()!
|
||||
const res = await handleSnapshotById(req, id)
|
||||
if (res) return res
|
||||
}
|
||||
|
||||
// Gitea
|
||||
if (path === '/api/gitea/repo' && req.method === 'POST') {
|
||||
return handleGiteaRepo(req)
|
||||
|
||||
49
server/routes/snapshots.ts
Normal file
49
server/routes/snapshots.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { db } from '../db'
|
||||
import { jsonResponse, errorResponse } from '../utils/cors'
|
||||
|
||||
export async function handleSnapshots(req: Request) {
|
||||
if (req.method === 'GET') {
|
||||
const rows = db.query(
|
||||
'SELECT id, name, thumbnail, created_at FROM canvas_snapshots ORDER BY created_at DESC'
|
||||
).all()
|
||||
return jsonResponse(rows)
|
||||
}
|
||||
|
||||
if (req.method === 'POST') {
|
||||
const body = await req.json()
|
||||
const id = body.id || `snap-${Date.now()}`
|
||||
const stmt = db.prepare(
|
||||
'INSERT OR REPLACE INTO canvas_snapshots (id, name, data, thumbnail, created_at) VALUES (?, ?, ?, ?, ?)'
|
||||
)
|
||||
stmt.run(
|
||||
id,
|
||||
body.name,
|
||||
typeof body.data === 'string' ? body.data : JSON.stringify(body.data),
|
||||
body.thumbnail || null,
|
||||
body.created_at || Date.now()
|
||||
)
|
||||
return jsonResponse({ success: true, id })
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export async function handleSnapshotById(req: Request, id: string) {
|
||||
if (req.method === 'GET') {
|
||||
const row = db.query('SELECT * FROM canvas_snapshots WHERE id = ?').get(id) as any
|
||||
if (!row) {
|
||||
return errorResponse('Snapshot not found', 404)
|
||||
}
|
||||
return jsonResponse({
|
||||
...row,
|
||||
data: JSON.parse(row.data)
|
||||
})
|
||||
}
|
||||
|
||||
if (req.method === 'DELETE') {
|
||||
db.run('DELETE FROM canvas_snapshots WHERE id = ?', [id])
|
||||
return jsonResponse({ success: true })
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
Reference in New Issue
Block a user