README: add PDF attachments, maintenance menu, mv-sync build step, resizable pane, URL-based navigation, word-split search, grid highlights. future-plans: add render_field_value integrations, field types, PDF paging, inventory/grid URL state; update state variable list. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
167 lines
5.8 KiB
Markdown
167 lines
5.8 KiB
Markdown
# Electronics Inventory
|
|
|
|
A self-hosted web app for managing an electronics component collection. Track components, their
|
|
field values, physical storage locations, and visual panel/grid layouts.
|
|
|
|
## Features
|
|
|
|
### Components
|
|
Define reusable component types (e.g. Resistor, Capacitor, IC) with custom fields per component.
|
|
Components show up in inventory entries and can be navigated to directly from storage locations.
|
|
|
|
- Word-split search: searching `0603 res` matches `Resistor 0603`
|
|
- Selected component reflected in URL (`/components/:id`) — survives page refresh
|
|
- Resizable list pane (width persisted in localStorage)
|
|
- Duplicate button to quickly clone a component
|
|
- Field values sorted alphabetically, rendered centrally (units, URLs as links, extensible)
|
|
|
|
### Fields
|
|
Create custom field definitions (e.g. `resistance`, `capacitance`, `package`) with an optional
|
|
unit suffix. Unit is appended directly to the value with no space, so you can write `4k7` for a
|
|
resistance field with unit `Ω` and it displays as `4k7Ω`.
|
|
|
|
URL-like field values (beginning with `http://` or `https://`) are automatically rendered as
|
|
clickable links.
|
|
|
|
### Inventory
|
|
Log where things are stored. Each inventory entry links a component to a storage location.
|
|
Supported location types:
|
|
|
|
- **Grid cell** — a specific row/column in a named grid (e.g. drawer divider box), picked
|
|
visually with a graphical cell picker
|
|
- *(plain entries without a grid reference work too)*
|
|
|
|
Notes on inventory entries are per storage location (not per component).
|
|
|
|
### Grids
|
|
Model physical storage grids (drawer organizers, parts boxes, etc.).
|
|
|
|
1. Upload a photo of the grid
|
|
2. Set up corner points to map the image to a logical grid (corners can extend outside image bounds)
|
|
3. Define row/column counts
|
|
4. Click any cell to see what's stored there — component entries are links, middle-click opens in new tab
|
|
5. Each cell shows a green count badge of how many components reference it
|
|
6. Navigating to a grid from a component detail highlights and scrolls to the relevant cell
|
|
|
|
### PDF Attachments
|
|
Attach PDF datasheets or other documents to components.
|
|
|
|
- PDFs are stored with a sanitized human-readable filename derived from the display name
|
|
- Rename a PDF and the file on disk is also renamed, atomically (uses `renameat2 RENAME_NOREPLACE`
|
|
via `tools/mv-sync`)
|
|
- First-page thumbnails generated automatically via `pdftoppm` (poppler-utils) if available
|
|
- Multiple components can share the same PDF
|
|
- Click any thumbnail to open it full-size in a lightbox
|
|
|
|
### Templates (Name Formatters)
|
|
Write JavaScript formatter functions that generate smart display names for components based on their
|
|
field values. Example:
|
|
|
|
```js
|
|
(c) => {
|
|
const F = c.fields;
|
|
if (F.resistance && F.imperial_package) {
|
|
return `${F.resistance}Ω ${F.imperial_package}`;
|
|
}
|
|
}
|
|
```
|
|
|
|
Fields are accessed by name (e.g. `c.fields.resistance`). If the formatter returns a non-empty
|
|
string it's used as the display name; otherwise the component's base name is used as fallback.
|
|
Multiple formatters are tried in order.
|
|
|
|
The template editor includes a test data box so you can preview the output without needing real
|
|
inventory data.
|
|
|
|
### Maintenance
|
|
A ⚙ menu in the top-right corner provides maintenance operations:
|
|
|
|
- **Generate missing PDF thumbnails** — scans all PDFs and generates thumbnails for any that
|
|
don't have one yet (useful if `pdftoppm` was unavailable at upload time)
|
|
|
|
## Requirements
|
|
|
|
- Node.js >= 25
|
|
- npm
|
|
- `gcc` (to build `tools/mv-sync` — only needed once)
|
|
- `pdftoppm` from poppler-utils (optional, for PDF thumbnails)
|
|
|
|
## Install
|
|
|
|
Install directly from the git repository — there is no npm package yet:
|
|
|
|
```bash
|
|
npm install git+https://gitea.efforting.tech/mikael-lovqvists-claude-agent/electronics-inventory.git
|
|
```
|
|
|
|
Or clone if you prefer to keep the source around:
|
|
|
|
```bash
|
|
git clone https://gitea.efforting.tech/mikael-lovqvists-claude-agent/electronics-inventory
|
|
cd electronics-inventory
|
|
npm install
|
|
```
|
|
|
|
## Build native tools
|
|
|
|
```bash
|
|
cd tools && make
|
|
```
|
|
|
|
This compiles `mv-sync`, a small helper that performs an atomic rename-without-overwrite using
|
|
`renameat2(RENAME_NOREPLACE)` (Linux 3.15+). It is required for PDF file operations.
|
|
|
|
## Run
|
|
|
|
```bash
|
|
npm start
|
|
```
|
|
|
|
The server starts on port 3020, bound to `localhost` only, by default.
|
|
Open [http://localhost:3020](http://localhost:3020) in your browser.
|
|
|
|
Both can be overridden with environment variables:
|
|
|
|
```bash
|
|
PORT=8080 npm start # different port
|
|
BIND_ADDRESS=0.0.0.0 npm start # all interfaces (LAN accessible)
|
|
BIND_ADDRESS=192.168.1.50 npm start # specific interface
|
|
PORT=8080 BIND_ADDRESS=0.0.0.0 npm start # both
|
|
```
|
|
|
|
> **Note:** The default `localhost` binding means the app is only reachable from the same machine.
|
|
> Set `BIND_ADDRESS=0.0.0.0` to expose it on your local network.
|
|
|
|
## Data Storage
|
|
|
|
All data is stored locally in a `data/` directory created automatically on first run:
|
|
|
|
- `data/db.json` — component, field, inventory, grid, template, and PDF records (flat KV store)
|
|
- `data/images/` — uploaded source images and component/inventory photos
|
|
- `data/pdfs/` — uploaded PDF files and their thumbnails
|
|
|
|
No external database is required.
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
server.mjs Express 5 API server + SPA host
|
|
lib/
|
|
storage.mjs Server-side KV store wrappers
|
|
kv-store.mjs JSON file-backed key-value store
|
|
ids.mjs ID generation
|
|
grid-image.mjs Grid image processing helpers
|
|
tools/
|
|
mv-sync.c Atomic rename helper (renameat2 RENAME_NOREPLACE)
|
|
Makefile
|
|
public/
|
|
app.mjs Single-page app (vanilla JS ES modules)
|
|
templates.html HTML templates (lazy-loaded)
|
|
style.css Styles
|
|
lib/
|
|
api.mjs Fetch wrappers for the REST API
|
|
dom.mjs DOM helpers
|
|
views/
|
|
grid-setup.mjs Canvas-based grid corner editor
|
|
```
|