Documents the intended API, template loading strategy, scan modes, ZXing bundling approach, and project structure for the reusable barcode scanner library. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4.0 KiB
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
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
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:
// 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:
.bcs-root {
--bcs-accent: #00e87a;
--bcs-bg: #0a0c0f;
}
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.