Add grid image system with multi-panel support and SPA routing
- Grid images: photograph component boxes in sub-sections, assemble into one logical grid via perspective warp (homography) - Source image gallery: bulk upload photos separately from grid setup - Grid drafts: persist partial work, resume across sessions - Multi-panel grids: define rows/cols per photo, system computes panel layout; process partially configured grids, edit individual panels - Pan/zoom canvas editor (HiDPI-aware, touch support) for corner alignment - SPA routing with canonical URLs (history.pushState, server catch-all) - Express error visibility: uncaughtException/unhandledRejection handlers and 4-arg error middleware - Original filename stored on source image upload - Various null-safety fixes and CSS [hidden] override fixes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -80,3 +80,69 @@ export function set_inventory_entry(entry) {
|
||||
export function delete_inventory_entry(id) {
|
||||
return store.delete(`i:${id}`);
|
||||
}
|
||||
|
||||
// --- Grid drafts ---
|
||||
|
||||
export function list_grid_drafts() {
|
||||
const result = [];
|
||||
for (const [key] of store.data.entries()) {
|
||||
if (key.startsWith('d:')) result.push(store.get(key));
|
||||
}
|
||||
return result.sort((a, b) => b.updated_at - a.updated_at);
|
||||
}
|
||||
|
||||
export function get_grid_draft(id) {
|
||||
return store.get(`d:${id}`) ?? null;
|
||||
}
|
||||
|
||||
export function set_grid_draft(draft) {
|
||||
store.set(`d:${draft.id}`, draft);
|
||||
}
|
||||
|
||||
export function delete_grid_draft(id) {
|
||||
return store.delete(`d:${id}`);
|
||||
}
|
||||
|
||||
// --- Source images ---
|
||||
|
||||
export function list_source_images() {
|
||||
const result = [];
|
||||
for (const [key] of store.data.entries()) {
|
||||
if (key.startsWith('s:')) result.push(store.get(key));
|
||||
}
|
||||
return result.sort((a, b) => b.created_at - a.created_at);
|
||||
}
|
||||
|
||||
export function get_source_image(id) {
|
||||
return store.get(`s:${id}`) ?? null;
|
||||
}
|
||||
|
||||
export function add_source_image(src) {
|
||||
store.set(`s:${src.id}`, src);
|
||||
}
|
||||
|
||||
export function delete_source_image(id) {
|
||||
return store.delete(`s:${id}`);
|
||||
}
|
||||
|
||||
// --- Grid images ---
|
||||
|
||||
export function list_grid_images() {
|
||||
const result = [];
|
||||
for (const [key] of store.data.entries()) {
|
||||
if (key.startsWith('g:')) result.push(store.get(key));
|
||||
}
|
||||
return result.sort((a, b) => b.created_at - a.created_at);
|
||||
}
|
||||
|
||||
export function get_grid_image(id) {
|
||||
return store.get(`g:${id}`) ?? null;
|
||||
}
|
||||
|
||||
export function set_grid_image(grid) {
|
||||
store.set(`g:${grid.id}`, grid);
|
||||
}
|
||||
|
||||
export function delete_grid_image(id) {
|
||||
return store.delete(`g:${id}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user