Add bins feature: upload, de-perspective, gallery

- lib/storage.mjs: bin CRUD with bin: prefix
- lib/grid-image.mjs: compute_bin_size() capped at 1024px
- server.mjs: POST/GET/PUT/DELETE /api/bins routes; PUT /api/bins/:id/corners
  re-processes image via process_grid_image with rows=1 cols=1
- public/lib/api.mjs: bin API wrappers including upload_bin()
- public/index.html: Bins nav button
- public/templates.html: t-section-bins, t-bin-card, t-dialog-bin-editor
- public/app.mjs: render_bins(), open_bin_editor() using Grid_Setup,
  save/cancel wiring in init()
- public/style.css: bin gallery and card styles

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-01 04:28:03 +00:00
parent f370b6d48d
commit 28b4590903
8 changed files with 510 additions and 11 deletions

View File

@@ -174,6 +174,28 @@ export function find_pdf_references(pdf_id) {
return list_components().filter(c => c.file_ids?.includes(pdf_id));
}
// --- Bins ---
export function list_bins() {
const result = [];
for (const [key] of store.data.entries()) {
if (key.startsWith('bin:')) result.push(store.get(key));
}
return result.sort((a, b) => b.created_at - a.created_at);
}
export function get_bin(id) {
return store.get(`bin:${id}`) ?? null;
}
export function set_bin(bin) {
store.set(`bin:${bin.id}`, bin);
}
export function delete_bin(id) {
return store.delete(`bin:${id}`);
}
// --- Grid images ---
export function list_grid_images() {