@alphinex/printing
v1.1.1
Published
Thermal (58mm/80mm) and A4 print rendering, barcode/QR generation, and the usePrint() hook. See ADR-0008.
Readme
@alphinex/printing
Thermal (58mm/80mm) and A4 print rendering, barcode/QR generation, the usePrint() hook, and
direct ESC/POS hardware printing over WebUSB/Web Serial.
<PrintFrame> + usePrint()
import { PrintFrame, usePrint } from "@alphinex/printing";
function InvoicePrintButton({
invoiceNumber,
children,
}: {
invoiceNumber: string;
children: React.ReactNode;
}) {
const { print } = usePrint({ documentTitle: `Invoice-${invoiceNumber}` });
return (
<>
<button onClick={print}>Print</button>
<PrintFrame paperSize="a4">{children}</PrintFrame>
</>
);
}<PrintFrame> hides everything else on the page under @media print and sizes the @page box
to paperSize ("a4" | "80mm" | "58mm"). usePrint() just triggers window.print() and
optionally swaps document.title for the print job's default filename.
Known constraint: the visibility rule is class-based (.aui-print-frame), not scoped per
instance. If more than one <PrintFrame>/<PrintLayout> is mounted at the same time, printing
will make all of them visible at once, overlapping. Only mount one active print frame at a
time (e.g. behind a tab/toggle for "which document is currently the print target").
<Barcode> / <QRCode>
import { Barcode, QRCode } from "@alphinex/printing";
<Barcode value="4006381333931" format="EAN13" />;
<QRCode value="https://alphinex.example/invoice/1042" size={96} />;Both render pure SVG (no canvas/raster assets) via jsbarcode/qrcode-generator — see
ADR-0008 for why those two
libraries were chosen.
Direct hardware printing: EscPosBuilder + useThermalPrinter
A second, independent printing path from <PrintFrame>/usePrint() — direct byte-level ESC/POS
output to real thermal-printer hardware, for receipts needing a cut command, cash-drawer kick, or
narrower control than a full-page browser print gives you.
import {
EscPosBuilder,
MockTransport,
useThermalPrinter,
WebUSBTransport,
} from "@alphinex/printing";
import { useMemo } from "react";
function ReceiptPrintButton() {
// Swap MockTransport for WebUSBTransport/WebSerialTransport against real hardware.
const transport = useMemo(() => new WebUSBTransport({ vendorId: 0x04b8 }), []);
const printer = useThermalPrinter({ transport });
const printReceipt = async () => {
if (printer.status !== "connected") {
await printer.connect();
}
const bytes = new EscPosBuilder()
.initialize()
.align("center")
.bold(true)
.line("RECEIPT")
.bold(false)
.align("left")
.line("1x Widget — $9.99")
.feed(2)
.cut()
.build();
await printer.print(bytes);
};
return <button onClick={printReceipt}>Print receipt</button>;
}WebUSBTransport/WebSerialTransport/MockTransport all implement the same
ThermalPrinterTransport interface (connect/write/disconnect), so useThermalPrinter
never branches on which one is in use — use MockTransport in your own tests and demos.
Important — hardware verification gap: WebUSBTransport and WebSerialTransport are
implemented against the published WebUSB/Web Serial specs and unit-tested for the exact API call
sequence and bytes written, but have not been verified against real thermal-printer hardware
in this environment (no physical device available). Test against your actual printer before
shipping — see ADR-0014 for
the full rationale and the trigger for closing this gap. WebUSB/Web Serial themselves are
Chromium-only browser APIs (not Firefox/Safari as of this writing).
EscPosBuilder's text() encodes via TextEncoder (UTF-8) — it does not select or convert to a
printer-specific codepage. ASCII text prints correctly on any codepage; non-ASCII characters may
not render as expected depending on the printer's default codepage.
See documentation/PRODUCT-RESEARCH-MASTER-PLAN.md §8.3 for the full package contract.
