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:
@@ -86,6 +86,22 @@ function bilinear_sample(pixels, width, height, x, y, out, out_idx) {
|
||||
// Public API
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Compute natural size for a single de-perspectived bin image (cap at 1024px)
|
||||
export function compute_bin_size(corners) {
|
||||
const [tl, tr, br, bl] = corners;
|
||||
const top_w = Math.hypot(tr.x - tl.x, tr.y - tl.y);
|
||||
const bot_w = Math.hypot(br.x - bl.x, br.y - bl.y);
|
||||
const left_h = Math.hypot(bl.x - tl.x, bl.y - tl.y);
|
||||
const rgt_h = Math.hypot(br.x - tr.x, br.y - tr.y);
|
||||
const raw_w = Math.max(top_w, bot_w);
|
||||
const raw_h = Math.max(left_h, rgt_h);
|
||||
const scale = Math.min(1.0, 1024 / Math.max(raw_w, raw_h));
|
||||
return {
|
||||
bin_w: Math.round(Math.max(48, raw_w * scale)),
|
||||
bin_h: Math.round(Math.max(48, raw_h * scale)),
|
||||
};
|
||||
}
|
||||
|
||||
// Compute natural cell size from corner quadrilateral + grid dimensions
|
||||
export function compute_cell_size(corners, rows, cols) {
|
||||
const [tl, tr, br, bl] = corners;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user