Files
phone-barcode/plan.md

149 lines
4.4 KiB
Markdown

# Phone Barcode Scanner — Library Refactor Plan
## Goal
Refactor the current monolithic app into a reusable library that can be embedded
as a feature in other applications, with a clean API and self-contained UI.
## Project Structure
```
phone-barcode/
lib/
scanner.mjs — camera, decode pipeline, ZXing wrapper (no DOM)
scanner-ui.mjs — clones template node into a container, wires controls
template.html — UI markup fragment (no <html>/<body>, just the widget)
style.css — scoped styles for the widget
index.mjs — public API (re-exports)
vendor/
zxing.min.js — bundled, populated by `make build`
demo/
index.html — demo app, built as a consumer of the library
app.mjs
Makefile
package.json
```
## API
### Low-level: bring your own container
```javascript
import { Barcode_Scanner } from './phone-barcode/index.mjs';
const scanner = new Barcode_Scanner(container_el, {
mode: 'continuous', // 'continuous' | 'single' | 'aim-tap'
on_scan(text, format) {}, // called on each successful decode
on_close() {}, // called when user dismisses
});
await scanner.start();
scanner.stop();
```
The scanner clones the UI template into `container_el` and takes it over.
Caller is responsible for showing/hiding the container.
### High-level: managed dialog
```javascript
import { scan_once, scan_continuous } from './phone-barcode/index.mjs';
// Creates a <dialog>, runs scanner, resolves on first scan, cleans up
const result = await scan_once();
// → { text: '...', format: 'CODE_128' } | null (if dismissed)
// Same but keeps scanning; on_scan called for each result
const handle = await scan_continuous({ on_scan(text, format) {} });
handle.close();
```
Both functions create a `<dialog>` internally, inject the scanner, and manage
the full lifecycle. They accept an optional `dialog_el` parameter if the caller
wants to provide their own dialog element instead.
## Template Loading
The UI template (`template.html`) is a plain HTML fragment — no `<html>` or
`<body>`, just the widget markup.
Two ways to get the template node:
```javascript
// 1. Library fetches it (convenience)
import { load_template } from './phone-barcode/index.mjs';
const tmpl = await load_template(); // fetches template.html relative to lib
// 2. Caller provides it (e.g. already in the DOM, or loaded differently)
const tmpl = document.getElementById('barcode-scanner-template');
const scanner = new Barcode_Scanner(container, { template: tmpl });
```
`Barcode_Scanner` accepts either a `<template>` element or a plain node. If no
`template` option is given, it calls `load_template()` automatically.
## Scan Modes
| Mode | Behaviour |
|--------------|--------------------------------------------------------|
| `continuous` | Scans forever, fires `on_scan` for each new code |
| `single` | Resolves/fires once then stops; UI shows result briefly |
| `aim-tap` | Scanning paused; user taps shutter button to attempt |
## ZXing Bundling
`vendor/zxing.min.js` is committed to the repo (populated by `make build`).
The library loads it via a `<script>` tag injected into the document if
`window.ZXing` is not already present, so the caller does not need to think
about it.
## Styling
`style.css` uses a scoped class prefix (`bcs-`) on all elements to avoid
collisions with host app styles. The host can override via CSS custom properties:
```css
.bcs-root {
--bcs-accent: #00e87a;
--bcs-bg: #0a0c0f;
}
```
## Code Structure
All stateful components must be classes, not bare module-level variables. The
current `app.mjs` has scanner state (`running`, `last_decoded_text`, `fps`,
etc.) scattered as module globals — this needs to be encapsulated in
`Barcode_Scanner` before the library refactor proceeds. Module scope is only
for constants and exports.
## Lifecycle
```
Barcode_Scanner.start()
→ inject template into container
→ inject ZXing if needed
→ start camera (auto-select main back camera)
→ start decode loop
Barcode_Scanner.stop()
→ stop camera tracks
→ cancel decode loop
→ remove injected nodes from container
```
## Demo App
`demo/` contains a minimal page that exercises both the low-level and
high-level APIs — a persistent embedded scanner plus a "Scan once" button
that opens the managed dialog.