Files
phone-barcode/plan.md
mikael-lovqvists-claude-agent 730cbf5b1b Add library refactor plan
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>
2026-03-21 23:51:22 +00:00

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.