Frontend of my app that searches through all the documents (pdf, mostly) I have scanned or received
  • JavaScript 85%
  • CSS 10.4%
  • Python 3.8%
  • HTML 0.7%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Nicolas de Jong e4e53e172d Add deploy wrapper and deploy documentation
`./deploy.sh` (no arguments) bumps the minor version and ships the
static files to the Mac Mini, where nginx serves them at
https://rutilo.nl/scanbox/static/. See ~/dev/ops/deploy-runbook.md
§Frontends for the mechanism.

The CLAUDE.md section records the two things this project must keep
satisfying now that it is served from a subpath rather than the domain
root: asset paths stay relative, and the API base needs the /scanbox
prefix. Both httpService.js and sw.js still hardcode a bare /api, which
is noted there rather than changed in this ops-focused commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 14:07:46 +02:00
.claude Initial version 2026-08-23 07:42:47 +02:00
src Webworkers, cache and reconnection 2026-08-30 21:00:01 +02:00
test Webworkers, cache and reconnection 2026-08-30 21:00:01 +02:00
tools Webworkers, cache and reconnection 2026-08-30 21:00:01 +02:00
vendor/pdfjs PDF rendering 2026-08-25 22:09:28 +02:00
.gitignore Webworkers, cache and reconnection 2026-08-30 21:00:01 +02:00
API-CONTRACT.md Webworkers, cache and reconnection 2026-08-30 21:00:01 +02:00
apple-touch-icon-source.svg Added iOS homescreen icon 2026-08-25 07:03:43 +02:00
apple-touch-icon.png Added iOS homescreen icon 2026-08-25 07:03:43 +02:00
CLAUDE.md Add deploy wrapper and deploy documentation 2026-09-02 14:07:46 +02:00
CONFORMANCE-HISTORY.md Webworkers, cache and reconnection 2026-08-30 21:00:01 +02:00
CONFORMANCE.md Webworkers, cache and reconnection 2026-08-30 21:00:01 +02:00
deploy.sh Add deploy wrapper and deploy documentation 2026-09-02 14:07:46 +02:00
favicon.svg Initial version 2026-08-23 07:42:47 +02:00
icon-192.png Webworkers, cache and reconnection 2026-08-30 21:00:01 +02:00
icon-512.png Webworkers, cache and reconnection 2026-08-30 21:00:01 +02:00
index.html Webworkers, cache and reconnection 2026-08-30 21:00:01 +02:00
manifest.webmanifest Webworkers, cache and reconnection 2026-08-30 21:00:01 +02:00
package.json Initial version 2026-08-23 07:42:47 +02:00
README.md PDF rendering 2026-08-25 22:09:28 +02:00
serve.py Webworkers, cache and reconnection 2026-08-30 21:00:01 +02:00
SERVER-CONFORMANCE.md Webworkers, cache and reconnection 2026-08-30 21:00:01 +02:00
SPEC.md Webworkers, cache and reconnection 2026-08-30 21:00:01 +02:00
sw.js Webworkers, cache and reconnection 2026-08-30 21:00:01 +02:00
TASKS.md Labels with equal count are ordered alphabetically, except numbers which are ordered high to low. 2026-08-26 21:58:22 +02:00

ScanBox frontend

Client for ScanBox, an application giving access to a decades-long archive of scanned and received documents: finding them by date and label, browsing them visually, and reading them.

What it covers and why is in SPEC.md. This file is only about working with this repository.

Status

Early, and running. Searching, the label area, the results grid, the unparseable-documents view and the viewer work against the mock. The viewer shows a document's own content where the mock has real bytes behind a document — a multi-page document is stepped through page by page, using the same slide gesture that steps between documents — and falls back to the generated thumbnail for every other document. There is no backend — finding out what its endpoints should be is the point of building the frontend first.

Not built yet: locked searches (deferred) and overviews.

Running

python3 serve.py 8430

Then open http://localhost:8430. serve.py sends no-store, so a refresh always picks up what is on disk — plain http.server caches ES modules hard enough to survive a reload. Port 8430 rather than 8420 because the sister project CasaFluxFE uses 8420.

npm test

Runs the logic tests under node --test. No browser and no network involved.

npm run snapshot

Regenerates the mock's data from the archive. Reads filenames only, and never writes to the archive.

Documents

File Job
SPEC.md What the client does, and why each choice was made
CONFORMANCE.md Client rules, numbered CF-, derived from SPEC.md
CONFORMANCE-HISTORY.md Older revision notes for the above
API-CONTRACT.md What goes over the wire — shared with the backend
SERVER-CONFORMANCE.md What the backend must guarantee, numbered CFS-
CLAUDE.md How to work on this repository

SPEC.md and the conformance documents are implementation-agnostic on purpose: they should hold for a rewrite in another framework, or a native app. Anything that is true only of this web client belongs here.

Stack

Vanilla JavaScript, no build step, no dependencies at all so far. Libraries are added only where they earn their place — PDF rendering being the one where that is already clear. JSDoc annotations with tsc --checkJs --noEmit where type checking is useful.

Logic with no DOM access — query parsing and evaluation, filename parsing, label ranking and typo tolerance — lives in src/lib and is tested under node --test. That is where the correctness risk is and none of it needs a browser.

Directory Holds
src/lib Logic with no DOM access, and the data-service choice
src/mock The mock service, its filename snapshot, and generated thumbnails
src/ui The pill, search field, label area, results grid and viewer
test node --test suites, named after the requirements they exercise
tools makeSnapshot.js, which reads the archive

The label pill is a custom element (<scanbox-pill>). A pill is exactly what custom elements are good at, and the alternative was a framework that would have had to earn its place against one small file.

The archive

The documents live in the Scans directory named in the global ~/.claude/CLAUDE.md, and that directory has a CLAUDE.md of its own describing the naming convention, the filing workflow and the folder map. It is the authority on the data; read it before reasoning about what a document or a label is.

It is read-only from this repository. Nothing here writes to it. Mutation happens in a session aimed at the archive itself, or eventually through the ScanBox backend.

Reading it to ground a decision is wanted, though — the measurements in SPEC.md §What the data actually looks like came from there and contradicted the first draft of the design more than once. When the question is "how many labels are there really", count rather than guess:

find ~/Jottacloud/Scans -type f -not -path '*/bak/*' -exec basename {} \; | grep -E '^[0-9]{8}_' | sed -E 's/^[0-9]{8}_//; s/\.[^.]+$//' | tr '_' '\n' | sort | uniq -c | sort -rn

Backend

There is no backend yet. The client runs against a mock data service, and the mock is permanent — it stays after the backend arrives, for testing and for interface work that should not depend on a live archive. It also carries the scenarios that make the conformance rules checkable.

There is one data service with two implementations, mock and real HTTP, and nothing above that service knows which it is talking to. A component reaching for the mock directly would have to be changed twice: once to use the real service, and once more the next time somebody wanted to debug without one.

The backend will be Java, built on LabelDB — which is where the query syntax in API-CONTRACT.md §3 comes from, and where the per-label result counts that make the label sidebar work come from essentially for free.