Bin types store a name, physical W×H in mm, and optional description.
When editing a bin, a type can be selected from a dropdown; this
pre-fills and locks the dimension inputs. Custom dimensions remain
available when no type is selected.
- lib/storage.mjs: bin type CRUD with bt: prefix
- server.mjs: /api/bin-types CRUD routes; type_id accepted on bin
create/update routes; DELETE protected if any bin references the type;
type dims copied onto bin when type_id is set
- public/lib/api.mjs: bin type wrappers; rename_bin → update_bin (accepts
any fields)
- public/templates.html: Types tab in bins section; t-bin-type-row;
t-dialog-bin-type; type selector in bin editor dialog
- public/app.mjs: all_bin_types state loaded at startup; render_bin_types_list();
open_bin_type_dialog(); type selector in open_bin_editor(); /bins/types routing
- public/style.css: bin types list styles
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When de-perspectiving a bin photo taken at an angle, inferring the
output size from the quadrilateral shape squishes the result. Entering
real-world W×H (mm) lets the server use the correct aspect ratio,
scaled to the same resolution as the inferred size.
- Bin editor dialog: W×H number inputs, pre-filled from saved phys_w/phys_h
- PUT /api/bins/:id/corners: accepts optional phys_w/phys_h; when provided,
derives bin_w/bin_h from the physical aspect ratio at equivalent area
- phys_w/phys_h stored on the bin record for re-use on next edit
- future-plans.md: bin types note (reusable dimensions per model)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
showModal() must be called before load_image() so the canvas has its
correct layout dimensions when parentElement.clientWidth is read.
Calling it after caused css_w to be computed against an unlaid-out
dialog, making drawn handle positions not match hit-test positions.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New top-level nav section showing all source images in a list view
with checkboxes to edit the uses array (grid, bin) per image. Allows
correcting wrongly-tagged images without code changes.
Server PUT /api/source-images/:id was already in place; re-added the
frontend API wrapper that was prematurely removed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removing toggle interactivity from use badges — they were confusing
and the wrong place to manage uses. The uses array is now managed
automatically by upload context. Badges are plain spans.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bins section now mirrors the grids section with two tabs:
- Bins: gallery of processed bin records
- Sources: source images tagged with uses=['bin'], with upload and
'+ Bin' button to create a bin record from an existing source image
Server: POST /api/bins/from-source accepts source_id, creates bin
record and adds 'bin' to the source image's uses array.
URL state: /bins → bins tab, /bins/sources → sources tab.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Inactive badges (uses not present on a source image) are now hidden
rather than shown at low opacity, which was visually noisy and
confusing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
Drag indices 4-7 correspond to top/right/bottom/left edge midpoints.
Dragging a midpoint applies the delta to both adjacent corners, making
it easier to align bins with rounded corners where corner handles may
be obscured.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Upload dialog now has distinct display name + filename fields, both pre-filled
from the uploaded file but independently editable
- Rename in file picker shows and edits both display name and filename separately
- Filename conflict checked against both KV store and disk (via rename_no_replace)
- Display name and filename are fully independent — no longer derived from each other
- Add find_pdf_references() helper in storage.mjs for future use
- CSS: fp-name-wrap shows display name + dim monospace filename below it;
rename mode stacks two inputs; fp-field-label for upload form labels
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix MV_SYNC path: ../tools resolved to /workspace/tools, should be ./tools
relative to server.mjs entry point
- Pre-populate display name input when file is selected (strips .pdf extension),
only if the field is currently empty so manual edits are not overwritten
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Components URL reflects selected component (/components/:id), survives refresh
- Word-split search: "0603 res" matches "Resistor 0603"
- Left pane resizable with localStorage persistence
- Field values rendered via central render_field_value() (units, URLs, extensible)
- Fields sorted alphabetically in both detail view and edit dialog
- Edit component dialog widened; field rows use shared grid columns (table-like)
- No space between value and unit (supports prefix suffixes like k, M, µ)
- Grid viewer highlights and scrolls to cell when navigating from component detail
- Cell inventory overlay items are <a> tags — middle-click opens in new tab
- PDF files stored with sanitized human-readable filename, not random ID
- PDF rename also renames file on disk
- Atomic rename via renameat2(RENAME_NOREPLACE) through tools/mv-sync
- Fix .cell-thumb-link → .cell-thumb-preview (div, not anchor), cursor: zoom-in
- Fix field name overflow in detail view (auto column width, overflow-wrap)
- Fix link color: use --accent instead of browser default dark blue
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Labels now overlay image with gradient background and visible colour.
Green badge top-right shows number of inventory entries for that cell.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Click a cell in the visual grid to select it. Cell images shown where
available. Selected cell highlighted with accent border.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Click any thumbnail to open full-size in overlay. Click backdrop or
press Escape to close. PDF thumbs now clickable too.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Generated at upload time, stored alongside the PDF in data/pdfs/.
Shown in the file picker (48px) and component detail view (80px).
Gracefully skipped if pdftoppm is unavailable.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
body was silently clipping all horizontal overflow. min-width:0 on
#main prevents flex child from expanding beyond container.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cells are sized in CSS px to fit at load time. Browser zoom now scales
everything uniformly; grid scrolls horizontally instead of reflowing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Images and labels scale together with a range slider (40–300px).
Grid scrolls horizontally when cells exceed viewport width.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Grid-type inventory entries now display the warped cell image from the
grid as a read-only thumbnail (highlighted with accent border) alongside
any user-uploaded images for that entry.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Enter a JS snippet returning a fields object (e.g. return { resistance: '10k' })
to preview the formatter against synthetic data instead of the first real component.
Both the formatter and test data textareas update the preview on input.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
c.fields was keyed by generated field IDs, so c.fields?.resistance
was always undefined. Now fields are remapped by name before being
passed to formatters, so the documented API works as expected.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Templates section:
- Define JS formatter functions per template (e.g. resistor, capacitor)
- First non-null result from any formatter is used as display name
- Live preview in template editor against first component
- Display names applied in component list, detail view, and inventory rows
Grid navigation:
- Grid-type inventory entries in component detail view show a '⊞' button
to navigate directly to that grid's viewer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Component dialog: 'New...' button next to field selector creates a
new field definition and immediately adds it to the component form
- Inventory dialog: same pattern for component selector
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New 'grid' location type on inventory entries (grid_id, grid_row, grid_col)
- Clicking a grid cell shows a popup with what's stored there
- Popup has '+ Add entry' pre-filled with the cell coordinates
- Inventory dialog: 'New...' button next to component selector opens
component creation dialog on top, returns with new component selected
- Grid entries display as e.g. 'Black Component Box R3C5' in lists
- Store original filename on source image upload
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>